;: Single-file version of the interpreter. ;; Easier to submit to server, probably harder to use in the development process (load "chez-init.ss") ;-------------------+ ; | ; DATATYPES | ; | ;-------------------+ ; parsed expression. You'll probably want to replace this ; code with your expression datatype from A11b (define-datatype expression expression? [var-exp ; variable references (id symbol?)] [lit-exp ; "Normal" data. Did I leave out any types? (datum (lambda (x) (ormap (lambda (pred) (pred x)) (list number? vector? boolean? symbol? string? pair? null?))))] [app-exp ; applications (rator expression?) (rands (list-of expression?))] ) ;; environment type definitions (define scheme-value? (lambda (x) #t)) (define-datatype environment environment? [empty-env-record] [extended-env-record (syms (list-of symbol?)) (vals (list-of scheme-value?)) (env environment?)]) ; datatype for procedures. At first there is only one ; kind of procedure, but more kinds will be added later. (define-datatype proc-val proc-val? [prim-proc (name symbol?)]) ;-------------------+ ; | ; PARSER | ; | ;-------------------+ ; This is a parser for simple Scheme expressions, such as those in EOPL 3.1 thru 3.3. ; You will want to replace this with your parser that includes more expression types, more options for these types, and error-checking. ; Procedures to make the parser a little bit saner. (define 1st car) (define 2nd cadr) (define 3rd caddr) ; Again, you'll probably want to use your code form A11b (define parse-exp (lambda (datum) (cond [(symbol? datum) (var-exp datum)] [(number? datum) (lit-exp datum)] [(pair? datum) (cond [else (app-exp (parse-exp (1st datum)) (map parse-exp (cdr datum)))])] [else (eopl:error 'parse-exp "bad expression: ~s" datum)]))) ;-------------------+ ; | ; ENVIRONMENTS | ; | ;-------------------+ ; Environment definitions for CSSE 304 Scheme interpreter. ; Based on EoPL sections 2.2 and 2.3 (define empty-env (lambda () (empty-env-record))) (define extend-env (lambda (syms vals env) (extended-env-record syms vals env))) (define list-find-position (lambda (sym los) (let loop ([los los] [pos 0]) (cond [(null? los) #f] [(eq? sym (car los)) pos] [else (loop (cdr los) (add1 pos))])))) (define apply-env (lambda (env sym) (cases environment env [empty-env-record () (eopl:error 'env "variable ~s not found." sym)] [extended-env-record (syms vals env) (let ((pos (list-find-position sym syms))) (if (number? pos) (list-ref vals pos) (apply-env env sym)))]))) ;-----------------------+ ; | ; SYNTAX EXPANSION | ; | ;-----------------------+ ; To be added in assignment 14. ;--------------------------------------+ ; | ; CONTINUATION DATATYPE and APPLY-K | ; | ;--------------------------------------+ ; To be added in assignment 18a. ;-------------------+ ; | ; INTERPRETER | ; | ;-------------------+ ; top-level-eval evaluates a form in the global environment (define top-level-eval (lambda (form) ; later we may add things that are not expressions. (eval-exp form))) ; eval-exp is the main component of the interpreter (define eval-exp (lambda (exp) (cases expression exp [lit-exp (datum) datum] [var-exp (id) (apply-env init-env id)] [app-exp (rator rands) (let ([proc-value (eval-exp rator)] [args (eval-rands rands)]) (apply-proc proc-value args))] [else (eopl:error 'eval-exp "Bad abstract syntax: ~a" exp)]))) ; evaluate the list of operands, putting results into a list (define eval-rands (lambda (rands) (map eval-exp rands))) ; Apply a procedure to its arguments. ; At this point, we only have primitive procedures. ; User-defined procedures will be added later. (define apply-proc (lambda (proc-value args) (cases proc-val proc-value [prim-proc (op) (apply-prim-proc op args)] ; You will add other cases [else (eopl:error 'apply-proc "Attempt to apply bad procedure: ~s" proc-value)]))) (define *prim-proc-names* '(+ - * add1 sub1 cons =)) (define init-env ; for now, our initial global environment only contains (extend-env ; procedure names. Recall that an environment associates *prim-proc-names* ; a value (not an expression) with an identifier. (map prim-proc *prim-proc-names*) (empty-env))) ; Usually an interpreter must define each ; built-in procedure individually. We are "cheating" a little bit. (define apply-prim-proc (lambda (prim-proc args) (case prim-proc [(+) (+ (1st args) (2nd args))] [(-) (- (1st args) (2nd args))] [(*) (* (1st args) (2nd args))] [(add1) (+ (1st args) 1)] [(sub1) (- (1st args) 1)] [(cons) (cons (1st args) (2nd args))] [(=) (= (1st args) (2nd args))] [else (error 'apply-prim-proc "Bad primitive procedure name: ~s" prim-proc)]))) (define rep ; "read-eval-print" loop. (lambda () (display "--> ") ;; notice that we don't save changes to the environment... (let ([answer (top-level-eval (parse-exp (read)))]) ;; TODO: are there answers that should display differently? (eopl:pretty-print answer) (newline) (rep)))) ; tail-recursive, so stack doesn't grow. (define eval-one-exp (lambda (x) (top-level-eval (parse-exp x))))