CompilerDev 板


LINE

「yacc/bison 系列 (2) - 输出 AST, if statement」展示了 bison 和 c++ 的用法, 虽 然可以用, 但不算是正式的用法, bison 有「支援真正的 c++」用法, 输出的 parser 是 c++ 版本, 还跟上 c++20 的标准, 对於我这个 c++ 爱好者来说, 这样很棒。 但是我不会用 ... 好不容易花了很大的力气才有点会用 bison, 突然要改用 c++ 版本, 又要突破一些障碍才行, 感觉又要重学。我真的应该为了使用 c++ 而去学习吗? 而且网路上的文章很少这样用, 用 bison, c++ keyword 找到的文章, 大部份找到和 c++ 的搭配都是我之前的那种用法; 另外的就是 bison 文件里头的 c++ 说明 - 10.1.1 A Simple C++ Example 还有范例: https://github.com/akimd/bison/tree/master/examples/c%2B%2B bison 文件除了 c++ 还有 d, java 的说明。 其中的 calc++ 范例从 bison 弄出可以编译的版本有点麻烦, 我直接把 calc++ 这个范 例 放在 bitbucket。 另外找到这篇: Flex and Bison in C++ flex 也有个输出 c++ lexer 的版本, 组合下来的情况有点乱, 都不知道怎麽相互搭配了 。另外还有一个 bisoncpp, 让情况更复杂了。 以 calc++ 来说明 flex/bison 怎麽搭配使用。bison 输出的是 c++ code parser, flex 输出的是 c++ code, 但不是 class 版本的 yylex(), 然後使用的 yylex() prototype 是 yy::parser::symbol_type yylex(driver& drv), 看传回值的部份, 不是原本的 int, 所 以这边用了 driver.hh 26 // Give Flex the prototype of yylex we want ... 27 # define YY_DECL \ 28 yy::parser::symbol_type yylex (driver& drv) 29 // ... and declare it for the parser's sake. 30 YY_DECL; 这样会就使用 yy::parser::symbol_type yylex() 而不是 int yylex(), 那一定要用 yy::parser::symbol_type yylex(), 不能用 int yylex() 吗? 看起来是不行, 如果可以 return token::NUMBER 也许还可以, 不过 list 1 定义的 enum 是被放在 class private, 所以无法直接存取, 还是得透过 make_XXXX 来使用这些 token enum, 就算可 以 好了, 也没有 yylval 来把 yylex 的 token 传给 bison。补充的 hoc_cpp_1.yy 勉强可 以这样用。 list 1. hoc_cpp.cpp 695 /// Token kinds. 696 struct token 697 { 698 enum token_kind_type 699 { 700 YYEMPTY = -2, 701 END_OF_FILE = 0, // END_OF_FILE 702 YYerror = 256, // error 703 YYUNDEF = 257, // "invalid token" 704 NUMBER = 258, // NUMBER 705 ASSIGN = 259, // ":=" 706 MINUS = 260, // "-" 707 PLUS = 261, // "+" 708 STAR = 262, // "*" 709 SLASH = 263, // "/" 710 LPAREN = 264, // "(" 711 RPAREN = 265, // ")" 712 NEWLINE = 266 // "\n" 713 }; [S: 目前我遇到的困境是, 使用 bison 输出 c++ parser 的版本, 不知道怎麽和 flex 输 出的 lexer 搭配。原本的 c parser 是搭配 int yylex(), 但是 c++ parser 是搭配 parser::symbol_type yylex(), 我目前还不知道怎麽用 flex 输出 parser::symbol_type yylex()。 :S] 不过没关系, 先来搞定 bison 输出 c++ parser 的用法。为什麽要这麽麻烦呢? 因为我 想 要用 std::string, 但是原本的 c parser union 在使用 std::string 时, 会有问题, bison 会输出类似 u.cpp 的 union, 用 c++ 编译会有问题, 需要自己补上相关的 ctor 才行, 而要让 bison 输出 c parser 编译可以过, 还要 copy ctor。 u.cpp 2 #include <cstdio> 3 #include <string> 4 using namespace std; 5 6 union YYSTYPE 7 { 8 string id; 9 int num; 10 #if 0 11 YYSTYPE(){}; 12 ~YYSTYPE(){}; 13 YYSTYPE operator=(const YYSTYPE&){} 14 #endif 15 }; 16 17 YYSTYPE yylval; 18 19 int main(int argc, char *argv[]) 20 { 21 22 return 0; 23 } 24 25 g++ -g -std=c++17 -Wall a1.cpp -o a1 26 a1.cpp:16:9: error: use of deleted function ‘YYSTYPE::YYSTYPE()’ 27 16 | YYSTYPE yylval; 前言说完了, 该进入正题, 来把最一开始的四则运算改写为 c++ 版本的 bison 语法。 hoc_cpp.yy 1 %require "3.2" 2 %debug 3 %language "c++" 4 %define api.token.constructor 5 %define api.value.type variant 6 %define api.location.file none 7 %define parse.assert 8 %locations 9 10 %code requires // *.hh 11 { 12 #include <string> 13 #include <vector> 14 typedef std::vector<std::string> strings_type; 15 } 16 17 %code // *.cc 18 { 19 #include <iostream> 20 #include <sstream> 21 22 namespace yy 23 { 24 // Prototype of the yylex function providing subsequent tokens. 25 static parser::symbol_type yylex (); 26 27 // Print a vector of strings. 28 std::ostream& 29 operator<< (std::ostream& o, const strings_type& ss) 30 { 31 o << '{'; 32 const char *sep = ""; 33 for (strings_type::const_iterator i = ss.begin (), end = ss.end (); 34 i != end; ++i) 35 { 36 o << sep << *i; 37 sep = ", "; 38 } 39 return o << '}'; 40 } 41 } 42 43 // Convert to string. 44 template <typename T> 45 std::string 46 to_string (const T& t) 47 { 48 std::ostringstream o; 49 o << t; 50 return o.str (); 51 } 52 } 53 54 %token <int> NUMBER; 55 %token END_OF_FILE 0; 56 %token 57 ASSIGN ":=" 58 MINUS "-" 59 PLUS "+" 60 STAR "*" 61 SLASH "/" 62 LPAREN "(" 63 RPAREN ")" 64 NEWLINE "\n" 65 ; 66 67 %type <int> list; 68 %type <int> expr; 69 70 %left "+" "-" 71 %left "*" "/" 72 73 %% 74 75 list: {printf("\taaempty\n");} 76 | list "\n" {printf("list \\n\n");} 77 | list expr "\n" { printf("%d\n", $2); } 78 79 expr: NUMBER {$$ = $1; printf("xx num %d\n", $1);} 80 | expr "+" expr {$$ = $1 + $3;} 81 | expr "-" expr {$$ = $1 - $3;} 82 | expr "*" expr {$$ = $1 * $3;} 83 | expr "/" expr {$$ = $1 / $3;} 84 | '(' expr ')' 85 86 87 %% 88 89 char *progname; 90 int lineno = 1; 91 92 namespace yy 93 { 94 // Use nullptr with pre-C++11. 95 #if !defined __cplusplus || __cplusplus < 201103L 96 # define NULLPTR 0 97 #else 98 # define NULLPTR nullptr 99 #endif 100 101 // The yylex function providing subsequent tokens: 102 // TEXT "I have three numbers for you." 103 // NUMBER 1 104 // NUMBER 2 105 // NUMBER 3 106 // TEXT "And that's all!" 107 // END_OF_FILE 108 109 static 110 parser::symbol_type 111 yylex () 112 { 113 int c; 114 int input_val; 115 static int count = 0; 116 const int stage = count; 117 ++count; 118 parser::location_type loc (NULLPTR, stage + 1, stage + 1); 119 120 while ((c=getchar()) == ' ' || c == '\t') 121 ; 122 123 if (c == EOF) 124 return parser::make_END_OF_FILE (loc); 125 if (c == '.' || isdigit(c) ) 126 { 127 ungetc(c, stdin); 128 //scanf("%lf", &input_val); 129 scanf("%d", &input_val); 130 //val = 5; 131 return parser::make_NUMBER (input_val, loc); 132 } 133 134 switch (c) 135 { 136 case '+': 137 { 138 return parser::make_PLUS(loc); 139 break; 140 } 141 case '-': 142 { 143 return parser::make_MINUS(loc); 144 break; 145 } 146 } 147 148 if (c == '\n') 149 { 150 ++lineno; 151 return parser::make_NEWLINE(loc); 152 } 153 //return c; 154 return parser::make_NUMBER (c, loc); 155 156 #if 0 157 static int count = 0; 158 const int stage = count; 159 ++count; 160 parser::location_type loc (NULLPTR, stage + 1, stage + 1); 161 switch (stage) 162 { 163 case 0: 164 return parser::make_TEXT ("I have three numbers for you.", loc); 165 case 1: 166 case 2: 167 case 3: 168 return parser::make_NUMBER (stage, loc); 169 case 4: 170 return parser::make_TEXT ("And that's all!", loc); 171 default: 172 return parser::make_END_OF_FILE (loc); 173 } 174 #endif 175 } 176 177 // Mandatory error function 178 void parser::error (const parser::location_type& loc, const std::string& msg) 179 { 180 std::cerr << loc << ": " << msg << '\n'; 181 } 182 } 183 184 int main () 185 { 186 yy::parser p; 187 p.set_debug_level (!!getenv ("YYDEBUG")); 188 return p.parse (); 189 } 190 191 // Local Variables: 192 // mode: C++ 193 // End: hoc_cpp.yy L1 ~ 52 从 https://github.com/akimd/bison/blob/master/examples/ c%2B%2B/variant.yy 这边照抄, 其他部份也是从这个档案改写而来。 最主要是 parser::symbol_type yylex (); 的改写, 本来 return NUMBER 这样的 macro 改为 return parser::make_NUMBER (input_val, loc), 另外也要定义 hoc_cpp.yy L55 ~ L65 的 token, 这样才能用 parser::make_END_OF_FILE(), parser::make_NEWLINE(), parser::make_PLUS(), parser::make_MINUS() 这些 member function。 来看看 make_NUMBER (int v, location_type l) ref: list 2, 怎麽那麽巧, 第一个参 数 是 int, 那就是 hoc_cpp.yy L54 定义的 54 %token <int> NUMBER;, 如果是写成 %token <std::string> NUMBER;, 那 make_NUMBER(std::string v, location_type l) 就会长这 样。 list 2. hoc_cpp.cpp 1071 #if 201103L <= YY_CPLUSPLUS 1072 static 1073 symbol_type 1074 make_NUMBER (int v, location_type l) 1075 { 1076 return symbol_type (token::NUMBER, std::move (v), std::move (l)); 1077 } 1078 #else 1079 static 1080 symbol_type 1081 make_NUMBER (const int& v, const location_type& l) 1082 { 1083 return symbol_type (token::NUMBER, v, l); 1084 } 1085 #endif 比较麻烦的是本来可以 return getch 的 c, 我不知道要怎麽产生一个类似 make_CHAR 的 member function, 所以用 parser::make_NUMBER 代替, 另外要处理 parser::make_PLUS (), parser::make_MINUS() 也比原本 return c 麻烦不少。 L58, L59 MINUS, PLUS 似乎要用 "", 用 '' 就会有奇怪的错误。 再来 main call parse() 也不一样, 变成 member function 了。 以下是编译指令: g++ -g -std=c++17 -Wall -c hoc_cpp.cpp g++ -g -std=c++17 -Wall hoc_cpp.o -o hoc_cpp 这样就完成一个 c++ 版本的 bison parser。 另外补充一个写法 hoc_cpp_1.yy, 没有使用 %define api.token.constructor, 影响到 什 麽呢? yylex 的 function prototype, hoc_cpp_1.yy L24 那样, 而 yylex return 也不 同, 改成 hoc_cpp_1.yy L133, L134, 使用了 emplace(), 相当奇怪的用法。「10.1.7 C++ Scanner Interface」提到了这个, 有兴趣的朋友自己看, 就不说明了。 hoc_cpp_1.yy 1 %language "c++" 2 %require "3.2" 3 %debug 4 %define api.value.type variant 5 %define parse.assert 6 %locations 7 8 %code requires // *.hh 9 { 10 #include <string> 11 #include <vector> 12 typedef std::vector<std::string> strings_type; 13 14 #include "hoc_cpp_1.tab.hh" 15 } 16 17 %code // *.cc 18 { 19 #include <iostream> 20 #include <sstream> 21 22 namespace yy 23 { 24 int yylex (yy::parser::value_type *yylval, yy::parser::location_type *yylloc); 25 26 // Print a vector of strings. 27 std::ostream& 28 operator<< (std::ostream& o, const strings_type& ss) 29 { 30 o << '{'; 31 const char *sep = ""; 32 for (strings_type::const_iterator i = ss.begin (), end = ss.end (); 33 i != end; ++i) 34 { 35 o << sep << *i; 36 sep = ", "; 37 } 38 return o << '}'; 39 } 40 } 41 42 // Convert to string. 43 template <typename T> 44 std::string 45 to_string (const T& t) 46 { 47 std::ostringstream o; 48 o << t; 49 return o.str (); 50 } 51 } 52 53 %token <int> NUMBER; 54 %token END_OF_FILE 0; 55 %token 56 ASSIGN ":=" 57 MINUS "-" 58 PLUS "+" 59 STAR "*" 60 SLASH "/" 61 LPAREN "(" 62 RPAREN ")" 63 NEWLINE "\n" 64 ; 65 66 %type <int> list; 67 %type <int> expr; 68 69 %left "+" "-" 70 %left "*" "/" 71 72 %% 73 74 list: {printf("\taaempty\n");} 75 | list "\n" {printf("list \\n\n");} 76 | list expr "\n" { printf("%d\n", $2); } 77 78 expr: NUMBER {$$ = $1; printf("xx num %d\n", $1);} 79 | expr "+" expr {$$ = $1 + $3;} 80 | expr "-" expr {$$ = $1 - $3;} 81 | expr "*" expr {$$ = $1 * $3;} 82 | expr "/" expr {$$ = $1 / $3;} 83 | '(' expr ')' 84 85 86 %% 87 88 char *progname; 89 int lineno = 1; 90 91 namespace yy 92 { 93 // Use nullptr with pre-C++11. 94 #if !defined __cplusplus || __cplusplus < 201103L 95 # define NULLPTR 0 96 #else 97 # define NULLPTR nullptr 98 #endif 99 100 // The yylex function providing subsequent tokens: 101 // TEXT "I have three numbers for you." 102 // NUMBER 1 103 // NUMBER 2 104 // NUMBER 3 105 // TEXT "And that's all!" 106 // END_OF_FILE 107 108 int yylex (yy::parser::value_type *yylval, yy::parser::location_type *yylloc) 109 { 110 int c; 111 int input_val; 112 static int count = 0; 113 const int stage = count; 114 ++count; 115 //parser::location_type loc (NULLPTR, stage + 1, stage + 1); 116 117 while ((c=getchar()) == ' ' || c == '\t') 118 ; 119 120 if (c == EOF) 121 { 122 ;//return parser::make_END_OF_FILE (loc); 123 return yy::parser::token::END_OF_FILE; 124 } 125 if (c == '.' || isdigit(c) ) 126 { 127 ungetc(c, stdin); 128 //scanf("%lf", &input_val); 129 scanf("%d", &input_val); 130 //scanf("%d", yyla->value); 131 //val = 5; 132 ;//return parser::make_NUMBER (input_val, loc); 133 yylval->emplace<int>() = input_val; 134 return yy::parser::token::NUMBER; 135 } 136 137 switch (c) 138 { 139 case '+': 140 { 141 ;//return parser::make_PLUS(loc); 142 return yy::parser::token::PLUS; 143 break; 144 } 145 case '-': 146 { 147 ;//return parser::make_MINUS(loc); 148 return yy::parser::token::MINUS; 149 break; 150 } 151 } 152 153 if (c == '\n') 154 { 155 ++lineno; 156 ;//return parser::make_NEWLINE(loc); 157 return yy::parser::token::NEWLINE; 158 } 159 yylval->emplace<int>() = c; 160 //return yy::parser::token::NUMBER; 161 return c; 162 //return parser::make_NUMBER (c, loc); 163 164 #if 0 165 static int count = 0; 166 const int stage = count; 167 ++count; 168 parser::location_type loc (NULLPTR, stage + 1, stage + 1); 169 switch (stage) 170 { 171 case 0: 172 return parser::make_TEXT ("I have three numbers for you.", loc); 173 case 1: 174 case 2: 175 case 3: 176 return parser::make_NUMBER (stage, loc); 177 case 4: 178 return parser::make_TEXT ("And that's all!", loc); 179 default: 180 return parser::make_END_OF_FILE (loc); 181 } 182 #endif 183 } 184 185 // Mandatory error function 186 void parser::error (const parser::location_type& loc, const std::string& msg) 187 { 188 std::cerr << loc << ": " << msg << '\n'; 189 } 190 } 191 192 int main () 193 { 194 yy::parser p; 195 p.set_debug_level (!!getenv ("YYDEBUG")); 196 return p.parse (); 197 } 198 199 // Local Variables: 200 // mode: C++ 201 // End: 编译指令: bison -d hoc_cpp_1.yy g++ hoc_cpp_1.tab.cc -o hoc_cpp_1 ref: ‧ 文本解析工具使用总结 (觉得不太正确) ‧ Flex and Bison in C++ blog 版本 https://descent-incoming.blogspot.com/2022/02/bison-c.html -- 纸上得来终觉浅,绝知此事要躬行。 --



※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 1.200.148.76 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/CompilerDev/M.1645523600.A.B51.html
1F:推 mshockwave: 居然会生出c++20的parser XDD 02/23 13:10
2F:→ ketrobo: 想玩纯C++可以用Boost Spirit 02/24 21:53







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:BuyTogether站内搜寻

TOP