Шаблон строки: сделать объявление всех переменных глобальным
Я пытаюсь реализовать переводчик с помощью ANTLR+StringTemplate. У меня есть начальный язык, который похож на Java, и несколько языков назначения.
Я использовал пример: http://www.antlr.org/wiki/display/ST/Language+Translation+Using+ANTLR+and+StringTemplate
Один из моих целевых языков требует, чтобы все переменные были объявлены глобально. Я написал грамматику, которая распознает переменные, но я не могу найти способ в моем шаблоне для создания локальной переменной, которая будет объявлена глобально.
Конечно, если бы у меня был только один перевод, я мог бы это сделать, но у меня есть несколько переводов, и некоторые из них имеют локальные и глобальные переменные. Я хотел бы сделать это в конкретном файле шаблона.
Например, было бы здорово, если бы я мог определить какие-то переменные внутри шаблона для хранения списка всех объявлений переменных и использовать его в конце, когда я определяю глобальную область видимости... но я не знаю, возможно ли это,
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. Обратите внимание, что 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
Ключ заключается в том, чтобы отслеживать глобальные переменные в синтаксическом анализаторе независимо от целевого языка и передавать их вместе с неглобальной информацией в языковой шаблон независимо. Файл шаблона целевого языка либо обрабатывает глобальные переменные, либо игнорирует их. Шаблон получает достаточно информации для определения обоих типов языков (использует ли он все или нет), поэтому нет необходимости создавать новый синтаксический анализатор.