パーサーは、変数をテンプレートに渡す前に変数を追跡する必要があります。これは、グローバル ベースのターゲット用に 1 つのパーサーが必要であり、他のターゲット用に別のパーサーが必要であるという意味ではなく、ターゲットでいくつかの空のテンプレートを定義する必要があることを意味するだけです。
これを行う方法の非常に簡単な例を次に示します。あなたのケースがこれほど理想的だとは思いませんが、それで十分に仕事ができることを願っています。
Java に似たソース文法が、次のようなコードを受け入れると仮定します。
class Foobar {
var a;
var b;
var myMethod(var x, var y) {
var c;
var d;
}
}
クラスFoobar
にはメンバー フィールドa
とが含まれb
、メンバー メソッドmyMethod
にはローカルc
とが含まれますd
。a
議論のために、 、b
、c
、および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">
>>
グローバル ターゲットのテンプレートを次に示します。クラス テンプレートの多くは空ですが、それはcompilationUnit
input を処理することに注意してください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
重要なのは、ターゲット言語に関係なくパーサーでグローバルを追跡し、それらを非グローバル情報とともに言語のテンプレートに渡すことです。ターゲット言語のテンプレート ファイルは、グローバルを処理するか、グローバルを無視します。テンプレートは両方のタイプの言語を定義するのに十分な情報を受け取るので (すべてを使用するかどうかに関係なく)、新しいパーサーを作成する必要はありません。