【问题标题】:String Template: make all variable declaration global字符串模板:使所有变量声明全局化
【发布时间】:2012-11-27 17:10:22
【问题描述】:

我正在尝试使用 ANTLR+StringTemplate 实现翻译器。 我有一种类似于 java 的起始语言和多种目标语言。

我用的例子是:http://www.antlr.org/wiki/display/ST/Language+Translation+Using+ANTLR+and+StringTemplate

我的一种目标语言需要全局声明所有变量。 我写了一个识别变量的语法,但是我在我的模板中找不到使局部变量全局声明的方法。

当然,如果我只有一个翻译,我可以做到,但我有多个翻译,其中一些有局部和全局变量。 我想在特定的模板文件中制作它。

例如,如果我可以在模板中定义某种变量来保存所有变量声明的列表并在我定义全局范围时在最后使用它,那就太好了......但我不知道是否这是可能的。

【问题讨论】:

    标签: antlr grammar stringtemplate code-translation


    【解决方案1】:

    解析器必须在将变量传递给模板之前对其进行跟踪。这并不意味着您需要一个解析器用于基于全局的目标,而另一个解析器用于其他目标,这只是意味着您需要在目标中定义一些空模板。

    这里有一个非常简单的例子来说明如何做到这一点。我不建议您的案例是这样理想的,但我希望它能给您足够的工作。

    假设您的源语法(类似于 Java 的语法)接受如下代码:

    class Foobar { 
        var a;
        var b;
        var myMethod(var x, var y) { 
           var c;
           var d;
        }
    }
    

    Foobar包含成员字段ab,成员方法myMethod包含局部变量cd。为了论证,假设您希望将abcd 视为全局目标的全局变量,否则就像普通变量一样。

    这是一个接受上面定义的输入的语法,为模板输出做好准备:

    grammar JavaLikeToTemplate;
    
    options { 
        output = template;
    }
    
    @members { 
        private java.util.ArrayList<String> globals = new java.util.ArrayList<String>();
    
    }
    
    compilationUnit : class_def EOF 
                        -> compilationUnit(classDef={$class_def.st}, globals={globals});
    class_def       : CLASS ID LCUR class_body RCUR
                        -> class(name={$ID.text}, body={$class_body.st});
    class_body      : (t+=class_element)+
                        -> append(parts={$t});
    class_element   : class_field
                        -> {$class_field.st}
                    | class_method
                        -> {$class_method.st};
    class_field     : VAR ID SEMI {globals.add($ID.text);}
                        -> classField(name={$ID.text});
    class_method    : VAR ID LPAR paramlist? RPAR LCUR method_body RCUR
                        -> classMethod(name={$ID.text}, params={$paramlist.st}, body={$method_body.st});
    method_body     : (t+=method_element)+
                        -> append(parts={$t});
    method_element  : method_field
                        -> {$method_field.st};
    method_field    : VAR ID SEMI {globals.add($ID.text);}
                        -> methodField(name={$ID.text});
    paramlist       : VAR t+=ID (COMMA VAR t+=ID)*
                        -> paramList(params={$t});
    
    CLASS   : 'class';
    VAR     : 'var';
    ID      : ('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'_'|'0'..'9')*;
    INT     : ('0'..'9')+;
    COMMA   : ',';
    SEMI    : ';';
    LCUR    : '{';
    RCUR    : '}';
    LPAR    : '(';
    RPAR    : ')';
    EQ      : '=';
    WS      : (' '|'\t'|'\f'|'\r'|'\n'){skip();};
    

    请注意,解析器成员globals 会跟踪仅全局目标所关注的变量名称,但仍会调用与字段/变量相关的模板。这确保了语法是目标中立的。

    这是一个生成 Java 代码的模板。请注意,compilationUnit 会忽略输入 globals,因为 Java 不使用它们。

    group JavaLikeToJava;
    
    compilationUnit(globals, classDef) ::=
    <<
    <classDef>
    >>
    
    class(name, body) ::= 
    <<
    public class <name> { 
        <body>
    }
    >>
    
    classField(name) ::=
    <<
    private Object <name>;
    >>
    
    classMethod(name, params, body) ::=
    <<
    public Object <name>(<params>) {
        <body> 
    }
    >>
    
    methodField(name) ::=
    <<
        Object <name>;
    >>
    
    paramList(params) ::=
    <<
        <params:{p|Object <p.text>}; separator=", ">
    >>
    
    append(parts) ::=
    <<
     <parts;separator="\n">
    >>
    

    这是一个全局目标的模板。请注意,许多类模板是空的,但 compilationUnit 处理输入 globals

    group JavaLikeToGlobal;
    
    globals(names) ::=
    <<
        <names:global()>
    >>
    
    global(name) ::=
    <<
    global <name>
    >>
    
    compilationUnit(globals, classDef) ::=
    <<
    <globals:globals();separator="\n">
    <classDef>
    >>
    
    class(name, body) ::= 
    <<
    <body>
    >>
    
    classField(name) ::=
    <<>>
    
    classMethod(name, params, body) ::=
    <<
    <name>(<params>):
        <body>
    end
    >>
    
    methodField(name) ::=
    <<
    >>
    
    paramList(params) ::=
    <<
        <params:{p| <p.text>}; separator=", ">
    >>
    
    append(parts) ::=
    <<
     <parts;separator="\n">
    >>
    

    这是我将用来测试语法和模板的启动器类。

    public class JavaLikeToTemplateTest {
    
        public static void main(String[] args) throws Exception {
    
            final String code = "class Foobar {\n var Foobar_a;\n var Foobar_b;\n var doSomething() {\n  var doSomething_a;\n  var doSomething_b;\n }\n}"; 
    
            process(code, "JavaLikeToJava.stg");
            process(code, "JavaLikeToGlobal.stg");
    
        }
    
        private static void process(final String code, String templateResourceName)
                throws IOException, RecognitionException, Exception {
            CharStream input = new ANTLRStringStream(code);
            JavaLikeToTemplateLexer lexer = new JavaLikeToTemplateLexer(input);
            CommonTokenStream tokens = new CommonTokenStream(lexer);
    
            JavaLikeToTemplateParser parser = new JavaLikeToTemplateParser(tokens);
    
            InputStream stream = JavaLikeToTemplateTest.class.getResourceAsStream(templateResourceName);
            Reader reader = new InputStreamReader(stream);
            parser.setTemplateLib(new StringTemplateGroup(reader));
            reader.close();
            stream.close();
    
            JavaLikeToTemplateParser.compilationUnit_return result = parser.compilationUnit();
    
            if (parser.getNumberOfSyntaxErrors() > 0){
                throw new Exception("Syntax Errors encountered!");
            }
    
            System.out.printf("Result with %s:%n%n", templateResourceName);
            System.out.println(result.toString());
        }
    }
    

    这是测试类中硬编码的输入:

    class Foobar {
     var Foobar_a;
     var Foobar_b;
     var doSomething() {
      var doSomething_a;
      var doSomething_b;
     }
    }
    

    这是代码产生的输出,使用两个模板:

    Result with JavaLikeToJava.stg:
    
    public class Foobar { 
         private Object Foobar_a;
         private Object Foobar_b;
         public Object doSomething() {
                Object doSomething_a;
                Object doSomething_b; 
         }
    }
    
    Result with JavaLikeToGlobal.stg:
    
        global Foobar_a
        global Foobar_b
        global doSomething_a
        global doSomething_b
    
    
     doSomething():
    
    
     end
    

    关键是在解析器中跟踪全局变量,而不考虑目标语言,并将它们与非全局信息一起传递给语言的模板。目标语言的模板文件要么处理全局变量,要么忽略它们。模板接收到足够的信息来定义两种类型的语言(无论是否全部使用),因此无需创建新的解析器。

    【讨论】:

    • 感谢您的回答。你给了我解决问题的正确提示。
    猜你喜欢
    • 2020-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-30
    • 2011-06-30
    相关资源
    最近更新 更多