0
|
1 //
|
|
2 // 四則演算パーサー
|
|
3 //
|
|
4 // Chihiro.SAKAMOTO
|
|
5 //
|
|
6 #include <climits>
|
|
7 #include <cstdio>
|
|
8 #include "parser.h"
|
|
9
|
|
10 cparser::cparser()
|
|
11 : token_type(OP_EOF)
|
|
12 {
|
|
13 }
|
|
14
|
|
15 cparser::~cparser()
|
|
16 {
|
|
17 }
|
|
18
|
|
19 // 構文解析
|
|
20 //
|
|
21 // statement ::= <value> "=" <expr>
|
|
22 // | "print" <expr>
|
|
23 // | "list"
|
|
24 //
|
|
25 bool cparser::parse(calc_driver *driver, const std::string &f)
|
|
26 {
|
|
27 if ((fp = std::fopen(f.c_str(), "r")) == NULL)
|
|
28 return false;
|
|
29
|
|
30 int token;
|
|
31 std::string str;
|
|
32 while ((token = get_token(str)) != OP_EOF) {
|
|
33 if (token == OP_LIST) { // listコマンド
|
|
34 driver->list();
|
|
35 }
|
|
36 else if (token == OP_PRINT) { // printコマンド
|
|
37 cnode *node = add_sub_expr();
|
|
38 driver->print(node);
|
|
39 delete node;
|
|
40 }
|
|
41 else if (token == OP_VALUE) { // 代入
|
|
42 std::string equ;
|
|
43 if (get_token(equ) != OP_EQU)
|
|
44 return false;
|
|
45 cnode *node = add_sub_expr();
|
|
46 driver->assign(str, node);
|
|
47 delete node;
|
|
48 }
|
|
49 else if (token != OP_NEWLINE) { // 空行以外はエラー
|
|
50 return false;
|
|
51 }
|
|
52
|
|
53 token = get_token(str);
|
|
54 if (token != OP_NEWLINE) // 行末の\nを読み捨てる
|
|
55 unget_token(token, str);
|
|
56 }
|
|
57
|
|
58 std::fclose(fp);
|
|
59
|
|
60 return true;
|
|
61 }
|
|
62
|
|
63 // +−の処理
|
|
64 // expr ::= <muldiv> (("+" | "-") <muldiv>)*
|
|
65
|
|
66 cnode *cparser::add_sub_expr()
|
|
67 {
|
|
68 cnode *left = mul_div_expr();
|
|
69 if (left == NULL)
|
|
70 return NULL;
|
|
71
|
|
72 for (;;) {
|
|
73 std::string str;
|
|
74 int token = get_token(str);
|
|
75 if (token != OP_PLUS && token != OP_MINUS) {
|
|
76 unget_token(token, str);
|
|
77 break;
|
|
78 }
|
|
79
|
|
80 cnode *right = mul_div_expr();
|
|
81 if (right == NULL) {
|
|
82 delete left;
|
|
83 return NULL;
|
|
84 }
|
|
85 left = new cnode(token, left, right);
|
|
86 }
|
|
87 return left;
|
|
88 }
|
|
89
|
|
90 // */の処理
|
|
91 // muldiv ::= <term> (("*" | "/") <term>)*
|
|
92
|
|
93 cnode *cparser::mul_div_expr()
|
|
94 {
|
|
95 cnode *left = term();
|
|
96 if (left == NULL)
|
|
97 return NULL;
|
|
98
|
|
99 for (;;) {
|
|
100 std::string str;
|
|
101 int token = get_token(str);
|
|
102 if (token != OP_TIMES && token != OP_DIVIDE) {
|
|
103 unget_token(token, str);
|
|
104 break;
|
|
105 }
|
|
106
|
|
107 cnode *right = term();
|
|
108 if (right == NULL) {
|
|
109 delete left;
|
|
110 return NULL;
|
|
111 }
|
|
112 left = new cnode(token, left, right);
|
|
113 }
|
|
114 return left;
|
|
115 }
|
|
116
|
|
117 // 終端
|
|
118 // term ::= "-" <term> | "(" <expr> ")" | <number> | <value>
|
|
119
|
|
120 cnode *cparser::term()
|
|
121 {
|
|
122 std::string str;
|
|
123 int token = get_token(str);
|
|
124
|
|
125 if (token == OP_MINUS) {
|
|
126 return new cnode(OP_NEG, term());
|
|
127 }
|
|
128 if (token == OP_OPAR) {
|
|
129 cnode *node = add_sub_expr();
|
|
130 if (node == NULL)
|
|
131 return NULL;
|
|
132
|
|
133 token = get_token(str);
|
|
134 if (token != OP_CPAR) {
|
|
135 delete node;
|
|
136 unget_token(token, str);
|
|
137 return NULL;
|
|
138 }
|
|
139 return node;
|
|
140 }
|
|
141 if (token == OP_CONST) {
|
|
142 return new cnode(OP_CONST, atoi(str.c_str()));
|
|
143 }
|
|
144 if (token == OP_VALUE) {
|
|
145 return new cnode(OP_VALUE, new std::string(str));
|
|
146 }
|
|
147 return NULL;
|
|
148 }
|
|
149
|
|
150 // ロケール無視の文字判定
|
|
151
|
|
152 inline bool _isspace(int c)
|
|
153 {
|
|
154 return c == ' ' || c == '\t';
|
|
155 }
|
|
156
|
|
157 inline bool _isnum(int c)
|
|
158 {
|
|
159 return c >= '0' && c <= '9';
|
|
160 }
|
|
161
|
|
162 inline bool _isalpha(int c)
|
|
163 {
|
|
164 return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_';
|
|
165 }
|
|
166
|
|
167 // 字句解析
|
|
168 //
|
|
169 // トークン トークン種別
|
|
170 //
|
|
171 // '+' OP_PLUS
|
|
172 // '-' OP_MINUS
|
|
173 // '*' OP_TIMES
|
|
174 // '/' OP_DIVIDE
|
|
175 // '(' OP_OPAR
|
|
176 // ')' OP_CPAR
|
|
177 // '=' OP_EQU
|
|
178 // '\n' OP_NEWLINE
|
|
179 // EOF OP_EOF
|
|
180 //
|
|
181 // "print" OP_PRINT
|
|
182 // "list" OP_LIST
|
|
183 // [0-9]+ OP_CONST
|
|
184 // [a-zA-Z_][a-zA-Z0-9_]* OP_VALUE
|
|
185 //
|
|
186 int cparser::_get_token(std::string &str)
|
|
187 {
|
|
188 int c;
|
|
189
|
|
190 // 先行する空白類を読み飛ばす。
|
|
191 do {
|
|
192 c = std::fgetc(fp);
|
|
193 } while (_isspace(c)) ;
|
|
194
|
|
195 // strは再利用されているかもしれないので、クリアしておく
|
|
196 str.clear();
|
|
197
|
|
198 // 記号の場合は、1文字で1トークンなので、種別を返す。
|
|
199 switch (c) {
|
|
200 case '+': return OP_PLUS;
|
|
201 case '-': return OP_MINUS;
|
|
202 case '*': return OP_TIMES;
|
|
203 case '/': return OP_DIVIDE;
|
|
204 case '(': return OP_OPAR;
|
|
205 case ')': return OP_CPAR;
|
|
206 case '=': return OP_EQU;
|
|
207 case '\n': return OP_NEWLINE;
|
|
208 case EOF: return OP_EOF;
|
|
209 }
|
|
210
|
|
211 // 1文字目が数字の場合は、数字文字列としてトークンを切り出す。
|
|
212 if (_isnum(c)) {
|
|
213 while (_isnum(c)) {
|
|
214 str += c;
|
|
215 c = std::fgetc(fp);
|
|
216 }
|
|
217 std::ungetc(c, fp);
|
|
218 return OP_CONST;
|
|
219 }
|
|
220
|
|
221 // 1文字目が英字の場合は、「識別子」としてトークンを切り出す。
|
|
222 if (_isalpha(c)) {
|
|
223 while (_isalpha(c) || _isnum(c)) {
|
|
224 str += c;
|
|
225 c = fgetc(fp);
|
|
226 }
|
|
227 std::ungetc(c, fp);
|
|
228
|
|
229 // 切り出したトークンが「予約語」の場合は、対応したトークン種別を返す。
|
|
230 if (str == "print")
|
|
231 return OP_PRINT;
|
|
232 if (str == "list")
|
|
233 return OP_LIST;
|
|
234 return OP_VALUE;
|
|
235 }
|
|
236
|
|
237 // その他の文字はエラーとする。
|
|
238 return OP_ERROR;
|
|
239 }
|