2012-12-10 48 views
3

我想实现一个使用ANTLR + StringTemplate的转换器。 我有一个开始的语言是Java和多种目标语言。字符串模板:使所有变量声明全局

我使用的示例:需要http://www.antlr.org/wiki/display/ST/Language+Translation+Using+ANTLR+and+StringTemplate

我的一个目标语言,以在全球范围内声明的所有变量。 我写了一个识别变量的语法,但是我无法在我的模板中找到一个用于全局声明局部变量的方法。

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

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

回答

3

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

这是一个非常简单的例子,可以做到这一点。我不建议你的情况是这样的理想,但我希望它给你足够的工作。

假设你的源语法,类似Java的一个,接受这样的代码:

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

Foobar包含成员字段ab,以及成员方法myMethod包含当地人cd。出于论点的缘故,假设您想将a,b,cd视为全局目标的全局变量,否则就像正常变量一样。

这里是接受上述定义的输入,准备向模板输出语法:

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 

的关键是无论目标语言如何,都可以跟踪解析器中的全局变量,并将它们与非全局信息一起传递给语言的模板。目标语言的模板文件要么处理全局变量,要么忽略它们。模板接收足够的信息来定义这两种语言(不管它是否全部使用它),所以不需要创建新的分析器。

+0

谢谢你的回答。你给我正确的提示来解决我的问题。 –