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

kmp*_*kmp 1 java antlr antlr4

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

我使用C语法文件从ANTLR项目在这里与C的下列位工作:

#include <stdio.h>

int main()
{
   printf("Hello");
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

(保存为C:\ Users\Public\tc).

我生成了C解析器,如下所示:

java -cp lib/antlr-4.4-complete.jar org.antlr.v4.Tool -o src/cparser src/C.g4
Run Code Online (Sandbox Code Playgroud)

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

然后,我启动了一个包含这些生成文件的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();
        }
    }   
}
Run Code Online (Sandbox Code Playgroud)

并且为了完整性,虽然我认为它不重要,但听众看起来像这样(只是空的,我打算在这里放一些东西):

package antlrtest;

import cparser.CBaseListener;

public class MyListener extends CBaseListener {
}
Run Code Online (Sandbox Code Playgroud)

现在当我运行时会发生什么,当我调用compilationUnit方法时,我将以下错误打印到控制台:

line 1:0 token recognition error at: '#i'
line 1:9 no viable alternative at input 'nclude<'
Run Code Online (Sandbox Code Playgroud)

我很确定C代码是有效的,我根本没有编辑过C.g4文件所以我在这里做错了 - 为什么我会收到这些错误?

正在调用compilationUnit()错误的事情也许这样做,如果是的话我怎么称呼通入树遍历?

Onu*_*nur 6

问题是:

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

#define FOO  if (a
void main ()
{
    int a;
    FOO );
}
Run Code Online (Sandbox Code Playgroud)

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

  1. 对完整文件进行标记
  2. 让预处理解析器完成其工作,并与代表预处理器宏的更换"虚拟"令牌替换一些预处理程序标记(此处if,a,().
  3. 使用修改后的令牌流使用常规解析器.

你能做的是以下几点:

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

SomePreprocessorStuff
     :   '#' ~[\r\n]*
          -> skip
     ;
Run Code Online (Sandbox Code Playgroud)


Sam*_*ell 5

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

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

  • 跳过预处理器的内容可能不是一个通用的解决方案,但是如果代码中没有宏(包括include和pragma除外),而您只想做一些简单的事情,例如提取变量/声明等,则比进行预处理要容易得多。 (2认同)