Pergunta

Eu estou trabalhando em um simples DSL manipulação de string para fins internos, e eu gostaria o idioma a interpolação de string apoio como ele é usado em Ruby.

Por exemplo:

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

Eu estou tentando implementar meu analisador em ANTLRv3, mas estou bastante inexperiente com o uso de ANTLR por isso estou inseguro sobre como implementar este recurso. Até agora, eu tenha especificado meus strings literais na lexer, mas neste caso eu vou, obviamente, precisa lidar com o conteúdo de interpolação no analisador.

As minhas cordas atual literais gramática esta aparência:

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

Mover o manuseio literal string no analisador parece fazer tudo parar de trabalhar outra pessoa como deveria. pesquisas na web superficiais não deu qualquer informação. Todas as sugestões de como começar a fazer isso?

Foi útil?

Solução

Não sou especialista ANTLR, mas aqui está uma possível gramática:

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
    ;

Como você pode observar, há um par de (Space)*-es dentro do exemplo gramática. Isso ocorre porque o stringLiteral é uma parser-regra em vez de um lexer-regra . Por isso quando tokenizing o arquivo de origem, o lexer não pode saber se um espaço em branco é parte de um literal string, ou é apenas um espaço dentro do arquivo de origem que podem ser ignorados.

Eu testei o exemplo, com um pouco de classe Java e tudo funcionou como esperado:

/* 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
    ;

E uma classe com um método principal para testar tudo:

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

que produz o seguinte resultado:

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

Mais uma vez, não sou especialista, mas isso (pelo menos) dá-lhe a maneira de resolvê-lo.

HTH.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top