昨天有人问怎么用RichTextBox做语法高亮
这就需要用到语法分析
还有很多论坛里也支持语法高亮的功能、代码折叠的功能
这也算是一个常用的功能
所以就随手搭建一个语法分析的框架
思路是利用正则表达式逐步取得各种语法构成部分这里欢迎大家测试、找BUG、完善正则表达式
谢谢参与
初步代码如下:
/// <summary>
/// 语法分析引擎基类
/// </summary>
class SyntaxEngine
{
/// <summary>
/// 语法项
/// </summary>
public class SyntaxItem
{
#region 私有字段
private string FPattern; // 正则表达式
private RegexOptions FOptions; // 正则表达式附加选项
private string FName; // 语法名称
private int FIndex; // 序号
#endregion 私有字段 #region 公有属性
public string Pattern { get { return FPattern; } } // 正则表达式
public RegexOptions Options { get { return FOptions; } } // 正则表达式附加选项
public string Name { get { return FName; } } // 名称
public int Index { get { return FIndex; } } // 序号
#endregion 公有属性 public SyntaxItem(string APattern, RegexOptions AOptions,
string AName, int AIndex)
{
FPattern = APattern;
FOptions = AOptions;
FName = AName;
FIndex = AIndex;
}
}
/// <summary>
/// 语法分析返回项
/// </summary>
public class AnalyzeReslut
{
#region 私有字段
private SyntaxItem FItem; // 所属语法项
private string FBlock; // 文字块
#endregion 私有字段 #region 公有属性
public SyntaxItem Item { get { return FItem; } }
public string Block { get { return FBlock; } }
#endregion 公有属性 public AnalyzeReslut(SyntaxItem AItem, string ABlock)
{
FItem = AItem;
FBlock = ABlock;
}
} private List<SyntaxItem> FSyntaxItems = new List<SyntaxItem>();
private List<AnalyzeReslut> FAnalyzeResluts = new List<AnalyzeReslut>(); public List<SyntaxItem> SyntaxItems { get { return FSyntaxItems; } }
public List<AnalyzeReslut> AnalyzeResluts { get { return FAnalyzeResluts; } } /// <summary>
/// 进行语法分析
/// </summary>
/// <param name="ACode">所分析的代码</param>
/// <returns>返回分析是否成功</returns>
public virtual bool Analyze(string ACode)
{
if (FSyntaxItems.Count <= 0) return false;
if (ACode == null) return false;
AnalyzeResluts.Clear();
string vCode = ACode;
bool vFind = true;
while (vFind && (vCode.Length > 0))
{
vFind = false;
foreach (SyntaxItem vSyntaxItem in FSyntaxItems)
{
if (Regex.IsMatch(vCode, vSyntaxItem.Pattern, vSyntaxItem.Options))
{
AnalyzeResluts.Add(new AnalyzeReslut(vSyntaxItem,
Regex.Match(vCode, vSyntaxItem.Pattern,
vSyntaxItem.Options).Value));
vCode = Regex.Replace(vCode, vSyntaxItem.Pattern, "",
vSyntaxItem.Options);
vFind = true;
break;
}
}
}
return true;
}
}class CSharpEngine : SyntaxEngine
{
public CSharpEngine()
{
SyntaxItems.Add(new SyntaxItem(@"^\s+", RegexOptions.None,
"空白", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^\/\/[^\n]*[\n]?", RegexOptions.None,
"单行注释", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^\/\*[^\*]*\*\/", RegexOptions.None,
"多行注释", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^#\s*(define|elif|else|endif|endregion|" +
@"error|if|line|pragma|region|undef|using|warning)\s*", RegexOptions.None,
"指令", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^(abstract|event|new|struct|as|explicit|" +
@"null|switch|base|extern|object|this|bool|false|operator|throw|break|" +
@"finally|out|true|byte|fixed|override|try|case|float|params|typeof|" +
@"catch|for|private|uint|char|foreach|protected|ulong|checked|goto|" +
@"public|unchecked|class|if|readonly|unsafe|const|implicit|ref|ushort|" +
@"continue|int|return|using|decimal|in|sbyte|virtual|default|interface|" +
@"sealed|volatile|delegate|internal|short|void|do|is|sizeof|while|" +
@"double|lock|stackalloc|else|long|static|enum|namespace|string)\s*",
RegexOptions.None, "关键字", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^(get|partial|set|value|where|yield)\s*",
RegexOptions.None, "上下文关键字", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(
@"^[\~\!\@\#\$\%\^\&\*\(\)_\+\-\=\{\}\[\]\\\|\;\\:\<\>\?\,\.\/]+",
RegexOptions.None, "标点符号", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^\d+", RegexOptions.None,
"整数", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^0x[\da-f]+", RegexOptions.IgnoreCase,
"十六进制数", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^(\@)?\""[^\""]*\""", RegexOptions.None,
"字符串", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^\'[^\']*\'", RegexOptions.None,
"字符", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^\w*", RegexOptions.None,
"标识符", SyntaxItems.Count));
}
}
这就需要用到语法分析
还有很多论坛里也支持语法高亮的功能、代码折叠的功能
这也算是一个常用的功能
所以就随手搭建一个语法分析的框架
思路是利用正则表达式逐步取得各种语法构成部分这里欢迎大家测试、找BUG、完善正则表达式
谢谢参与
初步代码如下:
/// <summary>
/// 语法分析引擎基类
/// </summary>
class SyntaxEngine
{
/// <summary>
/// 语法项
/// </summary>
public class SyntaxItem
{
#region 私有字段
private string FPattern; // 正则表达式
private RegexOptions FOptions; // 正则表达式附加选项
private string FName; // 语法名称
private int FIndex; // 序号
#endregion 私有字段 #region 公有属性
public string Pattern { get { return FPattern; } } // 正则表达式
public RegexOptions Options { get { return FOptions; } } // 正则表达式附加选项
public string Name { get { return FName; } } // 名称
public int Index { get { return FIndex; } } // 序号
#endregion 公有属性 public SyntaxItem(string APattern, RegexOptions AOptions,
string AName, int AIndex)
{
FPattern = APattern;
FOptions = AOptions;
FName = AName;
FIndex = AIndex;
}
}
/// <summary>
/// 语法分析返回项
/// </summary>
public class AnalyzeReslut
{
#region 私有字段
private SyntaxItem FItem; // 所属语法项
private string FBlock; // 文字块
#endregion 私有字段 #region 公有属性
public SyntaxItem Item { get { return FItem; } }
public string Block { get { return FBlock; } }
#endregion 公有属性 public AnalyzeReslut(SyntaxItem AItem, string ABlock)
{
FItem = AItem;
FBlock = ABlock;
}
} private List<SyntaxItem> FSyntaxItems = new List<SyntaxItem>();
private List<AnalyzeReslut> FAnalyzeResluts = new List<AnalyzeReslut>(); public List<SyntaxItem> SyntaxItems { get { return FSyntaxItems; } }
public List<AnalyzeReslut> AnalyzeResluts { get { return FAnalyzeResluts; } } /// <summary>
/// 进行语法分析
/// </summary>
/// <param name="ACode">所分析的代码</param>
/// <returns>返回分析是否成功</returns>
public virtual bool Analyze(string ACode)
{
if (FSyntaxItems.Count <= 0) return false;
if (ACode == null) return false;
AnalyzeResluts.Clear();
string vCode = ACode;
bool vFind = true;
while (vFind && (vCode.Length > 0))
{
vFind = false;
foreach (SyntaxItem vSyntaxItem in FSyntaxItems)
{
if (Regex.IsMatch(vCode, vSyntaxItem.Pattern, vSyntaxItem.Options))
{
AnalyzeResluts.Add(new AnalyzeReslut(vSyntaxItem,
Regex.Match(vCode, vSyntaxItem.Pattern,
vSyntaxItem.Options).Value));
vCode = Regex.Replace(vCode, vSyntaxItem.Pattern, "",
vSyntaxItem.Options);
vFind = true;
break;
}
}
}
return true;
}
}class CSharpEngine : SyntaxEngine
{
public CSharpEngine()
{
SyntaxItems.Add(new SyntaxItem(@"^\s+", RegexOptions.None,
"空白", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^\/\/[^\n]*[\n]?", RegexOptions.None,
"单行注释", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^\/\*[^\*]*\*\/", RegexOptions.None,
"多行注释", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^#\s*(define|elif|else|endif|endregion|" +
@"error|if|line|pragma|region|undef|using|warning)\s*", RegexOptions.None,
"指令", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^(abstract|event|new|struct|as|explicit|" +
@"null|switch|base|extern|object|this|bool|false|operator|throw|break|" +
@"finally|out|true|byte|fixed|override|try|case|float|params|typeof|" +
@"catch|for|private|uint|char|foreach|protected|ulong|checked|goto|" +
@"public|unchecked|class|if|readonly|unsafe|const|implicit|ref|ushort|" +
@"continue|int|return|using|decimal|in|sbyte|virtual|default|interface|" +
@"sealed|volatile|delegate|internal|short|void|do|is|sizeof|while|" +
@"double|lock|stackalloc|else|long|static|enum|namespace|string)\s*",
RegexOptions.None, "关键字", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^(get|partial|set|value|where|yield)\s*",
RegexOptions.None, "上下文关键字", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(
@"^[\~\!\@\#\$\%\^\&\*\(\)_\+\-\=\{\}\[\]\\\|\;\\:\<\>\?\,\.\/]+",
RegexOptions.None, "标点符号", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^\d+", RegexOptions.None,
"整数", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^0x[\da-f]+", RegexOptions.IgnoreCase,
"十六进制数", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^(\@)?\""[^\""]*\""", RegexOptions.None,
"字符串", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^\'[^\']*\'", RegexOptions.None,
"字符", SyntaxItems.Count));
SyntaxItems.Add(new SyntaxItem(@"^\w*", RegexOptions.None,
"标识符", SyntaxItems.Count));
}
}
private void button1_Click(object sender, EventArgs ce)
{
CSharpEngine vCSharpEngine = new CSharpEngine();
vCSharpEngine.Analyze(textBox1.Text); textBox2.Clear();
foreach (CSharpEngine.AnalyzeReslut
vAnalyzeReslut in vCSharpEngine.AnalyzeResluts)
{
textBox2.AppendText(vAnalyzeReslut.Item.Name + ":" +
vAnalyzeReslut.Block);
}
}
-----------------------输入范例:-----------------------
// 排列组合
public void CombinationCollocate(List<string> AStrings, string ANumbers, string APath, int ALen)
{
if (APath.Length >= ALen)
{
AStrings.Add(APath);
return;
}
for (int i = 0; i < ANumbers.Length; i++)
{
string vNumbers = ANumbers.Remove(i, 1);
CombinationCollocate(AStrings, vNumbers, APath + ANumbers.Substring(i, 1), ALen);
}
} /* CombinationCollocate */-----------------------输出范例:-----------------------
单行注释:// 排列组合
关键字:public 关键字:void 标识符:CombinationCollocate标点符号:(标识符:List标点符号:<关键字:string标点符号:>空白: 标识符:AStrings标点符号:,空白: 关键字:string 标识符:ANumbers标点符号:,空白: 关键字:string 标识符:APath标点符号:,空白: 关键字:int 标识符:ALen标点符号:)空白:
标点符号:{空白:
关键字:if 标点符号:(标识符:APath标点符号:.标识符:Length空白: 标点符号:>=空白: 标识符:ALen标点符号:)空白:
标点符号:{空白:
标识符:AStrings标点符号:.标识符:Add标点符号:(标识符:APath标点符号:);空白:
关键字:return标点符号:;空白:
标点符号:}空白:
关键字:for 标点符号:(关键字:int 标识符:i空白: 标点符号:=空白: 整数:0标点符号:;空白: 标识符:i空白: 标点符号:<空白: 标识符:ANumbers标点符号:.标识符:Length标点符号:;空白: 标识符:i标点符号:++)空白:
标点符号:{空白:
关键字:string 标识符:vNumbers空白: 标点符号:=空白: 标识符:ANumbers标点符号:.标识符:Remove标点符号:(标识符:i标点符号:,空白: 整数:1标点符号:);空白:
标识符:CombinationCollocate标点符号:(标识符:AStrings标点符号:,空白: 标识符:vNumbers标点符号:,空白: 标识符:APath空白: 标点符号:+空白: 标识符:ANumbers标点符号:.标识符:Substring标点符号:(标识符:i标点符号:,空白: 整数:1标点符号:),空白: 标识符:ALen标点符号:);空白:
标点符号:}空白:
标点符号:}空白: 多行注释:/* CombinationCollocate */
1.1
0.1
.01
.1d
1u
1e1所以正则表达式也很难写...
Regex.Match(@"/* /*hello **\n\r */ /* */", @"^\/\*(?!\/\*|\*\/).*\*\/", RegexOptions.None).Value;怎么匹配成:/* /*hello **\n\r */ /* */
理想的是:/* /*hello **\n\r */谢谢关注,路过有分,100%结贴率
发现上面@"(int|in|else)\s*"不能达到目的
如果是"integer"也被匹配
现在改成这样,不知道有没有问题:
Regex.Match("in", @"^(int|in)(?!\w)").Value
主要是会出现如下情况
string s1 = "\"";
string s2 = @"""";
char c = '\''
即引号中有引号
1、匹配数字(包括浮点数、指数表达)
2、匹配字符串
3、匹配多行注释大家有时间帮我想想办法
我还得写一个语法高亮输出的引擎
支持rtf和html格式
敬请关注
{
public class HighlightItem
{
private Color FForeColor; // 前景色
private bool FBold; // 是否加粗
private bool FItalic; // 是否斜体
private bool FUnderline; // 是否下划线 public Color ForeColor { get { return FForeColor; } } // 前景色
public bool Bold { get { return FBold; } } // 是否加粗
public bool Italic { get { return FItalic; } } // 是否斜体
public bool Underline { get { return FUnderline; } } // 是否下划线
public HighlightItem(Color AForeColor, bool ABold, bool AItalic, bool AUnderline)
{
FForeColor = AForeColor;
FBold = ABold;
FItalic = AItalic;
FUnderline = AUnderline;
}
}
private List<SyntaxEngine.AnalyzeReslut> FAnalyzeResluts;
private Font FDefaultFont;
private List<HighlightItem> FHighlightItems = new List<HighlightItem>();
public List<HighlightItem> HighlightItems { get { return FHighlightItems; } }
public SyntaxHighlight(SyntaxEngine ASyntaxEngine, Font
ADefaultFont)
{
FAnalyzeResluts = ASyntaxEngine.AnalyzeResluts;
FDefaultFont = ADefaultFont;
} public string TextToRtf(string AText)
{
string vReturn = "";
foreach (char vChar in AText)
{
switch (vChar)
{
case '\\':
vReturn += @"\\";
break;
case '{':
vReturn += @"\{";
break;
case '}':
vReturn += @"\}";
break;
default:
if (vChar > (char)127)
vReturn += @"\u" + ((int)vChar).ToString() + "?";
else vReturn += vChar;
break;
}
}
return vReturn;
}
[DllImport("user32.dll")]
public static extern uint GetKBCodePage();
[DllImport("kernel32.dll")]
public static extern ushort GetSystemDefaultLangID();
public string MakeRtf()
{
if (HighlightItems.Count <= 0) return "";
string Result = @"{\rtf1\ansi\ansicpg" + GetKBCodePage().ToString() +
@"\deff0\deflang1033\deflangfe" + GetSystemDefaultLangID().ToString() +
@"{\fonttbl{\f0\fmodern " +
FDefaultFont.Name + ";}}\r\n";
Result += @"{\colortbl ;";
foreach (HighlightItem vHighlightItem in HighlightItems)
Result += string.Format(@"\red{0}\green{1}\blue{2};",
vHighlightItem.ForeColor.R, vHighlightItem.ForeColor.G,
vHighlightItem.ForeColor.B);
Result += "}\r\n";
Result += @"\viewkind4\uc1\pard\f0\fs20" + "\r\n";
bool vBold = false, vItalic = false, vUnderline = false;
foreach (SyntaxEngine.AnalyzeReslut vAnalyzeReslut in
FAnalyzeResluts)
{
int i = vAnalyzeReslut.Item.Index;
if (i >= HighlightItems.Count) i = 0;
if (vBold != HighlightItems[i].Bold)
{
if (HighlightItems[i].Bold)
Result += @"\b1";
else Result += @"\b0";
}
if (vItalic != HighlightItems[i].Italic)
{
if (HighlightItems[i].Italic)
Result += @"\i1";
else Result += @"\i0";
}
if (vItalic != HighlightItems[i].Underline)
{
if (HighlightItems[i].Underline)
Result += @"\ul1";
else Result += @"\ul0";
}
Result += string.Format(@"\cf{0} ", i + 1);
vBold = HighlightItems[i].Bold;
vItalic = HighlightItems[i].Italic;
vUnderline = HighlightItems[i].Underline;
Result += TextToRtf(vAnalyzeReslut.Block).Replace("\r\n",
"\r\n" + @"\par");
}
return Result + "}";
} public string MakeHtml()
{
string Result = "";
// TODO :
return Result;
}
}class CSharpHighlight : SyntaxHighlight
{
public CSharpHighlight(SyntaxEngine ASyntaxEngine, Font
ADefaultFont)
: base(ASyntaxEngine, ADefaultFont)
{
//空白
HighlightItems.Add(new HighlightItem(Color.White, false, false, false));
//单行注释
HighlightItems.Add(new HighlightItem(Color.Green, false, false, false));
//多行注释
HighlightItems.Add(new HighlightItem(Color.Green, false, false, false));
//指令
HighlightItems.Add(new HighlightItem(Color.Blue, false, false, false));
//关键字
HighlightItems.Add(new HighlightItem(Color.Black, true, false, false));
//上下文关键字
HighlightItems.Add(new HighlightItem(Color.Black, true, false, false));
//标点符号
HighlightItems.Add(new HighlightItem(Color.BlueViolet, false, false, false));
//整数
HighlightItems.Add(new HighlightItem(Color.Red, true, false, false));
//十六进制数
HighlightItems.Add(new HighlightItem(Color.Red, true, false, false));
//字符串
HighlightItems.Add(new HighlightItem(Color.Maroon, false, false, false));
//字符
HighlightItems.Add(new HighlightItem(Color.Maroon, false, false, false));
//标识符
HighlightItems.Add(new HighlightItem(Color.Black, false, false, false));
}
}
{\colortbl ;\red255\green255\blue255;\red0\green128\blue0;\red0\green128\blue0;\red0\green0\blue255;\red0\green0\blue0;\red0\green0\blue0;\red138\green43\blue226;\red255\green0\blue0;\red255\green0\blue0;\red128\green0\blue0;\red128\green0\blue0;\red0\green0\blue0;}
\viewkind4\uc1\pard\f0\fs20
\cf2 // \u25490?\u21015?\u32452?\u21512?
\par\b1\cf5 public\b0\cf1 \b1\cf5 void\b0\cf1 \cf12 CombinationCollocate\cf7 (\cf12 List\cf7 <\b1\cf5 string\b0\cf7 >\cf1 \cf12 AStrings\cf7 ,\cf1 \b1\cf5 string\b0\cf1 \cf12 ANumbers\cf7 ,\cf1 \b1\cf5 string\b0\cf1 \cf12 APath\cf7 ,\cf1 \b1\cf5 int\b0\cf1 \cf12 ALen\cf7 )\cf1
\par\cf7 \{\cf1
\par \b1\cf5 if\b0\cf1 \cf7 (\cf12 APath\cf7 .\cf12 Length\cf1 \cf7 >=\cf1 \cf12 ALen\cf7 )\cf1
\par \cf7 \{\cf1
\par \cf12 AStrings\cf7 .\cf12 Add\cf7 (\cf12 APath\cf7 );\cf1
\par \b1\cf5 return\b0\cf7 ;\cf1
\par \cf7 \}\cf1
\par \b1\cf5 for\b0\cf1 \cf7 (\b1\cf5 int\b0\cf1 \cf12 i\cf1 \cf7 =\cf1 \b1\cf8 0\b0\cf7 ;\cf1 \cf12 i\cf1 \cf7 <\cf1 \cf12 ANumbers\cf7 .\cf12 Length\cf7 ;\cf1 \cf12 i\cf7 ++)\cf1
\par \cf7 \{\cf1
\par \b1\cf5 string\b0\cf1 \cf12 vNumbers\cf1 \cf7 =\cf1 \cf12 ANumbers\cf7 .\cf12 Remove\cf7 (\cf12 i\cf7 ,\cf1 \b1\cf8 1\b0\cf7 );\cf1
\par \cf12 CombinationCollocate\cf7 (\cf12 AStrings\cf7 ,\cf1 \cf12 vNumbers\cf7 ,\cf1 \cf12 APath\cf1 \cf7 +\cf1 \cf12 ANumbers\cf7 .\cf12 Substring\cf7 (\cf12 i\cf7 ,\cf1 \b1\cf8 1\b0\cf7 ),\cf1 \cf12 ALen\cf7 );\cf1
\par \cf7 \}\cf1
\par\cf7 \}\cf1 \cf3 /* CombinationCollocate */\cf1
\par}
2d //带类型、整数(包括2f、2m)
00098 //0开头的
2.0 //小数
2.0d //带类型、小数
.1 //缺少整数、小数
.1d //缺少整数部分、带类型、小数
1e1d //整数、指数
1.2e1d //小数、指数
0x12 //十六进制数
0x12d //十六进制数、带类型
0x12e1d //十六进制数、指数、带类型这些数字出现的情况不知道全没?
@"^(\d+(?!\.|x|e|d)u?)|^0x([\da-f]+(?!\.|x)u?)"浮点数
@"^(\d+)?\.\d+(e\d+)?(m|d|f)?|^\d+(e\d+)?(m|d|f)"
正則加起來沒學到一天....
@"^""((\\\\)*(\\"")*(\\[a-z])*[^""^\\]*)*"""
被逼的
@"^(\d+)?\.\d+((\+|\-)?e\d+)?(m|d|f)?|^\d+((\+|\-)?e\d+)?(m|d|f)"关键字表达式改成这样:
@"^(get|partial|set|value|where|yield)\b"等我加入html输出、整理后共享给大家
我有别人用Delphi写的,RTF格式,基本上完美,而且可以自己定义语法规则。
XML注释能不能标记用灰色,描述才用绿色。
SyntaxItems.Add(new SyntaxItem(@"^\/\/\/[^\n]*[\n]?", RegexOptions.None,
"xml注释", SyntaxItems.Count));
//相应位置增加,xml注释
HighlightItems.Add(new HighlightItem(Color.Gray, false, false, false));