2012-02-16 3 views
4

나는 도메인 특정 언어를 만들기 위해 ANTLR을 배우기 위해 노력 해왔다. 요구 사항 중 하나는이 DSL을 C로 변환하는 것입니다. DSL을 인식하는 기본 문법을 얻을 수 있었지만이 문제를 C로 변환하는 데 문제가 있습니다. 주로 문제는 DSL if 문을 C if 문. 문법에서 print 문을 사용해 보았습니다. 아무 소용이 없었습니다 (저는 C#을 사용하고 있습니다). 내가 상상했던 꽤입니다이 C# 클래스ANTLR 문법 if 문

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(); 
     } 
    } 
} 

출력과이를 테스트 할 때

**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'; 

: 여기

내가 함께 테스트 된 문법이다.

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

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

    } 
} 

문제는 문이 두 번 인쇄되어 있지만 순서대로 내가 기대했다하지 않을 경우 그 두 번째가 될 것으로 보인다. 필자는 단순히 인쇄문 내에서 문장 블록을 내보내려고하는 것과 관련이 있다고 가정하지만이 작업을 올바르게 수행하는 방법에 대해서는 확신 할 수 없습니다. 나는 StringTemplate을 읽거나, AST를 만들고 그것을 걷기 위해 Tree Walker를 사용했지만, 어쨌든 위의 결과를 이와 같이 보도록 고칠 수 있습니까?

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

내가 어떤 방향으로 나아 갔는지에 대한 도움을 주시면 감사하겠습니다. StringTemplate으로 도약하는 것이 나을지, 기본 액션 코드로 이것을 할 수있는 방법이 있습니까? 정보를 남겨두면 언제든지 물어보십시오.

+0

아, 그리고 매우 멋지게 구성된 질문 BTW. +1! – Task

답변

3

, 당신은 파서가 바로 C 코드를 빌드 할 수 있습니다.내가 '(

다음 역 추적 및 C 코드를 출력하지 않고 문법이있다 : 파서 ​​규칙은 매개 변수 (아래에있는 내 예제에서 들여 쓰기 수준)를 취할 수 있으며, 사용자 정의 객체 (예에서 String들)을 반환 할 수

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

: 당신이 지금 입력하여 파서을 테스트하는 경우

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 
        ; 

: 데모) 자바 그래서, C에서 # m 너무 좋지 않아 하여 콘솔에 인쇄되어 다음

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

문법의 다른 부분 (역 추적) 술어 (크게) 의존하면 같은 전략은 위와 같이 그냥 간단하지만 트리 문법 (그래서 적용 할 수 이후 backtracking-parser가 작업을 수행하고 AST를 생성했습니다.

+0

고마워요, 제가 정확히 찾고있는 것이 었습니다. – almostProgramming

+0

@ 프로그래밍을 매우 환영합니다. –

+0

너무 빨리 다른 질문을하는 것에 대해 사과드립니다. if 문에서 여러 표현식을 사용하는 기능을 추가하는 방법은 무엇입니까? 예를 들어, $ variable1 = 0 및 $ variable7 = 0이면 ... – almostProgramming

4

예, 문제는 구문 분석 단계에서 '컴파일 결과'(C 프로그램)를 내보내려고한다는 것입니다. 구문 분석기는 되돌아 갈 것이고 일반적으로 파서의 각 섹션이 한 번만 실행되고 매번 올바른 경로를 사용할 것으로 기대할 수는 없습니다.

AST 출력물은 분명히 내가 바라는 것이고 출력물을 만들기 위해 AST를 걷는 것입니다. TreeWalker는 확실히 유용한 도구처럼 들립니다.

전반적으로, 필자는 평범하지 않은 문법에 대해서만 구문 분석 작업으로 원하는 출력을 만들 수 있다고 생각하지 않습니다.

이상하게도 필자는 지난 몇 일 동안 내가 본 두 번째 사람입니다. 필자는 파서로 모든 것을 할 수 있다는 아이디어의 매력을 확실히 볼 수 있습니다.하지만 필자는 그것이 실현 가능하다고 생각하지 않습니다. ANTLR은 도구의 일종이지만 그 출력은 AST입니다. 컴파일 된 실행 파일이 아닙니다. 당신이 관심이 있다면

여기에 다른 비슷한 질문에 대한 링크 : 당신은 쉽게 경우에 이루어집니다 되돌아을 제거하면
Parsing Java code with ANTLR "need concept"