jj
JavaCCを使用してASTを作成することは、(ファイルで定義された)「通常の」パーサーを作成することによく似ています。すでに実用的な文法がある場合は、(比較的)簡単です:)
ASTを作成するために必要な手順は次のとおりです。
jj
文法ファイルの名前を次のように変更しますjjt
- ルートラベルで飾ります(斜体の単語は私自身の用語です...)
- 文法を呼び出す
jjtree
と、ファイルが生成されますjjt
jj
javacc
生成されたjj
文法を呼び出す
java
生成されたソースファイルをコンパイルします
- 試して
javacc.jar
MacOSまたは*nixを使用していて、ファイルが文法ファイルと同じディレクトリにありjava
、javac
システムのPATHにあると仮定して、簡単なステップバイステップのチュートリアルを次に示します。
1
jj
文法ファイルが呼び出されたと仮定してTestParser.jj
、名前を変更します。
mv TestParser.jj TestParser.jjt
2
ここで注意が必要なのは、適切なAST構造が作成されるように文法を装飾することです。AST(またはノード、またはプロダクションルール(すべて同じ))の後に(およびその前に)識別子を追加して、ASTを装飾します。元の質問では、さまざまなプロダクションに多くのことがあります。つまり、さまざまなプロダクションルールに対して同じタイプのASTを作成しているということです。これはあなたが望むものではありません。#
:
#void
プロダクションを装飾しない場合は、プロダクションの名前がノードのタイプとして使用されます(したがって、削除できます#void
)。
void decl() :
{}
{
var_decl()
| const_decl()
}
var_decl()
これで、ルールは、ルールまたは返されたASTを単純に返しますconst_decl()
。
var_decl
ここで、(簡略化された)ルールを見てみましょう。
void var_decl() #VAR :
{}
{
<VAR> id() <COL> id() <EQ> expr() <SCOL>
}
void id() #ID :
{}
{
<ID>
}
void expr() #EXPR :
{}
{
<ID>
}
タイプで飾りました#VAR
。これは、このルールが次のツリー構造を返すことを意味します。
VAR
/ | \
/ | \
ID ID EXPR
ご覧のとおり、端末はASTから破棄されます。これは、id
andexpr
ルールが<ID>
端末が一致したテキストを失うことも意味します。もちろん、これはあなたが望むものではありません。ターミナルの内部テキストを一致させておく必要があるルールの場合.value
、ツリーのを.image
一致したターミナルのに明示的に設定する必要があります。
void id() #ID :
{Token t;}
{
t=<ID> {jjtThis.value = t.image;}
}
void expr() #EXPR :
{Token t;}
{
t=<ID> {jjtThis.value = t.image;}
}
入力"var x : int = i;"
を次のようにします。
VAR
|
.---+------.
/ | \
/ | \
ID["x"] ID["int"] EXPR["i"]
これは、ASTの適切な構造を作成する方法です。以下は、main
すべてをテストするための小さな方法を含む、独自の文法の非常に単純なバージョンである小さな文法を示しています。
// TestParser.jjt
PARSER_BEGIN(TestParser)
public class TestParser {
public static void main(String[] args) throws ParseException {
TestParser parser = new TestParser(new java.io.StringReader(args[0]));
SimpleNode root = parser.program();
root.dump("");
}
}
PARSER_END(TestParser)
TOKEN :
{
< OPAR : "(" >
| < CPAR : ")" >
| < OBR : "{" >
| < CBR : "}" >
| < COL : ":" >
| < SCOL : ";" >
| < COMMA : "," >
| < VAR : "var" >
| < EQ : "=" >
| < CONST : "const" >
| < ID : ("_" | <LETTER>) ("_" | <ALPHANUM>)* >
}
TOKEN :
{
< #DIGIT : ["0"-"9"] >
| < #LETTER : ["a"-"z","A"-"Z"] >
| < #ALPHANUM : <LETTER> | <DIGIT> >
}
SKIP : { " " | "\t" | "\r" | "\n" }
SimpleNode program() #PROGRAM :
{}
{
(decl())* (function())* <EOF> {return jjtThis;}
}
void decl() :
{}
{
var_decl()
| const_decl()
}
void var_decl() #VAR :
{}
{
<VAR> id() <COL> id() <EQ> expr() <SCOL>
}
void const_decl() #CONST :
{}
{
<CONST> id() <COL> id() <EQ> expr() <SCOL>
}
void function() #FUNCTION :
{}
{
type() id() <OPAR> params() <CPAR> <OBR> /* ... */ <CBR>
}
void type() #TYPE :
{Token t;}
{
t=<ID> {jjtThis.value = t.image;}
}
void id() #ID :
{Token t;}
{
t=<ID> {jjtThis.value = t.image;}
}
void params() #PARAMS :
{}
{
(param() (<COMMA> param())*)?
}
void param() #PARAM :
{Token t;}
{
t=<ID> {jjtThis.value = t.image;}
}
void expr() #EXPR :
{Token t;}
{
t=<ID> {jjtThis.value = t.image;}
}
3
jjtree
クラス(に含まれてjavacc.jar
いる)にファイルを作成させjj
ます。
java -cp javacc.jar jjtree TestParser.jjt
4
前の手順でファイルが作成されましたTestParser.jj
(すべて問題がなかった場合)。それを処理させてくださいjavacc
(に存在しjavacc.jar
ます):
java -cp javacc.jar javacc TestParser.jj
5
すべてのソースファイルをコンパイルするには、次の手順を実行します。
javac -cp .:javacc.jar *.java
(Windowsでは、次のようにしますjavac -cp .;javacc.jar *.java
:)
6
真実の瞬間が到来しました。すべてが実際に機能するかどうかを見てみましょう。パーサーに入力を処理させるには:
var n : int = I;
const x : bool = B;
double f(a,b,c)
{
}
以下を実行します。
java -cp . TestParser "var n : int = I; const x : bool = B; double f(a,b,c) { }"
コンソールに次のように印刷されているはずです。
プログラム
decl
VAR
ID
ID
EXPR
decl
CONST
ID
ID
EXPR
働き
タイプ
ID
パラメータ
パラム
パラム
パラム
'が一致したテキストは表示されないことに注意してください。ただし、ID
私を信じてください。それらはそこにあります。メソッドdump()
は単にそれを表示しません。
HTH
編集
式を含む実用的な文法については、私の次の式エバリュエーターを見ることができます:https ://github.com/bkiers/Curta (文法はにありsrc/grammar
ます)。バイナリ式の場合にルートノードを作成する方法を確認することをお勧めします。