有匪君子, 如切如磋, 如琢如磨。
有了函式定義之後, 再來就是要呼叫這個函式。function call 的 AST 應該長什麼樣子呢? 像 list 1 這樣。
list 1. function call AST
21 int f1(char c, int i)
22 {
23 }
24
25 int main()
26 {
27 f1('c', 6);
28 return 0;
29 }
30
31
5 root
6 |
7 prog
8 ________________________|________________________
9 | | |
10 f1 |int |func |global main |int |func |global main
11 ____|_____ |
12 | | func_body
13 para func_body ___|___
14 ____|____ | |
15 | | f1 return
16 c |char i |int _|__ |
17 | | 0
18 c 6
list 2, list 3 是我早期的版本, 那時候還可以這樣寫, 現在已經不行了。得用 list 1 的寫法才行, 也更像 c 語言了。
list 2. test_pattern/fc1.tree
1 f1(1)
2 token: f1
3 token: (
4 token: 1
5 token: )
6
7 root
8 |
9 prog
10 |
11 func call
12 |
13 1
list 3. test_pattern/fc2.tree
1 f1(1, 2, x+y, "abc", 1+2*3)
2 token: f1
3 token: (
4 token: 1
5 token: ,
6 token: 2
7 token: ,
8 token: x
9 token: +
10 token: y
11 token: ,
12 token: abc
13 token: ,
14 token: 1
15 token: +
16 token: 2
17 token: *
18 token: 3
19 token: )
20
21 root
22 |
23 prog
24 |
25 func call
26 ________|________
27 | | | | |
28 1 2 + abc +
29 _|__ _|__
30 | | | |
31 x y 1 *
32 _|__
33 | |
34 2 3
test_pattern/fc3.tree
1 f1()
2 token: f1
3 token: (
4 token: )
5
6 root
7 |
8 prog
9 |
10 func call
c_parser.cpp L334 是 function call 的 EBNF, 比較麻煩的是 function 的參數可不見得只是變數或是整數, 還可能是一個 function 或是運算式。當然, 如果想簡化, 就不要處理這些, 程式會簡單些, 不過我就是不爽不能用 function 或是運算式, 堅持把這個加了進去。
一樣對照著 ebnf 看, ebnf 怎麼寫, 程式就怎麼寫, 難不倒你的。
一開始先判斷目前的 token 是不是 NAME (變數名稱), 再來的 token 是不是 (, 再來有點麻煩, 因為參數可以沒有, 若接下來的 token 是 ), 表示沒有參數, 這個就簡單了, parse 完成。
如果不是 ), 表示有參數, 看看是不是 function call 還是運算式, 再接著判斷是不是還有下一個參數 (L371), 再來看下一個 token 是不是 ',', 再來就和前面一樣, 看看是不是 function call 還是運算式, 我寫得很輕鬆, 你應該混亂了, 請慢慢欣賞, 不要心急, 平心靜氣一定看得懂, 在看不懂就出動 gdb 吧!
c_parser.cpp
159 /*!
160 * primary : "(" expr ")" | NUMBER | IDENTIFIER | STRING | func_call
161 */
162 ASTNode* primary()
163 {
164 Token token = peek_token();
165 if (is_func_call())
166 {
167 ASTNode *e = func_call();
168 return e;
169 }
170 ...
321 bool is_func_call()
322 {
323 Token t = peek_token();
324 if (t.ast_type() == NAME)
325 {
326 t = peek_token(1);
327 if (t.str() == "(") // func_call
328 return true;
329 }
330
331 return false;
332 }
334 /// func_call: NAME '(' [ (expr | func_call) { ',' (expr | func_call) } ] ')' // 這是左遞迴嗎?
335 ASTNode* func_call()
336 {
337 ASTNode *fc = 0;
338 Token t = peek_token();
339
340 if (t.ast_type() == NAME)
341 {
342 fc = new ASTNode(func_call_token);
343 fc->set_str(t.str());
344
345 ASTNode *e=0;
346 pop_token();
347 t = peek_token();
348 if (t.str() == ("("))
349 {
350 pop_token();
351
352 //if((e = expr()) != 0)
353 t = peek_token();
354 if(t.str() != ")")
355 {
356
357 if (is_func_call())
358 {
359 e = func_call();
360 }
361 else
362 {
363 e = expr();
364 }
365
366 if (e)
367 fc->add_child(e);
368
369 Token t = peek_token();
370
371 while (t.str() != ")")
372 {
373
374
375 if (t.str() == ",")
376 {
377 pop_token();
378
379 if (is_func_call())
380 {
381 e = func_call();
382 }
383 else
384 {
385 e = expr();
386 }
387
388 if (e);
389 fc->add_child(e);
390 }
391 else
392 {
393 err("func_call: should ','", t.str());
394 }
395 t = peek_token();
396 }
397
398 }
399 else // function call passes no argument
400 {
401
402 }
403
404 t = peek_token();
405 if (t.str() == (")"))
406 {
407 pop_token();
408 }
409 else
410 {
411 err("func_call: should )", t.str());
412 }
413 }
414 else
415 {
416 err("func_call: should (", t.str());
417 }
418 }
419 else
420 {
421 err("func_call: should NAME", t.str());
422 }
423
424 return fc;
425 }
https://github.com/descent/simple_compiler/blob/master/c_parser.cpp
source code:
git commit: 8fcfec025156ca9196c03433867fb4132acac0bd
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。