irony icon indicating copy to clipboard operation
irony copied to clipboard

Help understanding syntax error with non-terminal, ok with terminal

Open InfinitiesLoop opened this issue 7 years ago • 4 comments

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;
            }
        }
    }

InfinitiesLoop avatar Sep 18 '18 20:09 InfinitiesLoop

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;
            }
        }
    }

InfinitiesLoop avatar Sep 18 '18 20:09 InfinitiesLoop

Small world ...

sebastienros avatar Sep 18 '18 20:09 sebastienros

@sebastienros Didn't know you were here! HAI!

InfinitiesLoop avatar Sep 18 '18 21:09 InfinitiesLoop

@InfinitiesLoop This: https://github.com/sebastienros/fluid and this: https://github.com/OrchardCMS/OrchardCore/blob/dev/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlGrammar.cs

sebastienros avatar Sep 18 '18 21:09 sebastienros