2010-01-11 11 views
12

Je suis toujours sur my quest for a really simple language et je sais maintenant qu'il n'y en a pas. Donc j'en écris un moi-même en utilisant ANTLR3.Étendre simple grammaire ANTLR pour soutenir les variables d'entrée

Je trouve un exemple vraiment super dans 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;} 
    ; 

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

En utilisant cette Grammer ANTLR Je peux évaluer les expressions comme

(12+14)/2 

et d'obtenir 13 à la suite. Maintenant, la seule chose qui manque pour mon cas d'utilisation est une façon d'injecter des doubles variables simples, afin que je puisse évaluer ce qui suit en fournissant {"A": 12.0, "B": 14.0} comme entrée carte:

(A+B)/2 

Des idées?

Répondre

19

Vous pouvez créer un Map<String, Double> memory dans votre analyseur et introduire un Identifier dans votre grammaire:

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

Ensuite, votre règle d'analyseur atomExp ressemblerait à ceci:

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

Voici un petit (complet) 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;} 
    ; 

Et maintenant Theres aucun besoin d'instancier l'analyseur/lexer vous, vous pouvez simplement faire:

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

qui produira:

6.283185307179586 

Bonne chance!

+0

merci encore !!! – arturh

+0

Pas de problème, encore une fois! :) –

8

Bah, a pris le temps de mettre en œuvre cette sorte pourrait aussi bien poster, même si je bats au poinçon :)

Dans la grammaire ci-dessous, je l'ai mis en œuvre le format de la variable assignant que vous cherchez à faire .

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

Désolé pour ça Darien :). Eh bien, j'ai écrit la grammaire arturh posté en premier lieu, donc j'ai eu un peu d'avance. +1 de moi néanmoins! –

+0

Pas de soucis, ça fait partie des dangers de contribuer à SO :) –

Questions connexes