2012-02-16 3 views
4

Я работаю над изучением ANTLR, чтобы создать язык, специфичный для домена. Одно из требований заключается в том, чтобы перевести этот DSL в C. Я смог получить базовую грамматику, которая распознает DSL, однако у меня возникают проблемы с переводом этого на C. В основном, моя проблема связана с попыткой перевода выражения DSL if оператор C if. Я пробовал использовать инструкции печати в грамматике, безрезультатно (я использую C#).ANTLR Грамматика, если заявление

Вот грамматика я тестировал с:

**ifTest.g** 
grammar ifTest; 

options 
{ 
backtrack=true; 
output=AST; 
language=CSharp2; 
} 

/************************* 
PARSER RULES 
*************************/ 
prog : lambda 
| statements EOF; 

lambda : /* Empty */; 

statements 
: statement+; 

statement 
: logical 
| assignment 
| NEWLINE; 


logical : IF a=logical_Expr THEN b=statements 
     { 
      System.Console.Write("\tif (" + $a.text + ")\n\t{\n\t" + "\t" +  $b.text + "\n\n\t}"); 
     } 
     (ELSE c=statements  
     {  
     System.Console.Write("\n\telse {\n\t\t\t" + $c.text + "\n\t}"); 
    })? 
    ENDIF 
    { 
     System.Console.Write("\n}"); 
    } 
; 

logical_Expr 
    : expr  
    ; 

expr : (simple_Expr) (op expr)* 
    ; 

simple_Expr  : MINUS expr 
    | identifier 
    | number 
    ; 

identifier : parameter 
    | VARIABLE 
    ; 

parameter : norm_parameter 
    ; 

norm_parameter : spec_label 
    | reserved_parm 
    ; 

spec_label : LABEL 
       ; 

reserved_parm : RES_PARM 
       ; 

op : PLUS 
| MINUS 
| MULT 
| DIV 
| EQUALS 
| GT 
| LT 
| GE 
| LE 
; 

number  : INT 
    | FLOAT 
    | HEX 
       ; 

assignment : identifier GETS expr 
; 

/************************* 
    LEXER RULES 
*************************/ 
WS :  (' '|'\t')+ {$channel=HIDDEN;}; 

COMMENT : '/*' (options {greedy=false;}:.)* '*/' {$channel=HIDDEN;} 
       ; 

LINECOMMENT 
    : '#' ~('\n'|'\r')* NEWLINE {$channel=HIDDEN;} 
    ; 

NEWLINE : '\r'?'\n' {$channel=HIDDEN;}; 

IF : I F; 
THEN : T H E N; 
ELSE : E L S E; 
ENDIF : E N D I F; 

PLUS : '+'; 
MINUS : '-'; 
MULT : '*'; 
DIV : '/'; 
EQUALS : '='; 
GT : '>'; 
LT : '<'; 
GE : '>='; 
LE : '<='; 
ULINE : '_'; 
DOT : '.'; 
GETS : ':='; 

LABEL : (LETTER|ULINE)(LETTER|DIGIT|ULINE)*; 

INT  : '-'?DIGIT+; 

FLOAT : '-'? DIGIT* DOT DIGIT+; 

HEX : ('0x'|'0X')(HEXDIGIT)HEXDIGIT*; 

RES_PARM: DIGIT LABEL; 

VARIABLE: '\$' LABEL; 


fragment A:'A'|'a'; fragment B:'B'|'b'; fragment C:'C'|'c'; fragment D:'D'|'d';  
fragment E:'E'|'e'; fragment F:'F'|'f'; fragment G:'G'|'g'; fragment H:'H'|'h';  
fragment I:'I'|'i'; fragment J:'J'|'j'; fragment K:'K'|'k'; fragment L:'L'|'l'; 
fragment M:'M'|'m'; fragment N:'N'|'n'; fragment O:'O'|'o'; fragment P:'P'|'p';  
fragment Q:'Q'|'q'; fragment R:'R'|'r'; fragment S:'S'|'s'; fragment T:'T'|'t';  
fragment U:'U'|'u'; fragment V:'V'|'v'; fragment W:'W'|'w'; fragment X:'X'|'x'; 
fragment Y:'Y'|'y'; fragment Z:'Z'|'z'; 


fragment DIGIT 
: '0'..'9'; 

fragment LETTER 
: A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z; 

fragment HEXDIGIT 
: '0..9'|'a..f'|'A'..'F'; 

При тестировании этого с этим C# класса

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using Antlr.Runtime; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string inputString = "if $variable1 = 0 then\n if $variable2 > 250 then\n $variable3 := 0\n endif\n endif"; 

      Console.WriteLine("Here is the input string:\n " + inputString + "\n"); 

      ANTLRStringStream input = new ANTLRStringStream(inputString); 

      ifTestLexer lexer = new ifTestLexer(input); 

      CommonTokenStream tokens = new CommonTokenStream(lexer); 

      ifTestParser parser = new ifTestParser(tokens); 

      parser.prog(); 

      Console.Read(); 
     } 
    } 
} 

Выходной сигнал не совсем, как я себе представлял.

**Output** 
if ($variable2 > 250) 
    { 
      $variable3 := 0 

    } 
}  if ($variable1 = 0) 
    { 
      if $variable2 > 250 then 
      $variable3 := 0 
      endif 

    } 
} 

Проблема заключается в том, что второй оператор if печатает дважды, но не в том порядке, в котором я надеялся. Я предполагаю, что это связано с тем, что я просто пытаюсь испустить блок операторов в операциях печати, но я не совсем уверен, как это сделать, чтобы правильно работать. Я читал на StringTemplate или создавал AST и использовал Tree Walker для его ходьбы, но все равно, чтобы исправить вышеупомянутый вывод, чтобы посмотреть что-то вроде этого?

if ($variable1 = 0) 
{ 
    if ($variable2 > 250) 
    { 
     $variable3 := 0 
    } 
} 

Любая помощь по тому направлению, в котором я должен приниматься, была бы весьма признательна. Было бы лучше, если бы я сделал прыжок в StringTemplate, или есть ли способ сделать это с помощью базового кода действия? Если я оставил любую информацию, пожалуйста, не стесняйтесь спрашивать.

+0

О, и очень красиво построенный вопрос BTW. +1 для вас! – Task

ответ

3

Если удалить откаты, которое легко сделать в вашем случае, вы можете позволить парсер построить код C немедленно.

Обратите внимание, что правила синтаксического анализа могут принимать параметры (уровень отступа в моем примере ниже) и могут возвращать пользовательские объекты (String ых в примере):

Вот ваша грамматика без возвратов и вывода в код C (I» м не слишком хорошо C#, так что демка в Java):

grammar ifTest; 

prog  
: statements[""] EOF {System.out.println($statements.str);} 
; 

statements[String indent] returns [String str] 
@init{$str = "";} 
: (statement[indent] {$str += indent + $statement.str + "\n";})* 
; 

statement[String indent] returns [String str] 
: if_statement[indent] {$str = $if_statement.str;} 
| assignment   {$str = $assignment.str;} 
; 

if_statement[String indent] returns [String str] 
: IF expr THEN s1=statements[indent + " "] {$str = "if (" + $expr.str + ")\n" + indent + "{\n" + $s1.str;} 
    (ELSE s2=statements[indent + " "]  {$str += indent + "}\n" + indent + "else\n" + indent + "{\n" + $s2.str;})? 
    ENDIF          {$str += indent + "}";} 
; 

assignment returns [String str] 
: identifier GETS expr {$str = $identifier.str + " = " + $expr.str + ";";} 
; 

expr returns [String str] 
: rel_expr {$str = $rel_expr.str;} 
; 

rel_expr returns [String str] 
: e1=eq_expr {$str = $e1.str;} (LT e2=eq_expr {$str += " < " + $e2.str;} 
           | GT e2=eq_expr {$str += " > " + $e2.str;} 
           | LE e2=eq_expr {$str += " <= " + $e2.str;} 
           | GE e2=eq_expr {$str += " >= " + $e2.str;} 
           )? 
; 

eq_expr returns [String str] 
: e1=add_expr {$str = $e1.str;} (EQUALS e2=add_expr {$str += " == " + $e2.str;})? 
; 

add_expr returns [String str] 
: e1=mult_expr {$str = $e1.str;} (PLUS e2=mult_expr {$str += " + " + $e2.str;} 
            | MINUS e2=mult_expr {$str += " - " + $e2.str;} 
           )* 
; 

mult_expr returns [String str] 
: e1=unary_expr {$str = $e1.str;} (MULT e2=unary_expr {$str += " * " + $e2.str;} 
            | DIV e2=unary_expr {$str += "/" + $e2.str;} 
            )* 
; 

unary_expr returns [String str] 
: MINUS term {$str = "-" + $term.str;} 
| term  {$str = $term.str;} 
; 

term returns [String str] 
: identifier {$str = $identifier.str;} 
| number  {$str = $number.text;} 
; 

identifier returns [String str] 
: LABEL {$str = $LABEL.text;} 
| RES_PARM {$str = $RES_PARM.text;} 
| VARIABLE {$str = $VARIABLE.text.substring(1);} 
; 

number 
: INT 
| FLOAT 
| HEX 
; 

WS   : (' '|'\t')+ {$channel=HIDDEN;}; 
COMMENT  : '/*' .* '*/' {$channel=HIDDEN;}; 
LINECOMMENT : '#' ~('\n'|'\r')* NEWLINE {$channel=HIDDEN;}; 
NEWLINE  : '\r'?'\n' {$channel=HIDDEN;}; 
IF   : I F; 
THEN  : T H E N; 
ELSE  : E L S E; 
ENDIF  : E N D I F; 
PLUS  : '+'; 
MINUS  : '-'; 
MULT  : '*'; 
DIV   : '/'; 
EQUALS  : '='; 
GT   : '>'; 
LT   : '<'; 
GE   : '>='; 
LE   : '<='; 
ULINE  : '_'; 
DOT   : '.'; 
GETS  : ':='; 
LABEL  : (LETTER | ULINE) (LETTER | DIGIT | ULINE)*; 
INT   : DIGIT+;   // no '-' here, unary_expr handles this 
FLOAT  : DIGIT* DOT DIGIT+; // no '-' here, unary_expr handles this 
HEX   : '0' ('x'|'X') HEXDIGIT+; 
RES_PARM : DIGIT LABEL; 
VARIABLE : '$' LABEL; 

fragment A:'A'|'a'; fragment B:'B'|'b'; fragment C:'C'|'c'; fragment D:'D'|'d';  
fragment E:'E'|'e'; fragment F:'F'|'f'; fragment G:'G'|'g'; fragment H:'H'|'h';  
fragment I:'I'|'i'; fragment J:'J'|'j'; fragment K:'K'|'k'; fragment L:'L'|'l'; 
fragment M:'M'|'m'; fragment N:'N'|'n'; fragment O:'O'|'o'; fragment P:'P'|'p';  
fragment Q:'Q'|'q'; fragment R:'R'|'r'; fragment S:'S'|'s'; fragment T:'T'|'t';  
fragment U:'U'|'u'; fragment V:'V'|'v'; fragment W:'W'|'w'; fragment X:'X'|'x'; 
fragment Y:'Y'|'y'; fragment Z:'Z'|'z'; 

fragment HEXDIGIT : DIGIT |'a..f'|'A'..'F'; 
fragment DIGIT : '0'..'9'; 
fragment LETTER : A | B | C | D | E | F | G | H | I | J | K | L | M 
        | N | O | P | Q | R | S | T | U | V | W | X | Y | Z 
        ; 

Если теперь проверить ваш парсер с входом:

if $variable1 = 0 then 
    if $variable2 > 250 then 
    $variable3 := 0 
    else 
    $variable3 := 42 
    endif 
endif 

выводятся на консоль следующего:

if (variable1 == 0) 
{ 
    if (variable2 > 250) 
    { 
    variable3 = 0; 
    } 
    else 
    { 
    variable3 = 42; 
    } 
} 

Если другие части вашей грамматики полагаться (сильно) на предикатах (отслеживание источников), та же стратегия, как указано выше, может так же легко использовать, но затем в грамматике дерева (так после обработчик обратного отслеживания выполнил свою работу и подготовил AST).

+0

Спасибо, это именно то, что я искал. – almostProgramming

+0

Добро пожаловать @almostProgramming. –

+0

Я извиняюсь за то, что задал еще один вопрос так скоро, но как я могу добавить функциональность для использования нескольких выражений в операторах if? Например, если $ variable1 = 0 и $ variable7 = 0, то ... – almostProgramming

4

Да, проблема в том, что вы пытаетесь исправить свои «результаты компиляции» (программа C) во время этапа синтаксического анализа. Парсер будет отступать, и в целом вы не можете ожидать, что каждый раздел анализатора будет запускаться только один раз и каждый раз принимать правильный путь.

Выход AST определенно является тем, что я хотел бы предложить, а затем прогуливаясь по AST, чтобы произвести ваш выход. TreeWalker, безусловно, звучит как полезный инструмент.

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

Как ни странно, вы второй человек, которого я видел, пытаюсь сделать это за последние пару дней. Я, конечно же, вижу привлекательность идеи «все это с парсером!», Но я действительно не думаю, что это возможно. ANTLR - черта инструмента, но его выход - AST; не скомпилированный исполняемый файл.

Вот ссылка на другой аналогичный вопрос, если вы заинтересованы:
Parsing Java code with ANTLR "need concept"

Смежные вопросы