Interpretador de Comandos. Usar um parser ou não? [RESOLVIDO]

12 respostas
R

Olá. Estou trabalhando em um software que faz parte de um kit didático de manipuladores robóticos que eu estou desenvolvendo. Uma parte desse software é i interpretador de comandos. Basicamente, eu vou pegar alguns comandos do usuário, interpretar, e dependendo do comando enviá-los pela porta serial (atualmente, logo será ethernet) para a placa que controla o robô.

Os comandos são básicos, coisa do tipo:

Inicio;

Move_motor(+250);

Move_robô(+50,-200,+30,0);

Espera_entrada(8);

Abre_Garra;

Fecha_Garra;

Fim;

Talvez, tenha um pouco mais de complexidade, mas é algo que eu ainda vou discutir com meu orientador (o uso ou não de uma linguagem comercial).

Enfim, parando de enrolar, pergunta:

É melhor escrever esse interpretador na mão (já que o comandos são relativamente simples), ou utilizar um parser, como o JavaCC, por exemplo? E caso eu venha usar uma linguagem de robôs comercial (como a VAL) ?

Desde já agradeço aos que puderem ajudar.

12 Respostas

T

Pode ser que o java cup te ajude, http://www.cs.princeton.edu/~appel/modern/java/CUP/manual.html

D

Depende.

Se seus comandos forem só esses mesmo e você quiser aprender a escrever um parser, recomendo que escreva. Primeiro: sua DSL parece ser simples. Segundo: é uma boa oportunidade de estudar algo mais.

Caso não tenha tempo nem interesse em escrever um parser, use um gereador de parsers (JavaCC, ANTLR, etc.).
Recomendo o ANTLR.

[]´s

M

Se for tudo nesse tipo… pode ser um interpretador simples… .na minha opinião.

E

rock-skull:
Olá. Estou trabalhando em um software que faz parte de um kit didático de manipuladores robóticos que eu estou desenvolvendo. Uma parte desse software é i interpretador de comandos. Basicamente, eu vou pegar alguns comandos do usuário, interpretar, e dependendo do comando enviá-los pela porta serial (atualmente, logo será ethernet) para a placa que controla o robô.

Os comandos são básicos, coisa do tipo:

Inicio;

Move_motor(+250);

Move_robô(+50,-200,+30,0);

Espera_entrada(8);

Abre_Garra;

Fecha_Garra;

Fim;

Talvez, tenha um pouco mais de complexidade, mas é algo que eu ainda vou discutir com meu orientador (o uso ou não de uma linguagem comercial).

Enfim, parando de enrolar, pergunta:

É melhor escrever esse interpretador na mão (já que o comandos são relativamente simples), ou utilizar um parser, como o JavaCC, por exemplo? E caso eu venha usar uma linguagem de robôs comercial (como a VAL) ?

Desde já agradeço aos que puderem ajudar.

Você pode usar o interpretador Javascript que o Java já tem (a partir do Java 6.0 - o interpretador é o Rhino).

http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/

R

Ok. Darei uma olhada nesses parsers generators que vocês indicaram. O tempo tá curto, melhor usar um desses.

E

Se o tempo estiver REALMENTE curto, use o tal interpretador Javascript.
Então você terá que fazer um script mais ou menos assim:

// -- aqui vai a parte que obtém a classe Java que contém os comandos para o seu robô. 
var robo = ..........;
// Aqui o código Javascript
robo.move_motor (250)
robo.move_robo (50, -200, 30)
robo.espera_entrada()
while (!robo.interrompido()) {
    robo.abre_garra()
    robo.fecha_garra()
}

Obviamente isso não vai ser compatível com a tal linguagem que você quer implementar. Se realmente seu objetivo é implementar a VAL, você terá de ir pelo caminho que lhe indicaram.

EDIT - obviamente Javascript não é minha linguagem favorita, portanto eu tinha posto um monte de “;” que normalmente não se põem em Javascript.

R

É, pelo o que eu andei ilhando o script, apesar de rápido, não seria a melhor solução, vou nesse ANTRL mesmo. Parece bem interessante, e simples de usar.

Falando nisso, achei esse site: http://javadude.com/articles/antlr3xtut/index.html
Tem um tutorial muito bom do uso do ANTRL com eclipse, em video aulas, bem explicado. Vale a pena conferir.

D

Se você for mesmo usar o ANTLR, a gramática a seguir reconhece os programas na sua linguagem.

grammar Robo;

programa    :    'Inicio;' comandos* 'Fim;';
comandos    :    'Move_motor(' NUMERO ');'
            |    'Move_robo(' NUMERO ',' NUMERO ',' NUMERO ',' NUMERO ');'
            |    'Espera_entrada(' NUMERO ');'
            |    'Abre_garra;'
            |    'Fecha_garra;';
		
NUMERO    :    ('+'|'-')? ('0'..'9')+;

A gramática pode ainda ser melhorada, fiz do jeito mais simples que me veio na cabeça.
Seguem os diagramas sintáticos. Vou passar o lexer e o parser gerados em outro post.

Parece que fiz essa parte do trabalho para você. :oops:





D

Segue o lexer e o parser.
Para usar é fácil.

Note que tirei o acento do ô do Move_robô. Com acento não funciona :(

Coloque o .jar do antlr no classpath do seu projeto e faça algo assim:

String teste = "Inicio;"
        + "Move_motor(20);"
        + "Move_robo(20,-10,2,0);"
        + "Fim;";

RoboLexer lexer = new RoboLexer(
        new ANTLRStringStream( codigo ) );

// se for usar o parser é assim:
RoboParser parser = new RoboParser(
        new CommonTokenStream( lexer ) );

Token t = lexer.nextToken();

while ( t.getType() != RoboLexer.EOF  ) {

    // mostrando o texto do token
    System.out.println( t.getText() );

    /* 
     * aqui você faz um switch com o tipo do token
     * use o arquivo Robo.tokens do pacote como referência
     */

    t = lexer.nextToken();

}

Se você melhorar sua gramática (definir as palavras reservadas, etc), a manipulação do resultado do lexer pode ficar mais fácil ;)
Como exercício, vc pode modificar a gramática tbm para ignorar espaços em branco.

[]´s

R

Nossa! Isso que é ajuda. Brigadão mesmo.

Vou fuçar esses códigos amanhã. Valeu!

D

Olá,

Não resisti, e já escrevi o parser tbm no método main, melhore isso ok?
Fiz várias modificações na gramática tbm.

Gramática:
grammar Robo;

// regras sintáticas (iniciam com letra minúscula
programa        :    INICIO ';' (comandos ';')* FIM ';';
comandos        :    MOVE_MOTOR '(' NUMERO ')'
                |    MOVE_ROBO '(' NUMERO ',' NUMERO ',' NUMERO ',' NUMERO ')'
                |    ESPERA_ENTRADA '(' NUMERO ')'
                |    ABRE_GARRA
                |    FECHA_GARRA;

// regras léxicas (iniciam com letras maiúsculas)		
INICIO          :    'Inicio';
FIM             :    'Fim';
MOVE_MOTOR      :    'Move_motor';
MOVE_ROBO       :    'Move_robo';
ESPERA_ENTRADA  :    'Espera_entrada';
ABRE_GARRA      :    'Abre_garra';
FECHA_GARRA     :    'Fecha_garra';
NUMERO          :    ('-')? ('0'..'9')+;
Parser:
package testeslexers;

import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.Token;
import static testeslexers.RoboLexer.*;

/**
 *
 * @author David Buzatto
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        String codigo = "Inicio;"
                + "Abre_garra;"
                + "Move_motor(20);"
                + "Move_robo(20,-1,2,0);"
                + "Move_motor(50);"
                + "Move_robo(-5,-10,7,55);"
                + "Move_motor(-30);"
                + "Move_robo(80,40,-30,-10);"
                + "Espera_entrada(7);"
                + "Fecha_garra;"
                + "Fim;";

        RoboLexer lexer = new RoboLexer(
                new ANTLRStringStream( codigo ) );

        Token t = lexer.nextToken();

        while ( t.getType() != EOF  ) {


            switch ( t.getType() ) {

                case INICIO:
                    // consome o ";"
                    lexer.nextToken();
                    break;

                case MOVE_MOTOR:

                    // consome o "("
                    lexer.nextToken();

                    // consome o parâmetro
                    t = lexer.nextToken();
                    int p = Integer.parseInt( t.getText() );
                    System.out.printf( "Move motor: %d\n", p );

                    // consome o ")"
                    lexer.nextToken();
                    // consome o ";"
                    lexer.nextToken();
                    break;

                case MOVE_ROBO:

                    // consome o "("
                    lexer.nextToken();

                    // consome o parâmetro
                    t = lexer.nextToken();
                    int p1 = Integer.parseInt( t.getText() );

                    lexer.nextToken(); // consome a vírgula
                    t = lexer.nextToken();
                    int p2 = Integer.parseInt( t.getText() );

                    lexer.nextToken();
                    t = lexer.nextToken();
                    int p3 = Integer.parseInt( t.getText() );

                    lexer.nextToken();
                    t = lexer.nextToken();
                    int p4 = Integer.parseInt( t.getText() );

                    System.out.printf( "Move robo: %d, %d, %d, %d\n", p1, p2, p3, p4 );

                    // consome o ")"
                    lexer.nextToken();
                    // consome o ";"
                    lexer.nextToken();
                    break;

                case ESPERA_ENTRADA:

                    // consome o "("
                    lexer.nextToken();

                    // consome o parâmetro
                    t = lexer.nextToken();
                    int pe = Integer.parseInt( t.getText() );
                    System.out.printf( "Espera entrada: %d\n", pe );

                    // consome o ")"
                    lexer.nextToken();
                    // consome o ";"
                    lexer.nextToken();
                    break;

                case ABRE_GARRA:
                    System.out.println( "Abre garra" );

                    // consome o ";"
                    lexer.nextToken();
                    break;

                case FECHA_GARRA:
                    System.out.println( "Fecha garra" );

                    // consome o ";"
                    lexer.nextToken();
                    break;

                case FIM:
                    // consome o EOF
                    lexer.nextToken();
                    break;

            }

            t = lexer.nextToken();

        }

    }

}

Note que os espaços em branco ainda não são tratados e que eu retirei o sinal de + dos números por dois motivos: primeiro, a convenção para números positivos é não usar sinal; segundo, se você tentar dar um parseInt em um "+2" vai ocorrer uma NumberFormatException.

Veja que em cada lugar que coloquei um System.out.printf ou println é o lugar onde o comando já foi analizado e os parâmetros já estão carregados, bastando você mandar o robô executar o comando.

Outra coisa. Usei o ANTLR Works para editar a gramática e para gerar o código Java do lexer e do parser.

Como "tarefa" você pode então modificar a gramática para aceitar espaços em branco e ainda melhorar ela ainda mais.
Uma observação importante: as regras léxicas são tratadas como terminais na gramática e devem SEMPRE iniciar com letra maiúscula. As regras sintáticas representam os não terminais e suas respectivas regras de produção. Note que nessas produções você pode usar alguns meta-símbolos de expressões regulares (como o * por exemplo), o que não é permitido em uma gramática livre de contexto normal.

Segue em anexo os arquivos gerados, a gramática e os diagramas sintáticos.

P.S. Faltou falar: os erros sintáticos não estão sendo tratados tbm :(

R

Nossa, isso já vai ajudar bastante. Obrigado.

Quanto ao sinal, vou manter ele, mas vendo o seu código, é só fazer uma rotinazinha ali no case pra analisar se tem o sinal de mais. É que na hora de programar o robô, esse sinal é importante pra explicitar a direção do movimento da junta. Como o trabalho é um kit didático, acho melhor deixar.

Ok. Verei também essa parte de erros sintáticos. Mas muito obrigado, isso que você mandou já vai ser de grande ajuda.

Quando terminar posto aqui o resultado. Ainda vai demorar um pouco, que ainda tenho que me reunir com o orientador e tomar umas decisões, e a linguagem vai ser um pouco mais complexa que isso, eu coloquei isso como exemplo.

Mais uma vez, obrigado.

Criado 11 de outubro de 2010
Ultima resposta 13 de out. de 2010
Respostas 12
Participantes 5