java - ANTLR4 在访客不工作时调用下一步
问题描述
我的语法已经完成,现在想实现功能。但我得到它不起作用,我不明白如何在 functionExpr 中评估应该调用哪个命令。作为我正在使用的测试表达式
isSet(value, test) and isSet(value, foo)
目前我不知道如何评估单个 CommandIsSet 函数并在那里评估变量类型。为了解决这个问题,顺序应该是
- 访问程序
- visitAndExpr
- visitFunctionExpr (for first isSet(value, test)
- 访问命令集
- 访问变量参数
- 访问值类型
- visitFunctionExpr (对于第二个 isSet(value, foo)
- 访问命令集
- 访问变量参数
- 访问值类型
目前我停留在第 3 步,如何确定它是一个 commandIsSet 我需要使用 ctx.getText() 并检查它是否以 isSet 开头?
我已经从 SO 中的@BartKiers 找到了一个很好的答案,从 那里我使用了值对象。
这是我的语法
grammar FEL;
prog: expr+ EOF;
expr:
statement #StatementExpr
|NOT expr #NotExpr
| expr AND expr #AndExpr
| expr (OR | XOR) expr #OrExpr
| function #FunctionExpr
| LPAREN expr RPAREN #ParenExpr
| writeCommand #WriteExpr
;
writeCommand: setCommand | setIfCommand;
statement: ID '=' getCommand NEWLINE #Assign;
setCommand: 'set' LPAREN variableType '|' parameter RPAREN SEMI;
setIfCommand: 'setIf' LPAREN variableType '|' expr '?' parameter ':' parameter RPAREN SEMI;
getCommand: getFieldValue #FieldValue
| getInstanceAttribValue #InstanceAttribValue
| getFormAttribValue #FormAttributeValue
| getMandatorAttribValue #MandatorAttributeValue
;
getFieldValue: 'getFieldValue' LPAREN instanceID=ID COMMA fieldname=ID RPAREN;
getInstanceAttribValue: 'getInstanceAttrib' LPAREN instanceId=ID COMMA moduleId=ID COMMA attribname=ID RPAREN;
getFormAttribValue: 'getFormAttrib' LPAREN formId=ID COMMA moduleId=ID COMMA attribname=ID RPAREN;
getMandatorAttribValue: 'getMandatorAttrib' LPAREN mandator=ID COMMA moduleId=ID COMMA attribname=ID RPAREN;
twoParameterList: parameter '|' parameter;
parameter:
variableType #VariableParameter
| constType #ConstParameter
;
pdixFuncton:ID;
constType:
ID #ID_Without
| '"' ID '"' #ID_WITH
;
variableType:
valueType #ValueTyp
|instanceType #InstanceTyp
|formType #FormTyp
|bufferType #BufferTyp
|instanceAttribType #InstanceAttribTyp
|formAttribType #FormAttribTyp
|mandatorAttribType #ManatorTyp
;
valueType:'value' COMMA parameter (COMMA functionParameter)?;
instanceType: 'instance' COMMA instanceParameter;
formType: 'form' COMMA formParameter;
bufferType: 'buffer' COMMA parameter;
instanceParameter: 'instanceId'
| 'instanceKey'
| 'firstpenId'
| 'lastpenId'
| 'lastUpdate'
| 'started'
;
formParameter: 'formId'
|'formKey'
|'lastUpdate'
;
functionParameter: 'lastPen'
| 'fieldGroup'
| ' fieldType'
| 'fieldSource'
| 'updateId'
| 'sessionId'
| 'icrConfidence'
| 'icrRecognition'
| 'lastUpdate';
instanceAttribType:('instattrib' | 'instanceattrib') COMMA attributeType;
formAttribType:'formattrib' COMMA attributeType;
mandatorAttribType: 'mandatorattrib' COMMA attributeType;
attributeType:ID '#' ID;
function:
commandIsSet #IsSet
| commandIsEmpty #IsEmpty
| commandIsEqual #IsEqual
|commandIsNumLessEqual #IsLessEqual
|commandIsNumLess #IsNumLess
|commandIsNumGreaterEqual #IsNumGreaterEqual
|commandIsNumGreater #IsNumGreater
|commandIsNumEqual #IsNumEqual
|commandIsLess #IsLess
|commandIsLessEqual #IsLessEqual
|commandIsGreater #IsGreater
|commandIsGreaterEqual #IsGreaterEqual
|commandMatches #Matches
|commandContains #Contains
|commandEndsWith #EndsWith
|commandStartsWith #StartsWith
;
commandIsSet: IS_SET LPAREN parameter RPAREN;
commandIsEmpty: IS_EMPTY LPAREN parameter RPAREN;
commandIsEqual: IS_EQUAL LPAREN twoParameterList RPAREN;
commandStartsWith: 'startsWith' LPAREN twoParameterList RPAREN;
commandEndsWith: 'endsWith' LPAREN twoParameterList RPAREN;
commandContains: 'contains' LPAREN twoParameterList RPAREN;
commandMatches: 'maches' LPAREN twoParameterList RPAREN;
commandIsLess: 'isLess' LPAREN twoParameterList RPAREN;
commandIsLessEqual: 'isLessEqual' LPAREN twoParameterList RPAREN;
commandIsGreater: 'isGreater' LPAREN twoParameterList RPAREN;
commandIsGreaterEqual: 'isGreaterEqual' LPAREN twoParameterList RPAREN;
commandIsNumEqual: 'isNumEqual' LPAREN twoParameterList RPAREN;
commandIsNumGreater: 'isNumGreater' LPAREN twoParameterList RPAREN;
commandIsNumGreaterEqual: 'isNumGreaterEqual' LPAREN twoParameterList RPAREN;
commandIsNumLess: 'isNumLess' LPAREN twoParameterList RPAREN;
commandIsNumLessEqual: 'isNumLessEqual' LPAREN twoParameterList RPAREN;
/*
stringFunctionType:
a=substringStrFunction
| a=cutStrFunction
| a=replaceStrFunction
| a=reformatDateStrFunction
| a=translateStrFunction
| a=fillStrFunction
| a=concatStrFunction
| a=justifyStrFunction
| a=ifElseStrFunction
| a=tokenStrFunction
| a=toLowerFunction
| a=toUpperFunction
| a=trimFunction
;
*/
LPAREN : '(';
RPAREN : ')';
LBRACE : '{';
RBRACE : '}';
LBRACK : '[';
RBRACK : ']';
SEMI : ';';
COMMA : ',';
DOT : '.';
ASSIGN : '=';
GT : '>';
LT : '<';
BANG : '!';
TILDE : '~';
QUESTION : '?';
COLON : ':';
EQUAL : '==';
LE : '<=';
GE : '>=';
NOTEQUAL : '!=';
AND : 'and';
OR : 'or';
XOR :'xor';
NOT :'not' ;
INC : '++';
DEC : '--';
ADD : '+';
SUB : '-';
MUL : '*';
DIV : '/';
INT: [0-9]+;
NEWLINE: '\r'? '\n';
IS_SET:'isSet';
IS_EMPTY:'isEmpty';
IS_EQUAL:'isEqual';
WS: (' '|'\t' | '\n' | '\r' )+ -> skip;
ID
: JavaLetter JavaLetterOrDigit*
;
fragment
JavaLetter
: [a-zA-Z$_] // these are the "java letters" below 0xFF
| // covers all characters above 0xFF which are not a surrogate
~[\u0000-\u00FF\uD800-\uDBFF]
{Character.isJavaIdentifierStart(_input.LA(-1))}?
| // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF
[\uD800-\uDBFF] [\uDC00-\uDFFF]
{Character.isJavaIdentifierStart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}?
;
fragment
JavaLetterOrDigit
: [a-zA-Z0-9$_] // these are the "java letters or digits" below 0xFF
| // covers all characters above 0xFF which are not a surrogate
~[\u0000-\u00FF\uD800-\uDBFF]
{Character.isJavaIdentifierPart(_input.LA(-1))}?
| // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF
[\uD800-\uDBFF] [\uDC00-\uDFFF]
{Character.isJavaIdentifierPart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}?
;
fragment DoubleQuote: '"' ; // Hard to read otherwise.
这是我的带有测试表达式的访问者实现
public class FELCustomVisitor extends FELBaseVisitor<Value> {
// used to compare floating point numbers
public static final double SMALL_VALUE = 0.00000000001;
// store variables (there's only one global scope!)
private Map<String, Value> memory = new HashMap<String, Value>();
public static void main(String[] args) {
String expression = "isSet(value, test) and isSet(value, foo)";
FELLexer lexer = new FELLexer(new ANTLRInputStream(expression));
FELParser parser = new FELParser(new CommonTokenStream(lexer));
ParseTree tree = parser.prog();
Value answer = new FELCustomVisitor().visit(tree);
System.out.printf("answer is %s", answer.asBoolean());
}
@Override
public Value visitProg(FELParser.ProgContext ctx) {
System.out.println("called prog " + ctx.getText());
return this.visitChildren(ctx);
}
@Override
public Value visitAndExpr(FELParser.AndExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(2));
System.out.println("working on and expression: left == " + left.asBoolean() + " right is " + right.asBoolean());
return new Value(left.asBoolean() && right.asBoolean());
}
@Override
public Value visitFunctionExpr(FELParser.FunctionExprContext ctx) {
System.out.println("called function expresssion " + ctx.getText());
FELParser.FunctionContext function = ctx.function();
return null;
}
@Override
public Value visitCommandIsSet(FELParser.CommandIsSetContext ctx) {
//here I need to extract the inner variable type with this i can evaluate the return value
return super.visitCommandIsSet(ctx);
}
@Override
public Value visitVariableParameter(FELParser.VariableParameterContext ctx) {
return super.visitVariableParameter(ctx);
}
@Override
public Value visitValueTyp(FELParser.ValueTypContext ctx) {
return super.visitValueTyp(ctx);
}
解决方案
此方法中的部分this.visit(ctx.expr(2))
是错误的:
@Override
public Value visitAndExpr(FELParser.AndExprContext ctx) {
Value left = this.visit(ctx.expr(0));
Value right = this.visit(ctx.expr(2));
System.out.println("working on and expression: left == " + left.asBoolean() + " right is " + right.asBoolean());
return new Value(left.asBoolean() && right.asBoolean());
}
有 2 个子节点的索引为 0 和 1。
你让你的访问者有点太复杂了。从您想要评估的最简单的表达式开始,例如isSet(value, test)
,然后从树的底部到它的根,并实现这些visit...()
方法。在这种情况下isSet(value, test)
,将是规则:
@Override
public Value visitID_Without(FELParser.ID_WithoutContext ctx) {
// ID #ID_Without
}
@Override
public Value visitCommandIsSet(FELParser.CommandIsSetContext ctx) {
// commandIsSet: IS_SET LPAREN parameter RPAREN;
}
@Override
public Value visitProg(FELParser.ProgContext ctx) {
// expr+ EOF
}
当它起作用时,将and
表达式添加到它以支持isSet(value, test) and isSet(value, foo)
:
@Override
public Value visitID_Without(FELParser.ID_WithoutContext ctx) {
// ID #ID_Without
}
@Override
public Value visitCommandIsSet(FELParser.CommandIsSetContext ctx) {
// commandIsSet: IS_SET LPAREN parameter RPAREN;
}
@Override
public Value visitAndExpr(FELParser.AndExprContext ctx) {
// expr AND expr
}
@Override
public Value visitProg(FELParser.ProgContext ctx) {
// expr+ EOF
}
等等
一个小的工作示例如下所示:
public class FELCustomVisitor extends FELBaseVisitor<Value> {
private final Map<String, Value> memory;
public FELCustomVisitor(Map<String, Value> memory) {
this.memory = memory;
}
@Override
public Value visitID_Without(FELParser.ID_WithoutContext ctx) {
// ID #ID_Without
return this.memory.getOrDefault(ctx.ID().getText(), Value.NULL);
}
@Override
public Value visitCommandIsSet(FELParser.CommandIsSetContext ctx) {
// commandIsSet: IS_SET LPAREN parameter RPAREN;
Value param = this.visit(ctx.parameter());
return param == Value.NULL ? Value.FALSE : Value.TRUE;
}
@Override
public Value visitAndExpr(FELParser.AndExprContext ctx) {
// expr AND expr
Value lhs = this.visit(ctx.expr(0));
Value rhs = this.visit(ctx.expr(1));
return lhs.asBoolean() && rhs.asBoolean() ? Value.TRUE : Value.FALSE;
}
@Override
public Value visitProg(FELParser.ProgContext ctx) {
// expr+ EOF
Value result = Value.NULL;
for (FELParser.ExprContext expr : ctx.expr()) {
result = this.visit(expr);
}
return result;
}
public static void main(String[] args) {
String[] expressions = {
"isSet(value, test)",
"isSet(value, foo)",
"isSet(value, fooooooo)",
"isSet(value, test) and isSet(value, foo)",
"isSet(value, nope) and isSet(value, test) and isSet(value, foo)"
};
for (String expression : expressions) {
FELLexer lexer = new FELLexer(CharStreams.fromString(expression));
FELParser parser = new FELParser(new CommonTokenStream(lexer));
ParseTree tree = parser.prog();
Map<String, Value> memory = new HashMap<>();
memory.put("test", new Value(42));
memory.put("foo", new Value("bar"));
Value answer = new FELCustomVisitor(memory).visit(tree);
System.out.printf("%s = %s%n", expression, answer);
}
}
}
class Value {
public static final Value TRUE = new Value(true);
public static final Value FALSE = new Value(false);
public static final Value NULL = new Value(null);
private final Object value;
public Value(Object value) {
this.value = value;
}
public Boolean asBoolean() {
return (Boolean) this.value;
}
@Override
public String toString() {
return String.format("Value{type: %s, value: %s}",
this.value == null ? "null" : this.value.getClass().getSimpleName(), this.value);
}
}
运行它时,您会看到以下打印到您的控制台:
isSet(value, test) = Value{type: Boolean, value: true}
isSet(value, foo) = Value{type: Boolean, value: true}
isSet(value, fooooooo) = Value{type: Boolean, value: false}
isSet(value, test) and isSet(value, foo) = Value{type: Boolean, value: true}
isSet(value, nope) and isSet(value, test) and isSet(value, foo) = Value{type: Boolean, value: false}
编辑
[...]因此将调用visitVariableParameter,我如何区分不同的选项并调用正确的访问方法[...]
您不需要覆盖该方法。假设您要实现isSet(instance, instanceId)
,那么您所要做的就是覆盖visitInstanceType(...)
访问者。每当this.visit(ctx.parameter());
调用代码中的某个位置时,它都会自动确保parameter
调用 的子级。
您只需将此方法添加到访问者:
@Override
public Value visitInstanceType(FELParser.InstanceTypeContext ctx) {
// 'instance' COMMA instanceParameter
Value value = new Value(String.format("TODO: instance, `%s`", ctx.instanceParameter().getText()));
System.out.println("We're in visitInstanceType(...), returning: " + value);
return value;
}
现在在评估时"isSet(instance, instanceId)"
,您将看到上述方法被正确调用。
推荐阅读
- autodesk-forge - Forge BIM360 数据管理
- html - 如何让底部的两个盒子以移动尺寸相互堆叠?
- django - 使用表单将数据从服务器 A 发布到服务器 B
- asp.net-core - 从 C 核心客户端到 .NET 核心服务器的 HTTPS GRPC 连接:TLS 握手失败
- c - 从管道读取后程序停留在后台
- excel - 是否可以在 excel 数据透视表中仅将报表过滤器应用于具有 vba 的一列
- firebase - 如何制定云防火墙安全规则,使登录用户只能访问他们的数据?
- java - 错误:尝试执行回滚时,未将数据库 URL 指定为参数或属性文件
- mysql - MySql InnoDB:更改表 > 添加主键:错误“行大小太大”
- c++ - 使用后声明