有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何实现嵌套函数的访问者模式

我是Antlr的新手,我希望下面的实现使用Antlr4完成。我有下面的书面功能

1. FUNCTION.add(Integer a,Integer b)
2. FUNCTION.concat(String a,String b)
3. FUNCTION.mul(Integer a,Integer b)

我像这样存储函数元数据

Map<String,String> map=new HashMap<>();
        map.put("FUNCTION.add","Integer:Integer,Integer");
        map.put("FUNCTION.concat","String:String,String");
        map.put("FUNCTION.mul","Integer:Integer,Integer");

其中,Integer:Integer,Integer表示Integer是返回类型,函数将访问的输入参数是Integer,Integer

如果输入是这样的

FUNCTION.concat(Function.substring(String,Integer,Integer),String)
or
FUNCTION.concat(Function.substring("test",1,1),String)

使用visitor实现,我想根据map中存储的函数元数据检查输入是否有效

下面是我正在使用的lexer和parser:

Lexer MyFunctionsLexer。g4:

lexer grammar MyFunctionsLexer;

FUNCTION: 'FUNCTION';

NAME: [A-Za-z0-9]+;

DOT: '.';

COMMA: ',';

L_BRACKET: '(';

R_BRACKET: ')';

解析器MyFunctionsParser。g4:

parser grammar MyFunctionsParser;

options {
    tokenVocab=MyFunctionsLexer;
}

function : FUNCTION '.' NAME '('(function | argument (',' argument)*)')';

argument: (NAME | function);

WS : [ \t\r\n]+ -> skip;

我正在使用Antlr4

下面是我根据建议的答案使用的实现

访客实施: 公共类FunctionValidateVisitorImpl扩展了MyFunctionsArbaseVisitor{

    Map<String, String> map = new HashMap<String, String>();

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

    @Override
    public String visitFunctions(@NotNull MyFunctionsParser.FunctionsContext ctx) {
        System.out.println("entered the visitFunctions::");
        for (int i = 0; i < ctx.getChildCount(); ++i)
        {
            ParseTree c = ctx.getChild(i);
            if (c.getText() == "<EOF>")
                continue;
            String top_level_result = visit(ctx.getChild(i));
            System.out.println(top_level_result);
            if (top_level_result == null)
            {
                System.out.println("Failed semantic analysis: "+ ctx.getChild(i).getText());
            }
        }
        return null;
    }

    @Override
    public String visitFunction( MyFunctionsParser.FunctionContext ctx) {
        // Get function name and expected type information.
        String name = ctx.getChild(2).getText();
        String type=map.get("FUNCTION." + name);
        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(",");
        int j = 4;
        ParseTree a = ctx.getChild(j);
        if (a instanceof MyFunctionsParser.FunctionContext)
        {
            String v = visit(a);
            if (v != result_type)
            {
                return null; // Handle type mismatch.
            }
        } else {
            for (int i = j; i < ctx.getChildCount(); i += 2)
            {
                ParseTree parameter = ctx.getChild(i);
                String v = visit(parameter);
                if (v != expected_arg_type[(i - j)/2])
                {
                    return null; // Handle type mismatch.
                }
            }
        }
        return result_type;
    }


    @Override
    public String visitArgument(ArgumentContext ctx){
        ParseTree c = ctx.getChild(0);
        if (c instanceof 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);
    }


}

Testcalss:

public class FunctionValidate {


    public static void main(String[] args) {
        String input = "FUNCTION.concat(FUNCTION.substring(String,Integer,Integer),String)";
        ANTLRInputStream str = new ANTLRInputStream(input);
        MyFunctionsLexer lexer = new MyFunctionsLexer(str);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        MyFunctionsParser parser = new MyFunctionsParser(tokens);
        parser.removeErrorListeners(); // remove ConsoleErrorListener 
        parser.addErrorListener(new VerboseListener()); // add ours
        FunctionsContext tree = parser.functions();
        FunctionValidateVisitorImpl visitor = new FunctionValidateVisitorImpl();
        visitor.visit(tree);
    }


}

莱克瑟:

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

冗长的听众:

public class VerboseListener  extends BaseErrorListener  {

    @Override 
    public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { 
        List<String> stack = ((Parser)recognizer).getRuleInvocationStack();
        Collections.reverse(stack); 
        throw new FunctionInvalidException("line "+line+":"+charPositionInLine+" at "+ offendingSymbol+": "+msg);

    }
}

输出: 它没有进入访问者实现,因为它没有打印System.out.println("entered the visitFunctions::");语句


共 (1) 个答案

  1. # 1 楼答案

    下面是C#中的一个解决方案。这应该会让你知道如何继续。您应该能够轻松地将代码翻译成Java

    为了方便起见,我使用扩展名AntlrVSIX For Visual Studio 2019和NET Core C#实现了这段代码。使用支持构建拆分词法分析器/语法的完整IDE、调试,以及适合编辑Antlr语法的插件,可以简化工作

    你的语法有几点需要注意。首先,Antlr 4.7.2不接受您的解析器语法。生产“WS:[\t\r\n]+->;跳过是一个词法规则,不能放在语法分析器中。它必须进入词法语法(或者你定义一个组合语法)。其次,我个人不会定义点这样的词法符号,然后在解析器中直接在解析器语法中使用词法符号的RHS,例如“.”。这很令人困惑,而且我很确定没有一个IDE或编辑器知道如何定义“点:”在lexer语法中,如果将光标定位在“.”在语法分析器中。我一直不明白为什么在Antlr是允许的,但这就是生活。我会使用你定义的lexer符号。第三,我会考虑以EOF的一般方式扩充解析器语法,例如“函数:函数*EOF”。但是,这完全取决于你

    现在,在问题陈述中,您的示例输入包含不一致性。在第一种情况下,“substring(String,Integer,Integer)”,输入是substring()的类似元的描述。在第二种情况下,“子字符串(\“test\”,1,1)”,您正在解析代码。第一种情况使用语法进行解析,第二种情况不使用语法。在你的lexer语法中没有定义字符串文字lexer规则。尚不清楚您真正想要解析什么

    总的来说,我在字符串上定义了访问者代码,也就是说,每个方法返回一个表示函数或参数的输出类型的字符串,例如,“Integer”或“string”或null(如果出现错误)(或者可以对静态语义错误抛出异常)。然后,在parse树节点中的每个子节点上使用Visit(),检查结果字符串是否符合预期,并根据需要处理匹配项

    还有一件事需要注意。您可以通过visitor或listener类解决这个问题。visitor类对于纯合成属性很有用。在这个示例解决方案中,我返回一个表示函数类型的字符串,或者在关联的解析树中设置参数,检查每个重要子级的值。listener类对于L属性语法很有用,即,在树中的每个节点上,以面向DFS的方式从左到右传递属性。在本例中,可以使用listener类,只重写Exit()函数,但随后需要一个映射/字典将“上下文”映射到属性(字符串)中

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