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



我是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)

使用访问者实现,我想根据存储在map中的函数元数据检查输入是否经过验证。

下面是我正在使用的词法分析和解析器:

LexerMyFunctionsLexer.g4:

lexer grammar MyFunctionsLexer;
FUNCTION: 'FUNCTION';
NAME: [A-Za-z0-9]+;
DOT: '.';
COMMA: ',';
L_BRACKET: '(';
R_BRACKET: ')';

Parser MyFunctionsParser.g4:

parser grammar MyFunctionsParser;
options {
tokenVocab=MyFunctionsLexer;
}
function : FUNCTION '.' NAME '('(function | argument (',' argument)*)')';
argument: (NAME | function);
WS : [ trn]+ -> skip;

我正在使用Antlr4。

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

访客实施: public class FunctionValidateVisitorImpl extensions MyFunctionsParserBaseVisitor {

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

}

睾丸:

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 : [ trn]+ -> 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::");语句。

下面是C# 中的一个解决方案。这应该让您了解如何继续。您应该能够轻松地将代码转换为Java。

为了方便起见,我使用我的扩展AntlrVSIX for Visual Studio 2019和NET Core C#实现了代码。使用支持构建拆分词法分析器/解析器语法、调试和适合编辑 Antlr 语法的插件的完整 IDE,它使生活更轻松。

语法有几点需要注意。首先,Antlr 4.7.2不接受您的解析器语法。生产 "WS : [ \t\r]+ -> skip;" 是一个词法分析器规则,它不能进入解析器语法。它必须进入词法分析器语法(或者你定义一个组合语法(。其次,我个人不会定义像DOT这样的词法分析器符号,然后在解析器中直接在解析器语法中使用词法分析器符号的RHS,例如'.'。这很令人困惑,我很确定没有 IDE 或编辑器知道如何转到词法器语法中的定义"DOT:".;",如果您将光标放在解析器语法中的"."上。我一直不明白为什么在Antlr中允许它,但是c'est la vie。我会改用你定义的词法分析器符号。第三,我会考虑用EOF以通常的方式增强解析器语法,例如,"functions:function* EOF"。但是,这完全取决于您。

现在,在问题陈述中,您的示例输入包含不一致。在第一种情况下,"substring(String,Integer,Integer(",输入是在substring((的元描述中。在第二种情况下,"substring(\"test\",1,1(",您正在解析代码。第一种情况使用语法进行分析,第二种情况则不然 - 词法分析器语法中没有定义字符串文字词法分析器规则。目前还不清楚您真正想要解析的内容。

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

还有一件事需要注意。您可以通过访问者或侦听器类来解决此问题。访问者类对于纯合成属性很有用。在此示例解决方案中,我返回一个表示函数类型的字符串,或者arg关联的解析树,检查每个重要子项的值。侦听器类对于 L 属性语法很有用,即,您以面向 DFS 的方式传递属性,在树中的每个节点上从左到右。对于此示例,您可以使用侦听器类并且仅覆盖 Exit(( 函数,但随后您需要一个 Map/Dictionary 将"上下文"映射到属性(字符串(。

lexer grammar MyFunctionsLexer;
FUNCTION: 'FUNCTION';
NAME: [A-Za-z0-9]+;
DOT: '.';
COMMA: ',';
L_BRACKET: '(';
R_BRACKET: ')';
WS : [ trn]+ -> 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);
}
}
}

相关内容

  • 没有找到相关文章

最新更新