## 如何使用 JavaScript 實現一門編程語言(1) —— 前言
這是一系列關于?[如何實現編程語言](http://annn.me/tag/%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86)?的教程。如果你曾經寫過一個解釋器或編譯器,那么這里可能沒有什么新東西。但是,如果您使用正則表達式來“解析” 任何看起來像編程語言的東西,那么請至少閱讀解析部分。讓我們寫出更少的錯誤代碼!
目標受眾是普通的 JavaScript / NodeJS 程序員。
我們要學什么?
- 什么是解析器,以及如何編寫解析器。
- 如何編寫解釋器。
- 為什么它們很重要。
- 編寫一個編譯器。
- 如何將代碼轉換為延續傳遞樣式。
- 一些基本的優化技術。
在兩者之間,我會爭論為什么 Lisp 是一種優秀的編程語言。 但是,我們將要使用的語言不是 Lisp。它有一個更豐富的語法(每個人都知道的經典中綴符號),除宏之外,它的功能與 Scheme相當。
可悲的是,宏是 Lisp 的最終堡壘,其他語言無法克服(除非它們被稱為 Lisp 方言)。
### 首先,讓我們想想我們的要實現的編程語言應該是什么樣子。
我們應該想清楚自己想要實現的目標。把語法的嚴格描述放在一起是一個好主意,但是我會在本教程中使語法更加簡單,下面的示例就是我們要實現 “λ” 語言:
```
# this is a comment
println("Hello World!");
println(2 + 3 * 4);
# functions are introduced with `lambda` or `λ`
fib = lambda (n) if n < 2 then n else fib(n - 1) + fib(n - 2);
println(fib(15));
print-range = λ(a, b) # `λ` is synonym to `lambda`
if a <= b then { # `then` here is optional as you can see below
print(a);
if a + 1 <= b {
print(", ");
print-range(a + 1, b);
} else println(""); # newline
};
print-range(1, 5);
```
*請注意,標識符名稱可以包含負號字符(print-range)。這是個人品味的問題:我總是在操作符旁邊放置空格,我不喜歡太多的 camelCaseNames,而且我覺得短劃線比下劃線更好。編寫自己的語言的好處是,你可以隨心所欲地做到這一點。:)*
輸出是:
```
Hello World!
14
610
1, 2, 3, 4, 5
```
該語言看起來有點像 JavaScript,但它不同。首先,沒有聲明,只有表達式。表達式返回一個值,可以用來代替任何其他表達式。分號需要在“序列”中分隔表達式。花括號{和}創建這樣一個序列,它本身就是一個表達式。它的值是最后一個表達式返回的值。以下是一個有效的程序:
```
a = {
fib(10); # has no side-effects, but it's computed anyway
fib(15) # the last semicolon can be missing
};
print(a); # prints 610
```
函數被引入其中一個關鍵字 lambda 或 λ(它們是同義詞)。在關鍵字之后,必須有一個(可能為空的)用逗號分隔的變量名列表(可能為空),就像在 JavaScript 中一樣 —— 這些是參數名稱。函數體是一個單一的表達式,但它可以是一個包裹在{ … }中的序列。
沒有 return 語句 - 在函數返回最后一個表達式給出的返回值。
沒有 var。要引入新變量,可以使用 JSer 稱之為 “立即執行函數” 的方式。就像在 JavaScript 中一樣,使用 a lambda,聲明變量作為參數。變量具有函數作用域范圍,函數是閉包。
甚至 if 本身就是一種表達。在 JavaScript 中,您可以使用三元運算符獲得該效果:
```
a = foo() ? bar() : baz(); // JavaScript
a = if foo() then bar() else baz(); # λanguage
```
當分支以一個花括號開始時, then 關鍵字是可選,你可以在 print-range 上面看到,否則它是必需的。
當替代分支存在時,else 關鍵字是必須的。再次,then 的 else 的分支主題為一個單一的表達式,但也可以花括號 “{}” 包含多個通過 “;” 分隔的表達式。
當 else 分支不存在并且 if 條件結果為 false,if 表達式的結果是 false。因此,false 是一個關鍵詞,它表示我們的語言中唯一的 falsy 值:
```
if foo() then print("OK");
```
當 foo() 的結果不是 false 是,這點程序將會打印 “OK”。還有一個用于表示”真”的關鍵字 true,不是所非 false(當使用 JavaScript中 === 運算符的時候)的值都被理解成 true (其中包括數字 0 和空字符串 “”)。
還要注意,使用括號包裹 if 條件沒有意義。不過,雖然它們是多余的,你添加它們也沒有錯誤。
整個程序被解析,就好像它被嵌入大括號中一樣,因此,除了最后一個表達式,您需要在每個表達式后面放置一個分號。
好了,這是就我們的小 λ 語言。它不一定是很完善的的。它的語法看起來很可愛,但也有陷阱。它有很多缺失的功能,如對象或數組; 我們并不關注這些缺失,因為對我們的旅程并不重要。只要你掌握了這個教程的所有內容,你可以輕松實現這些缺失的功能。
- Web 開發筆記
- 從輸入 URL 到頁面加載完成的過程中都發生了什么事情?
- 從瀏覽器接收url到開啟網絡請求線程
- 開啟網絡線程到發出一個完整的http請求
- 從服務器接收到請求到對應后臺接收到請求
- 后臺和前臺的http交互
- http的緩存
- 解析頁面流程
- HTML解析,構建DOM
- CSS解析,構建CSSOM
- 資源外鏈的下載
- CSS的可視化格式模型
- 包含塊(Containing Block)
- 控制框(Controlling Box)
- BFC(Block Formatting Context)
- IFC(Inline Formatting Context)
- 其它
- JS引擎解析過程
- JS的解釋階段
- JS的預處理階段
- JS的執行階段
- 回收機制
- 參考資料
- JavaScript模塊化編程
- AMD
- requireJS
- CommonJS
- UMD
- ES6模塊
- 參考資料
- 使用 JavaScript 實現一門編程語言
- 如何使用 JavaScript 實現一門編程語言(1) —— 前言
- 如何使用 JavaScript 實現一門編程語言(2) —— 編寫一個解析器
- 如何使用 JavaScript 實現一門編程語言(3) —— Input stream
- 如何使用 JavaScript 實現一門編程語言(4) —— Token stream
- 如何使用 JavaScript 實現一門編程語言(5) —— AST
- 如何使用 JavaScript 實現一門編程語言(6) —— Interpreter
- 完整代碼
- 參考資料
- 前端布局概論
- 參考資料
- Windows 筆記
- 錯誤解決
- win10應用商店無法登錄提示0x80070426解決方法
- 使用技巧
- 設置 Hyper-V 和 VMware 共存
- Powershell
- WSL