SICP4章のMetacircular Evaluatorを写経する
なんとなく思い立ったのでbracket("[", "]")を使って書き直してみるテスト。
にわかSchemerとしては大分見やすくなった気がします。
[]の後は必ず改行するという自分ルールを適用した結果、かなりPython風味に(個人的に)。
Dr.Schemeで動きました。
;;;; Metacircular evaluator (define true #t) (define false #f) ;; Eval (define [my-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)) ([cond? exp] (my-eval (cond->if exp) env)) ([application? exp] (my-apply (my-eval (operator exp) env) (list-of-values (operands exp) env))) (else (error "Unknown expression type -- EVAL" exp)))) ;; Apply (define [my-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 "Unkown procedure type -- APPLY" procedure)))) ;; Procedure arguments (define [list-of-values exps env] (if [no-operands? exps] '() (cons (my-eval (first-operand exps) env) (list-of-values (rest-operands exps) env)))) ;; Conditionals (define [eval-if exp env] (if [true? (my-eval (if-predicate exp) env)] (my-eval (if-consequent exp) env) (my-eval (if-alternative exp) env))) ;; Sequences (define [eval-sequence exps env] (cond ([last-exp? exps] (my-eval (first-exp exps) env)) (else (my-eval (first-exp exps) env) (eval-sequence (rest-exps exps) env)))) ;; Assignments (define [eval-assignment exp env] (set-variable-value! (assignment-variable exp) (my-eval (assignment-value exp) env) env) 'ok) ;; Definitions (define [eval-definition exp env] (define-variable! (definition-variable exp) (my-eval (definition-value exp) env) env) 'ok) ;; Expressions (define [self-evaluating? exp] (cond ([number? exp] true) ([string? exp] true) (else false))) (define [variable? exp] (symbol? exp)) (define [quoted? exp] (tagged-list? exp 'quote)) (define [text-of-quotation exp] (cadr exp)) (define [tagged-list? exp tag] (if [pair? exp] (eq? (car exp) tag) false)) ;; Assignments (define [assignment? exp] (tagged-list? exp 'set!)) (define [assignment-variable exp] (cadr exp)) (define [assignment-value exp] (caddr exp)) ;; Definitions (define [definition? exp] (tagged-list? exp 'define)) (define [definition-variable exp] (if [symbol? (cadr exp)] (cadr exp) (caadr exp))) (define [definition-value exp] (if [symbol? (cadr exp)] (caddr exp) (make-lambda (cdadr exp) ; Formal parameters (cddr exp)))) ; body ;; Lambda (define [lambda? exp] (tagged-list? exp 'lambda)) (define [lambda-parameters exp] (cadr exp)) (define [lambda-body exp] (cddr exp)) (define [make-lambda parameters body] (cons 'lambda (cons parameters body))) ;; If (define [if? exp] (tagged-list? exp 'if)) (define [if-predicate exp] (cadr exp)) (define [if-consequent exp] (caddr exp)) (define [if-alternative exp] (if [not (null? (cdddr exp))] (caddr exp) 'false)) (define [make-if predicate consequent alternative] (list 'if predicate consequent alternative)) ;; Sequences (define [begin? exp] (tagged-list? exp 'begin)) (define [begin-actions exp] (cdr exp)) (define [last-exp? seq] (null? (cdr seq))) (define [first-exp seq] (car seq)) (define [rest-exps seq] (cdr seq)) (define [sequence->exp seq] (cond ([null? seq] seq) ([last-exp? seq] (first-exp seq)) (else (make-begin seq)))) (define [make-begin seq] (cons 'begin seq)) (define [application? exp] (pair? exp)) (define [operator exp] (car exp)) (define [operands exp] (cdr exp)) (define [no-operands? ops] (null? ops)) (define [first-operand ops] (car ops)) (define [rest-operands ops] (cdr ops)) ;; Conditionals (define [cond? exp] (tagged-list? exp 'cond)) (define [cond-clauses exp] (cdr exp)) (define [cond-else-clause? clause] (eq? (cond-predicate clause) 'else)) (define [cond-predicate clause] (car clause)) (define [cond-actions clause] (cdr clause)) (define [cond->if exp] (expand-clauses (cond-clauses exp))) (define [expand-clauses clauses] (if [null? clauses] 'false (let ([first (car clauses)] [rest (cdr clauses)]) (if [cond-else-clause? first] (if [null? rest] (sequence->exp (cond-actions first)) (error "ELSE clause isn't last -- COND->IF" clauses)) (make-if (cond-predicate first) (sequence->exp (cond-actions first)) (expand-clauses rest)))))) ;; Testing of predicates (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 "Unboud variable -- SET!" 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 primitive-procedures (list (list 'car car) (list 'cdr cdr) (list 'cons cons) (list 'null? null?) (list '+ +) (list '- -) (list '* *) (list '/ /) )) (define [primitive-procedure-names] (map car primitive-procedures)) (define [primitive-procedure-objects] (map (lambda [proc] (list 'primitive (cadr proc))) primitive-procedures)) (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 the-global-environment (setup-environment)) (define [primitive-procedure? proc] (tagged-list? proc 'primitive)) (define [primitive-implementation proc] (cadr proc)) (define [apply-primitive-procedure proc args] (apply (primitive-implementation proc) args)) (define input-prompt ";;; M-Eval input:") (define output-prompt ";;; M-Eval value:") (define [driver-loop] (prompt-for-input input-prompt) (let ([input (read)]) (let ([output (my-eval input the-global-environment)]) (announce-output output-prompt) (user-print output))) (driver-loop)) (define [prompt-for-input string] (newline) (newline) (display string) (newline)) (define [announce-output string] (newline) (display string) (newline)) (define (user-print object) (if [compound-procedure? object] (display (list 'compond-procedure (procedure-parameters object) (procedure-body object) '<procedure-env>)) (display object))) ;; Main (define the-global-environment (setup-environment)) (driver-loop)