Cyjb.Compilers
1.0.2
See the version list below for details.
dotnet add package Cyjb.Compilers --version 1.0.2
NuGet\Install-Package Cyjb.Compilers -Version 1.0.2
<PackageReference Include="Cyjb.Compilers" Version="1.0.2" />
paket add Cyjb.Compilers --version 1.0.2
#r "nuget: Cyjb.Compilers, 1.0.2"
// Install Cyjb.Compilers as a Cake Addin #addin nuget:?package=Cyjb.Compilers&version=1.0.2 // Install Cyjb.Compilers as a Cake Tool #tool nuget:?package=Cyjb.Compilers&version=1.0.2
Cyjb.Compilers
提供编译相关功能,基于 .NET 6。
本项目包括一些与编译相关的功能,目前包含词法分析器和 LALR 语法分析器。
定义词法分析器
可以通过正则表达式来创建词法分析器,例如下面简单的构造一个数学算式的词法分析器:
enum Calc { Id, Add, Sub, Mul, Div, Pow, LBrace, RBrace }
Lexer<Calc> lexer = new Lexer<Calc>();
// 终结符的定义。
lexer.DefineSymbol("[0-9]+").Kind(Calc.Id).Action(c => c.Accept(double.Parse(c.Text)));
lexer.DefineSymbol("\\+").Kind(Calc.Add);
lexer.DefineSymbol("\\-").Kind(Calc.Sub);
lexer.DefineSymbol("\\*").Kind(Calc.Mul);
lexer.DefineSymbol("\\/").Kind(Calc.Div);
lexer.DefineSymbol("\\^").Kind(Calc.Pow);
lexer.DefineSymbol("\\(").Kind(Calc.LBrace);
lexer.DefineSymbol("\\)").Kind(Calc.RBrace);
// 吃掉所有空白。
lexer.DefineSymbol("\\s");
ILexerFactory<Calc> lexerFactory = lexer.GetFactory();
// 要分析的源文件。
string source = "1 + 20 * 3 / 4*(5+6)";
ITokenizer<Calc> tokenizer = lexerFactory.CreateTokenizer(source);
// 构造词法分析器。
foreach (Token<Calc> token in tokenizer)
{
Console.WriteLine(token);
}
词法分析器使用 DefineSymbol
方法定义终结符,使用的正则表达式的定义与 C# 正则表达式一致,但不包含定位点、捕获、Lookaround、反向引用、替换构造和替代功能。
正则表达式支持通过 /
指定向前看符号,支持指定匹配的上下文,并在执行动作时根据需要切换上下文。
如果前缀可以与多个正则表达式匹配,那么:
- 总是选择最长的前缀。
- 如果最长的可能前缀与多个正则表达式匹配,总是选择先定义的正则表达式。
支持使用 DefineContext
或 DefineInclusiveContext
方法定义上下文或包含型上下文,在声明符号时可以通过构造器的 Context
方法指定生效的上下文。
支持使用 DefineRegex
方法定义公共正则表达式,在声明符号时可以通过 foo{RegexName}bar
引用公共正则表达式。
支持在调用 GetFactory
方法生成工厂时,传入 rejectable
参数开启 Reject 动作的支持。
还可以通过设计时定义词法分析控制器来创建词法分析器,例如下面构造一个与上面相同的的词法分析器:
enum Calc { Id, Add, Sub, Mul, Div, Pow, LBrace, RBrace }
[LexerSymbol("\\+", Kind = Calc.Add)]
[LexerSymbol("\\-", Kind = Calc.Sub)]
[LexerSymbol("\\*", Kind = Calc.Mul)]
[LexerSymbol("\\/", Kind = Calc.Div)]
[LexerSymbol("\\^", Kind = Calc.Pow)]
[LexerSymbol("\\(", Kind = Calc.LBrace)]
[LexerSymbol("\\)", Kind = Calc.RBrace)]
[LexerSymbol("\\s")]
// 必须是部分类,且继承自 LexerController<Calc>
public partial class CalcLexer : LexerController<Calc>
{
/// <summary>
/// 数字的终结符定义。
/// </summary>
[LexerSymbol("[0-9]+", Kind = Calc.Id)]
public void DigitAction()
{
Accept(double.Parse(Text));
}
}
设计时词法分析器的使用方法请参见 Cyjb.Compilers.Design。
具体用法可以参考 TestCompilers/Lexers 目录下的示例。
定义语法分析器
可以通过语法产生式来创建词法分析器,例如下面简单的构造一个数学算式的语法分析器:
// 非终结符的定义。
Parser<Calc> parser = new();
// 定义产生式
parser.DefineProduction(Calc.E, Calc.Id).Action(c => c[0].Value);
parser.DefineProduction(Calc.E, Calc.E, Calc.Add, Calc.E)
.Action(c => (double) c[0].Value! + (double) c[2].Value!);
parser.DefineProduction(Calc.E, Calc.E, Calc.Sub, Calc.E)
.Action(c => (double) c[0].Value! - (double) c[2].Value!);
parser.DefineProduction(Calc.E, Calc.E, Calc.Mul, Calc.E)
.Action(c => (double) c[0].Value! * (double) c[2].Value!);
parser.DefineProduction(Calc.E, Calc.E, Calc.Div, Calc.E)
.Action(c => (double) c[0].Value! / (double) c[2].Value!);
parser.DefineProduction(Calc.E, Calc.E, Calc.Pow, Calc.E)
.Action(c => Math.Pow((double) c[0].Value!, (double) c[2].Value!));
parser.DefineProduction(Calc.E, Calc.LBrace, Calc.E, Calc.RBrace)
.Action(c => c[1].Value);
// 定义运算符优先级。
parser.DefineAssociativity(AssociativeType.Left, Calc.Add, Calc.Sub);
parser.DefineAssociativity(AssociativeType.Left, Calc.Mul, Calc.Div);
parser.DefineAssociativity(AssociativeType.Right, Calc.Pow);
parser.DefineAssociativity(AssociativeType.NonAssociate, Calc.Id);
IParserFactory<Calc> parserFactory = parser.GetFactory();
// 解析词法单元序列。
string source = "1 + 20 * 3 / 4*(5+6)";
ITokenParser<Calc> parser = parserFactory.CreateParser(lexerFactory.CreateTokenizer(source));
Console.WriteLine(parser.Parse().Value);
// 输出 166.0
语法分析器使用 LALR 语法分析实现,使用 DefineProduction
方法定义产生式,并且可以通过 SymbolOption 的 Optional
、ZeroOrMore
和 OneOrMore
支持简单的子产生式,其功能类似于正则表达式中的 A?
、A*
和 A+
。
支持使用 DefineAssociativity
方法定义符号的结合性。
默认使用首个出现的非终结符作为起始符号,也支持使用 AddStart
方法指定起始符号。起始符号可以指定多个,并通过在语法分析器的 Parse
方法的参数来选择希望使用的起始符号。
还可以使用 ParseOption 指定起始符号的扫描方式。
还可以通过设计时定义语法分析控制器来创建语法分析器,例如下面构造一个与上面相同的的语法分析器:
[ParserLeftAssociate(Calc.Add, Calc.Sub)]
[ParserLeftAssociate(Calc.Mul, Calc.Div)]
[ParserRightAssociate(Calc.Pow)]
[ParserNonAssociate(Calc.Id)]
// 必须是部分类,且继承自 ParserController<Calc>
public partial class CalcParser : ParserController<Calc>
{
[ParserProduction(Calc.E, Calc.Id)]
private object? IdAction()
{
return this[0].Value;
}
[ParserProduction(Calc.E, Calc.E, Calc.Add, Calc.E)]
[ParserProduction(Calc.E, Calc.E, Calc.Sub, Calc.E)]
[ParserProduction(Calc.E, Calc.E, Calc.Mul, Calc.E)]
[ParserProduction(Calc.E, Calc.E, Calc.Div, Calc.E)]
[ParserProduction(Calc.E, Calc.E, Calc.Pow, Calc.E)]
private object? BinaryAction()
{
double left = (double)this[0].Value!;
double right = (double)this[2].Value!;
return this[1].Kind switch
{
Calc.Add => left + right,
Calc.Sub => left - right,
Calc.Mul => left * right,
Calc.Div => left / right,
Calc.Pow => Math.Pow(left, right),
_ => throw CommonExceptions.Unreachable(),
};
}
[ParserProduction(Calc.E, Calc.LBrace, Calc.E, Calc.RBrace)]
private object? BraceAction()
{
return this[1].Value;
}
}
设计时语法分析器的使用方法请参见 Cyjb.Compilers.Design。
具体用法可以参考 TestCompilers/Parsers 目录下的示例。
详细的类库文档,请参见 Wiki。
欢迎访问我的博客获取更多信息。
C# 词法分析器系列博文
- C# 词法分析器(一)词法分析介绍
- C# 词法分析器(二)输入缓冲和代码定位
- C# 词法分析器(三)正则表达式
- C# 词法分析器(四)构造 NFA
- C# 词法分析器(五)转换 DFA
- C# 词法分析器(六)构造词法分析器
- C# 词法分析器(七)总结
C# 语法法分析器系列博文
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net6.0
- Cyjb (>= 1.0.10)
- Cyjb.Compilers.Runtime (>= 1.0.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.0.22 | 106 | 6/4/2024 |
1.0.21 | 100 | 6/4/2024 |
1.0.20 | 128 | 3/23/2024 |
1.0.19 | 126 | 3/20/2024 |
1.0.18 | 123 | 3/15/2024 |
1.0.17 | 117 | 3/14/2024 |
1.0.16 | 157 | 1/15/2024 |
1.0.15 | 217 | 3/10/2023 |
1.0.14 | 241 | 3/3/2023 |
1.0.13 | 240 | 2/9/2023 |
1.0.12 | 282 | 1/28/2023 |
1.0.11 | 268 | 1/25/2023 |
1.0.10 | 275 | 1/24/2023 |
1.0.9 | 286 | 1/23/2023 |
1.0.8 | 246 | 1/16/2023 |
1.0.2 | 327 | 12/4/2022 |
1.0.1 | 340 | 11/1/2022 |
1.0.0 | 396 | 10/25/2022 |