写一个解释器

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)

这里我们就偷个懒,利用 lispread-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) 这个符号以及这个符号对应的值.