Skip to content

Commit

Permalink
parser fluent api : many operands
Browse files Browse the repository at this point in the history
  • Loading branch information
b3b00 committed Dec 26, 2024
1 parent 4cd3f8a commit 33b3da6
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 6 deletions.
29 changes: 29 additions & 0 deletions src/sly/parser/generator/ExpressionRulesGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,37 @@ private string GetOperandNonTerminal(Type parserClass, ParserConfiguration<IN,OU

if (configuration.OperandRules != null && configuration.OperandRules.Any())
{
if (configuration.OperandRules.Count == 1)
{
return configuration.OperandRules[0].NonTerminalName;
}

string nonTerminalName = string.Join("-",configuration.OperandRules.Select(x => x.NonTerminalName).Distinct());

// operandNonTerminalName = $"{parserClass.Name}_operand";
// var operandNonTerminals = operandMethods.Select<MethodInfo, string>(GetNonTerminalNameFromProductionMethod);
var operandNonTerminal = new NonTerminal<IN, OUT>(nonTerminalName);


foreach (var operand in configuration.OperandRules)
{
if (!string.IsNullOrEmpty(operand.NonTerminalName))
{
var rule = new Rule<IN, OUT>
{
IsByPassRule = true,
IsExpressionRule = true,
Clauses = new List<IClause<IN, OUT>> {new NonTerminalClause<IN, OUT>(operand.NonTerminalName)}
};
operandNonTerminal.Rules.Add(rule);
}
}

configuration.NonTerminals[nonTerminalName] = operandNonTerminal;


return nonTerminalName;

}

List<MethodInfo> methods;
Expand Down
193 changes: 188 additions & 5 deletions tests/ParserTests/samples/FluentIndentedWhileParserBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,190 @@ public IFluentLexemeBuilder<IndentedWhileTokenGeneric> GetLexer()
return lexer;
}

public BuildResult<Parser<IndentedWhileTokenGeneric, WhileAST>> GetParser()
public BuildResult<Parser<IndentedWhileTokenGeneric, WhileAST>> GetParserWithManyOperands()
{
var instance = new IndentedWhileParserGeneric();
var builder = FluentEBNFParserBuilder<IndentedWhileTokenGeneric, WhileAST>.NewBuilder(instance,"program", "en");

var binary = (Func<WhileAST, Token<IndentedWhileTokenGeneric>, WhileAST, WhileAST> instanceCallback) =>
{
Func<object[], WhileAST> callback = (object[] args) =>
{
WhileAST left = (WhileAST)args[0];
Token<IndentedWhileTokenGeneric> op = (Token<IndentedWhileTokenGeneric>)args[1];
WhileAST right = (WhileAST)args[2];
return instanceCallback(left, op, right);
};
return callback;
};

var prefix = (Func<Token<IndentedWhileTokenGeneric>, WhileAST, WhileAST> instanceCallback) =>
{
Func<object[], WhileAST> callback = (object[] args) =>
{
Token<IndentedWhileTokenGeneric> op = (Token<IndentedWhileTokenGeneric>)args[0];
WhileAST right = (WhileAST)args[1];
return instanceCallback(op, right);
};
return callback;
};

var postfix = (Func<WhileAST, Token<IndentedWhileTokenGeneric>, WhileAST> instanceCallback) =>
{
Func<object[], WhileAST> callback = (object[] args) =>
{
Token<IndentedWhileTokenGeneric> op = (Token<IndentedWhileTokenGeneric>)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<IndentedWhileTokenGeneric>)args[0]);
})
.Operand("id : IDENTIFIER", (args) =>
{
return instance.PrimaryId((Token<IndentedWhileTokenGeneric>)args[0]);
})
.Operand("bool : [TRUE|FALSE]", (args) =>
{
return instance.PrimaryBool((Token<IndentedWhileTokenGeneric>)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<WhileAST>)args[0];
return instance.fstring(elements);
;
})
.Production("fstring_element : FSTRING_CONTENT", args =>
{
return instance.FStringContent((Token<IndentedWhileTokenGeneric>)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<WhileAST>)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<Group<IndentedWhileTokenGeneric, WhileAST>>)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<IndentedWhileTokenGeneric>)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<Parser<IndentedWhileTokenGeneric, WhileAST>> GetParserWithSingleOperand()
{
var instance = new IndentedWhileParserGeneric();
var builder = FluentEBNFParserBuilder<IndentedWhileTokenGeneric, WhileAST>.NewBuilder(instance,"program", "en");
Expand Down Expand Up @@ -154,6 +337,10 @@ public BuildResult<Parser<IndentedWhileTokenGeneric, WhileAST>> GetParser()
{
return instance.PrimaryId((Token<IndentedWhileTokenGeneric>)args[0]);
})
.Operand("operand : primary", (args) =>
{
return (WhileAST)args[0];
})
.Production("primary : [TRUE|FALSE]", (args) =>
{
return instance.PrimaryBool((Token<IndentedWhileTokenGeneric>)args[0]);
Expand All @@ -171,10 +358,6 @@ public BuildResult<Parser<IndentedWhileTokenGeneric, WhileAST>> 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 =>
{
Expand Down
27 changes: 26 additions & 1 deletion tests/ParserTests/samples/FluentIndentedWhileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public BuildResult<Parser<IndentedWhileTokenGeneric, WhileAST>> buildParser()
{
var whileParser = new IndentedWhileParserGeneric();
FluentIndentedWhileParserBuilder builder = new FluentIndentedWhileParserBuilder();
Parser = builder.GetParser();
Parser = builder.GetParserWithManyOperands();
Check.That(Parser).IsOk();
}

Expand All @@ -34,6 +34,31 @@ public BuildResult<Parser<IndentedWhileTokenGeneric, WhileAST>> 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<SequenceStatement>();
var seq = result.Result as SequenceStatement;
Check.That(seq.Get(0)).IsInstanceOf<AssignStatement>();
var assign = seq.Get(0) as AssignStatement;
Check.That(assign.VariableName).IsEqualTo("a");
var val = assign.Value;
Check.That(val).IsInstanceOf<BinaryOperation>();
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;
Expand Down

0 comments on commit 33b3da6

Please sign in to comment.