Синтаксический анализатор должен будет отслеживать переменные перед передачей их в шаблон. Это не означает, что вам нужен один синтаксический анализатор для глобальной цели и другой для других целей, это просто означает, что вам нужно определить несколько пустых шаблонов в целях.
Вот очень простой пример того, как это можно сделать. Я не утверждаю, что ваш случай настолько идеален, но я надеюсь, что он даст вам достаточно для работы.
Предположим, что ваша исходная грамматика, похожая на 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
Ключевым моментом является отслеживание глобальных переменных в синтаксическом анализаторе независимо от целевого языка и передача их вместе с неглобальной информацией в шаблон языка независимо от этого. Файл шаблона целевого языка либо обрабатывает глобальные переменные, либо игнорирует их. Шаблон получает достаточно информации для определения обоих типов языков (независимо от того, использует он их все или нет), поэтому нет необходимости создавать новый синтаксический анализатор.
person
user1201210
schedule
10.12.2012