Antlr: преобразование согласованного ввода в правой части правила - PullRequest
1 голос
/ 13 мая 2011

Я пытаюсь написать анализатор языка и создать хороший AST. В языке функция по сути является переменной с вызываемым значением. Например:

int f(int arg) {...};
#int(int) f: int(int arg) {...};

оба равны, и я хочу превратить первое во второе. Как видите, тип переменной содержит параметры, но без имени. Для значения функции требуется имя параметра.

Таким образом, вопрос заключается в следующем: возможно ли получить как (int arg), так и (int) обратно из моего правила, которое соответствует списку параметров, или альтернативно возможно преобразовать первое во второе справа от ->

Я добавлю пример исходного кода и дерева результатов ниже

Input:
^(FUN_DEF
  ^(TYPE_SIMP 'int')
  'f'
  ^(PARAM_LIST
    ^(PARAM 'int' 'arg')
   )
  ^(BLOCK ...)
 )

Result:
^(VAR_DEF
  ^(TYPE_FUN
    ^(TYPE_SIMP 'int')
    ^(PARAM_LIST
      ^(PARAM 'int')
     )
   )
  'f'
  ^(FUN
    ^(TYPE_SIMP 'int')
    ^(PARAM_LIST
      ^(PARAM 'int' 'arg')
     )
    ^(BLOCK ...)
   )
 )

1 Ответ

1 голос
/ 14 мая 2011

В вашем правиле shortFunction может быть вызван пользовательский метод, который, учитывая paramList, удалит из них все идентификаторы, оставляя только типы, и вставит это дерево в нужное место:

shortFunction
  :  type ID '(' paramList ')' block 
     -> ^( ... {customMethod($paramList.tree)} ... )
  ;

Демо:

файл: Fun.g

grammar Fun;

options { 
  output=AST;
  ASTLabelType=CommonTree;
}

tokens {
  ROOT;
  PARAM;
  PARAM_LIST;
  BLOCK;
  VAR_DEF;
  FUN;
  TYPE_FUN;
  TYPE_SIMP;
}

@parser::members {
  private CommonTree stripIDs(CommonTree tree) {
    CommonTree copy = new CommonTree(new CommonToken(PARAM_LIST, "PARAM_LIST"));
    for(int i = 0; i < tree.getChildCount(); i++) {
      CommonTree temp = (CommonTree)tree.getChild(i);
      CommonTree child = new CommonTree(temp);
      child.addChild(new CommonTree((CommonTree)temp.getChild(0)));
      copy.addChild(child);
    }
    return copy;
  }
}

parse
  :  function+ EOF -> ^(ROOT function+)
  ;

function
  :  shortFunction
  |  longFunction
  ;

shortFunction
  :  type ID '(' paramList ')' block
     -> ^(VAR_DEF ^(TYPE_FUN ^(TYPE_SIMP type) {stripIDs($paramList.tree)}) ID ^(FUN ^(TYPE_SIMP type) paramList block))
  ;

longFunction
  :  '#' t1=type '(' typeList ')' ID ':' t2=type '(' paramList ')' block
     -> ^(VAR_DEF ^(TYPE_FUN ^(TYPE_SIMP $t1) typeList) ID ^(FUN ^(TYPE_SIMP $t2) paramList block))
  ;

paramList
  :  (param (',' param)*)? -> ^(PARAM_LIST param*)
  ;

param
  :  type ID -> ^(PARAM type ID)
  ;

typeList
  :  (type (',' type)*)? -> ^(PARAM_LIST ^(PARAM type)*)
  ;

type
  :  INT
  |  SHORT
  |  BYTE
  ;

block
  :  '{' '...' '}' -> ^(BLOCK '...')
  ;

SHORT : 'short';
BYTE  : 'byte';
INT   : 'int';
ID    : ('a'..'z' | 'A'..'Z') ('a'..'z' | 'A'..'Z' | '0'..'9')*;
SPACE : (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};

файл: Main.java

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;

public class Main {
  public static void main(String[] args) throws Exception {
    String source = "#short(byte, int) f: short(byte a, int b) { ... } short f(byte a, int b) { ... }";
    FunLexer lexer = new FunLexer(new ANTLRStringStream(source));
    FunParser parser = new FunParser(new CommonTokenStream(lexer));
    CommonTree tree = (CommonTree)parser.parse().getTree();
    DOTTreeGenerator gen = new DOTTreeGenerator();
    StringTemplate st = gen.toDOT(tree);
    System.out.println(st);
  }
}

Если вы запустите основной класс, вы увидите, что ввод:

#short(byte, int) f: short(byte a, int b) { ... } 
short f(byte a, int b) { ... }

производит два одинаковых дерева:

enter image description here

...