ANTLR:使用 stringTemplate

发布于 2024-08-31 16:16:01 字数 1978 浏览 3 评论 0原文

(我是 Antlr 的菜鸟)...我在使用 StringTemplates 获取语法时遇到困难。基本上我正在尝试编写一些 DSL。我可以按照我想要的方式获得语法(正确解析),但无法生成与模板一起使用的目标代码。这是我的语法的一个片段:

grammar Pfig;

options { 
    output=template;  
 language=CSharp2;
 }

conf 
    : globalName
    ;


globalName 
    : 'GlobalName:'  ID
     -> localConf(name ={$ID.text})
    ;

我将其简化了很多,只是为了表达本质。基本上,当 lex/parse 遇到“GlobalName: Foo”时,我希望它根据名为“localConf”的 StringTemplate 吐出文本。超级简单。

现在,让我们在测试应用程序中启动解析器并让它处理输入文件。

// C# processing a file with the lex/parser.
// the 'app.pfig' file just has one line that reads 'GlobalName: Bla'
using (FileStream fs = File.OpenRead("c:\\app.pfig"))
        {
            PfigParser parser = new PfigParser(new CommonTokenStream(
                new PfigLexer(new ANTLRInputStream(fs))));

            using (TextReader tr = File.OpenText("./Pfig.stg"))
            {
                parser.TemplateLib = new StringTemplateGroup(tr);
            }

            var parseResult = parser.conf();
            string code = parseResult.Template.ToString(); // Fail: template is null
        }

我可以单步执行解析器代码,看看它是否正确识别我的文本并正确应用 stringTemplate。问题是,由于这个“globalName”规则是“conf”的子规则,因此它不会直接执行——该方法只是找到它并返回。但是调用“Conf”方法并不会保留子规则的返回值——它会变得稀薄。这意味着我最后一行的结果模板为空。

如果我摆脱语法中的“conf”规则并直接调用“globalName”,它将起作用(因为它是堆栈上的唯一规则)。但我显然想要不止一条规则。我已经用 Java 生成了解析器,它做了同样的事情:

// antlr generated parser code
public PfigParser.conf_return conf() // throws RecognitionException [1]
{   
    PfigParser.conf_return retval = new PfigParser.conf_return();

    try 
 {
        {
         PushFollow(FOLLOW_globalName_in_conf30);
         globalName(); // <- it calls globalName() but doesn't keep the return.
         state.followingStackPointer--;

        }

        retval.Stop = input.LT(-1);

    }

// snip

很容易看出我没有得到一些关于模板方法应该如何与 Antlr 一起工作的基本概念。 我很确定这是我的问题,但我很想知道我做错了什么...我看到的例子并没有真正显示真实世界的模板发射代码。

(I'm a Noob with Antlr)... I'm having difficulties getting my grammar with StringTemplates. Basically I'm trying to write a little DSL. I can get my grammar the way I want (it parses correctly), but I can't get the generation of the target code to work with templates. So here's a snippet of my grammar:

grammar Pfig;

options { 
    output=template;  
 language=CSharp2;
 }

conf 
    : globalName
    ;


globalName 
    : 'GlobalName:'  ID
     -> localConf(name ={$ID.text})
    ;

I simplified it quite a bit just to get the essence across. Basically, when the lex/parse comes across `GlobalName: Foo' I want it to spit out text based on the StringTemplate called 'localConf'. Super straightforward.

So now, let's fire up the parser in a test app and have it process an input file.

// C# processing a file with the lex/parser.
// the 'app.pfig' file just has one line that reads 'GlobalName: Bla'
using (FileStream fs = File.OpenRead("c:\\app.pfig"))
        {
            PfigParser parser = new PfigParser(new CommonTokenStream(
                new PfigLexer(new ANTLRInputStream(fs))));

            using (TextReader tr = File.OpenText("./Pfig.stg"))
            {
                parser.TemplateLib = new StringTemplateGroup(tr);
            }

            var parseResult = parser.conf();
            string code = parseResult.Template.ToString(); // Fail: template is null
        }

I can step through the parser code and see that it correctly identifies my text and applies the stringTemplate correctly. The problem is that since this 'globalName' rule is a subrule of 'conf' it doesn't get executed directly--the method just finds it and returns. But the calling 'Conf' method does not keep the return value from the subrule--it goes to thin air. This means that my resultant template on the last line is null.

If I get rid of the 'conf' rule in my grammar and call 'globalName' directly, it will work (since it's the only rule on the stack). But I obviously want more than one rule. I've generated the parser in Java and it does the same thing:

// antlr generated parser code
public PfigParser.conf_return conf() // throws RecognitionException [1]
{   
    PfigParser.conf_return retval = new PfigParser.conf_return();

    try 
 {
        {
         PushFollow(FOLLOW_globalName_in_conf30);
         globalName(); // <- it calls globalName() but doesn't keep the return.
         state.followingStackPointer--;

        }

        retval.Stop = input.LT(-1);

    }

// snip

It's simple to see I don't get some basic concept with how the Template approach is supposed to work with Antlr. I'm quite sure this is my problem but I'm a loggerheads to know what I'm doing wrong... the examples I've seen don't really show real-world template emission of code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

贩梦商人 2024-09-07 16:16:01

基本上,您需要将模板输出显式转发到子规则:

conf 
    : a=globalName -> {$a.st}
    ;

它并不美观,但有效。

Basically, you need to explicitly forward the template output to the subrule:

conf 
    : a=globalName -> {$a.st}
    ;

It is not beautiful, but works.

往日 2024-09-07 16:16:01

看来我终于明白了模板的想法:)模板应该按层次结构“嵌套”到另一个模板中。以下示例运行良好:

TemplatesTest.g:

grammar TemplatesTest;

options
{
    output=template;
    language=CSharp2;
}

ID  :   ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
    ;

INT :   '0'..'9'+
    ;

COMMENT
    :   '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    |   '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
    ;

WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) {$channel=HIDDEN;}
    ;

STRING
    :  '\'' ( ESC_SEQ | ~('\\'|'\'') )* '\''
    ;

fragment
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;

fragment
ESC_SEQ
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   UNICODE_ESC
    |   OCTAL_ESC
    ;

fragment
OCTAL_ESC
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;

fragment
UNICODE_ESC
    :   '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
    ;

doc
    : (e+=expr)+ -> doc(expressions={$e})
    ;

expr
    : ID '=' INT -> expression(id={$ID.text}, value={$INT.text})
    ;

TemplatesTest.stg:

group TemplatesTest;

doc(expressions) ::=
<<
srart expressions
<expressions; separator="\n">
end
>>

expression(id, value) ::= 
<<
<id> := <value>;
>>

测试 C# 代码:

var lexer = new TemplatesTestLexer(new ANTLRFileStream("sample.txt"));
var parser = new TemplatesTestParser(new CommonTokenStream(lexer));

using (var reader = new StreamReader("TemplatesTest.stg"))
{
  parser.TemplateLib = new StringTemplateGroup(reader);
}

var doc = parser.doc();
Console.WriteLine(doc.Template);

示例输入:

a = 5
b = 6
c = 7

输出为:

srart expressions
a := 5;
b := 6;
c := 7;
end

It seems I've finally caught the templates idea :) Templates should be 'nested' one into another hierarchically. The following example works just fine:

TemplatesTest.g:

grammar TemplatesTest;

options
{
    output=template;
    language=CSharp2;
}

ID  :   ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
    ;

INT :   '0'..'9'+
    ;

COMMENT
    :   '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    |   '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
    ;

WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) {$channel=HIDDEN;}
    ;

STRING
    :  '\'' ( ESC_SEQ | ~('\\'|'\'') )* '\''
    ;

fragment
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;

fragment
ESC_SEQ
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   UNICODE_ESC
    |   OCTAL_ESC
    ;

fragment
OCTAL_ESC
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;

fragment
UNICODE_ESC
    :   '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
    ;

doc
    : (e+=expr)+ -> doc(expressions={$e})
    ;

expr
    : ID '=' INT -> expression(id={$ID.text}, value={$INT.text})
    ;

TemplatesTest.stg:

group TemplatesTest;

doc(expressions) ::=
<<
srart expressions
<expressions; separator="\n">
end
>>

expression(id, value) ::= 
<<
<id> := <value>;
>>

Test C# code:

var lexer = new TemplatesTestLexer(new ANTLRFileStream("sample.txt"));
var parser = new TemplatesTestParser(new CommonTokenStream(lexer));

using (var reader = new StreamReader("TemplatesTest.stg"))
{
  parser.TemplateLib = new StringTemplateGroup(reader);
}

var doc = parser.doc();
Console.WriteLine(doc.Template);

Sample input:

a = 5
b = 6
c = 7

Output is:

srart expressions
a := 5;
b := 6;
c := 7;
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文