Help understanding syntax error with non-terminal, ok with terminal
Here's a complete repro if the issue I'm trying to resolve. The output will be:
Error:Syntax error, expected: > ((1:10))
If all you do is comment out the line marked fails and uncomment the line marked succeeds, the expression parses successfully.
There's an ambuguity with "<" in that it might be part of an 'infix' expression or the beginning of a part of a 'componentAccess' expression, but I expected Irony to work it out since the componentAccess rule won't be met, whereas infix is. It seems to be able to do that when the infixOp is removed as a non-terminal and a pure terminal used instead.
Either this indicative of a bug or I just need help understanding this behavior, and whether or not its possible to have a grammar parsed successfully this way. I've played with grammar hints to no avail. This is a simplified version of the actual grammar I'm using, for demonstration purposes.
class Program
{
static void Main(string[] args)
{
Try("abc < xyz");
Console.ReadLine();
}
private static void Try(string expr)
{
var p = new Parser(new ExpressionGrammar());
var result = p.Parse(expr);
Console.WriteLine(result.Status + ":" + string.Join("\n", result.ParserMessages.Select(m => $"{m.Message} ({m.Location})")));
}
public class ExpressionGrammar : Grammar
{
public ExpressionGrammar() : base(false)
{
var formula = new NonTerminal("formula");
var infix = new NonTerminal("infix");
var infixOp = new NonTerminal("infixOp");
var componentAccess = new NonTerminal("componentaccess");
var identifier = new NonTerminal("identifier");
Root = formula;
formula.Rule =
identifier
| componentAccess
| infix
;
identifier.Rule = ToTerm("abc") | ToTerm("xyz");
infixOp.Rule = ToTerm("<");
// e.g.: abc<xyz>.abc
componentAccess.Rule = formula + "<" + identifier + ">" + "." + identifier;
// fails
infix.Rule = formula + infixOp + formula;
// succeeds (inlining infixOp)
//infix.Rule = formula + ToTerm("<") + formula;
}
}
}
For what it's worth I found a workaround, which is to have a special version of the infix rule that is specifically for the < case. The code that handles the parse tree just has to account for this possibility. Would really really love to know of a more correct way, or know if this is a bug.
class Program
{
static void Main(string[] args)
{
Try("abc < xyz");
Try("abc > xyz");
Try("abc<xyz>.abc");
Try("abc<xyz>.abc > abc<xyz>.abc");
Try("abc<xyz>.abc < abc<xyz>.abc");
Console.ReadLine();
}
private static void Try(string expr)
{
var p = new Parser(new ExpressionGrammar());
var result = p.Parse(expr);
Console.WriteLine(result.Status + ":" + string.Join("\n", result.ParserMessages.Select(m => $"{m.Message} ({m.Location})")));
}
public class ExpressionGrammar : Grammar
{
public ExpressionGrammar() : base(false)
{
var formula = new NonTerminal("formula");
var infix = new NonTerminal("infix");
var infixLt = new NonTerminal("infixLt");
var infixOp = new NonTerminal("infixOp");
var componentAccess = new NonTerminal("componentaccess");
var identifier = new NonTerminal("identifier");
Root = formula;
formula.Rule =
identifier
| componentAccess
| infixLt // workaround
| infix
;
identifier.Rule = ToTerm("abc") | ToTerm("xyz");
infixOp.Rule = ToTerm("<") | ToTerm(">");
componentAccess.Rule = formula + "<" + identifier + ">" + "." + identifier;
// 2 rules to work around it
infixLt.Rule = formula + ToTerm("<") + formula;
infix.Rule = formula + infixOp + formula;
}
}
}
Small world ...
@sebastienros Didn't know you were here! HAI!
@InfinitiesLoop This: https://github.com/sebastienros/fluid and this: https://github.com/OrchardCMS/OrchardCore/blob/dev/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlGrammar.cs