2010-01-11 3 views
12

Ich bin immer noch auf my quest for a really simple language und ich weiß jetzt, dass es keine gibt. Also schreibe ich selbst mit ANTLR3.Erweitern einfache ANTLR grammer zur Unterstützung von Eingabevariablen

fand ich ein wirklich gutes Beispiel in 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;} 
    ; 

Java-Code:

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

dieses ANTLR grammer Verwendung von I Ausdrücke auswerten können wie

(12+14)/2 

und erhalten 13 als Ergebnis.

Nun fehlt mir nur noch eine Möglichkeit, einfache Doppelvariablen in diesen zu injizieren, so dass ich folgendes auswerten kann, indem ich {"A": 12.0, "B": 14.0} als Eingabe gebe Karte:

(A+B)/2 

Irgendwelche Ideen?

Antwort

19

Sie könnten ein Map<String, Double> memory in Ihrem Parser erstellen und ein Identifier in Ihrer Grammatik vorstellen:

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

Dann wird Ihre atomExp Parser-Regel würde wie folgt aussehen:

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

Hier ist ein kleines (komplett) 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;} 
    ; 

Und jetzt keine Notwendigkeit, Theres der Parser/Lexer selbst zu instanziiert, können Sie einfach tun:

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

, die produzieren:

6.283185307179586 

Viel Glück!

+0

danke nochmal !!! – arturh

+0

Kein Problem, schon wieder! :) –

8

Bah, nahm sich die Zeit diese so könnte genauso gut zu implementieren es veröffentlichen, auch wenn ich auf den Stempel zu schlagen war :)

In der Grammatik unter ich das Format der Variable implementiert haben die Zuordnung Sie suchen zu tun .

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

Entschuldigung, Darien :). Nun, ich habe die Grammatik Arturh geschrieben, also habe ich ein bisschen Vorsprung gehabt. +1 von mir trotzdem! –

+0

Keine Sorge, es ist Teil der Gefahren, zu SO beizutragen :) –