2009-05-20 24 views
21

Có ai biết các hướng dẫn để đi bộ AST được tạo ra bởi ANTLR trong C# không? Gần nhất tôi có thể tìm thấy là this, nhưng nó không phải là hữu ích khủng khiếp.Hướng dẫn cho đi bộ AST ASTR trong C#?

Mục tiêu của tôi là đi qua các cây mà tôi đang tạo dựa trên ngôn ngữ cụ thể theo miền mà tôi đang làm việc và sử dụng các cây để tạo mã C# được tạo.

Hướng dẫn dựa trên Java cũng sẽ hữu ích - bất kỳ điều gì cung cấp ví dụ rõ ràng về cách đi qua các AST của ANTLR.

Trả lời

19

Tôi đã cố gắng tìm ra điều này bằng cách điều chỉnh ví dụ ở cuối Manuel Abadia's article.

Đây là phiên bản của tôi, mà tôi đang sử dụng để chuyển đổi mã được phân tách thành C#. Đây là những bước sau:

  1. Khởi tạo một ANTLRStringStream hoặc lớp con với đầu vào của bạn (nó có thể là một tập tin hoặc chuỗi).
  2. Khởi tạo từ khóa được tạo của bạn, chuyển qua luồng chuỗi đó.
  3. Tạo luồng mã thông báo bằng từ khóa.
  4. Làm nhanh trình phân tích cú pháp của bạn bằng luồng mã thông báo đó.
  5. Lấy giá trị cấp cao nhất từ ​​trình phân tích cú pháp của bạn và biến nó thành một CommonTree.
  6. Traverse cây:

Để có được văn bản đen của một nút, sử dụng node.Text. Để lấy tên mã thông báo của một nút, hãy sử dụng node.Token.Text.

Lưu ý rằng node.Token.Text sẽ chỉ cung cấp cho bạn tên thực của mã thông báo nếu đó là mã thông báo ảo không có chuỗi tương ứng. Nếu đó là mã thông báo thực, thì node.Token.Text sẽ trả về chuỗi của nó.

Ví dụ, nếu bạn có điều sau đây trong ngữ pháp của bạn:

tokens { PROGRAM, FUNCDEC } 

EQUALS : '=='; 
ASSIGN : '='; 

Sau đó, bạn sẽ nhận được "PROGRAM", "FUNCDEC", "==", và "=" từ truy cập tương ứng của node.Token.Text.

Bạn có thể xem một phần ví dụ của tôi bên dưới hoặc bạn có thể duyệt qua full version.


public static string Convert(string input) 
{ 
    ANTLRStringStream sStream = new ANTLRStringStream(input); 
    MyGrammarLexer lexer = new MyGrammarLexer(sStream); 

    CommonTokenStream tStream = new CommonTokenStream(lexer); 

    MyGrammarParser parser = new MyGrammarParser (tStream); 
    MyGrammarParser.program_return parserResult = parser.program(); 

    CommonTree ast = (CommonTree)parserResult.Tree; 

    Print(ast); 
    string output = header + body + footer; 

    return output; 
} 

public static void PrintChildren(CT ast) 
{ 
    PrintChildren(ast, " ", true); 
} 

public static void PrintChildren(CT ast, string delim, bool final) 
{ 
    if (ast.Children == null) 
    { 
     return; 
    } 

    int num = ast.Children.Count; 

    for (int i = 0; i < num; ++i) 
    { 
     CT d = (CT)(ast.Children[i]); 
     Print(d); 
     if (final || i < num - 1) 
     { 
      body += delim; 
     } 
    } 
} 

public static void Print(CommonTree ast) 
{ 
    switch (ast.Token.Text) 
    { 
     case "PROGRAM": 
      //body += header; 
      PrintChildren(ast); 
      //body += footer; 
      break; 
     case "GLOBALS": 
      body += "\r\n\r\n// GLOBALS\r\n"; 
      PrintChildren(ast); 
      break; 
     case "GLOBAL": 
      body += "public static "; 
      PrintChildren(ast); 
      body += ";\r\n"; 
      break; 

     .... 
    } 
} 
8

Thông thường bạn đi bộ AST với đệ quy và thực hiện các hành động khác nhau dựa trên loại nút. Nếu bạn đang sử dụng các nút cây đa hình (nghĩa là các lớp con khác nhau cho các nút khác nhau trong cây), thì việc gửi hai lần trong mẫu Khách truy cập có thể phù hợp; tuy nhiên, điều đó thường không thuận tiện với Antlr.

Trong giả, đi bộ thường trông hơi như thế này:

func processTree(t) 
    case t.Type of 
     FOO: processFoo t 
     BAR: processBar t 
    end 

// a post-order process 
func processFoo(foo) 
    // visit children 
    for (i = 0; i < foo.ChildCount; ++i) 
     processTree(foo.GetChild(i)) 
    // visit node 
    do_stuff(foo.getText()) 

// a pre-order process 
func processBoo(bar) 
    // visit node 
    do_stuff(bar.getText()) 
    // visit children 
    for (i = 0; i < foo.ChildCount; ++i) 
     processTree(foo.GetChild(i)) 

Các loại chế biến phụ thuộc nhiều vào ngữ nghĩa của ngôn ngữ. Ví dụ, xử lý một tuyên bố IF, với cấu trúc (IF <predicate> <if-true> [<if-false>]), khi tạo mã cho một máy chồng như JVM hoặc CLR, có thể trông hơi như thế này:

func processIf(n) 
    predicate = n.GetChild(0) 
    processExpr(predicate) // get predicate value on stack 
    falseLabel = createLabel() 
    genCode(JUMP_IF_FALSE, falseLabel) // JUMP_IF_FALSE is called brfalse in CLR, 
             // ifeq in JVM 
    if_true = n.GetChild(1) 
    processStmt(if_true) 
    if_false = n.ChildCount > 2 ? n.GetChild(2) : null 
    if (if_false != null) 
     doneLabel = createLabel() 
     genCode(JUMP, doneLabel) 
    markLabel(falseLabel) 
    if (if_false != null) 
     processStmt(if_false) // if-false branch 
     markLabel(doneLabel) 

Nói chung tất cả mọi thứ được thực hiện một cách đệ quy tùy thuộc vào loại của dòng điện nút, v.v.

+0

bất kỳ mẫu mã của Tree Grammar for C#? – Kiquenet

0

tôi đã làm một cái gì đó tương tự (nhưng không thực sự) và tôi đã kết thúc với một TreeParser.

Tôi cũng khuyên bạn nên mua sách ANTLR. Tôi thấy nó có giá trị hơn bất kỳ tài nguyên web nào. Nó có thể không có tất cả các câu trả lời nhưng nó chắc chắn sẽ giúp với những điều cơ bản.