Frage

Ich arbeite an einer einfachen String-Manipulation DSL für interne Zwecke, und ich mochte die Sprache wie String-Interpolation zu unterstützen, wie es in Ruby verwendet wird.

Zum Beispiel:

name = "Bob"
msg = "Hello ${name}!"
print(msg)   # prints "Hello Bob!"

Ich versuche, meinen Parser in ANTLRv3 zu implementieren, aber ich bin ziemlich unerfahren mit der Verwendung von ANTLR so bin ich nicht sicher, wie diese Funktion zu implementieren. Bisher habe ich meine Stringliterale in der Lexer angegeben, aber in diesem Fall werde ich natürlich die Interpolation Inhalt in den Parser behandeln müssen.

Meine aktuelle Stringliteral Grammatik sieht wie folgt aus:

STRINGLITERAL : '"' ( StringEscapeSeq | ~( '\\' | '"' | '\r' | '\n' ) )* '"' ;
fragment StringEscapeSeq : '\\' ( 't' | 'n' | 'r' | '"' | '\\' | '$' | ('0'..'9')) ;

die Stringliteral Handhabung in den Parser bewegen scheint zu funktionieren alles andere aufhören zu machen, wie es sollte. Flüchtige Websuchen brachte keine Informationen. Irgendwelche Vorschläge, wie auf diese zu beginnen?

War es hilfreich?

Lösung

Ich bin kein Experte ANTLR, aber hier ist eine mögliche Grammatik:

grammar Str;

parse
    :    ((Space)* statement (Space)* ';')+ (Space)* EOF
    ;

statement
    :    print | assignment
    ;

print
    :    'print' '(' (Identifier | stringLiteral) ')' 
    ;

assignment
    :    Identifier (Space)* '=' (Space)* stringLiteral
    ;

stringLiteral
    :    '"' (Identifier | EscapeSequence | NormalChar | Space | Interpolation)* '"'
    ;

Interpolation
    :    '${' Identifier '}'
    ;

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

EscapeSequence
    :    '\\' SpecialChar
    ;

SpecialChar
    :     '"' | '\\' | '$'
    ;

Space
    :    (' ' | '\t' | '\r' | '\n')
    ;

NormalChar
    :    ~SpecialChar
    ;

Wie Sie sehen, gibt es ein paar (Space)*-es in der Beispielgrammatik. Dies liegt daran, die stringLiteral ist eine Parser-Regel anstelle eine Lexer-Regel . Deshalb, wenn die Quelldatei Zeichenüber kann die Lexer nicht wissen, ob ein weißer Raum Teil eines Stringliteral ist, oder ist nur ein Raum innerhalb der Quelldatei, die ignoriert werden kann.

Getestet habe ich das Beispiel mit einer kleinen Java-Klasse und alle arbeiteten als erwartet:

/* the same grammar, but now with a bit of Java code in it */
grammar Str;

@parser::header {
    package antlrdemo;
    import java.util.HashMap;
}

@lexer::header {
    package antlrdemo;
}

@parser::members {
    HashMap<String, String> vars = new HashMap<String, String>();
}

parse
    :    ((Space)* statement (Space)* ';')+ (Space)* EOF
    ;

statement
    :    print | assignment
    ;

print
    :    'print' '(' 
         (    id=Identifier    {System.out.println("> "+vars.get($id.text));} 
         |    st=stringLiteral {System.out.println("> "+$st.value);}
         ) 
         ')' 
    ;

assignment
    :    id=Identifier (Space)* '=' (Space)* st=stringLiteral {vars.put($id.text, $st.value);}
    ;

stringLiteral returns [String value]
    :    '"'
        {StringBuilder b = new StringBuilder();} 
        (    id=Identifier           {b.append($id.text);}
        |    es=EscapeSequence       {b.append($es.text);}
        |    ch=(NormalChar | Space) {b.append($ch.text);}
        |    in=Interpolation        {b.append(vars.get($in.text.substring(2, $in.text.length()-1)));}
        )* 
        '"'
        {$value = b.toString();}
    ;

Interpolation
    :    '${' i=Identifier '}'
    ;

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

EscapeSequence
    :    '\\' SpecialChar
    ;

SpecialChar
    :     '"' | '\\' | '$'
    ;

Space
    :    (' ' | '\t' | '\r' | '\n')
    ;

NormalChar
    :    ~SpecialChar
    ;

Und eine Klasse mit einer main-Methode testen sie alle:

package antlrdemo;

import org.antlr.runtime.*;

public class ANTLRDemo {
    public static void main(String[] args) throws RecognitionException {
        String source = "name = \"Bob\";        \n"+
                "msg = \"Hello ${name}\";       \n"+
                "print(msg);                    \n"+
                "print(\"Bye \\${for} now!\");    ";
        ANTLRStringStream in = new ANTLRStringStream(source);
        StrLexer lexer = new StrLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        StrParser parser = new StrParser(tokens);
        parser.parse();
    }
}

, die die folgende Ausgabe:

> Hello Bob
> Bye \${for} now!

Noch einmal, ich bin kein Experte, aber das (mindestens) gibt Ihnen a Weg, es zu lösen.

HTH.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top