varias tres pruebas logicas funcion ejemplos condiciones con combinadas buscarv anidado anidada java listener antlr antlr4

java - tres - Si/else declaraciones en ANTLR utilizando oyentes



funcion si y buscarv combinadas (1)

Por defecto, ANTLR 4 genera oyentes. Pero si le da a org.antlr.v4.Tool el parámetro de línea de comando -visitor , ANTLR genera clases de visitante para usted. Funcionan de forma similar a los oyentes, pero le brindan más control sobre qué (sub) árboles se caminan / visitan. Esto es particularmente útil si desea excluir ciertos (sub) árboles (como else / if blocks, como en su caso). Si bien esto se puede hacer utilizando oyentes, es mucho más limpio hacer esto con un visitante. Con los oyentes, deberá introducir variables globales que sigan la pista si es necesario evaluar un (sub) árbol y cuáles no.

Como es el caso, estoy trabajando en un pequeño tutorial de ANTLR 4. Todavía no está hecho, pero publicaré un pequeño ejemplo de trabajo que demuestra el uso de estas clases de visitantes y una construcción de instrucciones if .

1. Gramática

Aquí hay una gramática simple que admite expresiones básicas, if -, while - y log -statements:

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 : . ;

Ahora digamos que le gustaría analizar y evaluar una entrada como esta:

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. Visitante I

Comience por generar el analizador y las clases de visitante:

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

El comando anterior habría generado, entre otros, el archivo MuBaseVisitor<T> . Esta es la clase que vamos a extender sin nuestra propia lógica:

EvalVisitor.java

public class EvalVisitor extends MuBaseVisitor<Value> { // ... }

donde Value es solo un contenedor para cualquiera de los tipos de nuestro idioma ( 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. Prueba I

Para probar las clases, use la siguiente clase 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); } }

y compilar y ejecutar los archivos fuente:

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

(en Windows, el último comando sería: java -cp .;antlr-4.0-complete.jar Main )

Después de ejecutar Main , no pasa nada (¿por supuesto?). Esto se debe a que no implementamos ninguna de las reglas en nuestra clase EvalVisitor . Para poder evaluar el archivo test.mu correctamente, debemos proporcionar una implementación adecuada para las siguientes reglas:

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

4. Visitante II y Prueba II

Aquí hay una implementación de estas reglas:

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; } }

Cuando vuelva a compilar y ejecute Main , lo siguiente se imprimirá en su consola:

2 :: a=true, b=false Done!

Para una implementación de todas las demás reglas, consulte: https://github.com/bkiers/Mu

EDITAR

De @pwwpche, en los comentarios:

para aquellos que usan jdk1.8 y encuentran IndexOutOfBoundsException , antlr 4.0 de alguna manera no es compatible con jdk1.8. Descargue antlr-4.6-complete.jar y reemplace expr POW<assoc=right> expr con <assoc=right>expr POW expr eliminará el error y las advertencias.

Estoy creando un lenguaje de programación simple para un proyecto escolar. Estoy usando ANTLR 4 para generar un analizador y un analizador de mi gramática. Hasta ahora, he estado usando el patrón de escucha ANTLRs para aplicar la funcionalidad real del lenguaje de programación.

Ahora me gustaría implementar las sentencias if / else, pero no estoy seguro de que puedan implementarse realmente cuando se utiliza el patrón de escucha, ya que ANTLR decide en qué orden atravesar el árbol de análisis sintáctico cuando se utilizan oyentes e imagino que la implementación de if / las sentencias else requerirán saltar alrededor del árbol de análisis, dependiendo de qué condición de la declaración se cumpla.

¿Alguien puede decirme si será posible implementar declaraciones if / else usando ANTLR o si tendré que implementar el patrón de visitante yo mismo? Además, ¿alguien puede dar un ejemplo extremadamente simple de la implementación de las declaraciones?