java - 使用antlr4的二义性语法

标签 java parsing antlr grammar antlr4

我在使用antlr4.1生成解析器时遇到了一个大问题 语法由以下2个文件组成:

grammar Common;

identifierNum: hostId DOT observableId DOT method ':Num';
identifierString: hostId DOT observableId DOT method  ':String';


hostId: ID;
observableId: ID;
method: ID'('')';

MUL: '*';
DIV: '/'; 
ADD: '+';
SUB: '-';
DOT: '.';
MIN: '<';
MAX: '>';


ID: [a-zA-Z0-9]+;
STRING: '\''[a-zA-Z0-9]+ '\'';
DIGIT: [0-9]+ ;
DOUBLE: [0-9]* DOT [0-9]+ ;


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

和这个文件

grammar Expression;
import Common;

expression:     stringExpr                      # StringExpression
    |           numExpr                         # NumExpression
    ;     

stringExpr:     stringExpr ADD stringExpr   # Concat
    |           STRING                      # BaseStr 
    |           identifierString            # IdString     
    |           '(' stringExpr ')'          # ParensString
    ;

numExpr:    numExpr op=(MUL|DIV) numExpr                # MulDiv
    |       numExpr op=(ADD|SUB) numExpr                # AddSub
    |       DIGIT                                       # Int
    |       DOUBLE                                      # Double
    |       identifierNum                               # IdNum      
    |       '(' numExpr ')'                             # ParensNum 
    ;

我使用antlr工具生成了访问者

antlr_grammars/Expression.g4 -o src/test -no-listener -visitor -package test.antlr_grammars

我创建了这个类

public class Visitor extends ExpressionBaseVisitor<Value> {

    @Override
    public Value visitIdentifierString(IdentifierStringContext ctx) {
        System.out.println("visit idString");
        String i = "aaa";
        return new Value(i);
    }

    @Override
    public Value visitIdentifierNum(IdentifierNumContext ctx) {
        System.out.println("visit identifierNum");
        System.out.println("visit " + ctx.getText());
        String i = "100";
        if (i.contains(".")){
            double value = Double.parseDouble(i);
            return new Value(value);
        }
        else {
            int value = Integer.parseInt(i);
            return new Value(value);
        }
    }


    @Override
    public Value visitNumExpression(NumExpressionContext ctx) {
        System.out.println("visit NumExpr");
        System.out.println("visit" + ctx.getText());
        return visit(ctx.numExpr());
    }

    @Override
    public Value visitStringExpression(StringExpressionContext ctx) {
        System.out.println("visit StringExpr");
        return visit(ctx.stringExpr());
    }

    @Override
    public Value visitDouble(DoubleContext ctx) {
        System.out.println("visit double");
        System.out.println("visit" + ctx.getText());
        String str = ctx.getText();
        double i = Double.parseDouble(str);
        return new Value(i);
    }

    @Override
    public Value visitInt(IntContext ctx) {
        System.out.println("visit Int");
        System.out.println("visit " + ctx.getText());
        String str = ctx.getText();
        Integer i = Integer.parseInt(str);
        return new Value(i);
    }

    @Override
    public Value visitBaseStr(BaseStrContext ctx) {
        System.out.println("visit baseString");
        String str = ctx.getText();
        return new Value(str);

    }

    @Override
    public Value visitAddSub(AddSubContext ctx) {
        System.out.println("visit addSub");
        System.out.println("visit" + ctx.getText());
        Value leftVal = visit(ctx.numExpr(0));
        Value rightVal = visit(ctx.numExpr(1));
        if (ctx.op.getType() == ExpressionParser.ADD) {
            if (leftVal.getType().toString() == "INT"
                    && rightVal.getType().toString() == "INT") {
                int left = visit(ctx.numExpr(0)).intVal();
                int right = visit(ctx.numExpr(1)).intVal();
                return new Value(left + right);
            } else if (leftVal.getType().toString() == "DOUBLE"
                    && rightVal.getType().toString() == "DOUBLE") {
                double left = visit(ctx.numExpr(0)).doubleVal();
                double right = visit(ctx.numExpr(1)).doubleVal();
                return new Value(left + right);
            } else if (leftVal.getType().toString() == "INT"
                    && rightVal.getType().toString() == "DOUBLE") {
                double left = (double) visit(ctx.numExpr(0)).intVal();
                double right = visit(ctx.numExpr(1)).doubleVal();
                return new Value(left + right);
            } else {
                double left = visit(ctx.numExpr(0)).doubleVal();
                double right = (double) visit(ctx.numExpr(1)).intVal();
                return new Value(left + right);
            }
        } else {
            if (leftVal.getType().toString() == "INT"
                    && rightVal.getType().toString() == "INT") {
                int left = visit(ctx.numExpr(0)).intVal();
                int right = visit(ctx.numExpr(1)).intVal();
                return new Value(left - right);
            } else if (leftVal.getType().toString() == "DOUBLE"
                    && rightVal.getType().toString() == "DOUBLE") {
                double left = visit(ctx.numExpr(0)).doubleVal();
                double right = visit(ctx.numExpr(1)).doubleVal();
                return new Value(left - right);
            } else if (leftVal.getType().toString() == "INT"
                    && rightVal.getType().toString() == "DOUBLE") {
                double left = (double) visit(ctx.numExpr(0)).intVal();
                double right = visit(ctx.numExpr(1)).doubleVal();
                return new Value(left - right);
            } else {
                double left = visit(ctx.numExpr(0)).doubleVal();
                double right = (double) visit(ctx.numExpr(1)).intVal();
                return new Value(left - right);
            }
        }
    }



    @Override
    public Value visitMulDiv(MulDivContext ctx) {
        System.out.println("visit MulDiv");
        System.out.println("visit" + ctx.getText());
        Value leftVal = visit(ctx.numExpr(0));
        Value rightVal = visit(ctx.numExpr(1));
        if (ctx.op.getType() == ExpressionParser.MUL) {
            if (leftVal.getType().toString() == "INT"
                    && rightVal.getType().toString() == "INT") {
                int left = visit(ctx.numExpr(0)).intVal();
                int right = visit(ctx.numExpr(1)).intVal();
                return new Value(left * right);
            } else if (leftVal.getType().toString() == "DOUBLE"
                    && rightVal.getType().toString() == "DOUBLE") {
                double left = visit(ctx.numExpr(0)).doubleVal();
                double right = visit(ctx.numExpr(1)).doubleVal();
                return new Value(left * right);
            } else if (leftVal.getType().toString() == "INT"
                    && rightVal.getType().toString() == "DOUBLE") {
                double left = (double) visit(ctx.numExpr(0)).intVal();
                double right = visit(ctx.numExpr(1)).doubleVal();
                return new Value(left * right);
            } else {
                double left = visit(ctx.numExpr(0)).doubleVal();
                double right = (double) visit(ctx.numExpr(1)).intVal();
                return new Value(left * right);
            }
        } else {
            if (leftVal.getType().toString() == "INT"
                    && rightVal.getType().toString() == "INT") {
                int left = visit(ctx.numExpr(0)).intVal();
                int right = visit(ctx.numExpr(1)).intVal();
                return new Value(left / right);
            } else if (leftVal.getType().toString() == "DOUBLE"
                    && rightVal.getType().toString() == "DOUBLE") {
                double left = visit(ctx.numExpr(0)).doubleVal();
                double right = visit(ctx.numExpr(1)).doubleVal();
                return new Value(left / right);
            } else if (leftVal.getType().toString() == "INT"
                    && rightVal.getType().toString() == "DOUBLE") {
                double left = (double) visit(ctx.numExpr(0)).intVal();
                double right = visit(ctx.numExpr(1)).doubleVal();
                return new Value(left / right);
            } else {
                double left = visit(ctx.numExpr(0)).doubleVal();
                double right = (double) visit(ctx.numExpr(1)).intVal();
                return new Value(left / right);
            }
        }
    }

    @Override
    public Value visitConcat(ConcatContext ctx) {
        System.out.println("visit Concat");
        String string1 = visit(ctx.stringExpr(0)).stringVal();
        String string2 = visit(ctx.stringExpr(1)).stringVal();
        return new Value(string1.concat(string2));
    }

    @Override
    public Value visitIdString(IdStringContext ctx) {
        return visit(ctx.identifierString());
    }

    @Override
    public Value visitIdNum(IdNumContext ctx) {
        System.out.println("visit idNum");
        System.out.println("visit " + ctx.getText());
        return visit(ctx.identifierNum());
    }

    @Override
    public Value visitParensString(ParensStringContext ctx) {
        System.out.println("visit parensString");
        return visit(ctx.stringExpr());
    }

    @Override
    public Value visitParensNum(ParensNumContext ctx) {
        System.out.println("visit parensNum");
        return visit(ctx.numExpr());
    }

}

问题是我的语法可能不明确,因为如果我编写一些操作,我只会收到以下消息“输入时没有可行的替代方案...” 实际上,我找到了另一种方法来解决这个问题(添加 :Num 和 :String 来区分不同类型的输入),但我想使用这种语义。

我的主要内容如下

  public static void main(String args[]) {
    String expr1 = "10" ;
    System.out.println("Result = " + parse(expr1));
  }

  private static final String parse(String expression) {
    ANTLRInputStream input = new ANTLRInputStream(expression);
    ExpressionLexer lexer = new ExpressionLexer(input);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    ExpressionParser testParser = new ExpressionParser(tokens);
    ParseTree parseTree = testParser.expression();
    Visitor visitor = new Visitor();
    return String.valueOf(visitor.visit(parseTree));
  }

}

不明白哪里错了。 感谢您的帮助 =)

最佳答案

您的 Java 代码中有很多问题:

leftVal.getType().toString() == "DOUBLE"

切勿使用 == 进行字符串相等性检查!

阅读此内容:How do I compare strings in Java?并真正理解它

但是您不应该从一开始就比较字符串。相反,比较整数类型:

leftVal.getType() == ExpressionParser.DOUBLE

这对我来说毫无意义:

String i = "100";
if (i.contains(".")){ 
    ...

事实上,您收到没有可行的替代方案消息是因为您的输入“10”被标记为ID。这是因为 ID token 仅匹配数字,就像 DIGIT 规则一样:

ID: [a-zA-Z0-9]+;
...
DIGIT: [0-9]+ ;

并且由于 ID 是在 DIGIT 之前定义的,因此它具有优先权。要么包含 ID 作为 numExpr 替代方案,要么让 ID 标记以字母开头:

ID: [a-zA-Z] [a-zA-Z0-9]*;

关于java - 使用antlr4的二义性语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23617940/

相关文章:

java - 从 Spring 应用程序停止并重新启动基于文件的 H2 数据库

java - 没有指定泛型类型,方法不返回元素列表,而只是一个列表

regex - 提取 Haskell 中特定类型的所有出现

用于简单 XML 节点字符串的 Android XML 解析器或库

python - 从哪里获得 Python ANTLR 包以使用 StringTemplate?

ANTLR 的 Java 树解析器输出

java - 简单日期格式字符串

java - 为 Unsafe.putOrdered*() 的发布实现获取?

Racket 中的 HTML 解析问题

c - ANTLR4 C 语法不支持 __cdecl?