用JavaScript实现一门编程语言 3-1 (解析器之抽象语法树)

352 阅读2分钟

抽象语法树(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/