;;;;NAIVECOM.SCM ;;;A Naive Code Generator ; By dropping most of the optimizations from the compiler in the text, we ; get a simpler compilation procedure, NAIVE-COMPILE, which helps clarify ; the core ideas of how Scheme programs are translated into register machine ; code. ; ; *** The compilation procedures below take an expression as their only ; parameter; each expression compiles in only one way. ; ; *** Compiled expressions and compiled procedures each fulfill the ; SAME contract wrt register and stack use. ; ; *** Compiled code is tail recursive. ; ; *** It is fairly easy to see what the generated register-machine code will ; look like, because the bodies of the subprocedures of NAIVE-COMPILE look ; very much like the code they generate; this is accomplished by making ; elementary use of BACKQUOTE. ; ; *** NAIVE-COMPILE is independent of, and not compatible with, the ; ECEVAL interpreter of the Notes because the register-code it generates ; obeys a different contract than expected by ECEVAL. Compatibility could ; be achieved with a local change in the ECEVAL apply-dispatch code to ; handle NAIVE-COMPILE procedures as a separate case. ; ; ; THE CONTRACT ; ; (NAIVE-COMPILE EXP) yields register machine code such that ; ; *** if register ENV contains an environment, and ; ; *** register CONTINUE contains a label, ; ; *** then execution of the code evaluates EXP, causing appropriate ; mutations of the environment and store, if any, and leaves the value in ; VAL. ; ; *** Execution preserves the stack. ; ; *** At the end of execution, control transfers to the label originally in ; CONTINUE. ; ; *** All labels explicitly in the code are ``fresh'' (do not appear in ; other code fragments). (define (naive-compile exp) (cond ((self-evaluating? exp) (naive-compile-self-evaluating exp)) ((quoted? exp) (naive-compile-quoted (text-of-quotation exp))) ((variable? exp) (naive-compile-variable-access exp)) ((assignment? exp) (naive-compile-assignment (assignment-variable exp) (assignment-value exp))) ((definition? exp) (naive-compile-definition (definition-variable exp) (definition-value exp))) ((if? exp) (naive-compile-if (if-predicate exp) (if-consequent exp) (if-alternative exp))) ((begin? exp) (if (no-more-exps? (begin-actions exp)) (error "Empty sequence of actions -- NAIVE-COMPILE" exp) (naive-compile-seq (begin-actions exp)))) ((cond? exp) (naive-compile (cond->if exp))) ((lambda? exp) (let ((params (lambda-parameters exp)) (body (lambda-body exp))) (if (no-more-exps? body) (error "Empty lambda body -- NAIVE-COMPILE" exp)) (naive-compile-lambda params body))) ((let? exp) (naive-compile (let->combination exp))) ((application? exp) (naive-compile-application (operator exp) (operands exp))) (else (error "Unknown expression type -- NAIVE-COMPILE" exp)))) (define (naive-compile-self-evaluating constant) `((assign val (const ,constant)) (goto (reg continue)))) (define (naive-compile-quoted text) `((assign val (const ,text)) (goto (reg continue)))) (define (naive-compile-variable-access var) `((assign val (op lookup-variable-value) (const ,var) (reg env)) (goto (reg continue)))) (define (naive-compile-assignment assignment-var assignment-valexp) (let ((after-valexp-label (make-label 'after-valexp))) (append `((save continue) (assign continue (label ,after-valexp-label)) (save env)) (naive-compile assignment-valexp) `(,after-valexp-label (restore env) (perform (op set-variable-value!) (const ,assignment-var) (reg val) (reg env)) (assign val (const ,the-unspecified-value)) (restore continue) (goto (reg continue)))))) (define (naive-compile-definition def-var def-valexp) (let ((after-def-value-label (make-label 'after-def-value))) (append `((save continue) (assign continue (label ,after-def-value-label)) (save env)) (naive-compile def-valexp) `(,after-def-value-label (restore env) (perform (op define-variable!) (const ,def-var) (reg val) (reg env)) (assign val (const ,the-unspecified-value)) (restore continue) (goto (reg continue)))))) (define (naive-compile-if pred true-brnch false-brnch) (let ((after-pred-label (make-label 'after-pred)) (false-branch-label (make-label 'false-branch))) (append `((save continue) (assign continue (label ,after-pred-label)) (save env)) (naive-compile pred) `(,after-pred-label (restore env) (restore continue) (test (op false?) (reg val)) (branch (label ,false-branch-label))) (naive-compile true-brnch) `(,false-branch-label) (naive-compile false-brnch)))) (define (naive-compile-seq explist) (append '((save continue)) (append-map (lambda (exp) (let ((after-current-exp-label (make-label 'after-current-exp))) (append `((save env) (assign continue (label ,after-current-exp-label))) (naive-compile exp) `(,after-current-exp-label (restore env))))) (all-but-last explist)) '((restore continue)) (naive-compile (last-exp explist)))) (define (all-but-last explist) (if (last-exp? explist) '() (cons (first-exp explist) (all-but-last (rest-exps explist))))) (define (last-exp explist) (list-ref explist (dec (length explist)))) (define (naive-compile-lambda params body) (let ((proc-entry-label (make-label 'proc-entry))) (append `((assign val (op make-compiled-procedure) (label ,proc-entry-label) (reg env)) (goto (reg continue)) ,proc-entry-label ; This code expects the procedure-object environment in ENV, a ; list of arguments in ARGL, and its continuation label in ; CONTINUE. It evaluates the body of abstraction in the ; procedure object's extended environment, leaving its value ; in VAL. It preserves the stack. Finally it goes to ; CONTINUE. (assign env (op extend-environment) (const ,params) (reg argl) (reg env))) (naive-compile-seq body)))) (define (naive-compile-application rator rands) (let ((after-rator-label (make-label 'after-rator)) (primitive-apply-label (make-label 'primitive-apply))) (append `((save continue) (assign argl (op empty-arglist))) (naive-compile-operands rands) `((assign continue (label ,after-rator-label)) (save argl)) (naive-compile rator) `(,after-rator-label (restore argl) (restore continue) (test (op primitive-procedure?) (reg val)) (branch (label ,primitive-apply-label)) ; doesn't handle interpreted procedures. (assign env (op compiled-procedure-env) (reg val)) (assign val (op compiled-procedure-entry) (reg val)) (goto (reg val)) ,primitive-apply-label (assign val (op apply-primitive-procedure) (reg val) (reg argl)) (goto (reg continue)))))) ; This code evaluates rands, adjoining their values to ARGL. ; It preserves STACK and ENV, returns to next instruction. (define (naive-compile-operands rands) (append-map (lambda (rand) (let ((after-rand-label (make-label 'after-rand))) (append `((assign continue (label ,after-rand-label)) (save env) (save argl)) (naive-compile rand) `(,after-rand-label (restore argl) (assign argl (op adjoin-arg) (reg val) (reg argl)) (restore env))))) rands)) ; PROCEDURES FOR RUNNING THE COMPILER (define (naive-compile-and-display expression) (fluid-let ((*unparser-list-breadth-limit* #f) (*unparse-uninterned-symbols-by-name?* #T)) (newline) (for-each (lambda (statement) (newline) (cond ((pair? statement) (display " ") (display statement)) (else (display statement)))) (naive-compile expression)))) (define compiler-ops `((lookup-variable-value ,lookup-variable-value) (set-variable-value! ,set-variable-value!) (define-variable! ,define-variable!) (false? ,false?) (empty-arglist ,empty-arglist) (adjoin-arg ,adjoin-arg) (make-compiled-procedure ,make-compiled-procedure) (extend-environment ,extend-environment) (compiled-procedure-entry ,compiled-procedure-entry) (compiled-procedure-env ,compiled-procedure-env) (primitive-procedure? ,primitive-procedure?) (apply-primitive-procedure ,apply-primitive-procedure))) (define (naive-compile-and-go exp) (let ((naive-machine (make-machine '(val env continue argl) compiler-ops (naive-compile exp)))) (set-register-contents! naive-machine 'env (make-initial-environment)) (set-register-contents! naive-machine 'continue '()) (naive-machine 'initialize-stack) (naive-machine 'initialize-ops-counter) (start naive-machine) (naive-machine 'print-stack-statistics) (naive-machine 'print-ops) (newline) (display ";value of expression: ") (user-print (get-register-contents naive-machine 'val))))