Pergunta

Gostaria de começar com o ANTLR, mas depois de passar algumas horas revisando os exemplos no antlr.org site, ainda não consigo entender claramente a gramática do processo Java.

Existe algum exemplo simples, algo como uma calculadora de quatro operações implementada com ANTLR passando pela definição do analisador até o código-fonte Java?

Foi útil?

Solução

Observação:esta resposta é para ANTLR3!Se você está procurando um ANTLR4 exemplo, então isso Perguntas e respostas demonstra como criar um analisador de expressão simples e um avaliador usando ANTLR4.


Você primeiro cria uma gramática.Abaixo está uma pequena gramática que você pode usar para avaliar expressões construídas usando os 4 operadores matemáticos básicos:+, -, * e /.Você também pode agrupar expressões usando parênteses.

Observe que esta gramática é muito básica:ele não lida com operadores unários (o sinal de menos em:-1+9) ou decimais como 0,99 (sem número inicial), para citar apenas duas deficiências.Este é apenas um exemplo que você pode trabalhar sozinho.

Aqui está o conteúdo do arquivo de gramática Exp.g:

grammar Exp;

/* This will be the entry point of our parser. */
eval
    :    additionExp
    ;

/* Addition and subtraction have the lowest precedence. */
additionExp
    :    multiplyExp 
         ( '+' multiplyExp 
         | '-' multiplyExp
         )* 
    ;

/* Multiplication and division have a higher precedence. */
multiplyExp
    :    atomExp
         ( '*' atomExp 
         | '/' atomExp
         )* 
    ;

/* An expression atom is the smallest part of an expression: a number. Or 
   when we encounter parenthesis, we're making a recursive call back to the
   rule 'additionExp'. As you can see, an 'atomExp' has the highest precedence. */
atomExp
    :    Number
    |    '(' additionExp ')'
    ;

/* A number: can be an integer value, or a decimal value */
Number
    :    ('0'..'9')+ ('.' ('0'..'9')+)?
    ;

/* We're going to ignore all white space characters */
WS  
    :   (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;}
    ;

(As regras do analisador começam com uma letra minúscula e as regras do lexer começam com uma letra maiúscula)

Depois de criar a gramática, você desejará gerar um analisador e um lexer a partir dela.Faça o download do Frasco ANTLR e armazene-o no mesmo diretório do seu arquivo de gramática.

Execute o seguinte comando em seu shell/prompt de comando:

java -cp antlr-3.2.jar org.antlr.Tool Exp.g

Não deve produzir nenhuma mensagem de erro e os arquivos ExpLexer.java, ExpParser.java e Exp.tokens agora deve ser gerado.

Para ver se tudo funciona corretamente, crie esta classe de teste:

import org.antlr.runtime.*;

public class ANTLRDemo {
    public static void main(String[] args) throws Exception {
        ANTLRStringStream in = new ANTLRStringStream("12*(5-6)");
        ExpLexer lexer = new ExpLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        ExpParser parser = new ExpParser(tokens);
        parser.eval();
    }
}

e compile-o:

// *nix/MacOS
javac -cp .:antlr-3.2.jar ANTLRDemo.java

// Windows
javac -cp .;antlr-3.2.jar ANTLRDemo.java

e execute-o:

// *nix/MacOS
java -cp .:antlr-3.2.jar ANTLRDemo

// Windows
java -cp .;antlr-3.2.jar ANTLRDemo

Se tudo correr bem, nada será impresso no console.Isso significa que o analisador não encontrou nenhum erro.Quando você muda "12*(5-6)" em "12*(5-6" e então recompilar e executá-lo, deverá ser impresso o seguinte:

line 0:-1 mismatched input '<EOF>' expecting ')'

Ok, agora queremos adicionar um pouco de código Java à gramática para que o analisador realmente faça algo útil.Adicionar código pode ser feito colocando { e } dentro da sua gramática com algum código Java simples dentro dela.

Mas primeiro:todas as regras do analisador no arquivo gramatical devem retornar um valor duplo primitivo.Você pode fazer isso adicionando returns [double value] depois de cada regra:

grammar Exp;

eval returns [double value]
    :    additionExp
    ;

additionExp returns [double value]
    :    multiplyExp 
         ( '+' multiplyExp 
         | '-' multiplyExp
         )* 
    ;

// ...

que precisa de pouca explicação:espera-se que cada regra retorne um valor duplo.Agora, para "interagir" com o valor de retorno double value (que NÃO está dentro de um bloco de código Java simples {...}) de dentro de um bloco de código, você precisará adicionar um cifrão na frente de value:

grammar Exp;

/* This will be the entry point of our parser. */
eval returns [double value]                                                  
    :    additionExp { /* plain code block! */ System.out.println("value equals: "+$value); }
    ;

// ...

Aqui está a gramática, mas agora com o código Java adicionado:

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

e desde o nosso eval regra agora retorna um duplo, mude seu ANTLRDemo.java para isto:

import org.antlr.runtime.*;

public class ANTLRDemo {
    public static void main(String[] args) throws Exception {
        ANTLRStringStream in = new ANTLRStringStream("12*(5-6)");
        ExpLexer lexer = new ExpLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        ExpParser parser = new ExpParser(tokens);
        System.out.println(parser.eval()); // print the value
    }
}

Novamente (re) gere um novo lexer e analisador a partir de sua gramática (1), compile todas as classes (2) e execute ANTLRDemo (3):

// *nix/MacOS
java -cp antlr-3.2.jar org.antlr.Tool Exp.g   // 1
javac -cp .:antlr-3.2.jar ANTLRDemo.java      // 2
java -cp .:antlr-3.2.jar ANTLRDemo            // 3

// Windows
java -cp antlr-3.2.jar org.antlr.Tool Exp.g   // 1
javac -cp .;antlr-3.2.jar ANTLRDemo.java      // 2
java -cp .;antlr-3.2.jar ANTLRDemo            // 3

e agora você verá o resultado da expressão 12*(5-6) impresso em seu console!

De novo:esta é uma explicação muito breve.Eu encorajo você a navegar no Wiki ANTLR e leia alguns tutoriais e/ou brinque um pouco com o que acabei de postar.

Boa sorte!

EDITAR:

Esta postagem mostra como estender o exemplo acima para que um Map<String, Double> pode ser fornecido que contenha variáveis ​​na expressão fornecida.

Para que esse código funcionasse com uma versão atual do Antlr (junho de 2014), precisei fazer algumas alterações. ANTLRStringStream precisava para se tornar ANTLRInputStream, o valor retornado necessário para mudar de parser.eval() para parser.eval().value, e eu precisava remover o WS cláusula no final, porque valores de atributos como $channel não podem mais aparecer em ações lexer.

Outras dicas

Tutorial de mega mega antlr por gabriele tomassetti é muito útil

Possui exemplos gramaticais, exemplos de visitantes em diferentes idiomas (Java, JavaScript, C# e Python) e muitas outras coisas. Altamente recomendado.

EDIT: Outros artigos úteis de Gabriele Tomassetti no Antlr

Para Antlr 4, o processo de geração de código Java está abaixo:-

java -cp antlr-4.5.3-complete.jar org.antlr.v4.Tool Exp.g

Atualize seu nome de jarra no ClassPath de acordo.

No https://github.com/bitplan/com.bitplan.antlr Você encontrará uma biblioteca ANTLR Java com algumas classes de auxiliares úteis e alguns exemplos completos. Está pronto para ser usado com o Maven e se você gosta de eclipse e maven.

https://github.com/bitplan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/exp/exp.g4

é uma linguagem de expressão simples que pode fazer multiplicar e adicionar operações.https://github.com/bitplan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/testexpparser.java tem os testes de unidade correspondentes para isso.

https://github.com/bitplan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/iri/iriparser.g4 é um analisador IRI que foi dividido nas três partes:

  1. Gramática do analisador
  2. Gramática Lexer
  3. Gramática Lexbasica importada

https://github.com/bitplan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/testiriparser.javatem os testes de unidade para isso.

Pessoalmente, achei essa a parte mais complicada para acertar. Ver http://wiki.bitplan.com/index.php/antlr_maven_plugin

https://github.com/bitplan/com.bitplan.antlr/tree/master/src/main/antlr4/com/bitplan/expr

Contém mais três exemplos que foram criados para uma questão de desempenho do ANTLR4 em uma versão anterior. Enquanto isso, esses problemas foram corrigidos como o teste https://github.com/bitplan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/testissue994.java shows.

A versão 4.7.1 foi um pouco diferente: para importação:

import org.antlr.v4.runtime.*;

Para o segmento principal - observe os Charstreams:

CharStream in = CharStreams.fromString("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top