ANTLR грамматика синтаксического анализа -> грамматика деревьев

В нашем последнем задании для нашего класса теории компилятора мы создали компилятор для небольшого подмножества Java (не MiniJava). Наш профессор дал нам возможность использовать любые инструменты, которые мы пожелаем, и после долгих раздумий я остановился на ANTLR. Мне удалось запустить и запустить сканер и парсер, а парсер выдает AST. Я застрял сейчас, пытаясь получить файл грамматики дерева для компиляции. Я понимаю, что основная идея состоит в том, чтобы скопировать грамматические правила из синтаксического анализатора и удалить большую часть кода, оставив правила перезаписи на месте, но, похоже, не нужно компилировать (ошибка OffndingToken). Я на правильном пути? Я что-то упускаю тривиально?

Древовидная грамматика:

tree grammar J0_SemanticAnalysis;

options {
  language = Java;
  tokenVocab = J0_Parser;
  ASTLabelType = CommonTree;
}

@header 
{ 
  package ritterre.a4; 
  import java.util.Map;
  import java.util.HashMap;
}

@members
{

}

walk
  : compilationunit
  ;

compilationunit
  : ^(UNIT importdeclaration* classdeclaration*)
  ;

importdeclaration
  : ^(IMP_DEC IDENTIFIER+)
  ;

classdeclaration
  : ^(CLASS IDENTIFIER ^(EXTENDS IDENTIFIER)? fielddeclaration* methoddeclaration*)
  ;

fielddeclaration
  : ^(FIELD_DEC IDENTIFIER type visibility? STATIC?)
  ;

methoddeclaration
  : ^(METHOD_DEC IDENTIFIER type visibility? STATIC? ^(PARAMS parameter+)? body)
  ;

visibility
  : PRIVATE 
  | PUBLIC
  ;

parameter
  : ^(PARAM IDENTIFIER type)
  ;

body
  : ^(BODY ^(DECLARATIONS localdeclaration*) ^(STATEMENTS statement*))
  ;

localdeclaration 
  : ^(DECLARATION type IDENTIFIER)
  ;

statement
  : assignment      
  | ifstatement     
  | whilestatement  
  | returnstatement 
  | callstatement   
  | printstatement  
  | block
  ;

assignment
  : ^(ASSIGN IDENTIFIER+ expression? expression)
  ;

ifstatement
  : ^(IF relation statement ^(ELSE statement)?)
  ;

whilestatement
  : ^(WHILE relation statement)
  ;

returnstatement
  : ^(RETURN expression?)
  ;

callstatement
  : ^(CALL IDENTIFIER+ expression+)
  ;

printstatement
  : ^(PRINT expression)
  ;

block
  : ^(STATEMENTS statement*)
  ;

relation
//  : expression (LTHAN | GTHAN | EQEQ | NEQ)^ expression
  : ^(LTHAN expression expression)
  | ^(GTHAN expression expression)
  | ^(EQEQ expression expression)
  | ^(NEQ expression expression)
  ;

expression
//  : (PLUS | MINUS)? term ((PLUS | MINUS)^ term)*
  : ^(PLUS term term)
  | ^(MINUS term term)
  ;

term
//  : factor ((MULT | DIV)^ factor)*
  : ^(MULT factor factor)
  | ^(DIV factor factor)
  ;

factor
  : NUMBER
  | IDENTIFIER (DOT IDENTIFIER | LBRAC expression RBRAC)?
  | NULL
  | NEW IDENTIFIER LPAREN RPAREN
  | NEW (INT | IDENTIFIER) (LBRAC RBRAC)?
  ;

type
  : (INT | IDENTIFIER) (LBRAC RBRAC)? 
  | VOID
  ;

Грамматика парсера:

parser grammar J0_Parser;

options
{
  output = AST;             // Output an AST
  tokenVocab = J0_Scanner;   // Pull Tokens from Scanner
  //greedy = true; // forcing this throughout?! success!!
  //cannot force greedy true throughout. bad things happen and the parser doesnt build
}

tokens
{
  UNIT;
  IMP_DEC;
  FIELD_DEC;
  METHOD_DEC;
  PARAMS;
  PARAM;
  BODY;
  DECLARATIONS;
  STATEMENTS;
  DECLARATION;
  ASSIGN;
  CALL;
}

@header { package ritterre.a4; }

// J0 - Extended Specification - EBNF
parse
  : compilationunit EOF -> compilationunit
  ;

compilationunit
  : importdeclaration* classdeclaration*
    -> ^(UNIT importdeclaration* classdeclaration*)
  ;

importdeclaration
  : IMPORT IDENTIFIER (DOT IDENTIFIER)* SCOLON
    -> ^(IMP_DEC IDENTIFIER+)
  ;

classdeclaration
  : (PUBLIC)? CLASS n=IDENTIFIER (EXTENDS e=IDENTIFIER)? LBRAK (fielddeclaration|methoddeclaration)* RBRAK
    -> ^(CLASS $n ^(EXTENDS $e)? fielddeclaration* methoddeclaration*)
  ;

fielddeclaration
  : visibility? STATIC? type IDENTIFIER SCOLON
    -> ^(FIELD_DEC IDENTIFIER type visibility? STATIC?)
  ;

methoddeclaration
  : visibility? STATIC? type IDENTIFIER LPAREN (parameter (COMMA parameter)*)? RPAREN body
    -> ^(METHOD_DEC IDENTIFIER type visibility? STATIC? ^(PARAMS parameter+)? body)
  ;

visibility
  : PRIVATE 
  | PUBLIC
  ;

parameter
  : type IDENTIFIER
    -> ^(PARAM IDENTIFIER type)
  ;

body
  : LBRAK localdeclaration* statement* RBRAK
    -> ^(BODY ^(DECLARATIONS localdeclaration*) ^(STATEMENTS statement*))
  ;

localdeclaration 
  : type IDENTIFIER SCOLON
    -> ^(DECLARATION type IDENTIFIER)
  ;

statement
  : assignment
  | ifstatement
  | whilestatement
  | returnstatement
  | callstatement
  | printstatement
  | block
  ;

assignment
  : IDENTIFIER (DOT IDENTIFIER | LBRAC a=expression RBRAC)? EQ b=expression SCOLON
    -> ^(ASSIGN IDENTIFIER+ $a? $b)
  ;

ifstatement
  : IF LPAREN relation RPAREN statement (options {greedy=true;} : ELSE statement)?
    -> ^(IF relation statement ^(ELSE statement)?)
  ;

whilestatement
  : WHILE LPAREN relation RPAREN statement
    -> ^(WHILE relation statement)
  ;

returnstatement
  : RETURN expression? SCOLON
    -> ^(RETURN expression?)
  ;

callstatement
  : IDENTIFIER (DOT IDENTIFIER)? LPAREN (expression (COMMA expression)*)? RPAREN SCOLON
    -> ^(CALL IDENTIFIER+ expression+)
  ;

printstatement
  : PRINT LPAREN expression RPAREN SCOLON
    -> ^(PRINT expression)
  ;

block
  : LBRAK statement* RBRAK
    -> ^(STATEMENTS statement*)
  ;

relation
  : expression (LTHAN | GTHAN | EQEQ | NEQ)^ expression
  ;

expression
  : (PLUS | MINUS)? term ((PLUS | MINUS)^ term)*
  ;

term
  : factor ((MULT | DIV)^ factor)*
  ;

factor
  : NUMBER
  | IDENTIFIER (DOT IDENTIFIER | LBRAC expression RBRAC)?
  | NULL
  | NEW IDENTIFIER LPAREN RPAREN
  | NEW (INT | IDENTIFIER) (LBRAC RBRAC)?
  ;

type
  : (INT | IDENTIFIER) (LBRAC RBRAC)? 
  | VOID
  ;

1 ответ

Решение

Проблема в том, что в своей древовидной грамматике вы делаете следующее (я верю 3 раза):

classdeclaration
  : ^(CLASS ... ^(EXTENDS IDENTIFIER)? ... )
  ;

^(EXTENDS IDENTIFIER)? часть неверна: вам нужно обернуть дерево в круглые скобки, и только потом сделать его необязательным:

classdeclaration
  : ^(CLASS ... (^(EXTENDS IDENTIFIER))? ... )
  ;

Однако было бы слишком легко, если бы это было все, не так ли?:)

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

ошибка (211): J0_SemanticAnalysis.g:61:26: [фатальное] назначение правила имеет решение не-LL(*) из-за рекурсивных вызовов правил, достижимых из alts 1,2. Решить с помощью левого факторинга или используя синтаксические предикаты или используя параметр backtrack=true.

Жалуется на assignment правило в вашей грамматике:

assignment
  : ^(ASSIGN IDENTIFIER+ expression? expression)
  ;

Поскольку ANTLR является генератором синтаксического анализатора 1 LL, он анализирует токены слева направо. Для этого необязательное выражение в IDENTIFIER+ expression? expression делает грамматику неоднозначной. Исправьте это, переместив ? до конца expression:

assignment
  : ^(ASSIGN IDENTIFIER+ expression expression?)
  ;

Я не позволяю двум последним буквам в имени ANT LR вводить вас в заблуждение, они означают распознавание языка, а не класс парсеров, которые он генерирует!

Другие вопросы по тегам