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

Java 为什么ANTLR 4中的#include语句没有C语法的可行替代方案?,java,antlr,antlr4,Java,Antlr,Antlr4,我刚刚开始使用v4,我有点困惑 我正在使用antlr项目中的C语法文件来处理以下C代码: #include <stdio.h> int main() { printf("Hello"); return 0; } 我编辑了生成的文件,在顶部放了一个package语句 然后,我创建了一个包含这些生成文件的小Java项目,引用了antlr-runtime-4.4.jar,其中的一个主类如下所示: package antlrtest; import java.io.IOEx

我刚刚开始使用v4,我有点困惑

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

#include <stdio.h>

int main()
{
   printf("Hello");
   return 0;
}
我编辑了生成的文件,在顶部放了一个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();
        }
    }   
}
为了完整性,虽然我不认为这很重要,但听众看起来是这样的(只是空的,我当然计划在这里放一些东西):

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

line 1:0 token recognition error at: '#i'
line 1:9 no viable alternative at input 'nclude<'
“#i”处的
行1:0令牌识别错误

第1行:9输入时没有可行的替代方案,包括我认为您的代码没有任何问题。语法文件没有为
#include
定义规则


因此,您可以做的是扩展语法(当您不熟悉antlr时,这可能会相当复杂)或暂时删除include语句,以便antlr使用您的语法。

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

因此,您可以做的是扩展语法(当您不熟悉antlr时,这可能会相当复杂)或暂时删除include语句,以便antlr使用您的语法。

问题是:

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

#define FOO  if (a
void main ()
{
    int a;
    FOO );
}
因此,您必须首先创建预处理器语法。 我也做过类似的事情,并且是这样做的:

  • 标记整个文件
  • 让预处理器解析器完成它的工作,并将一些预处理器令牌替换为代表预处理器宏替换的“虚拟”令牌(此处:
    if
    a
  • 使用修改后的令牌流使用常规解析器
  • 您可以执行以下操作:

    将includes规则添加到文件末尾的语法文件中(以便尽可能匹配其他预处理器内容):

    问题是:

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

    #define FOO  if (a
    void main ()
    {
        int a;
        FOO );
    }
    
    因此,您必须首先创建预处理器语法。 我也做过类似的事情,并且是这样做的:

  • 标记整个文件
  • 让预处理器解析器完成它的工作,并将一些预处理器令牌替换为代表预处理器宏替换的“虚拟”令牌(此处:
    if
    a
  • 使用修改后的令牌流使用常规解析器
  • 您可以执行以下操作:

    将includes规则添加到文件末尾的语法文件中(以便尽可能匹配其他预处理器内容):


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


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

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


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

    作为更新,我查看了,并通过使用所述预处理器中包含的将其包装在一个文件包中使其工作

    这并不是最好的(至少在效率方面)方法,您可能应该从JCPP的令牌流中构建一个,因为这里我们要进行两次lexing(一次由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();
            }
        }
    }
    

    作为一个更新,我查看了,并通过使用包含在所述预处理器中的将其包装在一个文件中使其工作

    这并不是最好的(至少在效率方面)方法,您可能应该从JCPP的令牌流中构建一个,因为这里我们要进行两次lexing(一次由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();
            }
        }
    }
    

    猜猜看:这是因为
    #include
    是预处理器指令吗?那么我必须告诉它忽略预处理器指令还是什么吗?根据语法,似乎预处理器
    #include
    是不允许的。只有pragmas和line指令。(如果要查找位置,请在文件中搜索“#”).只是一个猜测:这是因为
    #include
    是一个预处理器指令吗?那么我必须告诉它忽略预处理器指令还是什么吗?根据语法,似乎不允许预处理器
    #include
    。只有prag
    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;