C#
The C# target generates a standalone partial PTParser class for .NET 8 projects with nullable reference types enabled.
Minimal working example
Create a console project in an empty directory:
dotnet new console --framework net8.0Keep the generated project file, create identifier.peg, and replace Program.cs with the content below. Parser.cs is generated by pegtool.
identifier.peg
Start = value:<Identifier> !. { return value; }
Identifier = [\pL_] [\pL\p{Nd}_]*Program.cs
var value = (string)PTParser.Parse("name_世界")!;
Console.WriteLine(value);Generate and run it:
pegtool -t cs -o Parser.cs identifier.peg
dotnet runOutput:
name_世界Public API
PTParser.Parse returns object?. On failure it throws PTParseException, which contains Position and Expected. A complete options call looks like:
var value = PTParser.Parse(source, new PTParser.ParserOptions
{
Filename = "input.txt",
Entrypoint = "Start",
MaxExpressions = 1_000_000,
CustomData = context,
});Initializers and actions
The C# initializer is placed inside the generated partial PTParser class. Declare nested types, fields, and helper methods there, not namespaces or top-level statements:
{
private static int ToInteger(string text) => int.Parse(text);
}
Integer = [0-9]+ { return ToInteger(c.Text); }Labels have type object?. When a PEG label is also a C# keyword, generated actions use a verbatim identifier. For example, access the label operator as @operator.
Allocation behavior
The current runtime does not create a List<object?> for an ordinary sequence. Repetition allocates a result collection only when its child returns a non-null value. In tests, an action-free long-repetition workload was about 18%-19% faster than the old implementation, and useless collection allocations remained essentially constant as input length increased. This optimization is automatic and needs no additional option.
-optimize-parser currently has no C#-specific effect. Rule indexing still carries a diff-stability cost; use the performance guide to decide.