5

我认为这是一个愚蠢的问题,但我刚刚开始使用 ANTLR。我将他们教程中的“SimpleCalc”语法放在一起,并以 C 作为目标语言生成它。我得到了 SimpleCalcParser.c/.h 和 SimpleCalcLexer.c/.h 作为输出,我能够编译它们并成功构建。但是现在,我如何实际使用生成的代码?我无法在文档中找到任何有用的内容。

下面是我的 main() 函数。这也来自教程。

 #include "SimpleCalcLexer.h"

 int main(int argc, char * argv[])
 {

    pANTLR3_INPUT_STREAM           input;
    pSimpleCalcLexer               lex;
    pANTLR3_COMMON_TOKEN_STREAM    tokens;
    pSimpleCalcParser              parser;

    input  = antlr3AsciiFileStreamNew          ((pANTLR3_UINT8)argv[1]);
    lex    = SimpleCalcLexerNew                (input);
    tokens = antlr3CommonTokenStreamSourceNew  (ANTLR3_SIZE_HINT, TOKENSOURCE(lex));
    parser = SimpleCalcParserNew               (tokens);

    parser  ->expr(parser);

    // Must manually clean up
    //
    parser ->free(parser);
    tokens ->free(tokens);
    lex    ->free(lex);
    input  ->close(input);

    return 0;
 }

编辑:根据第一个响应,我应该说我这样运行程序:“./testantlr test.txt”,其中 test.txt 包含“4+1”。没有输出。

例如,从这里,我将如何访问生成的语法树中的“4”,或者打印出整个语法树?基本上,我如何访问 ANTLR 生成的语法树中的内容?

4

4 回答 4

4

当我第一次尝试它时,我面临着同样的困惑。这是一个非常明显的问题/问题,这使得在教程中似乎没有明确和直接地解决它变得更加奇怪。

我发现的困惑的出路是'returns'关键字:

token returns [TreeNode value]
    :    WORD { $value = new TreeNode( "word", $WORD.Text ); }
    |    INT { $value = new TreeNode( "int", $INT.Text ); }
    ;

WORD:    ('a'..'z'|'A'..'Z')+;
INT :    ('0'..'9')+;

TreeNode 是我创建的一个类。棘手的地方是如何使用一系列比如说多个令牌来做到这一点。我想出的解决方案是递归:

expr returns [Accumulator value]
    :   a=token  (WS+ b=expr)?
    {
        if( b != null )
        {
            $value = new Accumulator( "expr", a.value, b.value );
        } else
        {
            $value = new Accumulator( "expr", a.value );
        }
    }
    ;

Accumulator 是我创建的一个类,它有两个不同的构造函数。一个构造函数封装单个令牌,另一个封装单个令牌和另一个 Accumulator 实例。请注意,规则本身是递归定义的,它b.value是一个 Accumulator 实例。为什么?因为 b 是一个 expr,而 expr 的定义有returns [Accumulator value].

最终生成的树是单个 Accumulator 实例,它已将所有令牌分组。要实际使用该树,您需要进行一些设置,然后调用与您正在解析内容的规则同名的方法:

Antlr.Runtime.ANTLRStringStream stringstream =  new Antlr.Runtime.ANTLRStringStream( script );
TokenLexer lexer = new TokenLexer( stringstream );
Antlr.Runtime.CommonTokenStream tokenstream = new Antlr.Runtime.CommonTokenStream( lexer );
TokenParser parser = new TokenParser( tokenstream );

Accumulator grandtree = parser.expr().value;

希望这对遇到这种困惑的人有所帮助。


更新

由于系统允许您在看似任意的模式位置散布目标语言代码,因此有一种更直接的方法可以将项目收集到列表中。成语是:

sequence returns [String k]
    :   (e=atom { $k = $e.k; })
        (e=atom { $k += ", " + $e.k; })*
        { $k = "sequence (" + $k + ")"; } ;

一个字符串k被初始化为第一个原子的 k 值,随后的原子+=得到k. 该片段$e.k是指在atom returns [String k]别处定义的规则。如果没有这样的规则,您可以使用该text属性(即$e.text,哪些令牌具有。我不确定非令牌是否具有此属性。如果没有,您可以这样做:

nonToken returns [String whatever] : e=TOKEN { $whatever = $e.text; } ;

然后您将在更高的规则中使用它,例如

e=nonToken { System.out.println($e.whatever); }
于 2009-07-01T01:37:23.223 回答
0

所以当你运行这个程序时,你在这里给出的第一个命令行参数将是要解析的文件的名称。

第一步,尝试(运行它并给它一个文件)。

第二步,回来编辑你的问题,但稍微改变方向。与其问“我如何使用代码”,不如问“我该怎么做__________”,其中空白被您尝试完成的一些描述所取代。

所以parser->expr(parser)似乎正在解析来自您的文件的令牌流,这应该会产生一个 AST。通过很多细节猜测我的方式,我建议看看它返回什么,特别是。value成员(如果有的话)。网上似乎有很多教程看起来与您正在做的事情相似,没有两个相同。

如果一切都失败了,请继续学习本教程,或者 1)它会回答你的问题,或者 2)它不会,你可以尝试另一个。

于 2009-03-12T04:24:36.337 回答
0

看看 Scott Stanchfield 的第 8 部分视频http://vimeo.com/groups/29150/videos/8377479。他是用 Java 做的,但同样的原理也可以用在 C(++) 中。

于 2010-02-25T10:15:39.830 回答
-1

我并不是要粗鲁,但您似乎并不真正知道 ANTLR 的目的是什么。我认为您需要先了解这一点,然后才能尝试使用它生成的文件。

非常简短的回答是 ANTLR 是一个解析器生成器,这意味着它会生成代码来解析文本。一种常见的用法是解析编程语言的文本。我还没有阅读你所指的教程,但我猜这个解析器解析的文本是一组计算器指令,比如

ADD 2 4
MULTIPLY 4 8

为了使用上面显示的程序,您可以像执行任何其他 C 程序一样执行它。第一个参数 (argc) 应该是参数的数量,第二个 (argv) 应该是要解析的文本。

为了从头开始学习 ANTLR,我建议你阅读ANTLR 的作者 Terence Parr 出版的书。

于 2009-03-12T04:32:44.930 回答