2012-02-16 108 views
4

我一直在努力学习ANTLR以创建一个领域特定的语言。其中一个要求是将此DSL转换为C语言。我已经能够获得识别DSL的基本语法,但是我将这个问题翻译为C.主要是,我的问题来自于尝试将DSL if语句转换为C if语句。我曾尝试在语法中使用打印语句,无济于事(我正在使用C#)。ANTLR语法if语句

这是我一直在测试语法:

**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 2012-02-16 16:04:49

回答

3

如果删除了回溯,这是很容易在你的情况下完成的,你可以让分析器立即建立C代码。

注意语法规则可以带参数(在我下面的例子中,缩进级别),并可以返回自定义对象(String S IN的例子):

这里是你的语法没有回溯,并输出到C代码(我米不太好,在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 2012-02-16 21:23:03

+0

不用客气@almostProgramming。 – 2012-02-16 21:58:52

+0

我很抱歉提出另一个问题这么快,但我将如何去添加功能,以在if语句中使用多个表达式?例如,如果$ variable1 = 0和$ variable7 = 0,则... – almostProgramming 2012-02-17 15:08:27

4

是的,问题是,你正试图在解析阶段发出你的'编译结果'(一个C程序)。解析器会回溯,并且通常不能指望解析器的每个部分只运行一次,并且每次都采用正确的路径。

AST输出绝对是我会建议看的,然后走AST来产生输出。 TreeWalker当然听起来像是一个有用的工具。

总的来说,不,我不相信任何非平凡的语法,只用解析动作就可以创建你想要的输出。

奇怪的是,你是我见过的第二个人在过去几天尝试做这件事。我当然可以看到“用解析器完成所有工作”这个想法的吸引力,但我真的认为这是不可行的。 ANTLR是一种工具,但它的输出是AST;不是编译的可执行文件

这里的其他类似的问题一个链接,如果你有兴趣:
Parsing Java code with ANTLR "need concept"