From 33b3da63fa7165d8dd64942af1692d4c3ad0868b Mon Sep 17 00:00:00 2001 From: b3b00 Date: Thu, 26 Dec 2024 15:46:35 +0100 Subject: [PATCH] parser fluent api : many operands --- .../generator/ExpressionRulesGenerator.cs | 29 +++ .../FluentIndentedWhileParserBuilder.cs | 193 +++++++++++++++++- .../samples/FluentIndentedWhileTests.cs | 27 ++- 3 files changed, 243 insertions(+), 6 deletions(-) diff --git a/src/sly/parser/generator/ExpressionRulesGenerator.cs b/src/sly/parser/generator/ExpressionRulesGenerator.cs index 99bea51f..6a30f45c 100644 --- a/src/sly/parser/generator/ExpressionRulesGenerator.cs +++ b/src/sly/parser/generator/ExpressionRulesGenerator.cs @@ -158,8 +158,37 @@ private string GetOperandNonTerminal(Type parserClass, ParserConfiguration x.NonTerminalName).Distinct()); + + // operandNonTerminalName = $"{parserClass.Name}_operand"; + // var operandNonTerminals = operandMethods.Select(GetNonTerminalNameFromProductionMethod); + var operandNonTerminal = new NonTerminal(nonTerminalName); + + + foreach (var operand in configuration.OperandRules) + { + if (!string.IsNullOrEmpty(operand.NonTerminalName)) + { + var rule = new Rule + { + IsByPassRule = true, + IsExpressionRule = true, + Clauses = new List> {new NonTerminalClause(operand.NonTerminalName)} + }; + operandNonTerminal.Rules.Add(rule); + } + } + + configuration.NonTerminals[nonTerminalName] = operandNonTerminal; + + return nonTerminalName; + } List methods; diff --git a/tests/ParserTests/samples/FluentIndentedWhileParserBuilder.cs b/tests/ParserTests/samples/FluentIndentedWhileParserBuilder.cs index 770a12b2..e34edfa6 100644 --- a/tests/ParserTests/samples/FluentIndentedWhileParserBuilder.cs +++ b/tests/ParserTests/samples/FluentIndentedWhileParserBuilder.cs @@ -67,7 +67,190 @@ public IFluentLexemeBuilder GetLexer() return lexer; } - public BuildResult> GetParser() + public BuildResult> GetParserWithManyOperands() + { + var instance = new IndentedWhileParserGeneric(); + var builder = FluentEBNFParserBuilder.NewBuilder(instance,"program", "en"); + + var binary = (Func, WhileAST, WhileAST> instanceCallback) => + { + Func callback = (object[] args) => + { + WhileAST left = (WhileAST)args[0]; + Token op = (Token)args[1]; + WhileAST right = (WhileAST)args[2]; + return instanceCallback(left, op, right); + }; + return callback; + }; + + var prefix = (Func, WhileAST, WhileAST> instanceCallback) => + { + Func callback = (object[] args) => + { + Token op = (Token)args[0]; + WhileAST right = (WhileAST)args[1]; + return instanceCallback(op, right); + }; + return callback; + }; + + var postfix = (Func, WhileAST> instanceCallback) => + { + Func callback = (object[] args) => + { + Token op = (Token)args[0]; + WhileAST right = (WhileAST)args[1]; + return instanceCallback(right, op); + }; + return callback; + }; + + var comparisonCallback = binary((left, op, right) => + { + return instance.binaryComparisonExpression(left, op, right); + }); + + var stringCallback = binary((left, op, right) => + { + return instance.binaryStringExpression(left, op, right); + }); + var factorCallback = binary((left, op, right) => + { + return instance.binaryFactorNumericExpression(left, op, right); + }); + var termCallback = binary((left, op, right) => + { + return instance.binaryTermNumericExpression(left, op, right); + }); + + + var parser = builder + .UseAutoCloseIndentations(true) + .UseMemoization(true) + // expressions + .Right(IndentedWhileTokenGeneric.LESSER, 50, comparisonCallback) + .Right(IndentedWhileTokenGeneric.GREATER, 50, comparisonCallback) + .Right(IndentedWhileTokenGeneric.EQUALS, 50, comparisonCallback) + .Right(IndentedWhileTokenGeneric.DIFFERENT, 50, comparisonCallback) + .Right(IndentedWhileTokenGeneric.CONCAT, 50, stringCallback) + .Right(IndentedWhileTokenGeneric.PLUS, 10, termCallback) + .Right(IndentedWhileTokenGeneric.MINUS, 10, termCallback) + .Right(IndentedWhileTokenGeneric.TIMES, 50, factorCallback) + .Right(IndentedWhileTokenGeneric.DIVIDE, 50, factorCallback) + .Prefix(IndentedWhileTokenGeneric.MINUS, 100, + prefix((op, value) => instance.unaryNumericExpression(op, value))) + .Right(IndentedWhileTokenGeneric.OR, 10, + binary((left, op, right) => instance.binaryOrExpression(left, op, right))) + .Right(IndentedWhileTokenGeneric.AND, 50, + binary((left, op, right) => instance.binaryAndExpression(left, op, right))) + .Prefix(IndentedWhileTokenGeneric.NOT, 100, prefix((op, value) => instance.unaryNotExpression(op, value))) + // operands + .Operand("int : INT", (args) => + { + return instance.PrimaryInt((Token)args[0]); + }) + .Operand("id : IDENTIFIER", (args) => + { + return instance.PrimaryId((Token)args[0]); + }) + .Operand("bool : [TRUE|FALSE]", (args) => + { + return instance.PrimaryBool((Token)args[0]); + }) + .Operand("group : OPEN_PAREN[d] IndentedWhileParserGeneric_expressions CLOSE_PAREN[d]", args => + { + return (WhileAST)args[0]; + }) + .Operand( + "ternary : QUESTION[d] IndentedWhileParserGeneric_expressions ARROW[d] IndentedWhileParserGeneric_expressions COLON[d] IndentedWhileParserGeneric_expressions", + args => + { + var condition = (WhileAST)args[0]; + var ifTrue = (WhileAST)args[1]; + var ifFalse = (WhileAST)args[2]; + return instance.TernaryQuestion(condition, ifTrue, ifFalse); + }) + // fstrings + .Operand("fstring : OPEN_FSTRING[d] fstring_element* CLOSE_FSTRING[d]", args => + { + var elements = (List)args[0]; + return instance.fstring(elements); + ; + }) + .Production("fstring_element : FSTRING_CONTENT", args => + { + return instance.FStringContent((Token)args[0]); + }) + .Production( + "fstring_element : OPEN_FSTRING_EXPPRESSION[d] IndentedWhileParserGeneric_expressions CLOSE_FSTRING_EXPPRESSION[d]", + args => + { + return (WhileAST)args[0]; + }) + // main + .Production("program: sequence", args => + { + return (WhileAST)args[0]; + }) + .Production("block : INDENT[d] sequence UINDENT[d]", args => + { + return (WhileAST)args[0]; + }) + // statements + .Production("statement : block", args => + { + return (WhileAST)args[0]; + }) + .Production("sequence: statement*", args => + { + return instance.sequence((List)args[0]); + }) + .Production("statement: IF[d] IndentedWhileParserGeneric_expressions THEN[d] block (ELSE[d] block)?", + args => + { + var condition = (WhileAST)args[0]; + var ifTrue = (WhileAST)args[1]; + var ifFalse = (ValueOption>)args[2]; + return instance.ifStmt(condition, ifTrue, ifFalse); + }) + .Production("statement: WHILE[d] IndentedWhileParserGeneric_expressions DO[d] block", args => + { + var condition = (WhileAST)args[0]; + var block = (WhileAST)args[1]; + return instance.whileStmt(condition, block); + }) + .Production("statement: IDENTIFIER ASSIGN[d] IndentedWhileParserGeneric_expressions", args => + { + var id = (Token)args[0]; + var value = (Expression)args[1]; + return instance.assignStmt(id, value); + }) + .Production("statement: SKIP[d]", args => + { + return new SkipStatement(); + }) + .Production("statement: RETURN[d] IndentedWhileParserGeneric_expressions", args => + { + var value = (Expression)args[0]; + return new ReturnStatement(value); + }) + .Production("statement: PRINT[d] IndentedWhileParserGeneric_expressions", args => + { + var value = (Expression)args[0]; + return new PrintStatement(value); + }) + .WithLexerbuilder(GetLexer()) + .BuildParser(); + + + return parser; + + + + } + + public BuildResult> GetParserWithSingleOperand() { var instance = new IndentedWhileParserGeneric(); var builder = FluentEBNFParserBuilder.NewBuilder(instance,"program", "en"); @@ -154,6 +337,10 @@ public BuildResult> GetParser() { return instance.PrimaryId((Token)args[0]); }) + .Operand("operand : primary", (args) => + { + return (WhileAST)args[0]; + }) .Production("primary : [TRUE|FALSE]", (args) => { return instance.PrimaryBool((Token)args[0]); @@ -171,10 +358,6 @@ public BuildResult> GetParser() var ifFalse = (WhileAST)args[2]; return instance.TernaryQuestion(condition, ifTrue, ifFalse); }) - .Operand("operand: primary", (args) => - { - return (WhileAST)args[0]; - }) // fstrings .Production("primary : OPEN_FSTRING[d] fstring_element* CLOSE_FSTRING[d]", args => { diff --git a/tests/ParserTests/samples/FluentIndentedWhileTests.cs b/tests/ParserTests/samples/FluentIndentedWhileTests.cs index bab5fd81..d371bc19 100644 --- a/tests/ParserTests/samples/FluentIndentedWhileTests.cs +++ b/tests/ParserTests/samples/FluentIndentedWhileTests.cs @@ -24,7 +24,7 @@ public BuildResult> buildParser() { var whileParser = new IndentedWhileParserGeneric(); FluentIndentedWhileParserBuilder builder = new FluentIndentedWhileParserBuilder(); - Parser = builder.GetParser(); + Parser = builder.GetParserWithManyOperands(); Check.That(Parser).IsOk(); } @@ -34,6 +34,31 @@ public BuildResult> buildParser() [Fact] public void TestAssignAdd() + { + var whileParser = new IndentedWhileParserGeneric(); + FluentIndentedWhileParserBuilder builder = new FluentIndentedWhileParserBuilder(); + var Parser = builder.GetParserWithSingleOperand(); + Check.That(Parser).IsOk(); + var buildResult = buildParser(); + var parser = buildResult.Result; + var result = parser.Parse("a:=1+1"); + Check.That(result).IsOkParsing(); + + Check.That(result.Result).IsInstanceOf(); + var seq = result.Result as SequenceStatement; + Check.That(seq.Get(0)).IsInstanceOf(); + var assign = seq.Get(0) as AssignStatement; + Check.That(assign.VariableName).IsEqualTo("a"); + var val = assign.Value; + Check.That(val).IsInstanceOf(); + var bin = val as BinaryOperation; + Check.That(bin.Operator).IsEqualTo(BinaryOperator.ADD); + Check.That((bin.Left as IntegerConstant)?.Value).IsEqualTo(1); + Check.That((bin.Right as IntegerConstant)?.Value).IsEqualTo(1); + } + + [Fact] + public void TestAssignAddSingleOperand() { var buildResult = buildParser(); var parser = buildResult.Result;