用JavaScript實現一門程式語言 3-1 (解析器之抽象語法樹)

繆宇發表於2017-10-30

抽象語法樹(AST)

正如前面提到的,解析器會建立一個結構來表示程式的語義。AST節點是一個普通的JavaScript物件,它有一個型別屬性,指定它是什麼型別的節點,以及根據特定型別指定的附加資訊。

總覽:

  • num
    { type: "num", value: NUMBER }

  • str
    { type: "str", value: STRING }

  • bool
    { type: "bool", value: true or false }

  • var
    { type: "var", value: NAME }

  • lambda
    { type: "lambda", vars: [ NAME... ], body: AST }

  • call
    { type: "call", func: AST, args: [ AST... ] }

  • if
    { type: "if", cond: AST, then: AST, else: AST }

  • assign
    { type: "assign", operator: "=", left: AST, right: AST }

  • binary
    { type: "binary", operator: OPERATOR, left: AST, right: AST }

  • prog
    { type: "prog", prog: [ AST... ] }


  • let
    { type: "let", vars: [ VARS... ], body: AST }

例子:

Numbers ("num")

123.5複製程式碼

{ type: "num", value: 123.5 }複製程式碼

Strings ("str")

"Hello World!"複製程式碼

{ type: "str", value: "Hello World!" }複製程式碼

Booleans ("bool")

true
false複製程式碼

{ type: "bool", value: true }
{ type: "bool", value: false }複製程式碼

識別符號 ("var")

foo複製程式碼

{ type: "var", value: "foo" }複製程式碼

函式 ("lambda")

lambda (x) 10   # or
λ (x) 10複製程式碼

Later we will add an optional "name" property, to support named
functions, but the first version of our parser won't have it.

{
  type: "lambda",
  vars: [ "x" ],
  body: { type: "num", value: 10 }
}複製程式碼

函式呼叫 ("call")

foo(a, 1)複製程式碼

{
  "type": "call",
  "func": { "type": "var", "value": "foo" },
  "args": [
    { "type": "var", "value": "a" },
    { "type": "num", "value": 1 }
  ]
}複製程式碼

條件語句 ("if")

if foo then bar else baz複製程式碼

{
  "type": "if",
  "cond": { "type": "var", "value": "foo" },
  "then": { "type": "var", "value": "bar" },
  "else": { "type": "var", "value": "baz" }
}複製程式碼

else 分支是可選的:

if foo then bar複製程式碼

{
  "type": "if",
  "cond": { "type": "var", "value": "foo" },
  "then": { "type": "var", "value": "bar" }
}複製程式碼

賦值 ("assign")

a = 10複製程式碼

{
  "type": "assign",
  "operator": "=",
  "left": { "type": "var", "value": "a" },
  "right": { "type": "num", "value": 10 }
}複製程式碼

二進位制表示式 ("binary")

x + y * z複製程式碼

{
  "type": "binary",
  "operator": "+",
  "left": { "type": "var", "value": "x" },
  "right": {
    "type": "binary",
    "operator": "*",
    "left": { "type": "var", "value": "y" },
    "right": { "type": "var", "value": "z" }
  }
}複製程式碼

序列 ("prog")

{
  a = 5;
  b = a * 2;
  a + b;
}複製程式碼

{
  "type": "prog",
  "prog": [
    {
      "type": "assign",
      "operator": "=",
      "left": { "type": "var", "value": "a" },
      "right": { "type": "num", "value": 5 }
    },
    {
      "type": "assign",
      "operator": "=",
      "left": { "type": "var", "value": "b" },
      "right": {
        "type": "binary",
        "operator": "*",
        "left": { "type": "var", "value": "a" },
        "right": { "type": "num", "value": 2 }
      }
    },
    {
      "type": "binary",
      "operator": "+",
      "left": { "type": "var", "value": "a" },
      "right": { "type": "var", "value": "b" }
    }
  ]
}複製程式碼

擁有塊作用域的變數 ("let")

let (a = 10, b = a * 10) {
  a + b;
}複製程式碼

在我們第一版的編譯器將不會有這個節點型別,我們會在之後加入這一特性。

{
  "type": "let",
  "vars": [
    {
      "name": "a",
      "def": { "type": "num", "value": 10 }
    },
    {
      "name": "b",
      "def": {
        "type": "binary",
        "operator": "*",
        "left": { "type": "var", "value": "a" },
        "right": { "type": "num", "value": 10 }
      }
    }
  ],
  "body": {
    "type": "binary",
    "operator": "+",
    "left": { "type": "var", "value": "a" },
    "right": { "type": "var", "value": "b" }
  }
}複製程式碼

原文:lisperator.net/pltut/

相關文章