2012-12-10 89 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

謝謝你的回答。你給我正確的提示來解決我的問題。 –