为什么在带有C语法的ANTLR 4中#include语句没有可行的替代方案?

时间:2014-07-29 07:48:17

标签: java antlr antlr4

我刚开始使用ANTLR v4而且我有点困惑......

我正在使用antlr项目here中的C语法文件来处理以下C:

#include <stdio.h>

int main()
{
   printf("Hello");
   return 0;
}

(保存为C:\ Users \ Public \ t.c)。

我像这样生成了C解析器:

java -cp lib/antlr-4.4-complete.jar org.antlr.v4.Tool -o src/cparser src/C.g4

我编辑了生成的文件,将包语句放在顶部。

然后,我启动了一个包含这些生成文件的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()或许是错误的事情,如果是这样,我应该调用什么来传递给树步行者?

4 个答案:

答案 0 :(得分:6)

问题是:

除非先对文件进行预处理,否则无法解析文件。这可能是为什么预处理器的东西只包括在非常有限的范围内。 一些简单的例子:

#define FOO  if (a
void main ()
{
    int a;
    FOO );
}

所以你必须先创建一个预处理器语法。 我已经做了类似的事情并且这样做了:

  1. 标记整个文件
  2. 让预处理器解析器完成其工作并用&#34; virtual&#34;替换一些预处理器令牌。代表预处理器宏替换的令牌(此处:ifa()。
  3. 使用修改后的令牌流使用常规解析器。
  4. 您可以做的是:

    将包含规则添加到文件末尾的语法文件中(如果可能,将匹配其他预处理器内容):

    SomePreprocessorStuff
         :   '#' ~[\r\n]*
              -> skip
         ;
    

答案 1 :(得分:5)

ANTLR项目中包含的C语法需要预处理的源文件作为输入。语法不执行任何文件包含,宏扩展或预处理器提供的任何其他功能。如果在使用此语法之前未执行预处理,则它生成的解析树将不是编译单元的准确表示。

请注意,跳过“预处理程序内容”不是提前使用预处理程序的替代方法,因为文件包含只是预处理程序的一部分。

答案 2 :(得分:2)

作为更新,我查看了JCPP preprocessor并通过使用所述预处理器中包含的Reader将其包装在CppReader中来实现它。

这不是最好的(至少在效率方面)方法,你应该从JCPP的令牌流中构建一个TokenStream,因为在这里我们要两次(一次是JCPP,以便它能够预先处理,然后再由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;

答案 3 :(得分:0)

我认为您的代码没有任何问题。语法文件没有为#include <foo.h>定义的规则。

所以你可以做的是扩展语法(当你不熟悉antlr时可能会相当复杂)或者暂时删除include语句以使antlr与你的语法一起工作。