2012-03-20 43 views
0

我一直在搜索過去幾個小時的網絡,試圖學習一個使用ANTLR的簡單示例。但我很難理解這些示例。是否有任何身體有簡單的例子,將這個輸出在Java中:ANTLR Java中的基本示例

如果我輸入的是 printf("Hello World");

輸出應該是:

的Hello World

,如果我輸入的是

inx = 1;

它應該給出一個錯誤信息。

我想用java創建一個C++編譯器(直到語義部分纔開始),我真的很想知道我該怎麼做。

+0

你輸入的語法是什麼?你輸入的是一個令牌嗎?如果那樣容易的話!如果你想像C一樣解析它,那麼它很不「簡單」。 – 2012-03-20 15:49:13

+0

爲了記錄,C++很難正確解析。它是上下文敏感的。 – 2012-03-20 15:55:10

+0

你提到你的輸出......所以你正在編寫一個解釋器,而不是編譯器? – 2012-03-20 15:58:08

回答

2

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也是一個好的開始。通過做recursive descent這兩種方法都可以在不使用ANTLR等工具的情況下實現。我已經在1000線以下實現了這兩者(分別使用C++和C#)。

雖然ANTLR是一個很棒的工具,但是一旦你將頭部纏繞在遞歸下降處,你可能想升級到更強大的解析器。我推薦Terrence Parr的兩本書,ANTLR ReferenceLanguage Implementation Patterns。 ANTLR書會告訴你一切(加上一些),你想知道ANTLR。第二本書將教你所有關於解析器和編譯器,從遞歸下降到黑魔法回溯。

可以找到類似SO問題的更多資源here。如果你使用Lisp或Scheme,你可以看看JScheme,它是用Java編寫的(少於1000行我相信)。

5

這裏是一個語法,幾乎你想要做什麼:

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的網站例如貼(我不相信我甚至改變了意見,這就是爲什麼評論指標準,但該代碼實際上使用一個字符串)。這裏是我用來做它的命令行。

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的「範圍」構造。更困難,但有時很有用:創建一個傳遞給每個解析規則的環境)。

最重要的是:找到巴爾基爾斯的答案,通過搜索更多反問題。他的帖子優秀的例子。