C文法を使用したANTLR4の#include文の実行可能な代替手段がないのはなぜですか?
質問
私はちょうど始めています ANTLR v4と私は少し混乱しています。..
私はantlrプロジェクトのC文法ファイルを使用しています ここに Cの次のビットで動作するには:
#include <stdio.h>
int main()
{
printf("Hello");
return 0;
}
(として保存されたC:\Users\Public .c)。
私は次のようにCパーサーを生成しました:
java -cp lib/antlr-4.4-complete.jar org.antlr.v4.Tool -o src/cparser src/C.g4
そして、生成されたファイルを編集して、package文を一番上に置きました。
私はその後、これらの生成されたファイルを含む小さなJavaプロジェクトをホイップし、参照しました antlr-runtime-4.4.jar
次のようなメインクラスを使用します:
package antlrtest;
import java.io.IOException;
import org.antlr.v4.runtime.ANTLRFileStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import cparser.CLexer;
import cparser.CParser;
import cparser.CParser.CompilationUnitContext;
public class AntlrTestMain {
public static void main(String[] arguments) {
try {
CParser parser = new CParser(
new CommonTokenStream(
new CLexer(
new ANTLRFileStream("C:\\Users\\Public\\t.c"))));
parser.setBuildParseTree(true);
// This line prints the error
CompilationUnitContext ctx = parser.compilationUnit();
MyListener listener = new MyListener();
ParseTreeWalker.DEFAULT.walk(listener, ctx);
} catch (IOException e) {
e.printStackTrace();
}
}
}
そして、完全性のために、私はそれが重要だとは思わないが、リスナーはこのように見える(ちょうど空、私はもちろんここに何かを置く予定):
package antlrtest;
import cparser.CBaseListener;
public class MyListener extends CBaseListener {
}
今、私が実行したときに何が起こるかは、私が呼び出すときです compilationUnit
メソッドコンソールに次のエラーが出力されます:
line 1:0 token recognition error at: '#i'
line 1:9 no viable alternative at input 'nclude<'
私はCコードが有効であり、私は編集していないと確信しています C.g4
すべてのファイルだから私はここで何が間違っているのですか-なぜ私はこれらのエラーを得るのですか?
呼び出しています compilationUnit()
間違ったことおそらく、もしそうなら、私はツリーウォーカーに渡すために何を呼ぶべきですか?
解決
問題は:
ファイルが最初に前処理されない限り、一般的にファイルを解析することはできません。それがおそらくプリプロセッサのものが非常に限られた拡張にのみ含まれている理由です。いくつかの簡単な例:
#define FOO if (a
void main ()
{
int a;
FOO );
}
したがって、最初にプリプロセッサ文法を作成する必要があります。私は似たようなことをして、このようにしました:
- 完全なファイルをトークン化する
- プリプロセッサパーサーにその仕事をさせ、いくつかのプリプロセッサトークンをプリプロセッサマクロの置換を表す"仮想"トークンに置き換えます(ここでは:
if
,a
,(
). - 変更されたトークンストリームを使用して、通常のパーサーを使用します。
あなたができることは次のとおりです:
ファイルの最後にある文法ファイルにインクルードのルールを追加します(可能であれば他のプリプロセッサのものが一致するようにします):
SomePreprocessorStuff
: '#' ~[\r\n]*
-> skip
;
他のヒント
ANTLRプロジェクトに含まれるC文法は、入力として前処理されたソースファイルを必要とします。文法は、ファイルの包含、マクロ展開、またはプリプロセッサによって提供されるその他の機能を実行しません。この文法を使用する前に前処理を実行しないと、生成される解析ツリーはコンパイル単位の正確な表現ではありません。
ファイルの包含はプリプロセッサの一部にすぎないため、「プリプロセッサのもの」をスキップすることは、事前にプリプロセッサを使用する代わり
更新として、私は見ていた JCPPプリプロセッサ そして、それはちょうどでそれをラップすることで動作して得た リーダー を使用して、 CppReader それは前記プリプロセッサに含まれています。
これは実際には(少なくとも効率の点で)最善のアプローチではありません、あなたはおそらく構築する必要があります トークンストリーム ここではJCPPのトークンストリームから2回字句解析しています(JCPPで1回前処理してからANTLRで再度処理できるようにします)が、動作させる方法として、少
とにかく、jcppを使用して前処理するために更新された質問のコードは次のとおりです:
public class AntlrTestMain {
public static void main(String[] args) {
String mainFileName = "C:\\Users\\Public\\t.c";
try {
// Construct the preprocessor with the main file to look at
Preprocessor pp = new Preprocessor(new File(mainFileName));
// Set up the preprocessor - you probably want to set more stuff
// here than just the include path - have a look in the javadoc
List<String> systemInclude = new ArrayList<String>();
systemInclude.add("C:\\MYCPPCOMPILER\\include");
pp.setSystemIncludePath(systemInclude);
// Get the parser by wrapping up the preprocessor in a reader
CParser parser = new CParser(
new CommonTokenStream(
new CLexer(
new ANTLRInputStream(new CppReader(pp)))));
// Use ANTLR to do whatever you want...
parser.setBuildParseTree(true);
MyListener listener = new MyListener();
ParseTreeWalker.DEFAULT.walk(listener, parser.compilationUnit());
} catch (IOException e) {
e.printStackTrace();
}
}
}
上記のコードには、これらのインポートが必要です:
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.anarres.cpp.CppReader;
import org.anarres.cpp.Preprocessor;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import cparser.CLexer;
import cparser.CParser;
私はあなたのコードに何か問題があるとは思わない。文法ファイルには、次のルールが定義されていません #include <foo.h>
.
だからあなたができることは、文法を拡張することです(あなたがantlrに慣れていないときはかなり複雑になる可能性があります)、今のところinclude-statementを削除して、antlrをあなたの文法で動作させることです。