Строковый шаблон: сделать все объявления переменных глобальными

Я пытаюсь реализовать переводчик, используя ANTLR+StringTemplate. У меня есть исходный язык, похожий на java, и несколько языков назначения.

Я использовал пример: http://www.antlr.org/wiki/display/ST/Language+Translation+Using+ANTLR+and+StringTemplate

Один из моих целевых языков требует, чтобы все переменные были объявлены глобально. Я написал грамматику, которая распознает переменные, но не могу найти в своем шаблоне способ объявить локальную переменную глобально.

Конечно, если бы у меня был только один перевод, я бы смог это сделать, но у меня несколько переводов, и некоторые из них имеют локальные и глобальные переменные. Я хотел бы сделать это в конкретном файле шаблона.

Например, было бы здорово, если бы я мог определить какие-то переменные внутри шаблона для хранения списка всех объявлений переменных и использовать его в конце, когда я определяю глобальную область... но я не знаю, возможно ли это .


person Lorenzo Camerini    schedule 10.12.2012    source источник


Ответы (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

Ключевым моментом является отслеживание глобальных переменных в синтаксическом анализаторе независимо от целевого языка и передача их вместе с неглобальной информацией в шаблон языка независимо от этого. Файл шаблона целевого языка либо обрабатывает глобальные переменные, либо игнорирует их. Шаблон получает достаточно информации для определения обоих типов языков (независимо от того, использует он их все или нет), поэтому нет необходимости создавать новый синтаксический анализатор.

person user1201210    schedule 10.12.2012
comment
Спасибо за ответ. Вы даете мне правильные советы для решения моей проблемы. - person Lorenzo Camerini; 11.12.2012