2012-03-20 4 views
0

ANTLRを使用した簡単な例を習得しようと、過去数時間、ネットを検索していますが、例を理解するのは苦労しています。すべてのボディは、Javaでの出力このだろう単純な例を持っています:JavaのANTLRの基本例

私の入力は printf("Hello World");

であれば、出力は次のようになります。

のHello World

と私の入力は

ある場合

inx = 1;

エラーメッセージが表示されます。

私はjavaを使用してC++コンパイラ(意味のある部分までの字句から始まる)を作成しようとしています。私は本当に何をすべきかを知りたいと思います。

+0

入力の文法は何ですか?あなたの入力はすべて1つのトークンですか?もしそうなら、簡単!あなたがCのようにそれを解析したいのであれば、それは "シンプル"から遠いです。 –

+0

レコードの場合、C++は正しく解析するのが非常に難しいです。文脈依存です。 –

+0

あなたはあなたの出力に言及します...あなたはコンパイラではなくインタープリタを書いていますか? –

答えて

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について知りたいと思うすべてのもの(そしていくつか)を教えてくれるでしょう。 2番目の本は、再帰的降下から黒マジックバックトラックまで、パーサとコンパイラに関するすべてを教えてくれるでしょう。

同様の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のウェブサイトの例から、ほぼそのままのコピー/ペーストであることに注意しますが、コードは実際にStringを使用します)。そして、私がこれまでやっていたコマンドラインがあります。

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"構造を参照してください)。

最も重要なこと:Bart Kiersの答えは、より多くの質問に対してSOを検索することで見つかります。彼は優れたの例を掲載しています。