摘要:也因此,語(yǔ)法樹(shù)生成過(guò)程異常簡(jiǎn)單,基本是和波蘭表達(dá)式生成沒(méi)區(qū)別了。這個(gè)沒(méi)啥好講的了,就是波蘭表達(dá)式的生成略改而已,改動(dòng)部分包括多了值棧和參數(shù)列表。其中立即量和參數(shù)這倆個(gè)分別是將數(shù)字和參數(shù)放入寄存器后壓棧。其他的操作則是首先分別執(zhí)行子節(jié)點(diǎn)。
前言
昨天完成了codewars上的1級(jí)題簡(jiǎn)單解釋器實(shí)現(xiàn),今天突發(fā)奇想上去看看總共有多少1級(jí)題,然后發(fā)現(xiàn)總共也只有三題。而且,這三題都是編譯器解釋器相關(guān)的,所以干脆都做了了事。
昨天做的是簡(jiǎn)單解釋器,還有兩題分別是編譯器以及一個(gè)以類型為重點(diǎn)的不完整的類lisp解釋器。其中編譯器這題和之前做的解釋器很像,所以就從編譯器開(kāi)始吧:
題目地址:http://www.codewars.com/kata/tiny-three-pass-compiler/train/javascript
github地址:https://github.com/woodensail/SimpleInteractiveInterpreter/blob/master/tiny-three-pass-compiler.js
前文地址:http://segmentfault.com/a/1190000004047915
本文地址:http://segmentfault.com/a/1190000004049048
首先這題的復(fù)雜度比之前要低的多,所以幾十分鐘就完成了。之前題目中的語(yǔ)言還算是結(jié)構(gòu)完整,而這題里的輸入都不能算是一個(gè)語(yǔ)言,只能說(shuō)是帶參數(shù)的表達(dá)式而已。
沒(méi)有參數(shù),沒(méi)有全局變量。相比算術(shù)表達(dá)式只多了參數(shù)而已。也因此,語(yǔ)法樹(shù)生成過(guò)程異常簡(jiǎn)單,基本是和波蘭表達(dá)式生成沒(méi)區(qū)別了。
這題比之前多出的部分則是語(yǔ)義分析和匯編代碼生成。
語(yǔ)義分析部分需要將常量運(yùn)算優(yōu)化掉,縮短代碼長(zhǎng)度。
匯編代碼生成部分取代了前一題的執(zhí)行部分。而是生成并返回匯編代碼即可。
這個(gè)沒(méi)啥好講的了,就是波蘭表達(dá)式的生成略改而已,改動(dòng)部分包括多了值棧和參數(shù)列表。
另外就是對(duì)參數(shù)和立即量做了區(qū)分,這一點(diǎn)做的比前一篇要好。前一篇里面參數(shù)與立即量部分不分家?guī)?lái)了不少麻煩。
Compiler.prototype.pass1 = function (program) { var tokens = this.tokenize(program), index = tokens.indexOf("]"), args = {}, next, dataStack = []; operatorStack = []; for (var i = 1; i < index; i++) { args[tokens[i]] = i - 1; } tokens = tokens.slice(index + 1); tokens.unshift("("); tokens.push(")"); while ((next = tokens.pop()) !== void 0) { if (operators[next]) { while (true) { if (!operatorStack.length) { operatorStack.push(next); break; } else if (operatorStack[operatorStack.length - 1] === ")") { operatorStack.push(next); break; } else if (operators[operatorStack[operatorStack.length - 1]] >= operators[next]) { operatorStack.push(next); break; } else { dataStack.push({op: operatorStack.pop(), a: dataStack.pop(), b: dataStack.pop()}); } } } else if (next === "(") { while ((next = operatorStack.pop()) !== ")") { if (next === void 0) { break } dataStack.push({op: next, a: dataStack.pop(), b: dataStack.pop()}); } } else if (next === ")") { operatorStack.push(next); } else { if (args[next] !== void 0) { dataStack.push({op: "arg", n: args[next]}); } else { dataStack.push({op: "imm", n: Number(next)}); } } } return dataStack[0]; };Pass2
pass2的目的是把立即量運(yùn)算優(yōu)化掉。實(shí)現(xiàn)方式是遞歸掃描。
如果當(dāng)前節(jié)點(diǎn)是參數(shù)或立即量則直接返回當(dāng)前節(jié)點(diǎn)。
否則依次對(duì)當(dāng)前節(jié)點(diǎn)的兩個(gè)參數(shù)調(diào)用pass2。這步過(guò)后,a和b應(yīng)該都是參數(shù)或立即量。
如果a和b都是立即量,那么直接計(jì)算當(dāng)前節(jié)點(diǎn)的結(jié)果。然后用計(jì)算出的結(jié)果構(gòu)建一個(gè)新的立即量最后返回。
反之則直接返回當(dāng)前節(jié)點(diǎn)。
Compiler.prototype.pass2 = function (ast) { if ((ast.op === "arg") || (ast.op === "imm")) { return ast; } ast.a = this.pass2(ast.a); ast.b = this.pass2(ast.b); if ((ast.a.op === "imm") && (ast.b.op === "imm")) { return {op: "imm", n: this.execOp(ast.op, ast.a.n, ast.b.n)} } else { return ast; } };Pass3
首先所有操作都是以"PU"壓棧結(jié)束的。
其中立即量和參數(shù)這倆個(gè)分別是將數(shù)字和參數(shù)放入寄存器后壓棧。
其他的操作則是首先分別執(zhí)行ab子節(jié)點(diǎn)。執(zhí)行完畢后棧頂?shù)囊欢胤謩e是b,a個(gè)操作的結(jié)果。通過(guò)"PO","SW","PO"取出后執(zhí)行算術(shù)操作,最后壓棧就完成了。
需要注意的是這種方式生成的匯編代碼有大量冗余,主要是無(wú)用的["PU", "PO"]以及["PU", "IM/AR", "PU", "PO" "SW", "PO"]。
前者可以完全刪去,后者可以優(yōu)化為["SW" , "IM/AR", "SW"]。
Compiler.prototype.pass3 = function (ast) { switch (ast.op) { case "imm": return ["IM " + ast.n, "PU"]; case "arg": return ["AR " + ast.n, "PU"]; case "+": return this.pass3(ast.a).concat(this.pass3(ast.b)).concat(["PO", "SW", "PO", "AD", "PU"]); case "-": return this.pass3(ast.a).concat(this.pass3(ast.b)).concat(["PO", "SW", "PO", "SU", "PU"]); case "*": return this.pass3(ast.a).concat(this.pass3(ast.b)).concat(["PO", "SW", "PO", "MU", "PU"]); case "/": return this.pass3(ast.a).concat(this.pass3(ast.b)).concat(["PO", "SW", "PO", "DI", "PU"]); } };總結(jié)
這題真是相當(dāng)簡(jiǎn)單,我待會(huì)兒去看看最后一題去,那題似乎和前兩題不太一樣。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/78191.html
摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開(kāi)發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開(kāi)發(fā)教程工程實(shí)踐深度閱讀開(kāi)源項(xiàng)目巔峰人生等欄目。利用降低三倍加載速度自推出之后,很多開(kāi)發(fā)者都開(kāi)始嘗試在小型項(xiàng)目中實(shí)踐,不過(guò)尚缺大型真實(shí)案例比較。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開(kāi)發(fā)者了解一周前端熱點(diǎn);分為新聞熱點(diǎn)、開(kāi)發(fā)教程、工程實(shí)踐、深度閱讀、開(kāi)源項(xiàng)目、巔峰人生等欄目...
摘要:本章用于講解如何在下構(gòu)建和運(yùn)行前端應(yīng)用。項(xiàng)目配置服務(wù)名稱鏡像版本映射容器端口到本地端口數(shù)據(jù)卷映射本地文件到容器映射文件到容器的目錄并覆蓋文件映射文件夾到容器的文件夾覆蓋容器啟動(dòng)后默認(rèn)執(zhí)行的命令。環(huán)境準(zhǔn)備參考文檔 本章用于講解如何在walle下構(gòu)建和運(yùn)行前端應(yīng)用。主要包含React,Angular應(yīng)用,以Nginx+Docker運(yùn)行(Vue方式不講,大家自行研究) 新建項(xiàng)目 項(xiàng)目中心 >...
摘要:以便對(duì)整個(gè)持續(xù)集成印象加深。配置完各環(huán)境發(fā)布腳本后,則可以使用構(gòu)建發(fā)起進(jìn)行觸發(fā)環(huán)境準(zhǔn)備。并會(huì)在遠(yuǎn)程環(huán)境上存放多次發(fā)布的版本,用于回退和切換服務(wù)停用。進(jìn)行等操作,停止原本運(yùn)行的服務(wù)切換啟用。 該文章用于建立一個(gè)小型的基于Walle的持續(xù)集成工具。解決java,react,angular項(xiàng)目的編譯發(fā)布。以便對(duì)整個(gè)持續(xù)集成印象加深。官方網(wǎng)站:https://walle-web.io/ 適用...
摘要:而調(diào)試器具有對(duì)模型控制器以及視圖的實(shí)時(shí)管理權(quán)限。項(xiàng)目地址是一個(gè)輕量級(jí)純寫的文本工具提示庫(kù)。它支持種不同國(guó)家的貨幣格式,以及超過(guò)種不同語(yǔ)言的本地化設(shè)置。項(xiàng)目地址是一個(gè)根據(jù)規(guī)范構(gòu)建的輕量級(jí)框架。它壓縮后僅有,同時(shí)它沒(méi)有預(yù)先設(shè)定的元素和內(nèi)置動(dòng)畫。 在十一月份的前端技術(shù)列表中,我們整合了一些令人感到驚嘆的 GitHub 項(xiàng)目,其中包含了新的 CSS 框架、node.js包管理器,以及用于實(shí)現(xiàn)圖...
摘要:而調(diào)試器具有對(duì)模型控制器以及視圖的實(shí)時(shí)管理權(quán)限。項(xiàng)目地址是一個(gè)輕量級(jí)純寫的文本工具提示庫(kù)。它支持種不同國(guó)家的貨幣格式,以及超過(guò)種不同語(yǔ)言的本地化設(shè)置。項(xiàng)目地址是一個(gè)根據(jù)規(guī)范構(gòu)建的輕量級(jí)框架。它壓縮后僅有,同時(shí)它沒(méi)有預(yù)先設(shè)定的元素和內(nèi)置動(dòng)畫。 在十一月份的前端技術(shù)列表中,我們整合了一些令人感到驚嘆的 GitHub 項(xiàng)目,其中包含了新的 CSS 框架、node.js包管理器,以及用于實(shí)現(xiàn)圖...
閱讀 1739·2021-09-26 09:46
閱讀 3017·2021-09-22 15:55
閱讀 2608·2019-08-30 14:17
閱讀 3027·2019-08-26 11:59
閱讀 1809·2019-08-26 11:35
閱讀 3155·2019-08-26 10:45
閱讀 3152·2019-08-23 18:28
閱讀 1106·2019-08-23 18:21