ANTLR: Existe um exemplo simples?

votos
201

Eu gostaria de começar com ANTLR, mas depois de passar algumas horas revisando os exemplos na antlr.org site, eu ainda não pode obter uma compreensão clara da gramática para o processo de Java.

Existe algum exemplo simples, algo como uma calculadora de quatro operações implementadas com ANTLR passando pela definição de analisador e todo o caminho para o código-fonte Java?

Publicado 19/12/2009 em 00:35
fonte usuário
Em outras línguas...                            


5 respostas

votos
409

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

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

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

(Regras Parser começar com uma letra minúscula, e regras lexer começar com uma letra maiúscula)

Depois de criar a gramática, você vai querer gerar um analisador e lexer dele. Baixe o frasco ANTLR e armazená-lo no mesmo diretório que o seu arquivo de gramática.

Execute o seguinte comando no prompt de shell / comando:

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

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

Para ver se tudo funciona corretamente, criar 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 compilá-lo:

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

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

e depois executá-lo:

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

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

Se tudo correr bem, nada está sendo impresso para o console. Isto significa que o analisador não encontrou qualquer erro. Quando você muda "12*(5-6)"para "12*(5-6"e, em seguida, recompilar e executá-lo, não deve ser impresso o seguinte:

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

Ok, agora nós queremos adicionar um pouco de código Java para a gramática para que o analisador realmente faz alguma coisa útil. Código acrescentando pode ser feito através da colocação {e }dentro de sua gramática com algum código Java simples em seu interior.

Mas primeiro: todas as regras do analisador no arquivo de gramática deve 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: cada regra é esperado para retornar 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ê vai precisar adicionar um sinal de dólar em frente 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 acrescentou:

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 a nossa evalregra agora retorna um duplo, mudar sua ANTLRDemo.java a este:

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

Mais uma vez (re) gerar um novo lexer e analisador de sua gramática (1), compilar todas as classes (2) e executar 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 você vai ver agora o resultado da expressão 12*(5-6)impressa para o seu console!

Mais uma vez: esta é uma breve explicação. Encorajo-vos a navegar na wiki ANTLR e ler alguns tutoriais e / ou jogar um pouco com o que eu só postou.

Boa sorte!

EDITAR:

Este pós mostra como para estender o exemplo acima de modo a que uma Map<String, Double>pode ser fornecida que contém variáveis na expressão fornecida.

E este Q & A demonstra como criar um analisador simples expressão, e avaliador usando ANTLR4 .

Para obter este código de trabalho com uma versão atual do Antlr (Junho de 2014) que eu precisava fazer algumas mudanças. ANTLRStringStreamnecessário para se tornar ANTLRInputStream, o valor retornado precisava mudar a partir parser.eval()de parser.eval().value, e eu precisava para remover a WScláusula no final, porque os valores de atributos, como $channeljá não são autorizados a aparecer em acções lexer.

Respondeu 19/12/2009 em 12:24
fonte usuário

votos
7

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 jar no classpath em conformidade.

Respondeu 16/04/2016 em 06:48
fonte usuário

votos
1

No https://github.com/BITPlan/com.bitplan.antlr você encontrará uma biblioteca ANTLR java com algumas classes auxiliares úteis e alguns exemplos completos. Ele está pronto para ser usado com Maven e se você gosta 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 ele.

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

  1. gramática analisador
  2. lexer gramática
  3. importados gramática LexBasic

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIRIParser.java tem os testes de unidade para ele.

Pessoalmente, achei esta a parte mais difícil de acertar. veja 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 por um problema de desempenho de ANTLR4 em uma versão anterior. Nesse meio tempo este problemas foi corrigido como os testcase https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIssue994.java shows.

Respondeu 23/10/2017 em 05:49
fonte usuário

votos
5

Para mim, este tutorial foi muito útil: https://tomassetti.me/antlr-mega-tutorial

Tem exemplos de gramática, exemplos de visitantes em línguas diferentes (Java, JavaScript, C # e Python) e muitas outras coisas. Altamente recomendado.

Respondeu 09/11/2017 em 08:18
fonte usuário

votos
0

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

import org.antlr.v4.runtime.*;

para o segmento principal - observe as CharStreams:

CharStream in = CharStreams.fromString("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
Respondeu 27/12/2018 em 03:00
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more