构建抽象语法树时要考虑的要点是什么

时间:2016-01-29 13:27:27

标签: bison abstract-syntax-tree

我正在研究一种简单的玩具语言来描述UI小部件。我用bison / flex来实现语法。我现在想从语法规则中创建一个AST。但是,我不确定AST的“粒度级别”以及应包括在内的内容。根据我的阅读,AST应该是“最小的”并避免提供冗余信息。同时显然我不应该从原始来源中删除任何信息。构建AST时是否有特定的规则?

语法如下:

%{

#include <string>
#include <iostream>

void yyerror (const char *error);
int  yylex();

extern int yylineno;

%}

%code{
 //   int lineno;
}

%union {
  char* s;
  double d;
  int i;
}

/* declare tokens */


%token APPLICATION GEOMETRY
%token FORM BUTTON LABEL TEXTINPUT
%token ID NAME 
%token WIDTH HEIGHT TEXT ONCLICK
%token ABSOLUTELAYOUT LINEARLAYOUT GRIDLAYOUT
%token ABSOLUTE_LAYOUT_DOT_LEFT ABSOLUTE_LAYOUT_DOT_TOP   
%token EOL ENDOFFILE

%token<s> IDENTIFIER STRING
%token<d> INTEGER

%%


appDef: APPLICATION '{' NAME ':' STRING formdefList '}' {std::cout << "found app" << std::endl;}

iddef : ID ':' IDENTIFIER


formdefList : /* nothing */
    | formdefList formdef
    ;


formdef : FORM  '{' formbodydef '}' 
    ;

formbodydef : /*nothing*/
    | iddef
    | formbodydef layoutdef
    | error                     { 
                                  std::cout << "found error in form body near line: " << yylineno << std::endl;
                                  std::exit(1);  
                                }
    ;

layoutdef : ABSOLUTELAYOUT '{' layoutBody '}' 
    | GRIDLAYOUT '{' layoutBody '}'
    | LINEARLAYOUT '{' layoutBody '}'
    ;

layoutBody : /* nothing */
    | layoutBody layoutdef       /* Layouts can be embedded in one another */
    | layoutBody buttonDef
    | layoutBody labelDef
    | error { std::cout << "Was expecting a child control near line: " << yylineno << std::endl; std::exit(1);}
    ;

geometrydef : GEOMETRY ':' '{' geometrybody '}' { std::cout << "start of geometry def:" << std::endl;}
    ;

geometrybody : /*nothing*/                      { std::cout << "start of geometrybody def" << std::endl;}
    | geometrybody WIDTH ':' INTEGER             
    | geometrybody HEIGHT ':' INTEGER
    | geometrybody ABSOLUTE_LAYOUT_DOT_LEFT ':' INTEGER
    | geometrybody ABSOLUTE_LAYOUT_DOT_TOP ':' INTEGER
    | error { std::cout << "error near line: " << yylineno << std::endl; std::exit(1);}
    ;

buttonDef : BUTTON '{' buttonBody '}'
    ;

buttonBody : /*nothing*/
    | buttonBody iddef
    | buttonBody TEXT ':' STRING
    | buttonBody geometrydef
    ;

labelDef : LABEL '{' labelBody '}'
    ;

labelBody : /*nothing*/
    | labelBody iddef
    | labelBody TEXT ':' STRING
    | labelBody geometrydef
    ;        

%%


void yyerror (const char *error)
{
  std::cout << error << std::endl;
}

以下是验证语法的一些示例输入:

Application{
name: "HelloWorld"
   Form{
      id: MainForm
      LinearLayout{
         Button{
           geometry: {width:20 height:20}
         }
         AbsoluteLayout{
            Button{
               geometry:{
                  width:20
                  height:20
                  AbsoluteLayout.Top:10
                  AbsoluteLayout.Left:20
               }
            }
         }
      }
   }
   Form{
      id: Secondary
   }
}

我现在正在为语法构建AST并需要一些关于其结构的建议:

  • 我应该如何将语法规则映射到AST节点?每个终端符号一个AST节点?
    • 我应该如何设计节点之间的父子关系?
    • 语法规则的左侧是formdefformbodydefformdefList显式出现在AST中吗?
    • 我应该在节点中包含哪些内容?仅包含终端令牌类型(IDNAME等...)和令牌值是否足够?
    • 我是否应该将所有标记值包含为字符串,并在稍后阶段执行实际类型转换,例如字符串转换为整数等?

感谢您的建议!

1 个答案:

答案 0 :(得分:3)

节点需要包含一个&#34;抽象&#34;语法类别(通常接近许多规则的LHS)。 有关AST与CST的讨论,请参阅https://stackoverflow.com/a/1916687/120163,以及为什么您可能希望节点包含具体的语法类别(例如,确切的LHS规则或叶子的具体名称)。 该链接讨论了何时应在AST中保留终端。

如果它们表示语法不记录的方式(例如,标识符名称,数字和字符串文字常量等),它们还应包含。请参阅https://stackoverflow.com/a/6320259/120163为什么你应该在lex时转换值,而不是以后。

此处显示:https://stackoverflow.com/a/17393852/120163是AST打印出来时的样子(最好构建AST打印机以帮助您调试它们)。

您需要在节点中保留多少信息取决于您最终要对其执行的操作。如果你想从AST重新生成源代码,你需要保留许多你可能没有考虑过的东西,例如:转换数字的基数。有关详细信息,请参阅https://stackoverflow.com/a/5834775/120163