diff options
-rw-r--r-- | chap5/part3.rkt | 5 | ||||
-rw-r--r-- | chap5/part5.rkt | 1446 |
2 files changed, 1433 insertions, 18 deletions
diff --git a/chap5/part3.rkt b/chap5/part3.rkt index 2fad35d..cf6cc92 100644 --- a/chap5/part3.rkt +++ b/chap5/part3.rkt @@ -143,6 +143,11 @@ (goto (reg continue)) append-done)) +(define (last-pair x) + (if (null? (cdr x)) + x + (last-pair (cdr x)))) + (#%provide append!) (define (append! x y) (set-cdr! (last-pair x) y) diff --git a/chap5/part5.rkt b/chap5/part5.rkt index 9db1e73..745defa 100644 --- a/chap5/part5.rkt +++ b/chap5/part5.rkt @@ -17,7 +17,7 @@ (lambda (inst) (display inst) (newline)) - (statements (compile exp 'val 'done)))) + (statements (compile exp 'val 'next)))) (#%provide compile) (define (compile exp target linkage) @@ -38,6 +38,14 @@ (compile-sequence (begin-actions exp) target linkage)) ((cond? exp) (compile (cond->if exp) target linkage)) + ((and (pair? exp) (eq? (car exp) '=)) + (compile-bin-op '= exp target linkage)) + ((and (pair? exp) (eq? (car exp) '*)) + (compile-bin-op '* exp target linkage)) + ((and (pair? exp) (eq? (car exp) '-)) + (compile-bin-op '- exp target linkage)) + ((and (pair? exp) (eq? (car exp) '+)) + (compile-bin-op '+ exp target linkage)) ((application? exp) (compile-application exp target linkage)) (else @@ -228,9 +236,9 @@ (assign env (op extend-environment) - (const ,formals)) + (const ,formals) (reg argl) - (reg env))) + (reg env)))) (compile-sequence (lambda-body exp) 'val 'return)))) (define label-counter 0) @@ -246,7 +254,7 @@ (number->string (new-label-number))))) (define (make-compiled-procedure entry env) - (list 'compile-procedure entry env)) + (list 'compiled-procedure entry env)) (define (compiled-procedure? proc) (tagged-list? proc 'compiled-procedure)) @@ -317,6 +325,7 @@ (let ((primitive-branch (make-label 'primitive-branch)) (compiled-branch (make-label 'compiled-branch)) + (compound-branch (make-label 'compound-branch)) (after-call (make-label 'after-call))) (let ((compiled-linkage @@ -326,11 +335,17 @@ '(proc) '() `((test (op primitive-procedure?) (reg proc)) - (branch (label ,primitive-branch)))) + (branch (label ,primitive-branch)) + (test (op compound-procedure?) (reg proc)) + (branch (label ,compound-branch)))) (parallel-instruction-sequences - (append-instruction-sequences - compiled-branch - (compile-proc-appl target compiled-linkage)) + (parallel-instruction-sequences + (append-instruction-sequences + compiled-branch + (compile-proc-appl target compiled-linkage)) + (append-instruction-sequences + compound-branch + (compound-proc-appl target compiled-linkage))) (append-instruction-sequences primitive-branch (end-with-linkage @@ -479,34 +494,562 @@ ;; An Example of Compiled Code +(#%provide fact-def) +(define fact-def + '(define (factorial n) + (if (= n 1) + 1 + (* (factorial (- n 1)) n)))) + #| 5.33 |# + +(#%provide fact-alt-def) +(define fact-alt-def + '(define (factorial-alt n) + (if (= n 1) + 1 + (* n (factorial-alt (- n 1)))))) + +;; The two factorial definitions produce +;; compiled code with the same number of instructions + +;; while factorial must save and restore argl +;; around the recursive call, +;; factorial-alt must save and restore env +;; around the recursive call + #| 5.34 |# + +(#%provide fact-iter-def) +(define fact-iter-def + '(define (factorial-iter n) + (define (iter product counter) + (if (> counter n) + product + (iter + (* counter product) + (+ counter 1)))) + (iter 1 1))) + +;; This version uses constant space +;; continue is restored before the recursive call +;; there is no work to do after the recursive call + #| 5.35 |# + +(#%provide decompiled) +(define decompiled + '(define (f x) + (+ x (g (+ x 2))))) + #| 5.36 |# + +;; The compiler evaluates arguments right-to-left. +;; left-to-right requires appending to the end of +;; the argument list rather than cons-ing on to +;; the front + #| 5.37 |# + +(define (preserving-bad regs seq1 seq2) + (if (null? regs) + (append-instruction-sequences seq1 seq2) + (let ((first-reg (car regs))) + (preserving-bad + (cdr regs) + (make-instruction-sequence + (list-union + (list first-reg) + (registers-needed seq1)) + (list-difference + (registers-modified seq1) + (list first-reg)) + (append + `((save ,first-reg)) + (statements seq1) + `((restore ,first-reg)))) + seq2)))) + #| 5.38 |# -;; Lexical Addressing +(define (spread-arguments op1 op2 next-code) + (preserving + '(env) + (compile op1 'arg1 'next) + (preserving + '(arg1) + (compile op2 'arg2 'next) + next-code))) + +(define (compile-bin-op prim-op exp target linkage) + (let ((ops (operands exp))) + (if (= (length ops) 2) + (end-with-linkage + linkage + (spread-arguments + (car ops) + (cadr ops) + (make-instruction-sequence + '(arg1 arg2) + (list target) + `((assign ,target (op ,prim-op) (reg arg1) (reg arg2)))))) + (error "Wrong number of operands -- COMPILE-BIN-OP" (length ops))))) -#| 5.39 |# -#| 5.40 |# -#| 5.41 |# -#| 5.42 |# -#| 5.43 |# -#| 5.44 |# +;; Lexical Addressing ;; Interfacing Compiled Code to the Evaluator #| 5.45 |# + +(#%provide total-pushes-compiled-factorial) +(define (total-pushes-compiled-factorial n) + (+ (* 2 n) 3)) + +(#%provide max-depth-compiled-factorial) +(define (max-depth-compiled-factorial n) + (max 3 (* 2 (- n 1)))) + +#| total-pushes: |# +#| compiled:interpreted = 2:32 = 1:16 |# +#| compiled:special-purpose = 2:2 = 1:1|# + +#| max-depth: |# +#| compiled:interpreted = 2:5 = 1:2.5 |# +#| compiled:special-purpose = 2:2 = 1:1 |# + +;; compilation of factorial gives +;; 16x speedup over interpretation +;; and uses 60% less space + +;; compilation with open-coded primitives matches +;; hand-written performance + #| 5.46 |# + +;; fib + +#| max-depth: |# +#| compiled:interpreted = 2:5 |# + +#| total-pushes |# +#| compiled: (14 21 35 56 91) |# +#| interpreted: (72 128 240 408 688) |# + +;; still exponential + #| 5.47 |# + +#| (compound-branch (make-label 'compound-branch)) |# + +#| (parallel-instruction-sequences |# +#| (append-instruction-sequences |# +#| compiled-branch |# +#| (compile-proc-appl target compiled-linkage)) |# +#| (append-instruction-sequences |# +#| compound-branch |# +#| (compound-proc-appl target compiled-linkage))) |# + +(define (compound-proc-appl target linkage) + (cond + ((and + (eq? target 'val) + (not (eq? linkage 'return))) + (make-instruction-sequence + '(proc) + all-regs + `((assign continue (label ,linkage)) + (save continue) + (goto (reg compapp))))) + ((and + (not (eq? target 'val)) + (not (eq? linkage 'return))) + (let ((proc-return (make-label 'proc-return))) + (make-instruction-sequence + '(proc) + all-regs + `((assign continue (label ,proc-return)) + (save continue) + (goto (reg compapp)) + ,proc-return + (assign ,target (reg val)) + (goto (label ,linkage)))))) + ((and + (eq? target 'val) + (eq? linkage 'return)) + (make-instruction-sequence + '(proc continue) + all-regs + '((save continue) + (goto (reg compapp))))) + (else + (error "return linkage, target not val: COMPILE" target)))) + #| 5.48 |# + +(define (compile-and-run expression) + (let + ((instructions + (assemble + (statements + (compile expression 'val 'next)) + eceval)) + (old-pc (get-register-contents eceval 'pc))) + (set-register-contents! eceval 'pc instructions) + (eceval 'execute) + (set-register-contents! eceval 'pc old-pc) + (get-register-contents eceval 'val))) + #| 5.49 |# -#| 5.50 |# + +(define read-compile-execute-print-loop + '(read-compile-execute-print-loop + (perform (op initialize-stack)) + (perform (op prompt-for-input) (const ";;; EC-Compile input:")) + (assign exp (op read)) + (assign env (op get-global-environment)) + (assign continue (label print-result)) + (assign val (op compile) (reg exp) (const val) (const return)) + (assign val (op statements) (reg val)) + (assign val (op assemble-eccompile) (reg val)) + (goto (reg val)) + print-result + (perform (op print-stack-statistics)) + (perform (op announce-output) (const ";;; EC-Compile value:")) + (perform (op user-print) (reg val)) + (goto (label read-compile-execute-print-loop)))) + +(define (flatten xss) + (if (null? xss) + '() + (append + (car xss) + (flatten (cdr xss))))) + #| 5.51 |# -#| 5.52 |# -; syntax procedures +;; https://github.com/jacquescomeaux/pdp11-lisp + +;; explicit-control evaluator + +(define eval-dispatch + '(eval-dispatch + (test (op self-evaluating?) (reg exp)) + (branch (label ev-self-eval)) + (test (op variable?) (reg exp)) + (branch (label ev-variable)) + (test (op quoted?) (reg exp)) + (branch (label ev-quoted)) + (test (op assignment?) (reg exp)) + (branch (label ev-assignment)) + (test (op definition?) (reg exp)) + (branch (label ev-definition)) + (test (op if?) (reg exp)) + (branch (label ev-if)) + (test (op lambda?) (reg exp)) + (branch (label ev-lambda)) + (test (op begin?) (reg exp)) + (branch (label ev-begin)) + (test (op cond?) (reg exp)) + (branch (label ev-cond)) + (test (op let?) (reg exp)) + (branch (label ev-let)) + (test (op application?) (reg exp)) + (branch (label ev-application)) + (goto (label unknown-expression-type)))) + +(define ev-self-eval + '(ev-self-eval + (assign val (reg exp)) + (goto (reg continue)))) + +(define ev-variable + '(ev-variable + (assign val (op lookup-variable-value) (reg exp) (reg env)) + (goto (reg continue)))) + +(define ev-quoted + '(ev-quoted + (assign val (op text-of-quotation) (reg exp)) + (goto (reg continue)))) + +(define ev-lambda + '(ev-lambda + (assign unev (op lambda-parameters) (reg exp)) + (assign exp (op lambda-body) (reg exp)) + (assign val (op make-procedure) (reg unev) (reg exp) (reg env)) + (goto (reg continue)))) + +(define (empty-arglist) '()) + +(define (adjoin-arg arg arglist) + (append arglist (list arg))) + +(define (last-operand? ops) + (null? (cdr ops))) + +(define ev-application + '(ev-application + (save continue) + (save env) + (assign unev (op operands) (reg exp)) + (save unev) + (assign exp (op operator) (reg exp)) + (assign continue (label ev-appl-did-operator)) + (goto (label eval-dispatch)) + ev-appl-did-operator + (restore unev) + (restore env) + (assign argl (op empty-arglist)) + (assign proc (reg val)) + (test (op no-operands?) (reg unev)) + (branch (label apply-dispatch)) + (save proc) + ev-appl-operand-loop + (save argl) + (assign exp (op first-operand) (reg unev)) + (test (op last-operand?) (reg unev)) + (branch (label ev-appl-last-arg)) + (save env) + (save unev) + (assign continue (label ev-appl-accumulate-arg)) + (goto (label eval-dispatch)) + ev-appl-accumulate-arg + (restore unev) + (restore env) + (restore argl) + (assign argl (op adjoin-arg) (reg val) (reg argl)) + (assign unev (op rest-operands) (reg unev)) + (goto (label ev-appl-operand-loop)) + ev-appl-last-arg + (assign continue (label ev-appl-accum-last-arg)) + (goto (label eval-dispatch)) + ev-appl-accum-last-arg + (restore argl) + (assign argl (op adjoin-arg) (reg val) (reg argl)) + (restore proc) + (goto (label apply-dispatch)))) + +(define apply-dispatch + '(apply-dispatch + (test (op primitive-procedure?) (reg proc)) + (branch (label primitive-apply)) + (test (op compound-procedure?) (reg proc)) + (branch (label compound-apply)) + (test (op compiled-procedure?) (reg proc)) + (branch (label compiled-apply)) + (perform (op user-print) (reg proc)) + (goto (label unknown-procedure-type)) + compiled-apply + (restore continue) + (assign val (op compiled-procedure-entry) (reg proc)) + (goto (reg val)) + primitive-apply + (assign val (op apply-primitive-procedure) (reg proc) (reg argl)) + (restore continue) + (goto (reg continue)) + compound-apply + (assign unev (op procedure-parameters) (reg proc)) + (assign env (op procedure-environment) (reg proc)) + (assign env (op extend-environment) (reg unev) (reg argl) (reg env)) + (assign unev (op procedure-body) (reg proc)) + (goto (label ev-sequence)))) + +(define ev-begin + '(ev-begin + (assign unev (op begin-actions) (reg exp)) + (save continue) + (goto (label ev-sequence)))) + +(define ev-sequence + '(ev-sequence + (assign exp (op first-exp) (reg unev)) + (test (op last-exp?) (reg unev)) + (branch (label ev-sequence-last-exp)) + (save unev) + (save env) + (assign continue (label ev-sequence-continue)) + (goto (label eval-dispatch)) + ev-sequence-continue + (restore env) + (restore unev) + (assign unev (op rest-exps) (reg unev)) + (goto (label ev-sequence)) + ev-sequence-last-exp + (restore continue) + (goto (label eval-dispatch)))) + +(define (no-more-exps? seq) (null? seq)) + +(define ev-sequence-bad + '(ev-sequence + (test (op no-more-exps?) (reg unev)) + (branch (label ev-sequence-end)) + (assign exp (op first-exp) (reg unev)) + (save unev) + (save env) + (assign continue (label ev-sequence-continue)) + (goto (label eval-dispatch)) + ev-sequence-continue + (restore env) + (restore unev) + (assign unev (op rest-exps) (reg unev)) + (goto (label ev-sequence)) + ev-sequence-end + (restore continue) + (goto (reg continue)))) + +(define ev-if + '(ev-if + (save exp) + (save env) + (save continue) + (assign continue (label ev-if-decide)) + (assign exp (op if-predicate) (reg exp)) + (goto (label eval-dispatch)) + ev-if-decide + (restore continue) + (restore env) + (restore exp) + (test (op true?) (reg val)) + (branch (label ev-if-consequent)) + ev-if-alternative + (assign exp (op if-alternative) (reg exp)) + (goto (label eval-dispatch)) + ev-if-consequent + (assign exp (op if-consequent) (reg exp)) + (goto (label eval-dispatch)))) + +(define ev-assignment + '(ev-assignment + (assign unev (op assignment-variable) (reg exp)) + (save unev) + (assign exp (op assignment-value) (reg exp)) + (save env) + (save continue) + (assign continue (label ev-assignment-1)) + (goto (label eval-dispatch)) + ev-assignment-1 + (restore continue) + (restore env) + (restore unev) + (perform (op set-variable-value!) (reg unev) (reg val) (reg env)) + (assign val (const ok)) + (goto (reg continue)))) + +(define ev-definition + '(ev-definition + (assign unev (op definition-variable) (reg exp)) + (save unev) + (assign exp (op definition-value) (reg exp)) + (save env) + (save continue) + (assign continue (label ev-definition-1)) + (goto (label eval-dispatch)) + ev-definition-1 + (restore continue) + (restore env) + (restore unev) + (perform (op define-variable!) (reg unev) (reg val) (reg env)) + (assign val (const ok)) + (goto (reg continue)))) + +(define ev-cond + '(ev-cond + (assign exp (op cond->if) (reg exp)) + (goto (label eval-dispatch)))) + +(define ev-let + '(ev-let + (assign exp (op let->combination) (reg exp)) + (goto (label eval-dispatch)))) + +(define read-eval-print-loop + '( (assign compapp (label compound-apply)) + (branch (label external-entry)) + read-eval-print-loop + (perform (op initialize-stack)) + (perform (op prompt-for-input) (const ";;; EC-Eval input:")) + (assign exp (op read)) + (assign env (op get-global-environment)) + (assign continue (label print-result)) + (goto (label eval-dispatch)) + print-result + (perform (op print-stack-statistics)) + (perform (op announce-output) (const ";;; EC-Eval value:")) + (perform (op user-print) (reg val)) + (goto (label read-eval-print-loop)) + external-entry + (perform (op initialize-stack)) + (assign env (op get-global-environment)) + (assign continue (label print-result)) + (goto (reg val)))) + +(define errors + '(unknown-expression-type + (assign val (const unknown-expression-type-error)) + (goto (label signal-error)) + unknown-procedure-type + (restore continue) + (assign val (const unknown-procedure-type-error)) + (goto (label signal-error)) + signal-error + (perform (op user-print) (reg val)) + (goto (label read-eval-print-loop)))) + +(define errors-2 + '(unknown-expression-type + (assign val (const unknown-expression-type-error)) + (goto (label signal-error)) + unknown-procedure-type + (restore continue) + (assign val (const unknown-procedure-type-error)) + (goto (label signal-error)) + signal-error + (perform (op user-print) (reg val)) + (goto (label read-compile-execute-print-loop)))) + +(#%provide eceval-controller) +(define eceval-controller + (flatten + (list + read-eval-print-loop + eval-dispatch + ev-self-eval + ev-variable + ev-quoted + ev-lambda + ev-application + apply-dispatch + ev-begin + ev-cond + ev-let + ev-sequence + ev-if + ev-assignment + ev-definition + errors))) + +(define eccompile-controller + (flatten + (list + read-compile-execute-print-loop + eval-dispatch + ev-self-eval + ev-variable + ev-quoted + ev-lambda + ev-application + apply-dispatch + ev-begin + ev-cond + ev-let + ev-sequence + ev-if + ev-assignment + ev-definition + errors-2))) + +;; syntax procedures (define (self-evaluating? exp) (cond @@ -659,3 +1202,870 @@ (map binding-var (let-bindings exp)) (let-body exp))) (map binding-exp (let-bindings exp))))) + +;; run-time procedures + +(define (true? x) + (not (eq? x false))) + +(define (false? x) + (eq? x false)) + +(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)) + +(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)))) + +(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)) + +(define (set-variable-value! var val env) + (define (env-loop env) + (define (scan vars vals) + (cond + ((null? vars) + (env-loop (enclosing-environment env))) + ((eq? var (car vars)) + (set-car! vals val)) + (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)) + +(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)))) + +(define (get-value binding) + (cdr binding)) + +(define (set-value! val) + (lambda (binding) + (set-cdr! binding val))) + +(define (setup-environment) + (let + ((initial-env + (extend-environment + (primitive-procedure-names) + (primitive-procedure-objects) + the-empty-environment))) + (define-variable! 'true true initial-env) + (define-variable! 'false false initial-env) + initial-env)) + +(define (primitive-procedure? proc) + (tagged-list? proc 'primitive)) + +(define (primitive-implementation proc) (cadr proc)) + +(define (primitive-procedure-names) + (map car primitive-procedures)) + +(define (primitive-procedure-objects) + (map + (lambda (proc) (list 'primitive (cadr proc))) + primitive-procedures)) + +(define (apply-primitive-procedure proc args) + (apply + (primitive-implementation proc) args)) + +(define (prompt-for-input string) + (newline) + (newline) + (display string) + (newline)) + +(define (announce-output string) + (newline) + (display string) + (newline)) + +(define (user-print object) + (cond + ((compound-procedure? object) + (display + (list + 'compound-procedure + (procedure-parameters object) + (procedure-body object) + '<procedure-env>))) + ((compiled-procedure? object) + (display '<compiled-procedure>)) + (else (display object)))) + +; register-machine simulator + +(define (get-contents register) + (register 'get)) + +(define (set-contents! register value) + ((register 'set) value)) + +(define (pop stack) + (stack 'pop)) + +(define (push stack value) + ((stack 'push) value)) + +(define (make-instruction text) + (list text false '())) + +(define (instruction-text inst) + (car inst)) + +(define (instruction-label inst) + (cadr inst)) + +(define (instruction-execution-proc inst) + (caddr inst)) + +(define (set-instruction-label! inst label) + (set-cdr! inst (cons label (cddr inst)))) + +(define (set-instruction-execution-proc! inst proc) + (set-cdr! (cdr inst) (list proc))) + +(define (make-new-machine) + (let + ((pc (make-register 'pc)) + (flag (make-register 'flag)) + (stack (make-statck)) + (the-instruction-sequence '()) + (instructions-used '()) + (entry-regs '()) + (stack-regs '()) + (reg-assignments '()) + (inst-count 0) + (trace? false)) + (let + ((the-ops + (list + (list + 'initialize-stack + (lambda () (stack 'initialize))) + (list + 'print-stack-statistics + (lambda () (stack 'print-statistics))))) + (register-table + (list (list 'pc pc) (list 'flag flag)))) + (define (allocate-register name) + (if (assoc name register-table) + (error "Multiply-defined register: " name) + (set! register-table + (cons + (list name (make-register name)) + register-table))) + 'register-allocated) + (define (lookup-register name) + (let ((val (assoc name register-table))) + (if val + (cadr val) + (error "Unknown register:" name)))) + (define (lookup-register- name) + (let ((val (assoc name register-table))) + (if val + (cadr val) + (begin + (allocate-register name) + (lookup-register name))))) + (define (execute) + (let ((insts (get-contents pc))) + (if (null? insts) + 'done + (begin + (if trace? + (begin + (if (instruction-label (car insts)) + (begin + (display (instruction-label (car insts))) + (newline))) + (display " ") + (display (instruction-text (car insts))) + (newline))) + ((instruction-execution-proc (car insts))) + (set! inst-count (+ inst-count 1)) + (execute))))) + (define (write-datapath-info insts) + (set! instructions-used (list-insts insts)) + (set! entry-regs + (dedup + (map + cadr + (filter + (lambda (x) (eq? (car x) 'reg)) + (map + goto-dest + (filter + (lambda (inst) (eq? (car inst) 'goto)) + instructions-used)))))) + (set! stack-regs + (dedup + (map + stack-inst-reg-name + (filter + (lambda (inst) + (or + (eq? (car inst) 'save) + (eq? (car inst) 'restore))) + instructions-used)))) + (set! reg-assignments + (map + (lambda (r) + (list + r + (map + assign-value-exp + (filter + (lambda (assign-inst) + (eq? (assign-reg-name assign-inst) r)) + (filter + (lambda (inst) (eq? (car inst) 'assign)) + instructions-used))))) + (map car register-table))) + 'done) + (define (dispatch message) + (cond + ((eq? message 'start) + (set-contents! pc the-instruction-sequence) + (execute)) + ((eq? message 'execute) (execute)) + ((eq? message 'install-instruction-sequence) + (lambda (seq) (set! the-instruction-sequence seq))) + ((eq? message 'allocate-register) allocate-register) + ((eq? message 'get-register) lookup-register) + ((eq? message 'get-register-alloc) lookup-register-) + ((eq? message 'install-operations) + (lambda (ops) (set! the-ops (append the-ops ops)))) + ((eq? message 'stack) stack) + ((eq? message 'instructions-used) instructions-used) + ((eq? message 'entry-regs) entry-regs) + ((eq? message 'stack-regs) stack-regs) + ((eq? message 'reg-assigns) reg-assignments) + ((eq? message 'operations) the-ops) + ((eq? message 'write-info) write-datapath-info) + ((eq? message 'reset-inst-count) + (display (list inst-count 'instructions 'executed)) + (newline) + (set! inst-count 0)) + ((eq? message 'trace-on) (set! trace? true)) + ((eq? message 'trace-off) (set! trace? false)) + (else (error "Unknown request -- MACHINE" message)))) + dispatch))) + +(#%provide start) +(define (start machine) + (machine 'start)) + +(define (get-register machine reg-name) + ((machine 'get-register) reg-name)) + +(define (get-register-contents machine register-name) + (get-contents (get-register machine register-name))) + +(define (set-register-contents! machine register-name value) + (set-contents! (get-register machine register-name) value) + 'done) + +(#%provide make-machine) +(define (make-machine ops controller-text) + (let ((machine (make-new-machine))) + ((machine 'install-operations) ops) + ((machine 'install-instruction-sequence) + (assemble controller-text machine)) + ((machine 'write-info) controller-text) + machine)) + +(define (make-label-entry label-name insts) + (cons label-name insts)) + +(define (lookup-label labels label-name) + (let ((val (assoc label-name labels))) + (if val + (cdr val) + (error "Undefined label -- ASSEMBLE" label-name)))) + +(define (extract-labels text receive) + (if (null? text) + (receive '() '()) + (extract-labels + (cdr text) + (lambda (insts labels) + (let ((next-inst (car text))) + (if (symbol? next-inst) + (if (assoc next-inst labels) + (error "Duplicate label -- ASSEMBLE" next-inst) + (begin + (if (not (null? insts)) + (set-instruction-label! (car insts) next-inst)) + (receive + insts + (cons + (make-label-entry next-inst insts) + labels)))) + (receive + (cons + (make-instruction next-inst) + insts) + labels))))))) + +(define (update-insts! insts labels machine) + (let + ((pc (get-register machine 'pc)) + (flag (get-register machine 'flag)) + (stack (machine 'stack)) + (ops (machine 'operations))) + (for-each + (lambda (inst) + (set-instruction-execution-proc! + inst + (make-execution-procedure + (instruction-text inst) + labels + machine + pc + flag + stack + ops))) + insts))) + +(define (assemble controller-text machine) + (extract-labels + controller-text + (lambda (insts labels) + (update-insts! insts labels machine) + insts))) + +(define (assemble-eccompile controller-text) + (extract-labels + controller-text + (lambda (insts labels) + (update-insts! insts labels eccompile) + insts))) + +(define primitive-procedures + (list + (list 'car car) + (list 'cdr cdr) + (list 'cons cons) + (list 'null? null?) + (list 'number? number?) + (list '+ +) + (list '- -) + (list '* *) + (list '/ /) + (list '> >) + (list '< <) + (list '= =) + (list '<= <=) + (list '>= >=) + (list 'compile-and-run compile-and-run))) + + +(define (make-execution-procedure + inst + labels + machine + pc + flag + stack + ops) + (cond + ((eq? (car inst) 'assign) + (make-assign inst machine labels ops pc)) + ((eq? (car inst) 'test) + (make-test inst machine labels ops flag pc)) + ((eq? (car inst) 'branch) + (make-branch inst machine labels flag pc)) + ((eq? (car inst) 'goto) + (make-goto inst machine labels pc)) + ((eq? (car inst) 'save) + (make-save inst machine stack pc)) + ((eq? (car inst) 'restore) + (make-restore inst machine stack pc)) + ((eq? (car inst) 'perform) + (make-perform inst machine labels ops pc)) + (else + (error "Unknown instruction type -- ASSEMBLE" inst)))) + +(define (make-assign inst machine labels operations pc) + (let + ((target + (get-register-alloc machine (assign-reg-name inst))) + (value-exp (assign-value-exp inst))) + (let + ((value-proc + (if (operation-exp? value-exp) + (make-operation-exp + value-exp + machine + labels + operations) + (make-primitive-exp + (car value-exp) + machine + labels)))) + (lambda () + (set-contents! target (value-proc)) + (advance-pc pc))))) + +(define (assign-reg-name assign-instruction) + (cadr assign-instruction)) + +(define (assign-value-exp assign-instruction) + (cddr assign-instruction)) + +(define (advance-pc pc) + (set-contents! pc (cdr (get-contents pc)))) + +(define (make-test inst machine labels operations flag pc) + (let ((condition (test-condition inst))) + (if (operation-exp? condition) + (let + ((condition-proc + (make-operation-exp + condition + machine + labels + operations))) + (lambda () + (set-contents! flag (condition-proc)) + (advance-pc pc))) + (error "Bad TEST instruction -- ASSEMBLE" inst)))) + +(define (test-condition test-instruction) + (cdr test-instruction)) + +(define (make-branch inst machine labels flag pc) + (let ((dest (branch-dest inst))) + (if (label-exp? dest) + (let ((insts (lookup-label labels (label-exp-label dest)))) + (lambda () + (if (get-contents flag) + (set-contents! pc insts) + (advance-pc pc)))) + (error "Bad BRANCH instruction -- ASSEMBLE" inst)))) + +(define (branch-dest branch-instruction) + (cadr branch-instruction)) + +(define (make-goto inst machine labels pc) + (let ((dest (goto-dest inst))) + (cond + ((label-exp? dest) + (let + ((insts (lookup-label labels (label-exp-label dest)))) + (lambda () (set-contents! pc insts)))) + ((register-exp? dest) + (let + ((reg (get-register-alloc machine (register-exp-reg dest)))) + (lambda () (set-contents! pc (get-contents reg))))) + (else (error "Bad GOTO instruction -- ASSEMBLE" inst))))) + +(define (goto-dest goto-instruction) + (cadr goto-instruction)) + +(define (make-save inst machine stack pc) + (let + ((reg + (get-register-alloc + machine + (stack-inst-reg-name inst)))) + (lambda () + (push stack (get-contents reg)) + (advance-pc pc)))) + +(define (make-restore inst machine stack pc) + (let + ((reg + (get-register-alloc + machine + (stack-inst-reg-name inst)))) + (lambda () + (set-contents! reg (pop stack)) + (advance-pc pc)))) + +(define (stack-inst-reg-name stack-instruction) + (cadr stack-instruction)) + +(define (make-perform inst machine labels operations pc) + (let ((action (perform-action inst))) + (if (operation-exp? action) + (let + ((action-proc + (make-operation-exp + action + machine + labels + operations))) + (lambda () + (action-proc) + (advance-pc pc))) + (error "Bad PERFORM instruction -- ASSEMBLE" inst)))) + +(define (perform-action inst) (cdr inst)) + +(define (make-primitive-exp exp machine labels) + (cond + ((constant-exp? exp) + (let ((c (constant-exp-value exp))) + (lambda () c))) + ((label-exp? exp) + (let ((insts (lookup-label labels (label-exp-label exp)))) + (lambda () insts))) + ((register-exp? exp) + (let ((r (get-register-alloc machine (register-exp-reg exp)))) + (lambda () (get-contents r)))) + (else + (error "Unknown expression type -- ASSEMBLE" exp)))) + +(define (register-exp? exp) (tagged-list? exp 'reg)) + +(define (register-exp-reg exp) (cadr exp)) + +(define (constant-exp? exp) (tagged-list? exp 'const)) + +(define (constant-exp-value exp) (cadr exp)) + +(define (label-exp? exp) (tagged-list? exp 'label)) + +(define (label-exp-label exp) (cadr exp)) + +(define (make-operation-exp exp machine labels operations) + (let + ((op (lookup-prim (operation-exp-op exp) operations)) + (aprocs + (map + (lambda (e) + (make-primitive-exp e machine labels)) + (operation-exp-operands exp)))) + (lambda () + (apply op (map (lambda (p) (p)) aprocs))))) + +(define (operation-exp? exp) + (and (pair? exp) (tagged-list? (car exp) 'op))) + +(define (operation-exp-op operation-exp) + (cadr (car operation-exp))) + +(define (operation-exp-operands operation-exp) + (cdr operation-exp)) + +(define (lookup-prim symbol operations) + (let ((val (assoc symbol operations))) + (if val + (cadr val) + (error "Unknown operation -- ASSEMBLE" symbol)))) + +(define (sort-insts insts) + (define (number inst) + (cond + ((symbol? inst) 0) + ((eq? (car inst) 'assign) 1) + ((eq? (car inst) 'test) 2) + ((eq? (car inst) 'branch) 3) + ((eq? (car inst) 'goto) 4) + ((eq? (car inst) 'save) 5) + ((eq? (car inst) 'restore) 6) + ((eq? (car inst) 'perform) 7) + (else 8))) + (define (compare inst1 inst2) + (cond + ((< (number inst1) (number inst2)) 'LT) + ((= (number inst1) (number inst2)) 'EQ) + (else 'GT))) + (define (split xs) + (cond + ((null? xs) (list '() '())) + ((null? (cdr xs)) (list xs '())) + (else + (let ((rest-parted (split (cddr xs)))) + (list + (cons (car xs) (car rest-parted)) + (cons (cadr xs) (cadr rest-parted))))))) + (define (merge insts1 insts2) + (cond + ((null? insts1) insts2) + ((null? insts2) insts1) + (else + (let + ((i1 (car insts1)) + (i2 (car insts2))) + (cond + ((eq? (compare i1 i2) 'LT) + (cons i1 (merge (cdr insts1) insts2))) + ((eq? (compare i1 i2) 'EQ) + (cons i1 (cons i2 (merge (cdr insts1) (cdr insts2))))) + (else + (cons i2 (merge insts1 (cdr insts2))))))))) + (if (or (null? insts) (null? (cdr insts))) + insts + (let ((parted (split insts))) + (merge + (sort-insts (car parted)) + (sort-insts (cadr parted)))))) + +(define (filter pred xs) + (cond + ((null? xs) '()) + ((pred (car xs)) + (cons (car xs) (filter pred (cdr xs)))) + (else + (filter pred (cdr xs))))) + +(define (nub xs) + (cond + ((null? xs) '()) + ((null? (cdr xs)) xs) + ((equal? (car xs) (cadr xs)) + (nub (cons (car xs) (cddr xs)))) + (else + (cons (car xs) (nub (cdr xs)))))) + +(define (list-insts insts) + (nub + (filter + (lambda (x) (not (symbol? x))) + (sort-insts insts)))) + +(define (dedup syms) + (cond + ((null? syms) '()) + ((memq (car syms) (cdr syms)) + (dedup (cdr syms))) + (else (cons (car syms) (dedup (cdr syms)))))) + +(define (get-register-alloc machine reg-name) + ((machine 'get-register-alloc) reg-name)) + +(define (make-statck) + (let + ((s '()) + (number-pushes 0) + (max-depth 0) + (current-depth 0)) + (define (push x) + (set! s (cons x s)) + (set! number-pushes (+ 1 number-pushes)) + (set! current-depth (+ 1 current-depth)) + (set! max-depth (max current-depth max-depth))) + (define (pop) + (if (null? s) + (error "Empty statck -- POP") + (let ((top (car s))) + (set! s (cdr s)) + (set! current-depth (- current-depth 1)) + top))) + (define (initialize) + (set! s '()) + (set! number-pushes 0) + (set! max-depth 0) + (set! current-depth 0) + 'done) + (define (print-statistics) + (display + (list + 'total-pushes '= number-pushes + 'maximum-depth '= max-depth)) + (newline)) + (define (dispatch message) + (cond + ((eq? message 'push) push) + ((eq? message 'pop) (pop)) + ((eq? message 'initialize) (initialize)) + ((eq? message 'print-statistics) + (print-statistics)) + (else + (error "Unknown request -- STATCK" message)))) + dispatch)) + +(define (make-register name) + (let + ((contents '*unassigned*) + (trace? false)) + (define (dispatch message) + (cond + ((eq? message 'get) contents) + ((eq? message 'set) + (lambda (value) + (if trace? + (begin + (display + (list + name + 'old-contents '= contents + 'new-contents '= value)) + (newline))) + (set! contents value))) + ((eq? message 'trace-on) (set! trace? true)) + ((eq? message 'trace-off) (set! trace? false)) + (else + (error "Unknown request -- REGISTER" message)))) + dispatch)) + +(#%provide trace-reg) +(define (trace-reg machine reg-name on?) + (let ((reg (get-register machine reg-name))) + (cond + ((eq? on? 'on) (reg 'trace-on)) + ((eq? on? 'off) (reg 'trace-off)) + (else "Unknown request - TRACE-REG" on?)))) + +(define the-global-environment (setup-environment)) + +(define (get-global-environment) the-global-environment) + +(define (start-eceval) + (set! the-global-environment (setup-environment)) + (set-register-contents! eceval 'flag false) + (start eceval)) + +(#%provide eceval-operations) +(define eceval-operations + (list + (list 'adjoin-arg adjoin-arg) + (list 'announce-output announce-output) + (list 'application? application?) + (list 'apply-primitive-procedure apply-primitive-procedure) + (list 'assignment? assignment?) + (list 'assignment-value assignment-value) + (list 'assignment-variable assignment-variable) + (list 'begin? begin?) + (list 'begin-actions begin-actions) + (list 'compiled-procedure? compiled-procedure?) + (list 'compiled-procedure-entry compiled-procedure-entry) + (list 'compiled-procedure-env compiled-procedure-env) + (list 'compound-procedure? compound-procedure?) + (list 'cond? cond?) + (list 'cond->if cond->if) + (list 'define-variable! define-variable!) + (list 'definition? definition?) + (list 'definition-value definition-value) + (list 'definition-variable definition-variable) + (list 'empty-arglist empty-arglist) + (list 'extend-environment extend-environment) + (list 'false? false?) + (list 'first-exp first-exp) + (list 'first-operand first-operand) + (list 'get-global-environment get-global-environment) + (list 'if? if?) + (list 'if-alternative if-alternative) + (list 'if-consequent if-consequent) + (list 'if-predicate if-predicate) + (list 'lambda? lambda?) + (list 'lambda-body lambda-body) + (list 'lambda-parameters lambda-parameters) + (list 'last-exp? last-exp?) + (list 'last-operand? last-operand?) + (list 'let? let?) + (list 'let->combination let->combination) + (list 'lookup-variable-value lookup-variable-value) + (list 'make-compiled-procedure make-compiled-procedure) + (list 'make-procedure make-procedure) + (list 'no-more-exps? no-more-exps?) + (list 'no-operands? no-operands?) + (list 'operands operands) + (list 'operator operator) + (list 'primitive-procedure? primitive-procedure?) + (list 'procedure-body procedure-body) + (list 'procedure-environment procedure-environment) + (list 'procedure-parameters procedure-parameters) + (list 'prompt-for-input prompt-for-input) + (list 'quoted? quoted?) + (list 'read read) + (list 'rest-exps rest-exps) + (list 'rest-operands rest-operands) + (list 'self-evaluating? self-evaluating?) + (list 'set-variable-value! set-variable-value!) + (list 'text-of-quotation text-of-quotation) + (list 'true? true?) + (list 'user-print user-print) + (list 'variable? variable?) + (list 'list list) + (list 'cons cons) + (list 'compile compile) + (list 'assemble-eccompile assemble-eccompile) + (list 'statements statements) + (list '= =) + (list '* *) + (list '- -) + (list '+ +))) + +(define eceval (make-machine eceval-operations eceval-controller)) + +(#%provide eccompile) +(define eccompile (make-machine eceval-operations eccompile-controller)) + +(#%provide compile-and-go) +(define (compile-and-go expression) + (let + ((instructions + (assemble + (statements + (compile expression 'val 'return)) + eceval))) + (set! the-global-environment (setup-environment)) + (set-register-contents! eceval 'val instructions) + (set-register-contents! eceval 'flag true) + #| (eceval 'trace-on) |# + #| (trace-reg eceval 'proc 'on) |# + (start eceval))) |