写一个解释器

1. 什么是解释器

1.1. 解释器

  • 同声传译
  • 一段能够理解并执行 你的程序 的 程序
    • 理解你的代码所表示的意图
    • 执行你的意图

1.2. 代码的意图

  • 赋值/定义

    (setf a 1)
    (defun plus (a b) (+ a b))
    
  • 取值

    a

  • 执行

    (plus 1 2)

1.3. LISP

  1. 语法(S Expression)
    • 原子 a, 1, "hello world"
    • 表 (), nil, (a 1 2), (a . b) , (a . nil), (a (b))
  2. 语义
    原子表达式
    即 a, 1 等原子,可直接求值或上下文中查找对应的值
    复合表达式
    函数
    特殊形式
    求值方式与函数不一致

2. 如何下手

2.1. 步骤   B_frame

Dragon book, 中文名 编译原理

dragon.jpg

Tiger book, 中文名 现代编译原理-C 语言描述

tiger.jpg

Whale book, 中文名 高级编译器设计与实现

whale.jpg

2.2. 结束

  • <+-> 好,分享结束,大家可以回去看书了.
  • <2-> 预计一年后,应该可以成功写出来了。
  • <3> 开个玩笑,我们继续. 看一个 半小时就能写出来的版本 。

3. 半小时版本

3.1. 核心逻辑

  • parse -> (eval1 -> apply2) loop…

eval-apply.png

3.2. 解析(1 min)

这里我们就偷个懒,利用 lisp 的 read-from-string / read 方法

(read-from-string "(1 2 3)")
;;; => (1 2 3)
(read-from-string "1")
;;; => 1
(read-from-string "nil")
 ;;; => NIL
(read-from-string "(defun plus (a b ) (+ a b))")
;;; => (DEFUN PLUS (A B) (+ A B))
(read-from-string "(defun plus (a b)")
;;; Exception

3.3. 表达式类型

  • 原子
    • 常量 1, "abc"
    • 变量 a, test
  • 列表
    • quote (quote (a b c))
    • if (if t 1 2)
    • lambda (lambda (a) (+ 1 a))
    • define (define a 1)
    • assign (set! a 2)
    • begin (begin (define b 1) (set! b 2))
    • apply procedure (plus 1 2)

3.4. 原子表达式

  1. 符号

    当遇到了一个符号的时候,从当前的上下文中去查找其对应的值,做替换

    a ;;; nil or unbound exception
    (set! a 1) ;;; => 1
    a ;;; => 1
    
  2. 常量

    常量表达式的值即为本身

    1 ;;; => 1
    "abc" ;;; "abc"
    

3.5. 特殊形式 if

(if predicate consuquence alternative)

先求值 predicate , 如果符合,则求值 consquence, 反之,则求值 alternative

特殊在于, 控制表内的求值顺序 。

并不会将表内表达式均求值,而是根据第一个元素的值,来决定后续如何进行求值。

3.6. 特殊形式 define 以及 set!

(define variable value)
(set! variable value)

只求值 value, 并将求值后的结果赋值给 variable3

特殊在于, 控制表内的求值顺序 以及 修改上下文

不对 variable 求值,仅求值 value, 而后修改上下文。

3.7. 特殊形式 quote

(quote (a b c))

返回其引用的表达式

syntactic sugar: '(a b c)

特殊在于, 控制表内的求值顺序 。

不对表达式内求值,仅返回其引用的表达式。

3.8. 特殊形式 lambda

(lambda (a) (+ 1 a))

生成一个 procedure, 包含 形式参数 列表,以及 待执行的表达式 列表。

特殊同样在于, 控制表内的求值顺序 。

只将待执行的表达式记录下来,留待需要时使用。

3.9. 特殊形式 begin

(begin
 (define a 3)
 (set! a 1)
 (+ a 2))

依次执行表达式序列

特殊在于, 控制表内的求值顺序

3.10. 函数调用

(define plus (lambda (a) (+ 1 a)))
(plus 2) ;;; 3
  1. 求值操作符
  2. 求值操作数
  3. 系统方法,则直接调用下层的 apply
  4. 自定义的方法
    1. 把形参对应的值添加到上下文中, 生成新的上下文
    2. 在新的上下文中,求值表达式列表

函数定义

;;; env => ((+) (<primitive +>))
(define plus1 (lambda (a) (+ 1 a)))
;;; env =>
;;;       ( (+ plus1)
;;;         (<primitive +> <procedure object>) )

(plus1 2)

执行过程

(eval 'plus1 env) ;;; <procedure object>
(eval 2) ;;; 2
(extend (a) (2) env)
;;; env' => ( (+ plus1 a)
;;;           (<primitive +> <procedure object> 2))

(eval '(+ 1 a) env')
(eval a env') ;;; 2
(apply-primitive '+ (1 2)) ;;; 3

3.11. 求值环境/上下文

context.png

4. 实现一个解释器

4.1. 声明

源代码来自 SICP 第 4 章,链接见附录。

4.2. eval (dispatch)

(define (eval exp env)
  (cond ((self-evaluating? exp) exp)
        ((variable? exp) (lookup-variable-value exp env))
        ((quoted? exp) (text-of-quotation exp))
        ((assignment? exp) (eval-assignment exp env))
        ((definition? exp) (eval-definition exp env))
        ((if? exp) (eval-if exp env))
        ((lambda? exp)
         (make-procedure (lambda-parameters exp)
                         (lambda-body exp)
                         env))
        ((begin? exp)
         (eval-sequence (begin-actions exp) env))
        ((application? exp)
         (apply (eval (operator exp) env)
                (list-of-values (operands exp) env)))
        (else
         (error "Unknown expression type -- EVAL" exp))))

4.3. apply

(define (apply procedure arguments)
  (cond ((primitive-procedure? procedure)
         (apply-primitive-procedure procedure arguments))
        ((compound-procedure? procedure)
         (eval-sequence
           (procedure-body procedure)
           (extend-environment
             (procedure-parameters procedure)
             arguments
             (procedure-environment procedure))))
        (else
         (error
          "Unknown procedure type -- APPLY" procedure))))

4.4. env 求值上下文

(define (enclosing-environment env) (cdr env))
(define (first-frame env) (car env))
(define the-empty-environment '())

(define (make-frame variables values)
  (cons variables values))
(define (frame-variables frame) (car frame))
(define (frame-values frame) (cdr frame))
(define (add-binding-to-frame! var val frame)
  (set-car! frame (cons var (car frame)))
  (set-cdr! frame (cons val (cdr frame))))
(define (extend-environment vars vals base-env)
  (if (= (length vars) (length vals))
      (cons (make-frame vars vals) base-env)
      (if (< (length vars) (length vals))
          (error "Too many arguments supplied" vars vals)
          (error "Too few arguments supplied" vars vals))))

4.5. eval-atom

(define (self-evaluating? exp)
  (cond ((number? exp) true)
        ((string? exp) true)
        (else false)))

(define (variable? exp) (symbol? exp))

(define (lookup-variable-value var env)
  (define (env-loop env)
    (define (scan vars vals)
      (cond ((null? vars)
             (env-loop (enclosing-environment env)))
            ((eq? var (car vars))
             (car vals))
            (else (scan (cdr vars) (cdr vals)))))
    (if (eq? env the-empty-environment)
        (error "Unbound variable" var)
        (let ((frame (first-frame env)))
          (scan (frame-variables frame)
                (frame-values frame)))))
  (env-loop env))

4.6. eval-define & eval-assign

(define (eval-assignment exp env)
  (set-variable-value!
     (assignment-variable exp)
     (eval (assignment-value exp) env)
     env)
  'ok)

(define (eval-definition exp env)
    (define-variable!
        (definition-variable exp)
        (eval (definition-value exp) env)
      env)
  'ok)
(define (define-variable! var val env)
  (let ((frame (first-frame env)))
    (define (scan vars vals)
      (cond ((null? vars)
             (add-binding-to-frame! var val frame))
            ((eq? var (car vars))
             (set-car! vals val))
            (else (scan (cdr vars) (cdr vals)))))
    (scan (frame-variables frame)
          (frame-values frame))))

4.7. eval-if

(define (eval-if exp env)
  (if (true? (eval (if-predicate exp) env))
      (eval (if-consequent exp) env)
      (eval (if-alternative exp) env)))

4.8. eval-quote

(define (quoted? exp)
  (tagged-list? exp 'quote))

(define (text-of-quotation exp) (cadr exp))

4.9. eval-lambda

(define (make-procedure parameters body env)
  (list 'procedure parameters body env))
(define (compound-procedure? p)
  (tagged-list? p 'procedure))
(define (procedure-parameters p) (cadr p))
(define (procedure-body p) (caddr p))
(define (procedure-environment p) (cadddr p))

4.10. eval-begin

(define (eval-sequence exps env)
  (cond ((last-exp? exps) (eval (first-exp exps) env))
        (else (eval (first-exp exps) env)
              (eval-sequence (rest-exps exps) env))))

5. 最后

5.1. 几个题外话

  • 如果没有 assign, 会不会简单很多
  • 如果使用 lazy 的求值, 而不是应用时求值,是否很多特殊形式就没有必要了
  • 如果增加一个 case 的关键字
  • 如果做语法分析
  • 如果要编译成 c

Footnotes:

1

处理表达式

2

处理值

3

赋值表示在上下文中添加 (install) 这个符号以及这个符号对应的值.