Cyjb.Compilers 1.0.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package Cyjb.Compilers --version 1.0.1
NuGet\Install-Package Cyjb.Compilers -Version 1.0.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Cyjb.Compilers" Version="1.0.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Cyjb.Compilers --version 1.0.1
#r "nuget: Cyjb.Compilers, 1.0.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Cyjb.Compilers as a Cake Addin
#addin nuget:?package=Cyjb.Compilers&version=1.0.1

// Install Cyjb.Compilers as a Cake Tool
#tool nuget:?package=Cyjb.Compilers&version=1.0.1

Cyjb.Compilers

alternate text is missing from this package README image

提供编译相关功能,基于 .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、反向引用、替换构造和替代功能。

正则表达式支持通过 / 指定向前看符号,支持指定匹配的上下文,并在执行动作时根据需要切换上下文。

如果前缀可以与多个正则表达式匹配,那么:

  1. 总是选择最长的前缀。
  2. 如果最长的可能前缀与多个正则表达式匹配,总是选择先定义的正则表达式。

支持使用 DefineContextDefineInclusiveContext 方法定义上下文或包含型上下文,在声明符号时可以通过构造器的 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 方法定义产生式,并且可以通过 SymbolOptionOptionalZeroOrMoreOneOrMore 支持简单的子产生式,其功能类似于正则表达式中的 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# 词法分析器系列博文

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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 74 6/4/2024
1.0.21 72 6/4/2024
1.0.20 101 3/23/2024
1.0.19 97 3/20/2024
1.0.18 95 3/15/2024
1.0.17 93 3/14/2024
1.0.16 129 1/15/2024
1.0.15 197 3/10/2023
1.0.14 221 3/3/2023
1.0.13 217 2/9/2023
1.0.12 262 1/28/2023
1.0.11 248 1/25/2023
1.0.10 253 1/24/2023
1.0.9 262 1/23/2023
1.0.8 238 1/16/2023
1.0.2 302 12/4/2022
1.0.1 319 11/1/2022
1.0.0 374 10/25/2022