目錄
- 用javascript實現一門程式語言-前言
- 用javascript實現一門程式語言-語言構想
- 用javascript實現一門程式語言-寫一個解析器
- 用javascript實現一門程式語言-字元輸入流
寫一個解析器
解析器的實現是需要根據語言特性來實現的,是一個較為複雜的任務。事實上,我們需要將一段程式碼或者字母轉換為抽象語法樹 (abstract syntax tree, AST)。抽象語法樹是程式在記憶體中展現的一種形式,抽象表示它不關心是由什麼原始碼構成的,但是他很確信是符合語義學的。
例如:
sum = lambda(a, b) {
a + b;
}
print(sum(1, 2));
複製程式碼
解析器會將上面的程式碼轉換為一個javascript物件
:
{
type: "prog",
prog: [
// 第一行
{
type: "assign",
operator: "=",
left: { type: "var", value: "sum" },
right: {
type: "lambda",
vars: [ "a", "b" ],
body: {
// body 部分也應該是 prog 型別,因為它包含一個表示式
type: "binary",
operator: "+",
left: { type: "var", value: "a" },
right: { type: "var", value: "b" }
}
}
},
// 第二行
{
type: "call",
func: { type: "var", value: "print" },
args: [{
type: "call",
func: { type: "var", value: "sum" },
args: [ { type: "num", value: 1 },
{ type: "num", value: 2 } ]
}]
}
]
}
複製程式碼
寫一個解析器最大的困難在於如何合理的組織程式碼。解析器應該站在比讀取字元更高的層面。這裡有一些建來控制程式的適度的複雜性:
- 寫小而精的函式。每個函式只做一件事,並把它做好。
- 不要用正規表示式去解析。在寫詞法分析器的時候正規表示式很有用,但是建議儘量不要把它用在很簡單的事情上
- 不要嘗試去猜。當不確定解析什麼的時候,丟擲一個包含位置的錯誤,比如: 第2行25列錯誤
為了保持膽碼的簡潔性,我將程式碼分割成了三部分,將來會被分割成更小的函式:
- 字元輸入流
- token輸入流
- 解析器