编译原理-语法制导翻译实现计算器
Jun 23, 2016 • 🍻 03min 31s read
🔖 编译原理语法制导翻译计算机
前言
¶设计一个文法,匹配合法的计算式,并返回正确计算式的结果。
一些定义:
- 语法制导定义(Syntax-directed definitions, SDD)
- 语法制导翻译方案(Syntax-directed Translation Schema, SDT)
其它一些编译原理相关的前置知识可以参考: 编译原理-语法分析 | 光和尘
文法
¶对于一个只支持加减乘除、括号、正负数的计算表达式,不难得到其产生式:
可求得它的
LL(1) 预测分析表:
¶Token | digit | |||||||
---|---|---|---|---|---|---|---|---|
SDD
¶# | 产生式 | 语义规则 |
---|---|---|
0 | ||
1 | ||
2 | ||
3 | ||
4 | ||
5 | ||
6 | ||
7 | ||
8 | ||
9 | ||
10 | ||
11 |
程序实现
¶C++
calculator.cpp | 288 lines.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288#include <algorithm>#include <cstdio>#include <cstring>#include <exception>#include <iostream>#include <vector>using namespace std;struct node {int id, syn, inh;node(int id = 0, int syn = 0, int inh = 0) : id(id), syn(syn), inh(inh) {}};char ll1Idx[128];int ll1Table[10][10];vector<int> ssdTable[10];inline int idx(char c) {return ll1Idx[c];}inline void initLL1Table() {memset(ll1Idx, 0, sizeof ll1Idx);memset(ll1Table, -1, sizeof ll1Table);for (int k = '0'; k <= '9'; ++k) ll1Idx[k] = 1;ll1Idx['('] = 2;ll1Idx[')'] = 3;ll1Idx['+'] = 4;ll1Idx['-'] = 5;ll1Idx['*'] = 6;ll1Idx['/'] = 7;ll1Idx['$'] = 8;ll1Idx['A'] = -1;ll1Idx['B'] = -2;ll1Idx['C'] = -3;ll1Idx['D'] = -4;ll1Idx['E'] = -5;// 0: A -> BDll1Table[1][1] = 0;ll1Table[1][2] = 0;// 1: A -> +BDll1Table[1][4] = 1;// 2: A -> -BDll1Table[1][5] = 2;// 3: B --> CEll1Table[2][1] = 3;ll1Table[2][2] = 3;// 4: C --> digitll1Table[3][1] = 4;// 5: C --> (A)ll1Table[3][2] = 5;// 6: D --> +BDll1Table[4][4] = 6;// 7: D --> -BDll1Table[4][5] = 7;// 8: D --> \varepsilonll1Table[4][3] = 8;ll1Table[4][8] = 8;// 9: E --> *CEll1Table[5][6] = 9;// 10: E --> /CEll1Table[5][7] = 10;// 11: E --> \varepsilonll1Table[5][3] = 11;ll1Table[5][4] = 11;ll1Table[5][5] = 11;ll1Table[5][8] = 11;}inline void initSSDTable() {#define pb push_backfor (int i = 0; i < 10; ++i) ssdTable[i].clear();// 0: A --> BDssdTable[0].pb(idx('B'));ssdTable[0].pb(idx('D'));// 1: A --> +BDssdTable[1].pb(idx('+'));ssdTable[1].pb(idx('B'));ssdTable[1].pb(idx('D'));// 2: A --> -BDssdTable[2].pb(idx('-'));ssdTable[2].pb(idx('B'));ssdTable[2].pb(idx('D'));// 3: B --> CEssdTable[3].pb(idx('C'));ssdTable[3].pb(idx('E'));// 4: C --> digitssdTable[4].pb(idx('0'));// 5: C -> (A)ssdTable[5].pb(idx('('));ssdTable[5].pb(idx('A'));ssdTable[5].pb(idx(')'));// 6: D --> +BDssdTable[6].pb(idx('+'));ssdTable[6].pb(idx('B'));ssdTable[6].pb(idx('D'));// 7: D --> -BDssdTable[7].pb(idx('-'));ssdTable[7].pb(idx('B'));ssdTable[7].pb(idx('D'));// 8: D --> \varepsilon// 9: E --> *CEssdTable[9].pb(idx('*'));ssdTable[9].pb(idx('C'));ssdTable[9].pb(idx('E'));// 10: E --> /CEssdTable[10].pb(idx('/'));ssdTable[10].pb(idx('C'));ssdTable[10].pb(idx('E'));// 11: E --> \varepsilon#undef pb}int getnum(const char*& s) {int num = 0;for (; isdigit(*s); ++s) num = num * 10 + *s - '0';return num;}const int endsym = idx('$');void calculate(const char*& s, node& sy, int cur) {int id = idx(*s);if (!id) throw "Syntax Error";if (sy.id != endsym) {if (sy.id == id) {if (id == 1) {sy.syn = getnum(s);} else {++s;}return;}if (sy.id > 0) throw "Syntax Error!";int Mid = ll1Table[-sy.id][id];if (Mid < 0) throw "Syntax Error!";node sym[4];for (int i = 0; i < ssdTable[Mid].size(); ++i) sym[i].id = ssdTable[Mid][i];if (ssdTable[Mid].size()) calculate(s, sym[0], cur + 1);switch (Mid) {// 0: A --> BDcase 0:sym[1].inh = sym[0].syn;calculate(s, sym[1], cur + 1);sy.syn = sym[1].syn;break;// 1: A --> +BDcase 1:calculate(s, sym[1], cur + 1);sym[2].inh = sym[1].syn;calculate(s, sym[2], cur + 1);sy.syn = sym[2].syn;break;// 2: A --> -BDcase 2:calculate(s, sym[1], cur + 1);sym[2].inh = -sym[1].syn;calculate(s, sym[2], cur + 1);sy.syn = sym[2].syn;break;// 3: B --> CEcase 3:sym[1].inh = sym[0].syn;calculate(s, sym[1], cur + 1);sy.syn = sym[1].syn;break;// 4: C --> digitcase 4:sy.syn = sym[0].syn;break;// 5: C --> (A)case 5:calculate(s, sym[1], cur + 1);sy.syn = sym[1].syn;calculate(s, sym[2], cur + 1);break;// 6: D --> +BDcase 6:calculate(s, sym[1], cur + 1);sym[2].inh = sy.inh + sym[1].syn;calculate(s, sym[2], cur + 1);sy.syn = sym[2].syn;break;// 7: D --> -BDcase 7:calculate(s, sym[1], cur + 1);sym[2].inh = sy.inh - sym[1].syn;calculate(s, sym[2], cur + 1);sy.syn = sym[2].syn;break;// 8: D --> \varepsiloncase 8:sy.syn = sy.inh;break;// 9: E --> *CEcase 9:calculate(s, sym[1], cur + 1);sym[2].inh = sy.inh * sym[1].syn;calculate(s, sym[2], cur + 1);sy.syn = sym[2].syn;break;// 10: E --> /CEcase 10:calculate(s, sym[1], cur + 1);sym[2].inh = sy.inh / sym[1].syn;calculate(s, sym[2], cur + 1);sy.syn = sym[2].syn;break;// 11: E --> \varepsiloncase 11:sy.syn = sy.inh;break;}}}int main() {string in;string coin;initLL1Table();initSSDTable();while (getline(cin, in)) {in.push_back('$');int len = in.length();const char* s = in.c_str();// 去除空格coin.clear();for (int i = 0; i < len; ++i)if (s[i] != ' ' && s[i] != '\t' && s[i] != '\n') coin.push_back(s[i]);coin.push_back('\0');try {node sy = node(idx('A'));s = coin.c_str();calculate(s, sy, 0);int ans = sy.syn;for (int i = 0; i < len - 1; ++i) putchar(in[i]);printf(" = %d\n", ans);printf("succuss!\n");} catch (const char* str) {puts(str);}}return 0;}Typescript (可以直接使用 @algorithm.ts/calculate)
calculator.ts | 242 lines.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242export enum TokenSymbol {DIGIT = 1,OPEN_PAREN = 2,CLOSE_PAREN = 3,PLUS = 4,MINUS = 5,MULTI = 6,DIVIDE = 7,END = 8,A = -1,B = -2,C = -3,D = -4,E = -5,}/*** Priority map.*/const ll1IdxMap: Record<string, TokenSymbol> = Object.freeze({'0': TokenSymbol.DIGIT,'1': TokenSymbol.DIGIT,'2': TokenSymbol.DIGIT,'3': TokenSymbol.DIGIT,'4': TokenSymbol.DIGIT,'5': TokenSymbol.DIGIT,'6': TokenSymbol.DIGIT,'7': TokenSymbol.DIGIT,'8': TokenSymbol.DIGIT,'9': TokenSymbol.DIGIT,'(': TokenSymbol.OPEN_PAREN,')': TokenSymbol.CLOSE_PAREN,'+': TokenSymbol.PLUS,'-': TokenSymbol.MINUS,'*': TokenSymbol.MULTI,'/': TokenSymbol.DIVIDE,$: TokenSymbol.END,A: TokenSymbol.A,B: TokenSymbol.B,C: TokenSymbol.C,D: TokenSymbol.D,E: TokenSymbol.E,})export const idx = (c: string): number => ll1IdxMap[c]export const sddTable: number[][] = ['BD', // 0: A --> BD'+BD', // 1: A --> +BD'-BD', // 2: A --> -BD'CE', // 3: B --> CE'0', // 4: C --> digit'(A)', // 5: C --> (A)'+BD', // 6: D --> +BD'-BD', // 7: D --> -BD'', // 8: D --> \varepsilon'*CE', // 9: E --> *CE'/CE', // 10: E --> /CE'', // 11: E --> \varepsilon].map(x => x.split('').map(idx))// tokens: A,B,C,D,Eexport const MAX_TOKENS = 5// symbols: digit, (, ), +, -, *, /, $export const MAX_SYMBOLS = 8// LL1 table.export const ll1Table: Int8Array[] = new Array(MAX_TOKENS + 1)// Initialize LL1Table{for (let i = 0; i <= MAX_TOKENS; ++i) {ll1Table[i] = new Int8Array(MAX_SYMBOLS + 1).fill(-1)}// 0: A -> BDll1Table[1][1] = 0ll1Table[1][2] = 0// 1: A -> +BDll1Table[1][4] = 1// 2: A -> -BDll1Table[1][5] = 2// 3: B --> CEll1Table[2][1] = 3ll1Table[2][2] = 3// 4: C --> digitll1Table[3][1] = 4// 5: C --> (A)ll1Table[3][2] = 5// 6: D --> +BDll1Table[4][4] = 6// 7: D --> -BDll1Table[4][5] = 7// 8: D --> \varepsilonll1Table[4][3] = 8ll1Table[4][8] = 8// 9: E --> *CEll1Table[5][6] = 9// 10: E --> /CEll1Table[5][7] = 10// 11: E --> \varepsilonll1Table[5][3] = 11ll1Table[5][4] = 11ll1Table[5][5] = 11ll1Table[5][8] = 11}export function getNum(s: string, start: number): [number, number] {let result = 0let i: number = startfor (; i < s.length; ++i) {const c = s[i]if (!/\d/.test(c)) breakresult = result * 10 + Number(c)}return [i, result]}export function calculate(rawExpression: string): number {let cur = 0const expression = rawExpression.replace(/[\s]+/g, '')const result: number = dfs(idx('A'), 0, 0)return cur === expression.length ? result : Number.NaNfunction dfs(id: number, syn: number, inh: number): number {if (cur === expression.length) {// Only D and E could be parsed as \varepsilonif (id === TokenSymbol.D || id === TokenSymbol.E) return inhreturn Number.NaN}const id0 = idx(expression[cur])// Unrecognized symbol.if (id0 === undefined) return Number.NaN// Matched an operator.if (id === id0) {// Matched digits.if (id0 === TokenSymbol.DIGIT) {const [nextCur, value] = getNum(expression, cur)// No valid digit found.if (cur === nextCur) return Number.NaNcur = nextCurreturn value}cur += 1return syn}// Syntax error.if (id > 0) return Number.NaNconst ssdId = ll1Table[-id][id0]if (ssdId < 0) return Number.NaNconst tokens: ReadonlyArray<number> = sddTable[ssdId]const syn0: number = tokens.length > 0 ? dfs(tokens[0], 0, 0) : 0switch (ssdId) {// 0: A --> BDcase 0:return dfs(tokens[1], 0, syn0)// 1: A --> +BDcase 1: {const val1: number = dfs(tokens[1], 0, 0)return dfs(tokens[2], 0, val1)}// 2: A --> -BDcase 2: {const val1: number = dfs(tokens[1], 0, 0)return dfs(tokens[2], 0, -val1)}// 3: B --> CEcase 3:return dfs(tokens[1], 0, syn0)// 4: C --> digitcase 4:return syn0// 5: C --> (A)case 5: {const result: number = dfs(tokens[1], 0, 0)dfs(tokens[2], 0, 0)return result}// 6: D --> +BDcase 6: {const val1: number = dfs(tokens[1], 0, 0)return dfs(tokens[2], 0, inh + val1)}// 7: D --> -BDcase 7: {const val1: number = dfs(tokens[1], 0, 0)return dfs(tokens[2], 0, inh - val1)}// 8: D --> \varepsiloncase 8:return inh// 9: E --> *CEcase 9: {const val1: number = dfs(tokens[1], 0, 0)return dfs(tokens[2], 0, inh * val1)}// 10: E --> /CEcase 10: {const val1: number = dfs(tokens[1], 0, 0)return dfs(tokens[2], 0, inh / val1)}// 11: E --> \varepsiloncase 11:return inh}return 0}}
Related
¶