我目前开始学习YACC。我只是想知道如何在YACC中编写一个归属语法。请举个例子。我们可以使用工会吗?
答案 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
}}