2010-01-11 12 views
12

Todavía estoy en my quest for a really simple language y ahora sé que no hay ninguna. Así que estoy escribiendo uno usando ANTLR3.Ampliando la gramática ANTLR simple para admitir variables de entrada

me encontré con un muy buen ejemplo de this answer:

Exp.g:

grammar Exp; 

eval returns [double value] 
    : exp=additionExp {$value = $exp.value;} 
    ; 

additionExp returns [double value] 
    : m1=multiplyExp  {$value = $m1.value;} 
     ('+' m2=multiplyExp {$value += $m2.value;} 
     | '-' m2=multiplyExp {$value -= $m2.value;} 
     )* 
    ; 

multiplyExp returns [double value] 
    : a1=atomExp  {$value = $a1.value;} 
     ('*' a2=atomExp {$value *= $a2.value;} 
     | '/' a2=atomExp {$value /= $a2.value;} 
     )* 
    ; 

atomExp returns [double value] 
    : n=Number    {$value = Double.parseDouble($n.text);} 
    | '(' exp=additionExp ')' {$value = $exp.value;} 
    ; 

Number 
    : ('0'..'9')+ ('.' ('0'..'9')+)? 
    ; 

WS 
    : (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;} 
    ; 

Código Java:

public Double evaluate(String string, Map<String, Double> input) throws RecognitionException { 
    ANTLRStringStream in = new ANTLRStringStream(string); 
    ExpLexer lexer = new ExpLexer(in); 
    CommonTokenStream tokens = new CommonTokenStream(lexer); 
    return new ExpParser(tokens).eval(); 
} 

El uso de este gramática antlr puedo evaluar expresiones como

(12+14)/2 

y obtenga 13 como resultado.

Ahora, lo único que falta para mi caso de uso es una forma de insertar variables dobles simples en esto, de modo que pueda evaluar lo siguiente al suministrar {"A": 12.0, "B": 14.0} como entrada mapa:

(A+B)/2 

¿Alguna idea?

Respuesta

19

Se puede crear un Map<String, Double> memory en su programa de análisis e introducir un Identifier en su gramática:

Identifier 
    : ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')* 
    ; 

A continuación, la regla de analizador atomExp se vería así:

atomExp returns [double value] 
    : n=Number    {$value = Double.parseDouble($n.text);} 
    | i=Identifier   {$value = memory.get($i.text);} // <- added! 
    | '(' exp=additionExp ')' {$value = $exp.value;} 
    ; 

He aquí una pequeña (completa) demo:

grammar Exp; 

@parser::members { 

    private java.util.HashMap<String, Double> memory = new java.util.HashMap<String, Double>(); 

    public static Double eval(String expression) throws Exception { 
    return eval(expression, new java.util.HashMap<String, Double>()); 
    } 

    public static Double eval(String expression, java.util.Map<String, Double> vars) throws Exception { 
    ANTLRStringStream in = new ANTLRStringStream(expression); 
    ExpLexer lexer = new ExpLexer(in); 
    CommonTokenStream tokens = new CommonTokenStream(lexer); 
    ExpParser parser = new ExpParser(tokens); 
    parser.memory.putAll(vars); 
    return parser.parse(); 
    } 
} 

parse returns [double value] 
    : exp=additionExp {$value = $exp.value;} 
    ; 

additionExp returns [double value] 
    : m1=multiplyExp  {$value = $m1.value;} 
     ('+' m2=multiplyExp {$value += $m2.value;} 
     | '-' m2=multiplyExp {$value -= $m2.value;} 
     )* 
    ; 

multiplyExp returns [double value] 
    : a1=atomExp  {$value = $a1.value;} 
     ('*' a2=atomExp {$value *= $a2.value;} 
     | '/' a2=atomExp {$value /= $a2.value;} 
     )* 
    ; 

atomExp returns [double value] 
    : n=Number    {$value = Double.parseDouble($n.text);} 
    | i=Identifier   {$value = memory.get($i.text);} 
    | '(' exp=additionExp ')' {$value = $exp.value;} 
    ; 

Identifier 
    : ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')* 
    ; 

Number 
    : ('0'..'9')+ ('.' ('0'..'9')+)? 
    ; 

WS 
    : (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;} 
    ; 

Y Ahora no hay necesidad de crear una instancia del analizador/analizador léxico a sí mismo, sólo tiene que hacer:

import org.antlr.runtime.*; 
import java.util.*; 

public class ANTLRDemo { 
    public static void main(String[] args) throws Exception { 
     Map<String, Double> vars = new HashMap<String, Double>(); 
     vars.put("two", 2.0); 
     vars.put("pi", Math.PI); 
     System.out.println(ExpParser.eval("two * pi", vars)); 
    } 
} 

que producirá:

6.283185307179586 

Buena suerte!

+0

gracias otra vez !!! – arturh

+0

¡No hay problema, otra vez! :) –

8

Bah, se tomó el tiempo para poner en práctica esta manera lo mismo que publicar que, a pesar de que me ganó de mano :)

En la gramática a continuación he implementado el formato de la variable de asignación buscaba hacer .

grammar Exp; 



eval returns [double value] 
scope 
{ 
    java.util.Hashtable varMap; 
} 
@init 
{ 
    $eval::varMap = new java.util.Hashtable(); 
} 
: exp=additionExp {$value = $exp.value;} 
    | varList 
; 

additionExp returns [double value] 
    : m1=multiplyExp  {$value = $m1.value;} 
     ('+' m2=multiplyExp {$value += $m2.value;} 
     | '-' m2=multiplyExp {$value -= $m2.value;} 
     )* 
    ; 

multiplyExp returns [double value] 
    : a1=atomExp  {$value = $a1.value;} 
     ('*' a2=atomExp {$value *= $a2.value;} 
     | '/' a2=atomExp {$value /= $a2.value;} 
     )* 
    ; 

atomExp returns [double value] 
    : n=Number    {$value = Double.parseDouble($n.text);} 
    | v=ID   {$value = $eval::varMap.get($v);} 
    | '(' exp=additionExp ')' {$value = $exp.value;} 
    ; 

varList 
    : 
    OPEN_BRACE assignVar (COMMA assignVar)+ CLOSE_BRACE 
    ; 

assignVar 
    :QUOTE var=ID n=Number QUOTE COLON { $eval::varMap.put($var, $n); } 
    ; 


Number 
    : ('0'..'9')+ ('.' ('0'..'9')+)? 
    ; 

WS 
    : (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;} 
    ; 



fragment LETTER: LOWER | UPPER; 
fragment LOWER: 'a'..'z'; 
fragment UPPER: 'A'..'Z'; 

OPEN_BRACE 
    : '{' 
    ; 

CLOSE_BRACE 
    : '}' 
    ; 

COLON : ';'; 
COMMA : ','; 

QUOTE : '"'; 

ID 
: LETTER*; 
+0

Lo siento por eso Darien :). Bueno, escribí la gramática arturh publicada en primer lugar, así que tuve un poco de ventaja. ¡+1 de mí sin embargo! –

+0

Sin preocupaciones, es parte de los peligros de contribuir a SO :) –

Cuestiones relacionadas