如何在YACC中编写属性语法?

时间:2013-10-09 17:41:51

标签: yacc

我目前开始学习YACC。我只是想知道如何在YACC中编写一个归属语法。请举个例子。我们可以使用工会吗?

4 个答案:

答案 0 :(得分:2)

是的,您可以将属性与解析树中的节点相关联。每个节点都有一个属性,即“美元说明符”。 下面是一个示例,其中该属性用于表达式的值:

expression : expression '+' expression { $$ = $1 + $3; }
           | expression '-' expression { $$ = $1 - $3; }
           | NUMBER { $$ = $1; }
           ;

默认情况下,此单个属性是整数,但您可以使用%union 指令更改类型。不同类型的节点可以具有不同类型的属性。 (这就是为什么它被称为%union 而不是像%type 。)如果你需要多个属性,你可以使用C结构或结构指针作为类型

答案 1 :(得分:2)

是的,您需要使用一些C ++功能:对于AST树存储,使用基于Sym 抽象类的“符号”类集。虚拟继承使您能够使用Sym*指针和dynamic_cast<Num*>(o)进行STL容器和对象操作。使用attr{}地图作为语法属性。

完整(c)词汇程序框架(c)源代码树见https://github.com/ponyatov/uc/tree/master/ast

struct Sym {        // universal algebraic symbolic type, struct applies public
    string tag;         // class/type marker, required for token elements
    string val;         // value, string is universal can represent _any_ data

    Sym(string T,string V);     // <T:V> pair constructor
    Sym(string V);              // token constructor

    vector<Sym*> nest;          // \ nest[]ed elements for AST tree
    void push(Sym*o);           // / push new nested element

    map<string,Sym*> attr;      // \ named dynamic attributes,
    double num ;                // / or extra attributes you need

    virtual string head();      // return "<T:V>" pair repr
    string pad(int);            // pad output of tree elements
    string dump(int depth=0);   // return tree dump
};

struct Num: Sym { Num(string);      // number tokens
    double val; string head(); };
struct Str: Sym { Str(string);      // 'string' tokens
    string head(); };

struct Vector: Sym { Vector(); };   // [vector]
struct Op: Sym { Op(string); };     // operator

typedef Sym*(*FN)(Sym*);                        // \ primitive function
struct Fn: Sym { Fn(string V, FN F); FN fn; };  // /

                                // == lexer interface ==
extern int yylex();             // get next token
extern int yylineno;            // line number
extern char* yytext;            // parsed lexeme (ASCIIZ string)
#define TOC(C,X) { yylval.o = new C(yytext); return X; } // gen.token macro
                                // == syntax parser interface ==
extern int yyparse();           // grammar parser
extern void yyerror(string);    // error callback function
#include "ypp.tab.hpp"          // shared lex/yacc token definitions

注意lpp.lpp中使用的lexer接口宏,用于表单

中的标记构造
[0-9]+(\.[0-9]*)?([eE](\+\-)?[0-9]+)?   TOC(Num,NUM)    /* number */

对于答案,请参阅上面的语法必须描述为

%defines %union { Sym*o; }
%token <o> NUMBER ADD SUB
%type <o> expression

expression : expression ADD expression {
    // build AST node
    $$=$2; $$->push($1); $$->push($3);
    // synth .num attribute from nested nodes
    $$->num = $1->num + $3->num ;
    }

expression : expression SUB expression {
    // build AST node
    $$=$2; $$->push($1); $$->push($3);
    // synth .num from nested nodes
    $$->num  = $1->num - $3->num ;
    }

expression : NUMBER { $$=$1; } /* terminal should be used directly */

或者如果你想要真正的符号方式:这个yacc语法将在attr{}中做动态可合成属性(在C ++中非常神秘,但在Python + PLY语法中看起来很清楚)

%%
REPL : | REPL ex { cout << $2->dump() << endl; } ;

ex : SYM                { $$=$1; /* terminal as is */ } ;
ex : NUM                { $$=$1; /* terminal as is */
    // synth
    $$->attr["num"] = new Num(dynamic_cast<Num*>($1)->val);
    } ;

ex : SYM LP ex RP       { $$=new Op("@");               // apply operator
                          $$->push(new Fn($1->val));    // new function
                          $$->push($3);                 // parameters
    // synth
    if ($1->val=="sin")
        $$->attr["num"] = new Num(std::sin(\
            dynamic_cast<Num*>($3->attr["num"])->val));
    } ;

ex : LP ex RP           { $$=$2; /* as is */ } ; // must be after SYM(ex)

ex : ex DIV ex          { $$=$2; $$->push($1); $$->push($3);
   $$->attr["num"] = new Num(\
    dynamic_cast<Num*>($1->attr["num"])->val \
    / \
    dynamic_cast<Num*>($3->attr["num"])->val \
    );
   } ;

给出树

<op:=> #0x5b1180
    <sym:A> #0x5b1118
    <op:+> #0x5b1348
        <op:-> #0x5b11e8
            1 #0x5b1250
                num =
                    1 #0x5b12a8
        <op:*> #0x4a07d8
            <op:+> #0x5b13b0
                2.3 #0x5b1418
                    num =
                        2.3 #0x5b1470
            <op:^> #0x4a1090
                4e-005 #0x4a1010
                    num =
                        4e-005 #0x4a1050
                <op:/> #0x5bb730
                    num =
                        -0.0399165 #0x5bb850
                    <op:@> #0x5bb648
                        num =
                            -0.279415 #0x5bb6d0
                        <fn:sin> #0x5bb680
                        6 #0x5bb570
                            num =
                                6 #0x5bb5b0
                    7 #0x5bb768
                        num =
                            7 #0x5bb7a8

(*)to answeres:请注意静默中的属性语法关键字。

答案 2 :(得分:1)

网络上有很多yacc语法的例子。对yacc example的简单谷歌搜索会带来很多链接。还有一堆有用的链接here

答案 3 :(得分:1)

我更喜欢最简单的方法,是的,使用这个语法: @ https://github.com/ponyatov/uc/blob/master/ast/union.yacc

此变体仍构建带注释的 AST树,但属性硬编码到类中。如果需要额外的属性,请使用虚拟继承,并手动跟踪属性树的有效性(按生产规则)。

%defines %union {
    struct AST {
        string name;
        double value;
        virtual string dump(int depth=0);
        vector<AST*> nest; void push(Sym*);
    } *node;
}

/* tokens name/value must be filled in lexer */

%token <node> SYM NUM EQ ADD SUB MUL DIV POW LP RP
%type <node> ex  
                    // precedence down higher
%right EQ
%left ADD SUB
%left MUL DIV
%right PFX

%%
REPL : | REPL ex    { cout << $2->dump() << endl } ;

ex : SYM            { $$=$1; } ; // token as is
ex : NUM            { $$=$1; } ; // token as is

ex : ADD ex %prec PFX {
    $$=$1; $$->push($2);                // unary operator AST subtree
    $$->value = + $2->value;            // + A
    };
ex : SUB ex %prec PFX {
    $$=$1; $$->push($2);                // unary operator AST subtree
    $$->value = - $2->value;            // - A
    };

ex : ex ADD ex      {
    $$=$2; $$->push($1); $$->push($3);  // build AST subtree
    $$->value = $1->value + $2->value;  // synth attr without cryptic code
    } ;

ex : ex MUL ex      {
    $$=$2; $$->push($1); $$->push($3);  // build AST subtree
    $$->value = $1->value * $2->value;  // synth attr without cryptic code
    } ;

实际上,yacc-ng应该支持%struct选项直接构建AST树,但是yacc不能这样做,你应该struct*嵌套%union }}