栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

Java 使用侦听器的ANTLR中的if / else语句

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java 使用侦听器的ANTLR中的if / else语句

默认情况下,ANTLR 4生成侦听器。但是,如果你提供

org.antlr.v4.Tool
命令行参数
-visitor
,则ANTLR会为你生成访问者类。它们的工作方式与侦听器非常相似,但是可以让你更好地控制要行走/访问的树(子树)。如果要排除某些(子)树(如你的情况那样,则else / if块),这特别有用。尽管可以使用侦听器来完成此操作,但是与访问者一起执行此操作会更加干净。使用侦听器,你需要引入全局变量,以跟踪是否需要评估(子)树,而不需要。

碰巧的是,我正在研究一个小的ANTLR 4教程。还没有完成,但是我将发布一个小的工作示例,演示这些访问者类和if语句构造的用法。

1.语法
这是一个支持基本表达式

if
-,
while
-和- 语句的简单语法
log

Mu.g4

grammar Mu;parse : block EOF ;block : stat* ;stat : assignment | if_stat | while_stat | log | OTHER {System.err.println("unknown char: " + $OTHER.text);} ;assignment : ID ASSIGN expr SCOL ;if_stat : IF condition_block (ELSE IF condition_block)* (ELSE stat_block)? ;condition_block : expr stat_block ;stat_block : OBRACE block CBRACE | stat ;while_stat : WHILE expr stat_block ;log : LOG expr SCOL ;expr : expr POW<assoc=right> expr#powExpr | MINUS expr     #unaryMinusExpr | NOT expr       #notExpr | expr op=(MULT | DIV | MOD) expr      #multiplicationExpr | expr op=(PLUS | MINUS) expr          #additiveExpr | expr op=(LTEQ | GTEQ | LT | GT) expr #relationalExpr | expr op=(EQ | NEQ) expr   #equalityExpr | expr AND expr  #andExpr | expr OR expr   #orExpr | atom#atomExpr ;atom : OPAR expr CPAR #parExpr | (INT | FLOAT)  #numberAtom | (TRUE | FALSE) #booleanAtom | ID  #idAtom | STRING         #stringAtom | NIL #nilAtom ;OR : '||';AND : '&&';EQ : '==';NEQ : '!=';GT : '>';LT : '<';GTEQ : '>=';LTEQ : '<=';PLUS : '+';MINUS : '-';MULT : '*';DIV : '/';MOD : '%';POW : '^';NOT : '!';SCOL : ';';ASSIGN : '=';OPAR : '(';CPAR : ')';OBRACE : '{';CBRACE : '}';TRUE : 'true';FALSE : 'false';NIL : 'nil';IF : 'if';ELSE : 'else';WHILE : 'while';LOG : 'log';ID : [a-zA-Z_] [a-zA-Z_0-9]* ;INT : [0-9]+ ;FLOAT : [0-9]+ '.' [0-9]*  | '.' [0-9]+ ;STRING : '"' (~["rn] | '""')* '"' ;COMMENT : '#' ~[rn]* -> skip ;SPACE : [ trn] -> skip ;OTHER : .  ;

现在,假设你要分析和评估如下输入:

test.mu

a = true;b = false;if a && b {  log "1 :: a=" + a +", b=" + b;}else if a || b {  log "2 :: a=" + a +", b=" + b;}else {  log "3 :: a=" + a +", b=" + b;}log "Done!";

2.访客我

首先生成解析器和访问者类:

java -cp antlr-4.0-complete.jar org.antlr.v4.Tool Mu.g4 -visitor

上面的命令会生成文件,等等MubaseVisitor。这是我们要扩展的类,没有自己的逻辑:

evalVisitor.javapublic class evalVisitor extends MubaseVisitor<Value> {    // ...}

这里

Value
只是我们的任何语言的类型的包装(
String
Boolean
Double
):

Value.javapublic class Value {    public static Value VOID = new Value(new Object());    final Object value;    public Value(Object value) {        this.value = value;    }    public Boolean asBoolean() {        return (Boolean)value;    }    public Double asDouble() {        return (Double)value;    }    public String asString() {        return String.valueOf(value);    }    public boolean isDouble() {        return value instanceof Double;    }    @Override    public int hashCode() {        if(value == null) { return 0;        }        return this.value.hashCode();    }    @Override    public boolean equals(Object o) {        if(value == o) { return true;        }        if(value == null || o == null || o.getClass() != value.getClass()) { return false;        }        Value that = (Value)o;        return this.value.equals(that.value);    }    @Override    public String toString() {        return String.valueOf(value);    }}

3.测试一

要测试这些类,请使用以下Main类:

Main.javaimport org.antlr.v4.runtime.ANTLRFileStream;import org.antlr.v4.runtime.CommonTokenStream;import org.antlr.v4.runtime.tree.ParseTree;public class Main {    public static void main(String[] args) throws Exception {        MuLexer lexer = new MuLexer(new ANTLRFileStream("test.mu"));        MuParser parser = new MuParser(new CommonTokenStream(lexer));        ParseTree tree = parser.parse();        evalVisitor visitor = new evalVisitor();        visitor.visit(tree);    }}

并编译并运行源文件:

javac -cp antlr-4.0-complete.jar *.javajava -cp .:antlr-4.0-complete.jar Main

(在Windows上,最后的命令是:

java -cp .;antlr-4.0-complete.jar Main

运行后

Main
,什么都没有发生(当然?)。这是因为我们没有在
evalVisitor
类中实现任何规则。为了能够
test.mu
正确评估文件,我们需要为以下规则提供正确的实现:

  • if_stat
  • andExpr
  • orExpr
  • plusExpr
  • assignment
  • idAtom
  • booleanAtom
  • stringAtom
  • log

4.访客II和测试II

这是这些规则的实现:

import org.antlr.v4.runtime.misc.NotNull;import java.util.HashMap;import java.util.List;import java.util.Map;public class evalVisitor extends MubaseVisitor<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>();    // assignment/id overrides    @Override    public Value visitAssignment(MuParser.AssignmentContext ctx) {        String id = ctx.ID().getText();        Value value = this.visit(ctx.expr());        return memory.put(id, value);    }    @Override    public Value visitIdAtom(MuParser.IdAtomContext ctx) {        String id = ctx.getText();        Value value = memory.get(id);        if(value == null) { throw new RuntimeException("no such variable: " + id);        }        return value;    }    // atom overrides    @Override    public Value visitStringAtom(MuParser.StringAtomContext ctx) {        String str = ctx.getText();        // strip quotes        str = str.substring(1, str.length() - 1).replace("""", """);        return new Value(str);    }    @Override    public Value visitNumberAtom(MuParser.NumberAtomContext ctx) {        return new Value(Double.valueOf(ctx.getText()));    }    @Override    public Value visitBooleanAtom(MuParser.BooleanAtomContext ctx) {        return new Value(Boolean.valueOf(ctx.getText()));    }    @Override    public Value visitNilAtom(MuParser.NilAtomContext ctx) {        return new Value(null);    }    // expr overrides    @Override    public Value visitParExpr(MuParser.ParExprContext ctx) {        return this.visit(ctx.expr());    }    @Override    public Value visitPowExpr(MuParser.PowExprContext ctx) {        Value left = this.visit(ctx.expr(0));        Value right = this.visit(ctx.expr(1));        return new Value(Math.pow(left.asDouble(), right.asDouble()));    }    @Override    public Value visitUnaryMinusExpr(MuParser.UnaryMinusExprContext ctx) {        Value value = this.visit(ctx.expr());        return new Value(-value.asDouble());    }    @Override    public Value visitNotExpr(MuParser.NotExprContext ctx) {        Value value = this.visit(ctx.expr());        return new Value(!value.asBoolean());    }    @Override    public Value visitMultiplicationExpr(@NotNull MuParser.MultiplicationExprContext ctx) {        Value left = this.visit(ctx.expr(0));        Value right = this.visit(ctx.expr(1));        switch (ctx.op.getType()) { case MuParser.MULT:     return new Value(left.asDouble() * right.asDouble()); case MuParser.DIV:     return new Value(left.asDouble() / right.asDouble()); case MuParser.MOD:     return new Value(left.asDouble() % right.asDouble()); default:     throw new RuntimeException("unknown operator: " + MuParser.tokenNames[ctx.op.getType()]);        }    }    @Override    public Value visitAdditiveExpr(@NotNull MuParser.AdditiveExprContext ctx) {        Value left = this.visit(ctx.expr(0));        Value right = this.visit(ctx.expr(1));        switch (ctx.op.getType()) { case MuParser.PLUS:     return left.isDouble() && right.isDouble() ?  new Value(left.asDouble() + right.asDouble()) :  new Value(left.asString() + right.asString()); case MuParser.MINUS:     return new Value(left.asDouble() - right.asDouble()); default:     throw new RuntimeException("unknown operator: " + MuParser.tokenNames[ctx.op.getType()]);        }    }    @Override    public Value visitRelationalExpr(@NotNull MuParser.RelationalExprContext ctx) {        Value left = this.visit(ctx.expr(0));        Value right = this.visit(ctx.expr(1));        switch (ctx.op.getType()) { case MuParser.LT:     return new Value(left.asDouble() < right.asDouble()); case MuParser.LTEQ:     return new Value(left.asDouble() <= right.asDouble()); case MuParser.GT:     return new Value(left.asDouble() > right.asDouble()); case MuParser.GTEQ:     return new Value(left.asDouble() >= right.asDouble()); default:     throw new RuntimeException("unknown operator: " + MuParser.tokenNames[ctx.op.getType()]);        }    }    @Override    public Value visitEqualityExpr(@NotNull MuParser.EqualityExprContext ctx) {        Value left = this.visit(ctx.expr(0));        Value right = this.visit(ctx.expr(1));        switch (ctx.op.getType()) { case MuParser.EQ:     return left.isDouble() && right.isDouble() ?  new Value(Math.abs(left.asDouble() - right.asDouble()) < SMALL_VALUE) :  new Value(left.equals(right)); case MuParser.NEQ:     return left.isDouble() && right.isDouble() ?  new Value(Math.abs(left.asDouble() - right.asDouble()) >= SMALL_VALUE) :  new Value(!left.equals(right)); default:     throw new RuntimeException("unknown operator: " + MuParser.tokenNames[ctx.op.getType()]);        }    }    @Override    public Value visitAndExpr(MuParser.AndExprContext ctx) {        Value left = this.visit(ctx.expr(0));        Value right = this.visit(ctx.expr(1));        return new Value(left.asBoolean() && right.asBoolean());    }    @Override    public Value visitOrExpr(MuParser.OrExprContext ctx) {        Value left = this.visit(ctx.expr(0));        Value right = this.visit(ctx.expr(1));        return new Value(left.asBoolean() || right.asBoolean());    }    // log override    @Override    public Value visitLog(MuParser.LogContext ctx) {        Value value = this.visit(ctx.expr());        System.out.println(value);        return value;    }    // if override    @Override    public Value visitIf_stat(MuParser.If_statContext ctx) {        List<MuParser.Condition_blockContext> conditions =  ctx.condition_block();        boolean evaluatedBlock = false;        for(MuParser.Condition_blockContext condition : conditions) { Value evaluated = this.visit(condition.expr()); if(evaluated.asBoolean()) {     evaluatedBlock = true;     // evaluate this block whose expr==true     this.visit(condition.stat_block());     break; }        }        if(!evaluatedBlock && ctx.stat_block() != null) { // evaluate the else-stat_block (if present == not null) this.visit(ctx.stat_block());        }        return Value.VOID;    }    // while override    @Override    public Value visitWhile_stat(MuParser.While_statContext ctx) {        Value value = this.visit(ctx.expr());        while(value.asBoolean()) { // evaluate the pre block this.visit(ctx.stat_block()); // evaluate the expression value = this.visit(ctx.expr());        }        return Value.VOID;    }}

重新编译并运行时Main,将在控制台上显示以下内容:

2 :: a=true, b=falseDone!

有关所有其他规则的实现,请参见:https : //github.com/bkiers/Mu

编辑

来自@pwwpche,在注释中:

对于使用jdk1.8并遇到的用户

IndexOutOfBoundsException,antlr 4.0
某种程度上与jdk1.8不兼容。下载
antlr-4.6-complete.jar
,并替换
expr POW<assoc=right> expr
<assoc=right>expr POW expr
将会消除错误和警告。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/431957.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号