问题描述
我正在尝试使用 ANTLR+StringTemplate 实现一个翻译器.我有一个类似于 java 的起始语言和多个目标语言.
I am trying to implement a translator using ANTLR+StringTemplate.I have a starting language that is java like and multiple destination language.
我使用了示例:http:///www.antlr.org/wiki/display/ST/Language+Translation+Using+ANTLR+and+StringTemplate
我的目标语言之一需要全局声明所有变量.我写了一个识别变量的语法,但在我的模板中找不到使局部变量在全局声明的方法.
One of my destination language needs all variables to be declared globally.I wrote a grammar that recognizes variables, but i cannot find e way in my template for making a local variable to be declared globally.
当然,如果我只有一个翻译,我就可以做到,但是我有多个翻译,其中一些有局部变量和全局变量.我想在特定的模板文件中制作它.
Of course if I would have just one translation I would be able to do it, but I have multiple translation and some of them have local and global variables.I'd like to make it in the specific template file.
例如,如果我可以在模板中定义某种变量以保留所有变量声明的列表并在最后定义全局范围时使用它,那就太好了……但我不知道是否这是可能的.
For example it would be great if I could define some sort of variables inside the template for keeping a list of all variable declarations and use it at the end when i define the global scope... but i don't know if this is possibile.
推荐答案
解析器必须在将变量传递给模板之前对其进行跟踪.这并不意味着您需要一个解析器用于基于全局的目标,而另一个用于其他目标,只是意味着您需要在目标中定义一些空模板.
The parser will have to track the variables before passing them to a template. This doesn't mean that you need one parser for a global-based target and another for the other targets, it just means that you need to define some empty templates in the targets.
这是一个非常简单的例子,说明如何做到这一点.我不认为你的情况是这样理想的,但我希望它给你足够的工作.
Here is a very simple example of how this can be done. I don't propose that your case is this ideal, but I hope it gives you enough to work with.
假设您的源语法(类似于 Java 的语法)接受如下代码:
Assume that your source grammar, the Java-like one, accepts code like this:
class Foobar {
var a;
var b;
var myMethod(var x, var y) {
var c;
var d;
}
}
Class Foobar
包含成员字段 a
和 b
,成员方法 myMethod
包含 locals c
和 d
.为了论证,假设您希望将 a
、b
、c
和 d
视为全局变量用于全局目标,否则类似于普通变量.
Class Foobar
contains member fields a
and b
, and member method myMethod
contains locals c
and d
. For argument's sake, assume that you want a
, b
, c
, and d
to be treated as global variables for a global target, and like normal variables otherwise.
这是一个接受上面定义的输入的语法,为模板输出做准备:
Here is a grammar that accepts the input defined above, prepped for template output:
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
跟踪仅全局对象关注的变量的名称,但仍会调用与字段/变量相关的模板.这确保了语法是目标中立的.
Note that parser member globals
tracks the names of variables that a globals-only target is concerned about, but that templates pertaining to fields/variables are still called. This ensures that the grammar is target-neutral.
这是一个生成 Java 代码的模板.请注意,compilationUnit
忽略输入 globals
,因为 Java 不使用它们.
Here is a template that produces Java code. Note that compilationUnit
ignores input globals
because Java doesn't use them.
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
.
Here is a template for a globals target. Note that many of the class templates are empty, but that compilationUnit
processes 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">
>>
这是我将用来测试语法和模板的启动器类.
Here is the launcher class I'll use to test the grammar and templates.
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());
}
}
这是测试类中硬编码的输入:
Here is the input hard-coded in the test class:
class Foobar {
var Foobar_a;
var Foobar_b;
var doSomething() {
var doSomething_a;
var doSomething_b;
}
}
这是代码生成的输出,使用两个模板:
And here is the output produced by the code, using both templates:
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
关键是在解析器中跟踪全局变量,而不管目标语言如何,并将它们与非全局信息一起传递给语言的模板.目标语言的模板文件要么处理全局变量,要么忽略它们.模板接收足够的信息来定义两种类型的语言(无论是否全部使用),因此无需创建新的解析器.
The key is to track globals in the parser regardless of the target language and pass them along with non-global information to the language's template regardless. The target language's template file either processes the globals or it ignores them. A template receive enough information to define both types of languages (whether it uses it all or not), so there's no need to create a new parser.
这篇关于字符串模板:将所有变量声明设为全局的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!