How to implement the visitor pattern for nested function

ε祈祈猫儿з 提交于 2019-12-05 03:38:26

Below is a solution in C#. This should give you an idea of how to proceed. You should be able to easily translate the code to Java.

For ease, I implemented the code using my extension AntlrVSIX for Visual Studio 2019 with NET Core C#. It makes life easier using a full IDE that supports the building of split lexer/parser grammars, debugging, and a plug-in that is suited for editing Antlr grammars.

There are several things to note with your grammar. First, your parser grammar isn't accepted by Antlr 4.7.2. Production "WS : [ \t\r\n]+ -> skip;" is a lexer rule, it can't go in a parser grammar. It has to go into the lexer grammar (or you define a combined grammar). Second, I personally wouldn't define lexer symbols like DOT, and then use in the parser the RHS of the lexer symbol directly in the parser grammar, e.g., '.'. It's confusing, and I'm pretty sure there isn't an IDE or editor would know how to go to the definition "DOT: '.';" in the lexer grammar if you positioned your cursor on the '.' in the parser grammar. I never understood why it's allowed in Antlr, but c'est la vie. I would instead use the lexer symbol you define. Third, I would consider augmenting the parser grammar in the usual way with EOF, e.g., "functions : function* EOF". But, this is entirely up to you.

Now, on the problem statement, your example input contains an inconsistency. In the first case, "substring(String,Integer,Integer)", the input is in a meta-like description of substring(). In the second case, "substring(\"test\",1,1)", you are parsing code. The first case parses with your grammar, the second does not--there's no string literal lexer rule defined in your lexer grammar. It's unclear what you really want to parse.

Overall, I defined the visitor code over strings, i.e., each method returns a string representing the output type of the function or argument, e.g., "Integer" or "String" or null if there was an error (or you could throw an exception for static semantic errors). Then, using Visit() on each child in the parse tree node, check the resulting string if it is expected, and handle matches as you like.

One other thing to note. You can solve this problem via a visitor or listener class. The visitor class is useful for purely synthesized attributes. In this example solution, I return a string that represents the type of the function or arg up the associated parse tree, checking the value for each important child. The listener class is useful for L-attributed grammars--i.e., where you are passing attributes in a DFS-oriented manner, left to right at each node in the tree. For this example, you could use the listener class and only override the Exit() functions, but you would then need a Map/Dictionary to map a "context" into an attribute (string).

lexer grammar MyFunctionsLexer;
FUNCTION: 'FUNCTION';
NAME: [A-Za-z0-9]+;
DOT: '.';
COMMA: ',';
L_BRACKET: '(';
R_BRACKET: ')';
WS : [ \t\r\n]+ -> skip;
parser grammar MyFunctionsParser;
options { tokenVocab=MyFunctionsLexer; }
functions : function* EOF;
function : FUNCTION '.' NAME '(' (function | argument (',' argument)*) ')';
argument: (NAME | function);
using Antlr4.Runtime;

namespace AntlrConsole2
{
    public class Program
    {
        static void Main(string[] args)
        {
            var input = @"FUNCTION.concat(FUNCTION.substring(String,Integer,Integer),String)";
            var str = new AntlrInputStream(input);
            var lexer = new MyFunctionsLexer(str);
            var tokens = new CommonTokenStream(lexer);
            var parser = new MyFunctionsParser(tokens);
            var listener = new ErrorListener<IToken>();
            parser.AddErrorListener(listener);
            var tree = parser.functions();
            if (listener.had_error)
            {
                System.Console.WriteLine("error in parse.");
            }
            else
            {
                System.Console.WriteLine("parse completed.");
            }
            var visitor = new Validate();
            visitor.Visit(tree);
        }
    }
}
namespace AntlrConsole2
{
    using System;
    using Antlr4.Runtime.Misc;
    using System.Collections.Generic;

    class Validate : MyFunctionsParserBaseVisitor<string>
    {
        Dictionary<String, String> map = new Dictionary<String, String>();

        public Validate()
        {
            map.Add("FUNCTION.add", "Integer:Integer,Integer");
            map.Add("FUNCTION.concat", "String:String,String");
            map.Add("FUNCTION.mul", "Integer:Integer,Integer");
            map.Add("FUNCTION.substring", "String:String,Integer,Integer");
        }

        public override string VisitFunctions([NotNull] MyFunctionsParser.FunctionsContext context)
        {
            for (int i = 0; i < context.ChildCount; ++i)
            {
                var c = context.GetChild(i);
                if (c.GetText() == "<EOF>")
                    continue;
                var top_level_result = Visit(context.GetChild(i));
                if (top_level_result == null)
                {
                    System.Console.WriteLine("Failed semantic analysis: "
                        + context.GetChild(i).GetText());
                }
            }
            return null;
        }

        public override string VisitFunction(MyFunctionsParser.FunctionContext context)
        {
            // Get function name and expected type information.
            var name = context.GetChild(2).GetText();
            map.TryGetValue("FUNCTION." + name, out string type);
            if (type == null)
            {
                return null; // not declared in function table.
            }
            string result_type = type.Split(":")[0];
            string args_types = type.Split(":")[1];
            string[] expected_arg_type = args_types.Split(",");
            const int j = 4;
            var a = context.GetChild(j);
            if (a is MyFunctionsParser.FunctionContext)
            {
                var v = Visit(a);
                if (v != result_type)
                {
                    return null; // Handle type mismatch.
                }
            } else {
                for (int i = j; i < context.ChildCount; i += 2)
                {
                    var parameter = context.GetChild(i);
                    var v = Visit(parameter);
                    if (v != expected_arg_type[(i - j)/2])
                    {
                        return null; // Handle type mismatch.
                    }
                }
            }
            return result_type;
        }

        public override string VisitArgument([NotNull] MyFunctionsParser.ArgumentContext context)
        {
            var c = context.GetChild(0);
            if (c is Antlr4.Runtime.Tree.TerminalNodeImpl)
            {
                // Unclear if what this is supposed to parse:
                // Mutate "1" to "Integer"?
                // Mutate "Integer" to "String"?
                // Or what?
                return c.GetText();
            }
            else
                return Visit(c);
        }
    }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!