SICP 読み (259) 5.2 レジスタ計算機での計算
問題 5.11 の c.
昨晩のハマりどころはレジスタのアクセスに get-register でなく、その中の手続きを使って試験を書いている箇所が多すぎたため、と思われます。抽象化してるんだから一回試験にパスしたんだったら、その手続きを使えよ、と。(とほほほ
面倒臭すぎて死にそうだった。
最初は試験の前面書き直しも考えていたのですが、ヤメ。以下に解とおぼしき手続きとその試験をサラしておきます。まず本体から。
(define (make-machine register-names ops controller-text) (let ((machine (make-new-machine))) (for-each (lambda (register-name) ((machine 'allocate-register) register-name)) register-names) ((machine 'install-operations) ops) ((machine 'install-instruction-sequence) (assemble controller-text machine)) machine)) (define (make-register name) (let ((contents '*unassigned*)) (define (dispatch message) (cond ((eq? message 'get) contents) ((eq? message 'set) (lambda (value) (set! contents value))) (else (error "Unknown request -- REGISTER" message)))) dispatch)) (define (get-contents register) (register 'get)) (define (set-contents! register value) ((register 'set) value)) ;;**original (unmonitored) version from section 5.2.1 (define (make-stack) (let ((s '())) (define (push x) (set! s (cons x s))) (define (pop) (if (null? s) (error "Empty stack -- POP") (let ((top (car s))) (set! s (cdr s)) top))) (define (initialize) (set! s '()) 'done) (define (dispatch message) (cond ((eq? message 'push) push) ((eq? message 'pop) (pop)) ((eq? message 'initialize) (initialize)) (else (error "Unknown request -- STACK" message)))) dispatch)) (define (pop stack) (stack 'pop)) (define (push stack value) ((stack 'push) value)) ;;**monitored version from section 5.2.4 (define (make-stack) (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 stack -- 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) (newline) (display (list 'total-pushes '= number-pushes 'maximum-depth '= max-depth))) (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 -- STACK" message)))) dispatch)) (define (make-new-machine) (let ((pc (make-register 'pc)) (flag (make-register 'flag)) (the-instruction-sequence '())) (let ((the-ops (list (list 'initialize-stack (lambda () (stack 'initialize))) ;;**next for monitored stack (as in section 5.2.4) ;; -- comment out if not wanted (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) (make-stack)) register-table))) 'register-allocated) (define (lookup-register sw name) (let ((val (assoc name register-table))) (if val (if (eq? sw 'reg) (cadr val) (caddr val)) (error "Unknown register:" name)))) (define (execute) (let ((insts (get-contents pc))) (if (null? insts) 'done (begin ((instruction-execution-proc (car insts))) (execute))))) (define (dispatch message) (cond ((eq? message 'start) (set-contents! pc the-instruction-sequence) (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 'install-operations) (lambda (ops) (set! the-ops (append the-ops ops)))) ; ((eq? message 'stack) stack) ((eq? message 'operations) the-ops) (else (error "Unknown request -- MACHINE" message)))) dispatch))) (define (start machine) (machine 'start)) (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) (define (get-register machine reg-name) ((machine 'get-register) 'reg reg-name)) (define (get-stack machine reg-name) ((machine 'get-register) 'stack reg-name)) (define (assemble controller-text machine) (extract-labels controller-text (lambda (insts labels) (update-insts! insts labels machine) insts))) (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 "Dup label entry -- ASSEMBLE" 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)) (ops (machine 'operations))) (for-each (lambda (inst) (set-instruction-execution-proc! inst (make-execution-procedure (instruction-text inst) labels machine pc flag ops))) insts))) (define (make-instruction text) (cons text '())) (define (instruction-text inst) (car inst)) (define (instruction-execution-proc inst) (cdr inst)) (define (set-instruction-execution-proc! inst proc) (set-cdr! inst proc)) (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 (make-execution-procedure inst labels machine pc flag 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 pc)) ((eq? (car inst) 'restore) (make-restore inst machine 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 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 () ; execution procedure for assign (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 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 pc) (let ((reg (get-register machine (stack-inst-reg-name inst))) (stack (get-stack machine (stack-inst-reg-name inst)))) (lambda () (push stack (get-contents reg)) (advance-pc pc)))) (define (make-restore inst machine pc) (let ((reg (get-register machine (stack-inst-reg-name inst))) (stack (get-stack 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 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) (if (label-exp? e) (error "cannot place label element -- ASSEMBLE" 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)))) ;; from 4.1 (define (tagged-list? exp tag) (if (pair? exp) (eq? (car exp) tag) #f))
以降、三点ほど試験を。
(use gauche.test) (add-load-path ".") (load "ch5-regsim") (test-start "ch5-regsim") (test-section "register (make & set)") (let ((reg (make-register 'a))) (test* "nonexistent reg" *test-error* (reg 'xxx)) (test* "unassigned" '*unassigned* (reg 'get)) (test* "unassigned" '*unassigned* (get-contents reg))) (test-section "register (get & set)") (let ((reg (make-register 'a))) ((reg 'set) 1) (test* "return 1" 1 (reg 'get)) (test* "return 1" 1 (get-contents reg)) (set-contents! reg 2) (test* "return 2" 2 (reg 'get)) (test* "return 2" 2 (get-contents reg))) (test-section "stack (make & pop)") (let ((s (make-stack))) (test* "stack is null" *test-error* (s 'pop))) (test-section "stack (posh & pop)") (let ((s (make-stack))) ((s 'push) 1) (test* "return 1" 1 (s 'pop)) (test* "stack is null" *test-error* (s 'pop)) ((s 'push) 2) ((s 'push) 1) (test* "return 1" 1 (s 'pop)) (test* "return 2" 2 (s 'pop)) (test* "stack is null" *test-error* (s 'pop))) (test-section "stack (initialize)") (let ((s (make-stack))) ((s 'push) 1) (s 'initialize) (test* "stack is null" *test-error* (s 'pop))) (test-section "stack (push & pop (2))") (let ((s (make-stack))) (push s 1) (push s 2) (test* "return 2" 2 (s 'pop)) (test* "return 1" 1 (s 'pop)) (test* "stack is null" *test-error* (s 'pop))) (test-section "make-instruction") (test* "return (1 ())" '(1) (make-instruction 1)) (test-section "instruction-text") (test* "return car" 1 (instruction-text '(1 2))) (test-section "instruction-execution-proc") (test* "return cdr" 2 (instruction-execution-proc '(1 . 2))) (test-section "set-instruction-execution-proc!") (let ((l '(cons 1 '()))) (set-instruction-execution-proc! l 1) (test* "set-cdr!" 1 (cdr l))) (test-section "make-label-entry") (test* "(label . inst)" '(1 . 2) (make-label-entry 1 2)) (test-section "lookup-label") (let ((labels (cons (make-label-entry 1 2) '()))) (test* "lookup failure" *test-error* (lookup-label labels 3)) (test* "return cdr" 2 (lookup-label labels 1))) (test-section "assign-reg-name") (let ((l1 '(assign n (op -) (reg n) (const 1))) (l2 '(assign continue (label afterfib-n-1)))) (test* "register name is n" 'n (assign-reg-name l1)) (test* "register name is continue" 'continue (assign-reg-name l2))) (test-section "assign-value-exp") (let ((l1 '(assign n (op -) (reg n) (const 1))) (l2 '(assign continue (label afterfib-n-1)))) (test* "return exp" '((op -) (reg n) (const 1)) (assign-value-exp l1)) (test* "return exp" '((label afterfib-n-1)) (assign-value-exp l2))) (test-section "operation-exp?") (let ((l1 '(assign n (op -) (reg n) (const 1))) (l2 '(assign continue (label afterfib-n-1)))) (test* "operation exp" #t (operation-exp? (assign-value-exp l1))) (test* "not operation exp" #f (operation-exp? (assign-value-exp l2)))) (test-section "operation-exp-op") (let ((l1 '(assign n (op -) (reg n) (const 1)))) (test* "operation is -" '- (operation-exp-op (assign-value-exp l1)))) (test-section "operation-exp-operands") (let ((l1 '(assign n (op -) (reg n) (const 1)))) (test* "operation input" '((reg n) (const 1)) (operation-exp-operands (assign-value-exp l1)))) (test-section "test-condition") (let ((l '(test (op <) (reg n) (const 2)))) (test* "condition exp" '((op <) (reg n) (const 2)) (test-condition l)) (test* "operation exp" #t (operation-exp? (test-condition l)))) (test-section "register-exp?") (test* "register exp" #t (register-exp? '(reg n))) (test* "not register exp" #f (register-exp? '(const 1))) (test* "not register exp" #f (register-exp? '(label afterfib-n-1))) (test-section "register-exp-reg") (test* "get register's name" 'n (register-exp-reg '(reg n))) (test-section "constant-exp?") (test* "constant exp" #t (constant-exp? '(const 1))) (test* "not constant exp" #f (constant-exp? '(reg n))) (test* "not constant exp" #f (constant-exp? '(label afterfib-n-1))) (test-section "constant-exp-value") (test* "constant exp value" 1 (constant-exp-value '(const 1))) (test-section "label-exp?") (test* "label exp" #t (label-exp? '(label afterfib-n-1))) (test* "not label exp" #f (label-exp? '(reg n))) (test* "not label exp" #f (label-exp? '(const 1))) (test-section "label-exp-label") (test* "label value" 'afterfib-n-1 (label-exp-label '(label afterfib-n-1))) (test-section "branch-dest") (let ((l '(branch (label immediate-answer)))) (test* "branch destination" '(label immediate-answer) (branch-dest l)) (test* "label" #t (label-exp? (branch-dest l)))) (test-section "goto-dest") (let ((l '(goto (reg continue)))) (test* "goto destination" '(reg continue) (goto-dest l))) (test-section "stack-inst-reg-name") (let ((l1 '(save val)) (l2 '(restore val))) (test* "save register name" 'val (stack-inst-reg-name l1)) (test* "restore register name" 'val (stack-inst-reg-name l2))) (test-section "perform-action") (let ((l1 '(perform (op print) (reg a)))) (test* "perform action exp" '((op print) (reg a)) (perform-action l1))) (test-section "make-new-machine") (let ((m (make-new-machine))) (test* "pc register is null" '*unassigned* (((m 'get-register) 'reg 'pc) 'get)) (test* "pc register is null" '*unassigned* (get-contents ((m 'get-register) 'reg 'pc))) (test* "flag register is null" '*unassigned* (((m 'get-register) 'reg 'flag) 'get)) (test* "flag register is null" '*unassigned* (get-contents ((m 'get-register) 'reg 'flag)))) (test-section "hand make-machine") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) (test* "register a is null" '*unassigned* (get-contents ((m 'get-register) 'reg 'a))) (test* "register b is null" '*unassigned* (get-contents ((m 'get-register) 'reg 'b))) (test* "register t is null" '*unassigned* (get-contents ((m 'get-register) 'reg 't))) (test* "stack is null" *test-error* ((get-stack m 'a) 'pop)) (test* "1st operation" 'initialize-stack (car (car (m 'operations)))) (test* "2nd operation" 'print-stack-statistics (car (cadr (m 'operations)))) ((m 'install-operations) (list (list 'rem remainder) (list '= =))) (test* "3rd operation" 'rem (car (caddr (m 'operations)))) (test* "4th operation" '= (car (cadddr (m 'operations))))) (test-section "label") (let ((m (make-new-machine)) (l '(test-b (test (op =) (reg b) (const 0)) (branch (label gcd-done)) (assign t (op rem) (reg a) (reg b)) (assign a (reg b)) (assign b (reg t)) (goto (label test-b)) gcd-done))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) ((m 'install-operations) (list (list 'rem remainder) (list '= =))) (extract-labels l (lambda (insts labels) (test* "1st labels name" 'test-b (car (car labels))) (test* "1st labels value" '(((test (op =) (reg b) (const 0))) ((branch (label gcd-done))) ((assign t (op rem) (reg a) (reg b))) ((assign a (reg b))) ((assign b (reg t))) ((goto (label test-b)))) (cdr (car labels))) (test* "2nd labels name" 'gcd-done (car (cadr labels))) (test* "2nd labels value" '() (cdr (cadr labels)))))) (test-section "(assign <register-name> (reg <register-name>))") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) ((m 'install-operations) (list (list 'rem remainder) (list '= =))) (set-contents! ((m 'get-register) 'reg 'a) 1) (test* "value of register a" 1 (get-contents ((m 'get-register) 'reg 'a))) (let ((ope (make-assign '(assign b (reg a)) m '() (m 'operations) ((m 'get-register) 'reg 'pc)))) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (ope) (test* "value of register b" 1 (get-contents ((m 'get-register) 'reg 'b)))) (test* "undefined register" *test-error* (make-assign '(assign c (reg a)) m '() (m 'operations) ((m 'get-register) 'reg 'pc))) (test* "undefined register" *test-error* (make-assign '(assign b (reg c)) m '() (m 'operations) ((m 'get-register) 'reg 'pc)))) (test-section "(assign <register-name> (const <constant-value>))") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) ((m 'install-operations) (list (list 'rem remainder) (list '= =))) (test* "undefined register" *test-error* (make-assign '(assign c (const 1)) m '() (m 'operations) ((m 'get-register) 'reg 'pc))) (let ((ope (make-assign '(assign b (const 1)) m '() (m 'operations) ((m 'get-register) 'reg 'pc)))) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (ope) (test* "value of register b" 1 (get-contents ((m 'get-register) 'reg 'b))))) (test-section "(assign <register-name> (label <label-name>))") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) ((m 'install-operations) (list (list 'rem remainder) (list '= =))) (let ((ope (make-assign '(assign b (label gcd-done)) m '((gcd-done . ())) (m 'operations) ((m 'get-register) 'reg 'pc)))) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (ope) (test* "value of register b" '() (get-contents ((m 'get-register) 'reg 'b))) (test* "undefined register" *test-error* (make-assign '(assign d (label gcd-done)) m '((gcd-done . ())) (m 'operations) ((m 'get-register) 'reg 'pc)))) (test* "undefined label" *test-error* (make-assign '(assign b (label x)) m '() (m 'operations) ((m 'get-register) 'reg 'pc)))) (test-section "(assign <register-name> (op <operation-name>) <input1> ... <inputn>)") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) ((m 'install-operations) (list (list 'rem remainder) (list '+ +))) ;; (assign b (op rem) (reg a) (label gcd-done)) ?? (let ((ope (make-assign '(assign a (op +) (reg t) (reg b)) m '() (m 'operations) ((m 'get-register) 'reg 'pc)))) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (set-contents! ((m 'get-register) 'reg 't) 3) (set-contents! ((m 'get-register) 'reg 'b) 2) (test* "value of register t" 3 (get-contents ((m 'get-register) 'reg 't))) (test* "value of register b" 2 (get-contents ((m 'get-register) 'reg 'b))) (test* "register a is null" '*unassigned* (get-contents ((m 'get-register) 'reg 'a))) (ope) (test* "value of register a" 5 (get-contents ((m 'get-register) 'reg 'a)))) (let ((ope (make-assign '(assign a (op rem) (reg a) (const 2)) m '() (m 'operations) ((m 'get-register) 'reg 'pc)))) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (set-contents! ((m 'get-register) 'reg 'a) 3) (ope) (test* "value of register a" 1 (get-contents ((m 'get-register) 'reg 'a)))) (let ((ope (make-assign '(assign a (op +) (const 1) (const 2) (const 3)) m '() (m 'operations) ((m 'get-register) 'reg 'pc)))) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (ope) (test* "value of register a" 6 (get-contents ((m 'get-register) 'reg 'a)))) (test* "undefined register" *test-error* (make-assign '(assign d (op =) (reg a) (reg b)) m '() (m 'operations) ((m 'get-register) 'reg 'pc))) (test* "undefined operation" *test-error* (make-assign '(assign b (op *) (reg a) (reg b)) m '() (m 'operations) ((m 'get-register) 'reg 'pc))) (test* "undefined register" *test-error* (make-assign '(assign b (op rem) (reg d) (reg b)) m '() (m 'operations) ((m 'get-register) 'reg 'pc))) (test* "undefined register" *test-error* (make-assign '(assign b (op rem) (reg a) (reg d)) m '() (m 'operations) ((m 'get-register) 'reg 'pc)))) (test-section "make-primitive (constant)") (let ((m (make-new-machine))) (let ((ope (make-primitive-exp '(const 2) m '()))) (test* "return constant value" 2 (ope)))) (test-section "make-primitive (labal)") (let ((m (make-new-machine))) (let ((ope (make-primitive-exp '(label gcd-done) m '((gcd-done . ()))))) (test* "return label value" '() (ope)) (test* "undefined label" *test-error* (make-primitive-exp '(label test) m '())))) (test-section "make-primitive (register)") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) (set-contents! ((m 'get-register) 'reg 'a) 1) (let ((ope (make-primitive-exp '(reg a) m '()))) (test* "return register value" 1 (ope)) (test* "undefined register" *test-error* (make-primitive-exp '(reg c) m '())))) (test-section "make-operation-exp (+)") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) ((m 'install-operations) (list (list 'rem remainder) (list '+ +))) (set-contents! ((m 'get-register) 'reg 'a) 3) (set-contents! ((m 'get-register) 'reg 'b) 2) (let ((ope1 (make-operation-exp '((ope +) (const 1) (const 2)) m '() (m 'operations))) (ope2 (make-operation-exp '((ope +) (const 1) (const 2) (const 3) (const 4) (const 5)) m '() (m 'operations))) (ope3 (make-operation-exp '((ope +) (reg a) (reg b)) m '() (m 'operations)))) (test* "(+ 1 2)" 3 (ope1)) (test* "(+ 1 2 3 4 5)" 15 (ope2)) (test* "(+ a b)" 5 (ope3)) (test* "undefined operation" *test-error* (make-operation-exp '((ope *) (const 2) (const 3)) m '())) (test* "undefined register" *test-error* (make-operation-exp '((ope +) (reg c) (reg d)) m '())))) (test-section "lookup-prim") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) ((m 'install-operations) (list (list 'rem remainder) (list '+ +))) (test* "undefined ope" *test-error* (lookup-prim '* (m 'operations))) (test* "apply +" 2 (apply (lookup-prim '+ (m 'operations)) '(1 1)))) (test-section "make-branch (error)") (test* "invalid branch" *test-error* (make-branch '(branch (reg a)) '() '() '())) (test-section "make-branch (flag is #t)") (let ((m (make-new-machine)) (l '(test-b (test (op =) (reg b) (const 0)) (branch (label gcd-done)) (assign t (op rem) (reg a) (reg b)) (assign a (reg b)) (assign b (reg t)) (goto (label test-b)) gcd-done))) (extract-labels l (lambda (insts labels) (set-contents! ((m 'get-register) 'reg 'flag) #t) (let ((thunk (make-branch '(branch (label test-b)) m labels ((m 'get-register) 'reg 'flag) ((m 'get-register) 'reg 'pc)))) (thunk) (test* "branch" '(test (op =) (reg b) (const 0)) (car (car (get-contents ((m 'get-register) 'reg 'pc))))))))) (test-section "make-branch (flag is #f)") (let ((m (make-new-machine)) (l '(test-b (test (op =) (reg b) (const 0)) (branch (label gcd-done)) (assign t (op rem) (reg a) (reg b)) (assign a (reg b)) (assign b (reg t)) (goto (label test-b)) gcd-done))) (extract-labels l (lambda (insts labels) (set-contents! ((m 'get-register) 'reg 'flag) #f) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (let ((thunk (make-branch '(branch (label test-b)) m labels ((m 'get-register) 'reg 'flag) ((m 'get-register) 'reg 'pc)))) (thunk) (test* "branch" 'gcd-done (car (car (get-contents ((m 'get-register) 'reg 'pc))))))))) (test-section "make-goto (error)") (test* "cannot jmp" *test-error* (make-goto '(goto (op =)) '() '() '())) (test* "cannot jmp" *test-error* (make-goto '(goto (const 2)) '() '() '())) (test-section "make-goto (label)") (let ((m (make-new-machine)) (l '(test-b (test (op =) (reg b) (const 0)) (branch (label gcd-done)) (assign t (op rem) (reg a) (reg b)) (assign a (reg b)) (assign b (reg t)) (goto (label test-b)) gcd-done))) (extract-labels l (lambda (insts labels) (let ((thunk (make-goto '(goto (label test-b)) m labels ((m 'get-register) 'reg 'pc)))) (thunk) (test* "goto label" '(test (op =) (reg b) (const 0)) (car (car (get-contents ((m 'get-register) 'reg 'pc))))))))) (test-section "make-goto (register)") (let ((m (make-new-machine)) (l '(test-b (test (op =) (reg b) (const 0)) (branch (label gcd-done)) (assign t (op rem) (reg a) (reg b)) (assign a (reg b)) (assign b (reg t)) (goto (label test-b)) gcd-done))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) (extract-labels l (lambda (insts labels) (let ((thunk (make-goto '(goto (reg a)) m labels ((m 'get-register) 'reg 'pc))) (ope (make-assign '(assign a (label test-b)) m labels (m 'operations) ((m 'get-register) 'reg 'pc)))) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (ope) (thunk) (test* "goto register" '(test (op =) (reg b) (const 0)) (car (car (get-contents ((m 'get-register) 'reg 'pc))))))))) (test-section "make-save") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) (set-contents! ((m 'get-register) 'reg 'a) 1) (let ((thunk (make-save '(save a) m ((m 'get-register) 'reg 'pc)))) (test* "stack is null" *test-error* ((get-stack m 'a) 'pop)) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (thunk) (test* "pc is gcd-done" '() (cadr (car (get-contents ((m 'get-register) 'reg 'pc))))) (test* "pop from stack" 1 ((get-stack m 'a) 'pop)) (test* "stack is null" *test-error* ((get-stack m 'a) 'pop)) )) (test-section "make-restore") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) (set-contents! ((m 'get-register) 'reg 'a) 1) (let ((push-thunk (make-save '(save a) m ((m 'get-register) 'reg 'pc))) (pop-thunk (make-restore '(restore b) m ((m 'get-register) 'reg 'pc)))) (test* "stack is null" *test-error* ((get-stack m 'a) 'pop)) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (push-thunk) (test* "pc advanced" '() (cadr (car (get-contents ((m 'get-register) 'reg 'pc))))) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (test* "stack b is null" *test-error* (pop-thunk)))) (test-section "make-machine") (let ((m (make-machine '(a b t) (list (list 'rem remainder) (list '= =)) '(test-b (test (op =) (reg b) (const 0)) (branch (label gcd-done)) (assign t (op rem) (reg a) (reg b)) (assign a (reg b)) (assign b (reg t)) (goto (label test-b)) gcd-done)))) (test* "a" '*unassigned* (get-contents ((m 'get-register) 'reg 'a))) (test* "b" '*unassigned* (get-contents ((m 'get-register) 'reg 'b))) (test* "t" '*unassigned* (get-contents ((m 'get-register) 'reg 't))) (test* "a" '*unassigned* (get-register-contents m 'a)) (test* "b" '*unassigned* (get-register-contents m 'b)) (test* "t" '*unassigned* (get-register-contents m 't)) (set-register-contents! m 'a 206) (set-register-contents! m 'b 40) (test* "a" 206 (get-register-contents m 'a)) (test* "b" 40 (get-register-contents m 'b)) (start m) (test* "a" 2 (get-register-contents m 'a))) (test-section "extract-labels (5.8)") (let ((m (make-new-machine)) (l '(start (goto (label here)) here (assign a (const 3)) (goto (label there)) here (assign a (const 4)) (goto (label there)) there))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a)) (test* "assemble error" *test-error* (assemble l m))) (test-section "make-operation-exp (5.9)") (let ((m (make-new-machine))) ((m 'install-operations) (list (list 'rem remainder) (list '+ +))) (test* "use only reg & const" *test-error* (make-operation-exp '((ope +) (label gcd-done) (const 2)) m '((gcd-done . ())) (m 'operations)))) (test-end)
次は make-* な試験。
(use gauche.test) (add-load-path ".") (load "ch5-regsim") (test-start "make-execution-procedure test") (test-section "assign") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) ((m 'install-operations) (list (list 'rem remainder) (list '= =))) (set-contents! ((m 'get-register) 'reg 'a) 1) (test* "a is 1" 1 (get-contents ((m 'get-register) 'reg 'a))) (let ((thunk (make-execution-procedure '(assign b (reg a)) '() m ((m 'get-register) 'reg 'pc) ((m 'get-register) 'reg 'flag) (m 'operations)))) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (thunk) (test* "b is 1" 1 (get-contents ((m 'get-register) 'reg 'b))))) (test-section "test (1)") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) ((m 'install-operations) (list (list 'rem remainder) (list '= =))) (let ((thunk (make-test '(test (op =) (const 1) (const 1)) m '() (m 'operations) ((m 'get-register) 'reg 'flag) ((m 'get-register) 'reg 'pc)))) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (thunk) (test* "true" #t (get-contents ((m 'get-register) 'reg 'flag))))) (test-section "test (2)") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) ((m 'install-operations) (list (list 'rem remainder) (list '= =))) (let ((thunk (make-execution-procedure '(test (op =) (const 1) (const 1)) '() m ((m 'get-register) 'reg 'pc) ((m 'get-register) 'reg 'flag) (m 'operations)))) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (thunk) (test* "true" #t (get-contents ((m 'get-register) 'reg 'flag))))) (test-section "goto label") (let ((m (make-new-machine)) (l '(test-b (test (op =) (reg b) (const 0)) (branch (label gcd-done)) (assign t (op rem) (reg a) (reg b)) (assign a (reg b)) (assign b (reg t)) (goto (label test-b)) gcd-done))) (extract-labels l (lambda (insts labels) (let ((thunk (make-execution-procedure '(goto (label test-b)) labels m ((m 'get-register) 'reg 'pc) ((m 'get-register) 'reg 'flag) (m 'operations)))) (thunk) (test* "goto test-b" '(test (op =) (reg b) (const 0)) (car (car (get-contents ((m 'get-register) 'reg 'pc))))))))) (test-section "save") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) (set-contents! ((m 'get-register) 'reg 'a) 1) (let ((thunk (make-execution-procedure '(save a) '() m ((m 'get-register) 'reg 'pc) ((m 'get-register) 'reg 'flag) (m 'operations)))) (test* "stack is empty" *test-error* ((m 'stack) 'pop)) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (thunk) (test* "pc is null" '() (cadr (car (get-contents ((m 'get-register) 'reg 'pc))))) (test* "pop" 1 ((get-stack m 'a) 'pop)) (test* "stack is null" *test-error* ((get-stack m 'a) 'pop)))) (test-section "restore") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b t)) (set-contents! ((m 'get-register) 'reg 'a) 1) (let ((push-thunk (make-save '(save a) m ; (m 'stack) ((m 'get-register) 'reg 'pc))) (pop-thunk (make-execution-procedure '(restore b) '() m ((m 'get-register) 'reg 'pc) ((m 'get-register) 'reg 'flag) (m 'operations)))) (test* "stack is empty" *test-error* ((get-stack m 'b) 'pop)) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (push-thunk) (test* "pc is ()" '() (cadr (car (get-contents ((m 'get-register) 'reg 'pc))))) (set-contents! ((m 'get-register) 'reg 'pc) '(() (gcd-done ()))) (test* "stack is empty" *test-error* ((get-stack m 'b) 'pop)))) (test-end)
次が 5.11-c な試験
(use gauche.test) (add-load-path ".") (load "ch5-regsim") (test-start "5.11-c") (test-section "make-new-machine") (let ((m (make-new-machine))) (for-each (lambda (register-name) ((m 'allocate-register) register-name)) '(a b)) (test* "register a allocated" '*unassigned* (get-contents (get-register m 'a))) (test* "register b allocated" '*unassigned* (get-contents (get-register m 'b))) (test* "stack a allocated" #t (not (null? (get-stack m 'a)))) (test* "stack b allocated" #t (not (null? (get-stack m 'b)))) (test* "stack a is null" *test-error* ((get-stack m 'a) 'pop)) (test* "stack b is null" *test-error* ((get-stack m 'b) 'pop)) (let ((push-a-thunk (make-save '(save a) m ((m 'get-register) 'reg 'pc))) (pop-a-thunk (make-restore '(restore a) m ((m 'get-register) 'reg 'pc))) (push-b-thunk (make-save '(save b) m ((m 'get-register) 'reg 'pc))) (pop-b-thunk (make-restore '(restore b) m ((m 'get-register) 'reg 'pc)))) (define (test-init) (set-contents! (get-register m 'a) 1) (set-contents! (get-register m 'b) 2) (set-contents! (get-register m 'pc) '(() (gcd-done ()))) (push-a-thunk) (set-contents! (get-register m 'pc) '(() (gcd-done ()))) (push-b-thunk) (set-contents! (get-register m 'a) 9) (set-contents! (get-register m 'b) 8) (test* "a" 9 (get-contents (get-register m 'a))) (test* "b" 8 (get-contents (get-register m 'b)))) (test-init) (set-contents! (get-register m 'pc) '(() (gcd-done ()))) (pop-b-thunk) (test* "pop b" 2 (get-contents (get-register m 'b))) (set-contents! (get-register m 'pc) '(() (gcd-done ()))) (pop-a-thunk) (test* "pop a" 1 (get-contents (get-register m 'a))) (test* "stack a is null" *test-error* (pop-a-thunk)) (test* "stack b is null" *test-error* (pop-b-thunk)) (test-init) (set-contents! (get-register m 'pc) '(() (gcd-done ()))) (pop-a-thunk) (test* "pop a" 1 (get-contents (get-register m 'a))) (set-contents! (get-register m 'pc) '(() (gcd-done ()))) (pop-b-thunk) (test* "pop b" 2 (get-contents (get-register m 'b))) (test* "stack a is null" *test-error* (pop-a-thunk)) (test* "stack b is null" *test-error* (pop-b-thunk)) ) ) (test-end)
次の 5.12 の設問の意味が理解できてません。書いてある通りにヤッてみれば良いのかなぁ。データパスというのは 294p とか 296p とかのソレなのでしょうか。