Flex Bison. 452 次赞· 1 人在谈论. Heavy rock n roll from Birmingham UK. ... Facebook wordmark. 登录. Flex Bison 的照片. 7 张照片. ... <看更多>
flex & bison 在 Flex & Bison Tutorial: Part 1 - Basics - YouTube 的美食出口停車場
Code - https://github.com/sanved77/flexbison/ (Star or fork it for easier access)In this tutorial, I have given the basic summary of flex ... ... <看更多>
flex & bison 在 [分享] bison and c++ - 看板CompilerDev - 批踢踢實業坊 的美食出口停車場
「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://www.ptt.cc/bbs/CompilerDev/M.1645523600.A.B51.html
... <看更多>