Базовый пример ANTLR на Java - PullRequest
0 голосов
/ 20 марта 2012

Последние несколько часов я искал в сети, пытаясь выучить простой пример использования ANTLR. Но мне трудно разобраться в примерах.Есть ли в любом теле простой пример, который выводил бы это на Java:

, если мой ввод printf("Hello World");

вывод должен быть:

Hello World

и если мой ввод

inx = 1;

, то должно появиться сообщение об ошибке.

Я пытаюсь создать компилятор c ++ (начиная с лексическогопока только семантическая часть) с использованием Java, и я действительно хотел бы знать, что я должен делать.

Ответы [ 2 ]

5 голосов
/ 20 марта 2012

Вот грамматика, которая почти делает то, что вы хотите:

grammar PrintLang;

sentence 
    :    statement
    ;

statement 
    :   functionCall '(' argument ')' ';'
    { 
      if ($functionCall.funName.equals("printf")) {
        System.out.println($argument.arg);
      }
    }
    ;

functionCall returns [String funName]
    :    ID 
    { $funName = $ID.text; }
    ;

argument returns [String arg]
    :   STRING
    { $arg = $STRING.text; }
    ;

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

WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) {$channel=HIDDEN;}
    ;

STRING
    :  '"' ( ESC_SEQ | ~('\\'|'"') )* '"'
    ;

fragment
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;

fragment
ESC_SEQ
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   UNICODE_ESC
    |   OCTAL_ESC
    ;

fragment
OCTAL_ESC
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;

fragment
UNICODE_ESC
    :   '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
    ;

Я создал это в AntlrWorks. Все правила токенов были сгенерированы для меня.

Вот файл java для его проверки.

import org.antlr.runtime.*;


public class PrintIt {
  public static void main(String args[]) {
    String inputString = "printf(\"HelloWorld\");";

    // Create an input character stream from standard in
    ANTLRStringStream input = new ANTLRStringStream(inputString); 
    // Create an ExprLexer that feeds from that stream 
    PrintLangLexer lexer = new PrintLangLexer(input);
    // Create a stream of tokens fed by the lexer 
    CommonTokenStream tokens = new CommonTokenStream(lexer); 
    // Create a parser that feeds off the token stream 
    PrintLangParser plParser = new PrintLangParser(tokens);
    try {
        plParser.sentence();
    } catch (Exception e) {
        e.printStackTrace();
    }
  }
}

Вы заметите, что этот java-код является почти дословным копированием / вставкой из примера на веб-сайте Antlr (я не верю, что я даже изменил комментарии, именно поэтому комментарий ссылается на Standard in, но код фактически использует строка). А вот командная строка, которую я использовал для этого.

bash$ java -cp ./antlr-3.4-complete.jar org.antlr.Tool PrintLang.g
bash$ javac -cp ./:./antlr-3.4-complete.jar PrintIt.java 
bash$ java -cp antlr-3.4-complete.jar:. PrintIt
"HelloWorld"

Ой, я забыл, что строка, которую я хотел напечатать, не совпадает с токеном ("HelloWorld", включая кавычки), это строка внутри кавычек.

Также вы заметите, что я жестко закодировал поиск printf как сравнение строк. В действительности вам понадобится среда, которая содержит символы, доступные в данной области видимости (связанные, см. Конструкцию antlr «scope». Более сложная, хотя иногда полезная: создать среду, которую вы передаете каждому правилу синтаксического анализа).

Самое важное: найти ответы Барт Киерс, выполнив поиск SO для получения дополнительных вопросов. Он публикует отлично примеров.

2 голосов
/ 20 марта 2012

From ANTLR Вот тривиальный пример анализа (и оценки) выражения.

grammar Expr;

@header {
package test;
import java.util.HashMap;
}

@lexer::header {package test;}

@members {
/** Map variable name to Integer object holding value */
HashMap memory = new HashMap();
}

prog:   stat+ ;

stat:   expr NEWLINE {System.out.println($expr.value);}
    |   ID '=' expr NEWLINE
        {memory.put($ID.text, new Integer($expr.value));}
    |   NEWLINE
    ;

expr returns [int value]
    :   e=multExpr {$value = $e.value;}
        (   '+' e=multExpr {$value += $e.value;}
        |   '-' e=multExpr {$value -= $e.value;}
        )*
    ;

multExpr returns [int value]
    :   e=atom {$value = $e.value;} ('*' e=atom {$value *= $e.value;})*
    ; 

atom returns [int value]
    :   INT {$value = Integer.parseInt($INT.text);}
    |   ID
        {
        Integer v = (Integer)memory.get($ID.text);
        if ( v!=null ) $value = v.intValue();
        else System.err.println("undefined variable "+$ID.text);
        }
    |   '(' e=expr ')' {$value = $e.value;}
    ;

    ID  :   ('a'..'z'|'A'..'Z')+ ;
    INT :   '0'..'9'+ ;
    NEWLINE:'\r'? '\n' ;
    WS  :   (' '|'\t')+ {skip();} ;

Но, как я уже упоминал в моих комментариях, C ++ очень трудно правильно анализировать. Есть много двусмысленностей и требует * заблаговременности (что обеспечивает ANTLR). Делать это в любой эффективной форме сложно. Вот почему я рекомендую реализовать что-то вроде PL / 0 , которое было разработано для студентов, чтобы написать свой первый компилятор. Tiny BASIC также хорошее начало. И то, и другое можно реализовать без использования такого инструмента, как ANTLR, выполнив рекурсивный спуск . Я реализовал как менее 1000 строк вместе (в C ++ и C # соответственно).

ANTLR - отличный инструмент, тем не менее, особенно после того, как вы обернули голову вокруг рекурсивного спуска, вы можете захотеть перейти на более мощный анализатор. Я рекомендую обе книги Терренса Парра, ANTLR Reference и Шаблоны языковой реализации . Книга ANTLR расскажет вам все (плюс кое-что), что вы хотите знать о ANTLR. Во второй книге вы узнаете все о синтаксических анализаторах и компиляторах, от рекурсивного спуска до отслеживания чёрной магии.

Дополнительные ресурсы по аналогичному вопросу SO можно найти здесь . И если вы в Lisp или Scheme, вы можете проверить JScheme , он написан на Java (я думаю, что меньше 1000 строк).

...