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

我正在为学校项目创建一种简单的编程语言。我正在使用ANTLR 4从我的语法生成词法分析器和解析器。到目前为止,我一直在使用ANTLR侦听器模式来应用编程语言的实际功能。

现在,我想实现if / else语句,但是我不确定在使用侦听器模式时实际上可以实现这些语句,因为ANTLR决定使用侦听器时以哪种顺序遍历解析树,我想像一下if /的实现else语句将需要根据满足该条件的条件在解析树中跳转。

谁能告诉我是否可以使用ANTLR实现if / else语句,或者我是否必须自己实现访问者模式?另外,有人可以给出一个非常简单的语句实现示例吗?

回答:

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

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

1.语法

这是一个支持基本表达式if-,while-和- 语句的简单语法log

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

: '"' (~["\r\n] | '""')* '"'

;

COMMENT

: '#' ~[\r\n]* -> skip

;

SPACE

: [ \t\r\n] -> 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!";

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

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

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

EvalVisitor.java

public class EvalVisitor extends MuBaseVisitor<Value> {

// ...

}

这里Value只是我们的任何语言的类型的包装(StringBooleanDouble):

Value.java

public 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.java

import 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 *.java

java -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

这是这些规则的实现:

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 code block

this.visit(ctx.stat_block());

// evaluate the expression

value = this.visit(ctx.expr());

}

return Value.VOID;

}

}

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

2 :: a=true, b=false

Done!

有关所有其他规则的实现,请参见: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将会消除错误和警告。

以上是 Java 使用侦听器的ANTLR中的if / else语句 的全部内容, 来源链接: utcz.com/qa/419746.html

回到顶部