我正在为学校项目创建一种简单的编程语言。我正在使用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
:
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
: '"' (~["\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!";
2.访客我
首先生成解析器和访问者类:
java -cp antlr-4.0-complete.jar org.antlr.v4.Tool Mu.g4 -visitor
上面的命令会生成文件,等等MuBaseVisitor
EvalVisitor.java
public class EvalVisitor extends MuBaseVisitor<Value> {
// ...
}
这里Value
只是我们的任何语言的类型的包装(String
,Boolean
,Double
):
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
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 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
将会消除错误和警告。
我有一个xslt文档。我想在这个文档中使用if语句。我的代码是: 如果返回的值是code1然后写A,如果返回的值是code2然后写B。 我该怎么做?
主要内容:if 结构,if-else 结构,多条件 if-else-if 语句,嵌套 if 的使用Java 支持两种选择语句:if 语句和 switch 语句。其中 if 语句使用布尔表达式或布尔值作为分支条件来进行分支控制,而 switch 语句则用于对多个整型值进行匹配,从而实现分支控制。这些语句允许你只有在程序运行时才能知道其状态的情况下,控制程序的执行过程。如果你没有 C/ C++ 的编程背景,你将会为这两个语句的强大功能和灵活性而感到吃惊。 选择结构(也叫分支结构)解决了顺序结构不能
Swift 条件语句 一个 if 语句 后可跟一个可选的 else if...else 语句,else if...else 语句 在测试多个条件语句时是非常有用的。 当你使用 if , else if , else 语句时需要注意以下几点: if 语句后可以有 0 个或 1 个 else,但是如果 有 else if 语句,else 语句需要在 else if 语句之后。 if 语句后可以有 0
else语句可以与if语句结合使用。 else语句包含else语句中的条件表达式解析为0或FALSE值时执行的代码块。 else语句是一个可选语句, else后面最多只能有一个else语句。 语法 (Syntax) if...else语句的语法是 - if expression: statement(s) else: statement(s) 流程图 (Flow Diagram) 例
我想问一下如何使用下面的代码执行if语句?我试过了,但它给了我一个错误;它说不能把void变成bool。有什么建议吗?