{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[{"file_id":"1AB3RNlLfkZBSZKu6_Wh20CkIpbxfJIve","timestamp":1663461789208}],"collapsed_sections":[],"machine_shape":"hm","authorship_tag":"ABX9TyPIcDQyFt4X4ilFml8HBsRK"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"},"gpuClass":"premium"},"cells":[{"cell_type":"markdown","source":["#計算機程式設計二\n","#第七週上課內容\n","#Calculator\n","\n","### 主題: 編譯器 Compiler、詞法分析器 Lexical Analyzer、語法分析器 Parser\n","\n"],"metadata":{"id":"9xoK4OIjIfZA"}},{"cell_type":"markdown","source":["###參考資料:\n","- MIT OCW 6.01 HW1: Calculator — Fall 2011   (google MIT6_01SCS11_hw1.pdf)\u000b\n","\n","- Wikipedia http://rosettacode.org/wiki/Arithmetic_Evaluator/C\n","\n","- “Compiler Design in C”,  Allen Holub\n","\n","\n"],"metadata":{"id":"05G1Fqg6GRZX"}},{"cell_type":"markdown","source":["這個單元的主要任務是實作出 Expression 計算機，能夠用來求出下面範例中的算式的值\n","```\n",">> x = 3\n","3\n",">> y = -5\n","-5\n",">> z = 4*x + y*-6\n","42\n","```\n","\n","我們的目標是藉由實作來了解基本的詞法分析、語法分析概念，以及粗淺的編譯器設計原理。編譯器設計是資工領域的基礎科目，未來如果有機會最好選修一下，我們在這裡只能提到一些淺顯的知識。所謂編譯器，是指一種程式工具，可以用來將某種高階語言翻譯成另一種較低階語言，譬如 C Compiler 能夠將 C 語言程式翻譯成組合語言 (Assembly) 程式。C和C++ 從發明以來，語法和功能還是持續在擴充，譬如 C11 或是 C++14、C++17。我們必須使用支援新語法的編譯器，才能夠編譯包含新語法的程式碼。C 編譯器將 C 語言翻譯成組合語言之後，還要再依靠組譯器 (Assembler) 將組合語言翻譯成 Object Code ，然後由 Linker 連結其他函式庫，再載入記憶體中執行。\n","\n","\n","開始寫 Expression 計算機的程式之前，我們要先定好希望處理的 Expression。我們要先確定 Expression 中會出現哪些符號和字詞，另外就是 Expression 可以接受哪些字詞的組合，也就是所謂的語法。在這個範例中，我們假設要處理的 Expression 只有 \n"," i) 加減乘除的運算以及 \n"," ii) 設定變數值，\n","而且任何 Expression 最後經過 Evaluation ，得到的都是整數值。所以，我們要實作的計算機，只有整數型別，不論是變數還是常數都只能得到整數值，而且加減乘除的四則運算也都是整數運算。\n","\n","先來處理符號和字詞。符號包含 `+ - * / =  (  )`  ，字詞則有 *整數值* 和 *變數名稱*。這個階段要做的事情通常稱做詞法分析 Lexical Analysis，需要寫出叫做 Tokenizer  的程式。我們的程式要能夠讀取一行輸入，然後把每個符號和字詞擷取出來，切成一個個 Token，每個 Token 有一個對應的代號，用一個整數值來代表。底下是以 `typedef` 定的整數列舉型別，`UNKNOWN` 對應的整數值是 `0`、`END` 對應的整數值是 `1`、`INT` 對應的整數值是 `2`、依此類推。\n","```\n","typedef enum {UNKNOWN, END, INT, ID, ADDSUB, MULDIV, ASSIGN, LPAREN, RPAREN} TokenSet;\n","```\n","\n","所以如果輸入的 Expression 是  `2*(3+x)`，對應的 Token 就是  `INT MULDIV LPAREN INT ADDSUB ID RPAREN`，因為 `2` 對應到 `INT`、`*` 對應到 `MULDIV`、 `(` 對應到 `LPAREN`、依此類推。\n"],"metadata":{"id":"azEB13vJtHte"}},{"cell_type":"markdown","source":["除了記住 Token 的代號之外，我們也要把對應的字串記下來，存在下面的 字元陣列中\n","```\n","static char lexeme[MAXLEN];\n","```\n","\n","接下來要寫出一個函數 \n","```\n","TokenSet getToken(void);\n","``` \n","每次呼叫能夠取出下一個 Token，輸入的字元如果是空白就忽略，換行字元對應的 Token 是 `END`。完整的程式碼如下："],"metadata":{"id":"kkghyhylNKIP"}},{"cell_type":"code","source":["%%writefile lex.c\n","\n","#include <stdio.h>\n","#include <ctype.h>\n","#include <string.h>\n","#include \"lex.h\"\n","static TokenSet getToken(void);\n","static TokenSet lookahead = UNKNOWN;\n","static char lexeme[MAXLEN];\n","TokenSet getToken(void)\n","{\n","    int i;\n","    char c;\n","    while ( (c = fgetc(stdin)) == ' ' || c== '\\t' );  // 忽略空白字元\n","    if (isdigit(c)) {\n","        lexeme[0] = c;\n","        c = fgetc(stdin);\n","        i = 1;\n","        while (isdigit(c) && i<MAXLEN) {\n","            lexeme[i] = c;\n","            ++i;\n","            c = fgetc(stdin);\n","        }\n","        ungetc(c, stdin);\n","        lexeme[i] = '\\0';\n","        return INT;\n","    } else if (c == '+' || c == '-') {\n","        lexeme[0] = c;\n","        lexeme[1] = '\\0';\n","        return ADDSUB;\n","    } else if (c == '*' || c == '/') {\n","        lexeme[0] = c;\n","        lexeme[1] = '\\0';\n","        return MULDIV;\n","    } else if (c == '\\n') {\n","        lexeme[0] = '\\0';\n","        return END;\n","    } else if (c == '=') {\n","        strcpy(lexeme, \"=\");\n","        return ASSIGN;\n","    } else if (c == '(') {\n","        strcpy(lexeme, \"(\");\n","        return LPAREN;\n","    } else if (c == ')') {\n","        strcpy(lexeme, \")\");\n","        return RPAREN;\n","    } else if (isalpha(c) || c == '_') {\n","        lexeme[0] = c;\n","        c = fgetc(stdin);\n","        i = 1;\n","        while (isalpha(c) || isdigit(c) || c == '_') {\n","            lexeme[i] = c;\n","            ++i;\n","            c = fgetc(stdin);\n","        }\n","        ungetc(c, stdin);\n","        lexeme[i] = '\\0';\n","        return ID;\n","    } else {\n","        return UNKNOWN;\n","    }\n","}\n","\n","void advance(void)\n","{\n","    lookahead = getToken();\n","}\n","\n","int match(TokenSet token)\n","{\n","    if (lookahead == UNKNOWN) advance();\n","    return token == lookahead;\n","}\n","\n","char* getLexeme(void)\n","{\n","    return lexeme;\n","}\n"],"metadata":{"id":"ueZaPjLItKXw","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1666697909285,"user_tz":-480,"elapsed":450,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"5715b640-8e24-4f25-d288-796dfed9403a"},"execution_count":70,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting lex.c\n"]}]},{"cell_type":"code","source":["%%writefile lex.h\n","\n","#ifndef __LEX__\n","#define __LEX__\n","\n","#define MAXLEN 256\n","typedef enum {UNKNOWN, END, INT, ID, ADDSUB, MULDIV, ASSIGN,\n","LPAREN, RPAREN} TokenSet;\n","\n","extern int match (TokenSet token);\n","extern void advance(void);\n","extern char* getLexeme(void);\n","\n","#endif // __LEX__"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"Q2IIYAclOCxr","executionInfo":{"status":"ok","timestamp":1666697899473,"user_tz":-480,"elapsed":617,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"1f1143f3-155a-40ba-efa5-2c27fcf7b173"},"execution_count":69,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting lex.h\n"]}]},{"cell_type":"markdown","source":["程式碼寫法其實很規律，讀到特定字元，就傳回對應的 Token 代號，並且順便把內容儲存在 `lexeme` 陣列中。比較複雜的是 `INT` 和 `ID`。以 `INT` 來說，我們需要讀取一連串包含 `0` 到 `9` 的數字，所以一旦發現開頭第一個字元是數字，就繼續把後面的數字吃進來，直到遇到的字元不是數字為止。由於最後多吃了一個字元 (因為不是數字而跳出 `while` 迴圈)，所以必須用 `ungetc` 把那個多讀到的字元吐回去。至於 `ID` 我們允許的變數名稱和 C 語言一樣，可以是由英文字母或是 底線 `_` 開頭，後面可以接更多英文字母或是數字或是底線，例如 `x, y, Mobile01, Gran_Torino`。\n","\n","實際上我們會把 `getToken` 隱藏起來，後續的程式碼並不會直接呼叫 `getToken`，而是透過底下的兩個函數來讀取和判斷目前的 Token：\n","```\n","void advance(void)\n","{\n","    lookahead = getToken();\n","}\n","\n","int match(TokenSet token)\n","{\n","    if (lookahead == UNKNOWN) advance();\n","    return token == lookahead;\n","}\n","```\n","\n","呼叫 `advance()` 就會讀取下一個 Token，讀取到的 Token 會儲存在全域變數 `lookahead` ，它的宣告如下：\n","```\n","static TokenSet lookahead = UNKNOWN;\n","```\n","\n","呼叫 `match(token)` 並且傳入希望判斷的 Token，就會比對目前記錄在 `lookahead` 的 Token 和預期的 Token是否相同。在第一次呼叫 `match` 的時候，由於 `lookahead` 是 `UNKNOWN`，所以會先呼叫 `advance` 取得 Token。 \n","        \n","        \n","由於我們把 `lexeme` 也隱藏起來，所以還需要底下的函數，讓外部的程式碼可以讀取 `lexeme`\n","```\n","char* getLexeme(void)\n","{\n","    return lexeme;\n","}\n","```\n","我們拿底下的程式碼測試一下是否可以正確運作。"],"metadata":{"id":"vDbcAirdZzO2"}},{"cell_type":"code","source":["%%writefile E07_01.c\n","#include <stdio.h>\n","#include \"lex.h\"\n","\n","int main(void)\n","{\n","    advance();\n","    printf(\"%d: %s\\n\", match(INT), getLexeme());\n","    advance();\n","    printf(\"%d: %s\\n\", match(MULDIV), getLexeme());\n","    advance();\n","    printf(\"%d: %s\\n\", match(RPAREN), getLexeme());\n","    advance();\n","    printf(\"%d: %s\\n\", match(ID), getLexeme());\n","\n","    return 0;\n","}"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"jbAgUj0HONZ1","executionInfo":{"status":"ok","timestamp":1666697841223,"user_tz":-480,"elapsed":509,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"333f3636-9c12-4e11-c669-09023f279b5b"},"execution_count":67,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E07_01.c\n"]}]},{"cell_type":"code","source":["%%shell\n","gcc lex.c E07_01.c -o E07_01\n","./E07_01"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"15HoZpdK5iKH","executionInfo":{"status":"ok","timestamp":1666697853541,"user_tz":-480,"elapsed":9998,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"bc04e653-7b5f-400f-91d8-03cc6c4a9c8d"},"execution_count":68,"outputs":[{"output_type":"stream","name":"stdout","text":["123*(num-5678)\n","1: 123\n","1: *\n","0: (\n","1: num\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":68}]},{"cell_type":"code","source":["ls"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"I8RuPjgn3WOn","executionInfo":{"status":"ok","timestamp":1666696707296,"user_tz":-480,"elapsed":952,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"e640f0b8-e513-48a7-a739-807f6e7366d8"},"execution_count":54,"outputs":[{"output_type":"stream","name":"stdout","text":["\u001b[0m\u001b[01;32mcalculator\u001b[0m*  E07_01.o  \u001b[01;32mE07_03\u001b[0m*   E07_04.c  lex.o\n","\u001b[01;32mE07_01\u001b[0m*      \u001b[01;32mE07_02\u001b[0m*   E07_03.c  lex.c     main.c\n","E07_01.c     E07_02.c  \u001b[01;32mE07_04\u001b[0m*   lex.h     \u001b[01;34msample_data\u001b[0m/\n"]}]},{"cell_type":"markdown","source":["接著要處理的是語法。對於希望處理的 Expression ，我們必須先定義出它對應的語法該長成甚麼樣子。一開始的例子裡提到，我們有興趣的 Expression 長得像這樣 `z = 4*x + y*-6`\n","要定義出明確的語法其實不容易，而且超出我們這門課要講的範圍，所以我們直接提供語法的定義：\n","```\n","  statement   := END | expr END\n","  expr        := term expr_tail\n","  expr_tail   := ADDSUB term expr_tail | NIL\n","  term        := factor term_tail\n","  term_tail   := MULDIV factor term_tail | NIL\n","  factor      := INT | ADDSUB INT | ADDSUB ID | ID ASSIGN expr | ID | LPAREN expr RPAREN\n","```\n","    \n","\n","我們把重點放在理解這樣的語法定義確實行得通，而先不去管如何設計出語法，以及為何非如此設計不可。上面的語法告訴我們，一個  `statement` 可以直接換行，或是由一個 `expr` 接著換行。而一個 `expr` 則是由 `term` 接著 `expr_tail`，然後 `expr_tail` 可以拆解成 “加或減的符號” 接上更多的 `term` 和 `expr_tail`。 我們可以看到這裡出現的遞迴的定義，所以必須加入能夠讓遞迴終止的狀況 (`NIL` 代表空字串)，讓 `expr_tail` 不要再繼續展開。接下來的 `term` 也是用類似的方式來定義。然後最終則是 `factor`，分成好幾種情況，可以是一個整數，或是正負號接整數或變數。另外也可能再包含其他 Expression，這時候就會回到 `expr` 繼續展開。我們如果套用實際的例子，就會發現依照上面的語法順序，任何輸入的 Expression 都可以被無誤地以遞迴方式拆解。譬如 \u000b`4 + y * 3`。\n","我們可以用樹狀圖來表示語法分析的流程\n"],"metadata":{"id":"mviKuWaapY32"}},{"cell_type":"markdown","source":["![image.png](data:image/png;base64,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)"],"metadata":{"id":"OZtS2GDLbRgx"}},{"cell_type":"markdown","source":["上面的樹狀圖叫做 Parse Tree，直接對應到語法的展開。有一點值得注意的地方，在這個語法裡面，每個 `term` 或 `expr` 都可以算出一個整數值。另外一種圖像表達語法的方式就是 Syntax Tree，如下圖。實作時我們可以只用函數呼叫來達成展開 Expression 的效果，也可以用資料結構建出樹狀圖。接下來就來看程式該如何寫。"],"metadata":{"id":"qjAlxttObaIa"}},{"cell_type":"markdown","source":["![image.png](data:image/png;base64,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)"],"metadata":{"id":"KScf1lIubgvH"}},{"cell_type":"markdown","source":["最直接的處理方式就是把每條語法規寫成一個函數。底下是第一個版本的程式碼，稍微對照就會發現，幾乎只要照著語法規則依序呼叫對應的函數，就能完成語法分析。在這個版本裡面我們還沒做真正的運算，只是把讀到的內容依照分析順序用 `printf` 顯示出來。\n","需要特別注意的是呼叫 `advance()` 的時間點\t：只有當我們用 `match` 判斷之後，確定讀取的是我們想要的 Token 之後，才會呼叫 `advance()` 讀取下一個 Token。"],"metadata":{"id":"CcRwh3m2cesR"}},{"cell_type":"code","source":["%%writefile E07_02.c\n","#include <stdio.h>\n","#include <string.h>\n","#include <ctype.h>\n","#include <stdlib.h>\n","#include \"lex.h\"\n","\n","void statement(void);\n","void expr(void);\n","void expr_tail(void);\n","void term(void);\n","void term_tail(void);\n","void factor(void);\n","\n","void statement(void)\n","{\n","    if (match(END)) {\n","        advance();\n","        // nothing\n","    } else {\n","        expr();\n","        match(END);\n","        advance();\n","    }\n","}\n","void expr(void)\n","{\n","    term();\n","    expr_tail();\n","}\n","\n","void expr_tail(void)\n","{\n","    if (match(ADDSUB)){\n","        printf(\"ADDSUB: %s\\n\", getLexeme());\n","        advance();\n","        term();\n","        expr_tail();\n","    } else {\n","        // NIL\n","    }\n","}\n","void term(void) \n","{\n","    factor();\n","    term_tail();\n","}\n","\n","void term_tail(void)\n","{\n","    if (match(MULDIV)) {\n","        printf(\"MULDIV: %s\\n\", getLexeme());\n","        advance();\n","        factor();\n","        term_tail();\n","    } else {\n","        // NIL\n","    }\n","}\n","void factor(void)\n","{\n","    if (match(INT)) {\n","        printf(\"INT: %s\\n\", getLexeme());\n","        advance();\n","    } else if (match(ADDSUB)) {\n","        printf(\"ADDSUB: %s\\n\", getLexeme());\n","        advance();\n","        if (match(INT)) {\n","            printf(\"INT: %s\\n\", getLexeme());\n","            advance();\n","        } else if (match(ID)) {\n","            printf(\"ID: %s\\n\", getLexeme());\n","            advance();\n","        }\n","    } else if (match(ID)) {\n","        printf(\"ID: %s\\n\", getLexeme());\n","        advance();\n","        if (match(ASSIGN)) {\n","            printf(\"ASSIGN: %s\\n\", getLexeme());\n","            advance();\n","            expr();\n","        }\n","    } else if (match(LPAREN)) {\n","        printf(\"LPAREN: %s\\n\", getLexeme());\n","        advance();\n","        expr();\n","        if (match(RPAREN)) {\n","            printf(\"RPAREN: %s\\n\", getLexeme());\n","            advance();\n","        } else {\n","            printf(\"Parenthesis mismatched\\n\");\n","        }\n","    }\n","}\n","\n","int main(void)\n","{\n","    printf(\">> \");\n","    while (strcmp(getLexeme(), \"exit\")) {\n","      statement();\n","      printf(\">> \");\n","    }\n","    return 0;\n","}\n"],"metadata":{"id":"bJ1QleIptpJo","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1666699852253,"user_tz":-480,"elapsed":609,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"9316ee71-73fe-4209-df0e-9f2fa1ad83c6"},"execution_count":89,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E07_02.c\n"]}]},{"cell_type":"code","source":["%%shell\n","gcc -o E07_02 lex.c E07_02.c\n","./E07_02"],"metadata":{"id":"9Fuv_VafwfkA"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["觀察第一個版本的程式碼，會發現 `expr` 和 `term` 都只有呼叫其他函數，而 `expr_tail` 和 `term_tail` 都是遞迴呼叫，而且是 tail recursion 形式的遞迴呼叫。所謂 tail recursion，是指遞迴呼叫發生在整個函數的最後面，遞迴呼叫之後不會再做任何額外的運算就直接 `return`。這種形式的遞迴有個很大的好處是不用保留 stack 內容， 因為不會再用到，所以每次呼叫都可以重複使用同一塊 stack 空間。符合 tail recursion 的遞迴呼叫都可以直接轉換成迴圈的形式。底下就是合併 `expr` 和 `expr_tail` 以及合併 `term` 和 `term_tail` 之後的新的版本，而且原本的 `expr_tail` 和 `term_tail` 的遞迴也改成迴圈。\n"],"metadata":{"id":"0JmfW6E6wtN7"}},{"cell_type":"code","source":["%%writefile E07_03.c\n","\n","#include <stdio.h>\n","#include <string.h>\n","#include <ctype.h>\n","#include <stdlib.h>\n","#include \"lex.h\"\n","\n","void statement(void);\n","void expr(void);\n","void term(void);\n","void factor(void);\n","\n","void statement(void)\n","{\n","    if (match(END)) {\n","        advance();\n","        // nothing\n","    } else {\n","        expr();\n","        match(END);\n","        advance();\n","    }\n","}\n","void expr(void)\n","{\n","    term();\n","    while (match(ADDSUB)){\n","        printf(\"ADDSUB: %s\\n\", \n","\t\tgetLexeme());\n","        advance();\n","        term();\n","    }\n","}\n","void term(void) {\n","    factor();\n","    while (match(MULDIV)) {\n","        printf(\"MULDIV: %s\\n\", \n","\t\tgetLexeme());\n","        advance();\n","        factor();\n","    }\n","}\n","\n","void factor(void)\n","{\n","    if (match(INT)) {\n","        printf(\"INT: %s\\n\", getLexeme());\n","        advance();\n","    } else if (match(ADDSUB)) {\n","        printf(\"ADDSUB: %s\\n\", getLexeme());\n","        advance();\n","        if (match(INT)) {\n","            printf(\"INT: %s\\n\", getLexeme());\n","            advance();\n","        } else if (match(ID)) {\n","            printf(\"ID: %s\\n\", getLexeme());\n","            advance();\n","        }\n","    } else if (match(ID)) {\n","        printf(\"ID: %s\\n\", getLexeme());\n","        advance();\n","        if (match(ASSIGN)) {\n","            printf(\"ASSIGN: %s\\n\", getLexeme());\n","            advance();\n","            expr();\n","        }\n","    } else if (match(LPAREN)) {\n","        printf(\"LPAREN: %s\\n\", getLexeme());\n","        advance();\n","        expr();\n","        if (match(RPAREN)) {\n","            printf(\"RPAREN: %s\\n\", getLexeme());\n","            advance();\n","        } else {\n","            printf(\"Parenthesis mismatched\\n\");\n","        }\n","    }\n","}\n","\n","int main(void)\n","{\n","    statement();\n","    return 0;\n","}"],"metadata":{"id":"VRy2iKRgHRuF","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1666700645984,"user_tz":-480,"elapsed":715,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"6788f72b-f3f9-4809-edc4-d9ddeb3c19a4"},"execution_count":90,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E07_03.c\n"]}]},{"cell_type":"code","source":["%%shell\n","gcc -o E07_03 lex.c E07_03.c\n","./E07_03"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"GdvmRhB2dTqL","executionInfo":{"status":"ok","timestamp":1666700661530,"user_tz":-480,"elapsed":10655,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"3f6610ca-ba1f-4192-b582-3334d52068bd"},"execution_count":91,"outputs":[{"output_type":"stream","name":"stdout","text":["2+3*5\n","INT: 2\n","ADDSUB: +\n","INT: 3\n","MULDIV: *\n","INT: 5\n","\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":91}]},{"cell_type":"markdown","source":["最終版本是要替換掉 `printf`  顯示，把實際該做的運算加進去。首先要把語法相關的函數的回傳值型別都改成 `int`，然後加入數值運算。\n","\n","底下是其他的函數。`error` 是用來顯示各種類型的錯誤訊息。比較複雜的應該是 `getval` 和 `setval`，這兩個函數是為了處理變數，做法是用一個表格陣列 `Symbol table[TBLSIZE];` 來記錄變數的狀態。對於 Expression 中出現的變數，會先搜尋 `table` 看看是否已有記錄。如果搜尋不到，就當作是初次出現的變數，會直接設定初值並且將變數記錄在 `table` 中。如果 `table` 之中已有記錄，則可以取出其中的值或是覆蓋已有的值。"],"metadata":{"id":"iN_0HlHBEUWS"}},{"cell_type":"code","source":["%%writefile E07_04.c\n","#include <stdio.h>\n","#include <string.h>\n","#include <ctype.h>\n","#include <stdlib.h>\n","#include \"lex.h\"\n","\n","#define TBLSIZE 65535\n","typedef struct {\n","    char name[MAXLEN];\n","    int val;\n","} Symbol;\n","Symbol table[TBLSIZE];\n","int sbcount = 0;\n","\n","void statement(void);\n","int expr(void);\n","int term(void);\n","int factor(void);\n","int getval(void);\n","int setval(char*, int);\n","\n","typedef enum {MISPAREN, NOTNUMID,\u000b NOTFOUND, RUNOUT} ErrorType;\n","void error(ErrorType errorNum);\n","\n","int getval(void)\n","{\n","    int i, retval, found;\n","    if (match(INT)) {\n","        retval = atoi(getLexeme());\n","    } else if (match(ID)) {\n","        i = 0; found = 0; retval = 0;\n","        while (i<sbcount && !found) {\n","            if (strcmp(getLexeme(), \n","\ttable[i].name) ==0) {\n","                retval = table[i].val;\n","                found = 1;\n","                break;\n","            } else {\n","                i++;\n","            }\n","        }\n","        if (!found) {\n","            if (sbcount < TBLSIZE) {\n","                strcpy(table[sbcount].name, \n","\t\tgetLexeme());\n","                table[sbcount].val = 0;\n","                sbcount++;\n","            } else {\n","                error(RUNOUT);\n","            }\n","        }\n","    }\n","    return retval;\n","}\n","\n","int setval(char *str, int val)\n","{\n","    int i, retval;\n","    i = 0;\n","    while (i<sbcount) {\n","        if (strcmp(str, table[i].name)==0) {\n","            table[i].val = val;\n","            retval = val;\n","            break;\n","        } else {\n","            i++;\n","        }\n","    }\n","    return retval;\n","}\n","\n","void error(ErrorType errorNum)\n","{\n","    switch (errorNum) {\n","    case MISPAREN:\n","        fprintf(stderr, \n","          \"Mismatched parenthesis\\n\");\n","        break;\n","    case NOTNUMID:\n","        fprintf(stderr, \n","          \"Number or identifier expected\\n\");\n","        break;\n","    case NOTFOUND:\n","        fprintf(stderr, \n","          \"%s not defined\\n\", getLexeme());\n","        break;\n","    case RUNOUT:\n","        fprintf(stderr,\n","          \"Out of memory\\n\");\n","    }\n","    exit(0);\n","}\n","\n","int expr(void)\n","{\n","    int retval;\n","    retval = term();\n","    while (match(ADDSUB)) {\n","        if (strcmp(getLexeme(), \"+\")==0) {\n","            advance();\n","            retval += term();\n","        } else if (strcmp(getLexeme(), \"-\")\n","\t\t        ==0) {\n","            advance();\n","            retval -= term();\n","        } else {\n","            // NIL\n","        }\n","    }\n","    return retval;\n","}\n","int term(void)\n","{\n","    int retval;\n","    retval = factor();\n","    while (match(MULDIV)) {\n","        if (strcmp(getLexeme(), \"*\")==0) {\n","            advance();\n","            retval *= factor();\n","        } else if (strcmp(getLexeme(), \"/\")\n","\t\t        ==0) {\n","            advance();\n","            retval /= factor();\n","        } else {\n","            // NIL\n","        }\n","    }\n","    return retval;\n","}\n","\n","\n","void statement(void)\n","{\n","    int retval;\n","\n","    if (match(END)) {\n","        printf(\">> \");\n","        advance();\n","    } else {\n","        retval = expr();\n","        if (match(END)) {\n","            printf(\"%d\\n\", retval);\n","            printf(\">> \");\n","            advance();\n","        }\n","    }\n","}\n","\n","int factor(void)\n","{\n","    int retval = 0;\n","    char tmpstr[MAXLEN];\n","\n","    if (match(INT)) {\n","        retval = getval();\n","        advance();\n","    } else if (match(ID)) {\n","        retval = getval();\n","        strcpy(tmpstr, getLexeme());\n","        advance();\n","        if (match(ASSIGN)) {\n","            advance();\n","            retval = expr();\n","            retval = setval(tmpstr, retval);\n","        }\n","    } else if (match(ADDSUB)) {\n","        strcpy(tmpstr, getLexeme());\n","        advance();\n","        if (match(ID) || match(INT)) {\n","            retval = getval();\n","            if (strcmp(tmpstr, \"-\")==0) {\n","                retval = -retval;\n","            }\n","            advance();\n","        } else {\n","            error(NOTNUMID);\n","        }\n","    } else if (match(LPAREN)) {\n","        advance();\n","        retval = expr();\n","        if (match(RPAREN)) {\n","            advance();\n","        } else {\n","            error(MISPAREN);\n","        }\n","    } else {\n","        error(NOTNUMID);\n","    }\n","    return retval;\n","}\n","\n","\n","int main()\n","{\n","    printf(\">> \");\n","    while (1) {\n","        statement();\n","    }\n","    return 0;\n","}\n"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"Oc5YmeWkODkB","executionInfo":{"status":"ok","timestamp":1666702262013,"user_tz":-480,"elapsed":618,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"bb2b56a6-1bf9-4cb8-aaf0-96a43604b8d1"},"execution_count":92,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E07_04.c\n"]}]},{"cell_type":"code","source":["%%shell\n","gcc -o E07_04 lex.c E07_04.c\n","./E07_04"],"metadata":{"id":"F06oSvqOQPf9"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["##Syntax Tree\n","\n","完成了上面的版本之後，我們可以再試試看，改成 建構 Syntax Tree 的版本。\n","\n","要用到的資料結構最近幾周看過的 Binary Tree\n","```\n","typedef struct _Node {\n","\tchar lexeme[MAXLEN];\n","\tTokenSet token;\n","\tint val;\n","\tstruct _Node *left, *right;\n","} BTNode;\n","```\n","\n","要把前一個版本的每個函數，從原本傳回 `int` ，都改成傳回 `BTNode*` 指標。\n","先把 Syntax Tree 建好，然後再用 `evaluateTree` 把對應的 Expression 整數值算出來。\n","\n","底下貼上 `main.c`  最終版程式檔案內容，至於 `lex.c`  `lex.h` 內容則跟前面的一樣。\n","\n","完整的程式碼也可以從這裡下載：\n","https://github.com/htchen/calculator_tree"],"metadata":{"id":"556G1EtRf5xw"}},{"cell_type":"code","source":["%%writefile main.c\n","\n","#include <stdio.h>\n","#include <string.h>\n","#include <ctype.h>\n","#include <stdlib.h>\n","#include \"lex.h\"\n","/*\n","Something like Python\n",">> y = 2\n",">> z = 2\n",">> x = 3*y + 4/(2*z)\n","*/\n","/*\n","the only type: integer\n","everything is an expression\n","  statement   := END | expr END\n","  expr        := term expr_tail\n","  expr_tail   := ADDSUB term expr_tail | NIL\n","  term        := factor term_tail\n","  term_tail := MULDIV factor term_tail | NIL\n","  factor      := INT | ADDSUB INT | ADDSUB ID | ID ASSIGN expr | ID | LPAREN expr RPAREN\n","*/\n","#define TBLSIZE 65535\n","typedef struct {\n","    char name[MAXLEN];\n","    int val;\n","} Symbol;\n","Symbol table[TBLSIZE];\n","int sbcount = 0;\n","\n","typedef struct _Node {\n","    char lexeme[MAXLEN];\n","    TokenSet token;\n","    int val;\n","    struct _Node *left, *right;\n","} BTNode;\n","\n","void statement(void);\n","BTNode* expr(void);\n","BTNode* term(void);\n","BTNode* factor(void);\n","int getval(void);\n","int setval(char*, int);\n","\n","typedef enum {MISPAREN, NOTNUMID, NOTFOUND, RUNOUT, NAN} ErrorType;\n","void error(ErrorType errorNum);\n","\n","/* create a node without any child */\n","BTNode* makeNode(TokenSet tok, const char *lexe)\n","{\n","    BTNode *node = (BTNode*) malloc(sizeof(BTNode));\n","    strcpy(node->lexeme, lexe);\n","    node->token= tok;\n","    node->val = 0;\n","    node->left = NULL;\n","    node->right = NULL;\n","    return node;\n","}\n","\n","/* clean a tree */\n","void freeTree(BTNode *root)\n","{\n","    if (root!=NULL) {\n","        freeTree(root->left);\n","        freeTree(root->right);\n","        free(root);\n","    }\n","}\n","\n","/* print a tree by pre-order */\n","void printPrefix(BTNode *root)\n","{\n","    if (root != NULL) {\n","        printf(\"%s \", root->lexeme);\n","        printPrefix(root->left);\n","        printPrefix(root->right);\n","    }\n","}\n","\n","/* traverse the syntax tree by pre-order\n","   and evaluate the underlying expression */\n","int evaluateTree(BTNode *root)\n","{\n","    int retval = 0, lv, rv;\n","    if (root != NULL) {\n","        switch (root->token) {\n","        case ID:\n","        case INT:\n","            retval = root->val;\n","            break;\n","        case ASSIGN:\n","        case ADDSUB:\n","        case MULDIV:\n","            lv = evaluateTree(root->left);\n","            rv = evaluateTree(root->right);\n","            if (strcmp(root->lexeme, \"+\") == 0)\n","                retval = lv + rv;\n","            else if (strcmp(root->lexeme, \"-\") == 0)\n","                retval = lv - rv;\n","            else if (strcmp(root->lexeme, \"*\") == 0)\n","                retval = lv * rv;\n","            else if (strcmp(root->lexeme, \"/\") == 0) {\n","                if (rv==0)\n","                    error(NAN);\n","                else\n","                    retval = lv / rv;\n","            } else if (strcmp(root->lexeme, \"=\") == 0)\n","                retval = setval(root->left->lexeme, rv);\n","            break;\n","        default:\n","            retval = 0;\n","        }\n","    }\n","    return retval;\n","}\n","\n","int getval(void)\n","{\n","    int i, retval, found;\n","\n","    if (match(INT)) {\n","        retval = atoi(getLexeme());\n","    } else if (match(ID)) {\n","        i = 0;\n","        found = 0;\n","        retval = 0;\n","        while (i<sbcount && !found) {\n","            if (strcmp(getLexeme(), table[i].name)==0) {\n","                retval = table[i].val;\n","                found = 1;\n","                break;\n","            } else {\n","                i++;\n","            }\n","        }\n","        if (!found) {\n","            if (sbcount < TBLSIZE) {\n","                strcpy(table[sbcount].name, getLexeme());\n","                table[sbcount].val = 0;\n","                sbcount++;\n","            } else {\n","                error(RUNOUT);\n","            }\n","        }\n","    }\n","    return retval;\n","}\n","int setval(char *str, int val)\n","{\n","    int i, retval = 0;\n","    i = 0;\n","    while (i<sbcount) {\n","        if (strcmp(str, table[i].name)==0) {\n","            table[i].val = val;\n","            retval = val;\n","            break;\n","        } else \n","            i++;\n","    }\n","    return retval;\n","}\n","//  expr        := term expr_tail\n","//  expr_tail   := ADDSUB term expr_tail | NIL\n","BTNode* expr(void)\n","{\n","    BTNode *retp, *left;\n","    retp = left = term();\n","    while (match(ADDSUB)) { // tail recursion => while\n","        retp = makeNode(ADDSUB, getLexeme());\n","        advance();\n","        retp->right = term();\n","        retp->left = left;\n","        left = retp;\n","    }\n","    return retp;\n","}\n","\n","//  term        := factor term_tail\n","//  term_tail := MULDIV factor term_tail | NIL\n","BTNode* term(void)\n","{\n","    BTNode *retp, *left;\n","    retp = left = factor();\n","    while (match(MULDIV)) { // tail recursion => while\n","        retp = makeNode(MULDIV, getLexeme());\n","        advance();\n","        retp->right = factor();\n","        retp->left = left;\n","        left = retp;\n","    }\n","    return retp;\n","}\n","BTNode* factor(void)\n","{\n","    BTNode* retp = NULL;\n","    char tmpstr[MAXLEN];\n","\n","    if (match(INT)) {\n","        retp =  makeNode(INT, getLexeme());\n","        retp->val = getval();\n","        advance();\n","    } else if (match(ID)) {\n","        BTNode* left = makeNode(ID, getLexeme());\n","        left->val = getval();\n","        strcpy(tmpstr, getLexeme());\n","        advance();\n","        if (match(ASSIGN)) {\n","            retp = makeNode(ASSIGN, getLexeme());\n","            advance();\n","            retp->right = expr();\n","            retp->left = left;\n","        } else \n","            retp = left;        \n","    } else if (match(ADDSUB)) {\n","        strcpy(tmpstr, getLexeme());\n","        advance();\n","        if (match(ID) || match(INT)) {\n","            retp = makeNode(ADDSUB, tmpstr);\n","            if (match(ID)) retp->right = makeNode(ID, getLexeme());\n","            else retp->right = makeNode(INT, getLexeme());\n","            retp->right->val = getval();\n","            retp->left = makeNode(INT, \"0\");\n","            retp->left->val = 0;\n","            advance();\n","        } else \n","            error(NOTNUMID);\n","    } else if (match(LPAREN)) {\n","        advance();\n","        retp = expr();\n","        if (match(RPAREN)) \n","            advance();\n","        else\n","            error(MISPAREN);\n","    } else \n","        error(NOTNUMID);    \n","    return retp;\n","}\n","\n","void error(ErrorType errorNum)\n","{\n","    switch (errorNum) {\n","    case MISPAREN:\n","        fprintf(stderr, \"Mismatched parenthesis\\n\");\n","        break;\n","    case NOTNUMID:\n","        fprintf(stderr, \"Number or identifier expected\\n\");\n","        break;\n","    case NOTFOUND:\n","        fprintf(stderr, \"%s not defined\\n\", getLexeme());\n","        break;\n","    case RUNOUT:\n","        fprintf(stderr, \"Out of memory\\n\");\n","        break;\n","    case NAN:\n","        fprintf(stderr, \"Not a number\\n\");\n","    }\n","    exit(0);\n","}\n","\n","void statement(void)\n","{\n","    BTNode* retp;\n","\n","    if (match(END)) {\n","        printf(\">> \");\n","        advance();\n","    } else {\n","        retp = expr();\n","        if (match(END)) {\n","            printf(\"%d\\n\", evaluateTree(retp));\n","            printPrefix(retp);\n","            printf(\"\\n\");\n","            freeTree(retp);\n","\n","            printf(\">> \");\n","            advance();\n","        }\n","    }\n","}\n","\n","int main()\n","{\n","    printf(\">> \");\n","    while (1) {\n","        statement();\n","    }\n","    return 0;\n","}\n","\n"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"LfMQGm7ngIJo","executionInfo":{"status":"ok","timestamp":1666704008828,"user_tz":-480,"elapsed":781,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"311fd188-ee3b-4ccb-fba0-9cc1f41fa069"},"execution_count":95,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting main.c\n"]}]},{"cell_type":"code","source":["%%shell\n","gcc -o calculator lex.c main.c\n","./calculator\n","\n","2*3 + 4*5 + 6\n","\n","          +\n","        /  \\\n","       +    6\n","      /  \\\n","     *    *\n","    / \\   / \\\n","    2  3  4  5"],"metadata":{"id":"KdfO12wJg-uc"},"execution_count":null,"outputs":[]}]}