3

ANTLR+StringTemplate を使用してトランスレータを実装しようとしています。Javaのような開始言語と複数の宛先言語があります。

例を使用しました: http://www.antlr.org/wiki/display/ST/Language+Translation+Using+ANTLR+and+StringTemplate

私の宛先言語の 1 つは、すべての変数をグローバルに宣言する必要があります。変数を認識する文法を書きましたが、ローカル変数をグローバルに宣言するための方法がテンプレートにありません。

もちろん、翻訳が 1 つしかない場合でも実行できますが、複数の翻訳があり、その中にはローカル変数とグローバル変数があります。特定のテンプレートファイルで作成したいと思います。

たとえば、すべての変数宣言のリストを保持するためにテンプレート内である種の変数を定義し、グローバルスコープを定義するときに最後にそれを使用できれば素晴らしいと思います...しかし、これが可能かどうかはわかりません.

4

1 に答える 1

3

パーサーは、変数をテンプレートに渡す前に変数を追跡する必要があります。これは、グローバル ベースのターゲット用に 1 つのパーサーが必要であり、他のターゲット用に別のパーサーが必要であるという意味ではなく、ターゲットでいくつかの空のテンプレートを定義する必要があることを意味するだけです。

これを行う方法の非常に簡単な例を次に示します。あなたのケースがこれほど理想的だとは思いませんが、それで十分に仕事ができることを願っています。

Java に似たソース文法が、次のようなコードを受け入れると仮定します。

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

クラスFoobarにはメンバー フィールドaとが含まれb、メンバー メソッドmyMethodにはローカルcとが含まれますda議論のために、 、bc、およびdをグローバル ターゲットのグローバル変数として扱い、それ以外の場合は通常の変数と同様に扱いたいと仮定します。

テンプレート出力用に準備された、上で定義された入力を受け入れる文法を次に示します。

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 コードを生成するテンプレートです。Java は入力を使用しないため、compilationUnitは入力を無視することに注意してください。globals

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">
>>

グローバル ターゲットのテンプレートを次に示します。クラス テンプレートの多くは空ですが、それはcompilationUnitinput を処理することに注意してください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

重要なのは、ターゲット言語に関係なくパーサーでグローバルを追跡し、それらを非グローバル情報とともに言語のテンプレートに渡すことです。ターゲット言語のテンプレート ファイルは、グローバルを処理するか、グローバルを無視します。テンプレートは両方のタイプの言語を定義するのに十分な情報を受け取るので (すべてを使用するかどうかに関係なく)、新しいパーサーを作成する必要はありません。

于 2012-12-10T21:00:45.167 に答える