-
Notifications
You must be signed in to change notification settings - Fork 34
parser fluent api
b3b00 edited this page Dec 24, 2024
·
2 revisions
All following example will assume the following fluent lexer definition, and output type
public class ExprOutput {
public string Name {get; set;}
}
public enum ExprToken {
ID,
INT
}
public class FluentExprLexer
{
public void ExprLexerTest()
{
var lexerBuilder = FluentLexerBuilder<ExprToken>.NewBuilder();
BuildResult<ILexer<MyToken>> lexerResult = lexerBuilder
.IgnoreEol(true) // ignore end of lines
.IgnoreWhiteSpace(true) // ignore white spaces
.AlphaId(MyToken.ID)
.Int(MyToken.INT)
.Build("en");
if (lexerResult.IsOk)
{
ILexer<MyToken> lexer = lexerResult.Result;
lexer.Tokenize(@"
identifier 42
# comment
$ money content €
");
}
}
}
As the purpose of a fluent API is to allow fluent use, we will define every fluent method in detail.
The lexer fluent builder for a parser taking MyFluent
tokens as input and returning a MyOutput
is declared as follow :
var parserBuilder = FluentParserBuilder<MyToken,MyOutput>.NewBuilder(object parserInstance, string rootRule, string lang);
with parameters :
-
parserInstance
... -
rootRule
: the starting non terminal; -
lang
: language used for error messages.
First we can configure the parser with some fluent methods :
-
UseMemoization(bool use = true)
: if use is true then use the Memoization optimization (seed opt-in optimizations) -
UseBroadenTokenWindow(bool use = true)
: if use is true then use the broaden token window optimization (seed opt-in optimizations) -
UseAutoCloseIndentations(bool use = true)
: if use and parser is indentation aware then automatically close opened indents. -
WithLexerbuilder(IFluentLexerBuilder<IN> lexerBuilder)
: use the lexer built with FluentLexerBuilder. Note that theFluentLexerBuilder.Build()
method must not have been called !
To get the final `IParser<IN,OUT>` object you simply call the `BuildParser()`method
```csharp
var parserBuilder = FluentParserBuilder<ExprToken,int>.NewBuilder(new ExprParser(),"root","en");
// .... token definitions
BuildResult<IParser<ExprToken,int>> parserResult = lexerBuilder.BuildParser();
// check if parserResult is OK
if (parserResult.IsOk()) {
IParser<ExprToken,int> parser = parserResult.Result;
}
### production rules
Production rules are defined with the `Production(string ruleString , Func<object[],OUT> callback)` (where OUT is the parser output type) :
- parserInstance : this parameter is only mandatory when using expression parser generator. TODO ....
- ruleString : the rule definition as in
- a callback called to visit the syntax tree.
The visitor callback accepts a list of object as parameters. It's your reponsibility to cast the parameters according to [parser typing](defining-your-parser#parserbuilder-and-parser-generic-input-and-output-type-convention)
```csharp
public class ExprParser {
// ....
}
// defines a rule that simply matches a identifier
var parserBuilder = FluentParserBuilder<ExprToken,ExprOutput>.NewBuilder(new ExprParser(), "root", "en");
parserBuilder.Production("root : ID")
-
Right(IN operation, int precedence, Func<object[], OUT> visitor)
: an infix right associative operation (as an enum value) with precedenceprecedence
and visitor callback -
Right(string operation, int precedence, Func<object[], OUT> visitor)
: an infix right associative operation (as a enum string value or explicit token) with precedenceprecedence
and visitor callback -
Left(IN operation, int precedence, Func<object[], OUT> visitor)
: : an infix left associative operation (as an enum value) with precedenceprecedence
and visitor callback -
Left(string operation, int precedence, Func<object[], OUT> visitor)
: an infix left associative operation (as a enum string value or explicit token) with precedenceprecedence
and visitor callback -
Prefix(IN operation, int precedence, Func<object[], OUT> visitor)
: a prefix operation (as an enum value) with precedenceprecedence
and visitor callback -
Prefix(string operation, int precedence, Func<object[], OUT> visitor)
: a prefix operation (as a enum string value or explicit token) with precedenceprecedence
and visitor callback -
Postfix(IN operation, int precedence, Func<object[], OUT> visitor)
: a postfix operation (as an enum value) with precedenceprecedence
and visitor callback -
Postfix(string operation, int precedence, Func<object[], OUT> visitor)
: a postfix operation (as a enum string value or explicit token) with precedenceprecedence
and visitor callback -
Operand(string ruleString, Func<object[], OUT>)
and Operand is the same as aProduction()
and is tagged as an operand so that it can be used as .... an operand for expression parsing.
Visitor callbacks. As for production rule you are responsible to correctly cast arguments :
- infix operations : they are lambdas taking 3 parameters and returning an OUT value
- first parameter is the left operand value
- second parameter is the token of the operator
- third is the right operand value
- prefix : a lambda with 2 parameter taking 2 parameters and returning an OUT value
- first parameter is the token of the operator
- second parameter is the value of the operand
- postfix : a lambda with 2 parameter taking 2 parameters and returning an OUT value
- first parameter is the value of the operand
- second parameter is the token of the operator
The type of the parser instance passed as the first argument of FluentParserBuilder<IN,OUT>.NewBuilder()
will be used to generate the root expression rule, just as described in Expressions parsing
var parserBuilder = FluentParserBuilder<MyToken,int>.NewBuilder(new ExprParser(), "root", "en");
ParseResult<IParser<ExprToken,int> parserResult = parserBuilder
.Production("root : ExprParser_expressions",)
.Left("'-'",10(object[] args) => (int)args[0] - (int)args[2])
.Right("'+'",10 (object[] args) => (int)args[0] + (int)args[2])
.Left("'/'",50(object[] args) => (int)args[0] / (int)args[2])
.Right("'*'",50 (object[] args) => (int)args[0] * (int)args[2])
.Prefix("'-'",100 (Tobject[] args) => -(int)args[1])
.Operand("value : INT",(object[] args) => ((Token<ExprToken>)args[0]).IntValue)
.BuildParser();
if (parserResult.IsOk) {
IParser<ExprToken, int> parser = parserResult.Result;
var result = parser.Parse("-1 + 2 * 3");
// result.IsOk should be true
// result.Result should be equal to -1 + 2 * 3 = -1 + 6 = 5
}