(define repl (lambda (env)
(let* (
(dummy1 (display "cs305> "))
(expr (read))
(new-env (if (define-stmt? expr)
(extend-env (cadr expr) (s6-interpret (caddr expr) env) env) env)
)
(val
(cond
((define-stmt? expr) (cadr expr))
((and (if-stmt? expr) (= (cadr expr) 0) ) (cadddr expr))
((and (if-stmt? expr) (not(= (cadr expr) 0)) ) (caddr expr))
(else (s6-interpret expr env))
)
)
(dummy2 (display "cs305: "))
(dummy3 (display val))
(dummy4 (newline))
(dummy4 (newline)))
(repl new-env))))
Here is how it should be
cs305> (if (- 5 x) (+ x 1) (* x 2))
cs305: 10
but i get 6 but it is supposed to be 0 because x is defined as 5. what is wrong with the function here?
When processing an if statement you need to interpret the condition and the selected consequent. I'm not sure why you're not getting an error from (= (cadr expr) 0) ), since (cadr expr) is a list, not a number.
(define repl (lambda (env)
(display "cs305> ")
(let* (
(expr (read))
(new-env (if (define-stmt? expr)
(extend-env (cadr expr) (s6-interpret (caddr expr) env) env)
env)
)
(val
(cond
((define-stmt? expr) (cadr expr))
((if-stmt? expr)
(if (= (s6-interpret (cadr expr) env) 0)
(s6-interpret (cadddr expr) env)
(s6-interpret (caddr expr) env)))
(else (s6-interpret expr env))
)
))
(display "cs305: ")
(display val)
(newline)
(newline))
(repl new-env)))
Related
I have the function getBoundedVars which uses the function boundsInLambda. In the end of it all the box bBox should contain all bounded variables in the expression exp.
I'm trying to debug this function and in order to do so I want to print the parameters of boundsInLambda every time the function is being activated but for some reason the values won't show up on the screen.
If I put the display operation in getBoundedVars it will print it but those are just the values in the first iteration.
If I run the following :
(getBoundedVars (lambda-simple (x) (lambda-simple (y) (const x))) bx)
when bx is an empty box,
'1 will be printed but the print commands in boundsInLambda will not
here's the code:
(define getBoundedVars
(lambda (exp bBox)
(if (atom? exp)
0 ;; don't put in box
(if (lambda? (car exp))
(begin
(display 1)
(newline)
(let ((pBox (make-pBox exp))
(lBox (box '()))
(bodyExp (make-body exp))
)
(boundsInLambda bodyExp lBox pBox bBox)))
(begin
(getBoundedVars (car exp) bBox)
(getBoundedVars (cdr exp) bBox))))))
(define boundsInLambda
(lambda (bodyExp lastBox paramBox boundsBox)
(newline)
(display `(bodyExp: ,bodyExp))
(newline)
(display `(lastBox: ,lastBox))
(newline)
(display `(paramBox: ,paramBox))
(newline)
(display `(boundsBox: ,boundsBox))
(newline)
(if (and (not (null? bodyExp))
(list bodyExp)
(equal? (car bodyExp) 'seq)
)
(map boundsInLambda (cadr bodyExp))
(let* ( (lists* (filter (lambda (el) (and (not (null? el)) (list? el) (not (equal? (car el) 'const)))) bodyExp))
(lists (map (lambda (el) (if (equal? (car el) 'set) (cddr el) el)) lists*))
(bounds (filter (lambda (el) (and (member el (unbox lastBox)) (not (member el (unbox paramBox))))) bodyExp))
(listsLeft? (> (length lists) 0))
(anyBounds? (> (length bounds) 0))
)
(if anyBounds?
(begin
(set-box! boundsBox (append (unbox boundsBox) bounds))))
(if listsLeft?
(map
(lambda (lst)
(if (lambda? (car lst))
(let* ((newBodyExp (make-body lst))
(newParamBox (make-pBox exp))
(newLastBox (box (append (unbox lastBox) (unbox paramBox))))
)
(boundsInLambda newBodyExp newLastBox newParamBox boundsBox))
(boundsInLambda lst lastBox paramBox boundsBox)))
lists)
0))
)))
(define make-pBox
(lambda (lamExp)
(if (equal? (car lamExp) 'lambda-simple)
(box (cadr lamExp))
(if (equal? (car lamExp) 'lambda-opt)
(box (cadr lamExp))
(box '())))))
(define make-body
(lambda (lamExp)
(if (equal? (car lamExp) 'lambda-opt)
(cdddr lamExp)
(cddr lamExp))))
any help would be very much appreciated.
I'm learning interpreter for quite a long time, after reading SICP chap 4.1~4.2, I tried to copy these code in my DrRacket in planet neil/sicp mode. I've carefully read these code but still cannot made the code running correctly.
during my copy, I made some changes:
eval function has been renamed to ewal;(because I want to avoid underlying scheme evaluating my code)
apply function has been renamed to epply (except that apply-in-underlying-scheme function);
rearranged the code structure for my better understanding
use #f and #t for my underlying implementation.
I have also disabled the driver-loop since I found the driver-loop never output a value for input.
the code failed to eval a compound procedure correctly, but can handle self-evaluating, define, and other special-form. I double checked the evaluate process, finding that if I change one point (I've marked with (*) in my code), so that the line
((compound-procedure? procedure) (eval-sequence (procedure-body procedure)
modified to
((compound-procedure? procedure) (ewal (procedure-body procedure)
the interpreter can finally eval compound procedure again. I don't know why but I think mine is correct. but SICP can't be wrong. My second question is how to make the driver-loop correctly output eval value.
the interpreter is also included in a gist because it's too long.
#lang planet neil/sicp
;; plot:
;; 1. env operation
;; 2. eval function
;; 3. test and eval for each special form and combination eval
;; 4. REPL
;; 5: test
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; environment
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define the-empty-environment '())
(define (first-frame env) (car env))
(define (enclosing-environment env)(cdr env))
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; env operation
;;;;;;;;;;;;;;;;;;;;;;;;;;
(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 args supplied" vars vals)
(error "Too few args 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 -- 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))))
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; frame operation
;;;;;;;;;;;;;;;;;;;;;;;;;;
(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))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; eval
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (ewal 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) (ewal (cond->if exp) env))
((application? exp) (epply (ewal (operator exp) env)
(list-of-values (operands exp) env)))
(else (error "Unknown type -- EWAL" exp))))
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; self-eval test and eval
;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (self-evaluating? exp)
(cond ((number? exp) #t)
((string? exp) #t)
(else #f)))
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; variable test an eval
;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (variable? exp) (symbol? exp))
;; (lookup-variable-value exp env) see below
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; quote test and eval
;;;;;;;;;;;;;;;;;;;;;;;;;;
(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)
#f))
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; assignment test and eval
;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (assignment? exp)
(tagged-list? exp 'set!))
(define (assignment-variable exp) (cadr exp))
(define (assignment-value exp) (caddr exp))
(define (eval-assignment exp env)
(set-variable-value! (assignment-variable exp)
(ewal (assignment-value exp) env)
env)
'ok)
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; definition test and eval
;;;;;;;;;;;;;;;;;;;;;;;;;;
(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
(define (make-lambda parameters body)
(cons 'lambda (cons parameters body)))
(define (eval-definition exp env)
(define-variable! (definition-variable exp)
(ewal (definition-value exp) env)
env)
'ok)
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; lambda test and eval
;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (lambda? exp) (tagged-list? exp 'lambda))
(define (lambda-parameters exp) (cadr exp))
(define (lambda-body exp) (caddr exp))
(define (make-procedure parameters body env)
(list 'procedure parameters body env))
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; if test and eval
;;;;;;;;;;;;;;;;;;;;;;;;;;
(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? (cadddr exp)))
(cadddr exp)
'false))
(define (eval-if exp env)
(if (true? (ewal (if-predicate exp) env))
(ewal (if-consequent exp) env)
(ewal (if-alternative exp) env)))
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; begin test and eval
;;;;;;;;;;;;;;;;;;;;;;;;;;
(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 (eval-sequence exps env)
(cond ((last-exp? exps) (ewal (first-exp exps) env))
(else (ewal (first-exp exps) env)
(eval-sequence (rest-exps exps) env))))
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; application test and eval
;;;;;;;;;;;;;;;;;;;;;;;;;;
(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))
(define (list-of-values exps env)
(if (no-operands? exps)
'()
(cons (ewal (first-operand exps) env)
(list-of-values (rest-operands exps) env))))
(define (epply procedure arguments)
(cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure arguments))
((compound-procedure? procedure) (ewal (procedure-body procedure) ;; (*)
(extend-environment (procedure-parameters procedure)
arguments
(procedure-environment procedure))))
(else (error "Unkown procedure type -- EPPLY" procedure))))
(define (primitive-procedure? proc)
(tagged-list? proc 'primitive))
(define (primitive-implementation proc) (cadr proc))
(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 (apply-primitive-procedure proc args)
(apply-in-underlying-scheme (primitive-implementation proc) args))
(define apply-in-underlying-scheme apply)
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; cond test and eval
;;;;;;;;;;;;;;;;;;;;;;;;;;
(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))))))
(define (make-if predicate consequent alternative)
(list 'if predicate consequent alternative))
(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))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; env setup
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (setup-environment)
(let ((initial-env
(extend-environment (primitive-procedure-names)
(primitive-procedure-objects)
the-empty-environment)))
(define-variable! '#t #t initial-env)
(define-variable! '#f #f initial-env)
initial-env))
(define primitive-procedures
(list(list 'car car)
(list 'cdr cdr)
(list 'null? null?)
(list 'cons cons)
(list '+ +)
(list '- -)
(list '* *)
(list '/ /)
(list '= =)))
(define (true? x)
(not (eq? x false)))
(define (false? x)
(eq? x false))
(define (primitive-procedure-names)
(map car primitive-procedures))
(define (primitive-procedure-objects)
(map (lambda (proc) (list 'primitive (cadr proc)))
primitive-procedures))
(define the-global-environment (setup-environment))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; REPL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(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 (ewal 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 'compound-procedure
(procedure-parameters object)
(procedure-body object)
'<procedure-env>))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; test
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define env0 the-global-environment)
(ewal '(define (p1 x) (+ x 1)) env0 )
(ewal '(p1 4) env0)
(ewal '(define (append x y)
(if (null? x)
y
(cons (car x)
(append (cdr x) y)))) env0)
(ewal '(define (factorial n)
(if (= 1 n)
1
(* n (factorial (- n 1))))) env0)
(ewal '(factorial 5) env0)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; init main loop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;(driver-loop)
;; this is commented since I found it run incorrectly
update :
#Will Ness said:
about the eval-sequence, your procedure-body function is incorrect. it
should be cddr, not caddr, to match the eval-sequence's expectations.
I think my procedure-body def is right. consider I am interpreting this exp:
((lambda (n)
(if (= 1 n)
1
0))
5)
this is an application, and the operator can be evaluated by eval
(ewal (operator '((lambda (n)
(if (= 1 n)
1
0))
5)) env0)
so the value of operator is a list(closure) like this:
(procedure (n) (if (= 1 n) 1 0) #new-env)
if my procedure-body is caddr of the closure, the body will be (if (= 1 n) 1 0).
but if I change procedure-body into (cddr p), procedure-body will become like : ((if (= 1 n) 1 0) #new-env)
this means the #new-env will be a part of my procedure body. anyway this is not incorrect because procedure body should not include the closure's environment.
but indeed, this interpreter cannot handle a function whose body is composed by a list of expressions. In this case, I don't know how to extract the body(exp list) from the closure.
about the eval-sequence, your procedure-body function is incorrect. it should be cddr, not caddr, to match the eval-sequence's expectations.
about the driver-loop, it uses user-print to show the output, but user-print is incomplete. it only shows values which are compound procedures, not anything else.
in my interpreter, the case lambda cannot eval a function whose body is composed by a list of expressions, bacause lambda-bodyis defined as (caddr exp). I should modify it :
(define (lambda-body exp) (cddr exp))
and in my epply function, the condition compound-procedure should eval-sequence of procedure body, because now procedure body is a list of expressions, not a expression.
I am watching the video lectures of SICP. Currently I am on 4A Pattern Matching and Rule Based Substitution.
So far, I found the Matcher and the Instantiator is easy. But I can't get my head into The simplifier.
(define (simplifier the-rules)
(define (simplify-exp exp)
(try-rules (if (compound? exp)
(map simplify-exp exp)
exp)))
(define (try-rules exp)
(define (scan rules)
(if (null? rules)
exp
(let ((dict (match (pattern (car rules))
exp
(empty-dictionary))))
(if (eq? dict 'failed)
(scan (cdr rules))
(simplify-exp (instantiate (skeleton (car rules)) dict))))))
(scan the-rules))
simplify-exp)
I saw another question here on this topic which defined compound? in terms of pair?. But, Then what simplify-exp feeding to try-rules?
Figured it out. The rules are going to apply in every node as promised. You can vote to delete the question. But, I would add some explanation on how I made it working.
I changed some code. The original code seems written with some other semantic in mind. I added some commentary where I made some decision on my own.
#lang racket
;matcher
(define (match pat exp dict)
(cond ((eq? dict 'failed) 'failed)
;matched
((and (null? pat) (null? exp)) dict)
;so far matched, but no more
((or (null? pat) (null? exp)) 'failed)
((atom? pat)
(if (atom? exp)
(if (eq? pat exp)
dict
'failed)
'failed))
((pat-const? pat)
(if (constant? exp)
(extend-dict pat exp dict)
'failed))
((pat-variable? pat)
(if (variable? exp)
(extend-dict pat exp dict)
'failed))
((pat-exp? pat)
(extend-dict pat exp dict))
((atom? exp) 'failed)
(else
(match (cdr pat)
(cdr exp)
(match (car pat) (car exp) dict)))))
(define (pat-const? pat)
(eq? (car pat) '?c))
(define (pat-variable? pat)
(eq? (car pat) '?v))
(define (pat-exp? pat)
(eq? (car pat) '?))
(define constant? number?)
(define variable? symbol?)
;instantiator
(define (instantiate skeleton dict)
(define (loop s)
(cond ((atom? s) s)
;we cant run past the nil line
((null? s) '())
((skeleton-evaluation? s) (evaluate s dict))
(else
(cons (loop (car s)) (loop (cdr s))))))
(loop skeleton))
(define (skeleton-evaluation? s)
(eq? (car s) ':))
;made it simpler, no environment constant, sorry
(define (evaluate s dict)
(let ((data (lookup (cadr s) dict)))
(if (null? data)
(display "error in rules. mismatch")
(cadr data))))
;simplifier
(define (simplifier rules)
(define (simplify-exp exp)
(try-rules (if (list? exp)
(map simplify-exp exp)
exp)))
(define (try-rules exp)
(define (scan rule)
(if (null? rule)
exp
(let ((dict (match (pattern (car rule)) exp (empty-dict))))
(if (eq? dict 'failed)
(scan (cdr rule))
(simplify-exp (instantiate (skeleton (car rule)) dict))))))
(scan rules))
simplify-exp)
(define pattern car)
(define skeleton cadr)
;dictionary
(define (empty-dict)
'())
(define (extend-dict pat exp dict)
(let ((v (lookup (cadr pat) dict)))
(if (null? v)
(cons (list (cadr pat) exp) dict)
(if (eq? (cadr v) exp)
dict
'failed))))
(define (lookup s dict)
(cond ((null? dict) '())
((eq? (caar dict) s) (car dict))
(else (lookup s (cdr dict)))))
;extend racket
(define (atom? a)
(and (not (null? a)) (not (pair? a))))
And? you know what? It works :)
I'm totally new in Scheme and interpreters. My job is modifying the following code. If I run
(run "sub1(12,2,3,4)")
in Drracket, it returns 11. I need to modify the interpreter so that it behaves correctly for a single numeric argument, but returns 0 otherwise (that is, whenever the number of arguments is different from 1, or the argument is of incompatible type)
I understand different modules of the code, but I'm totally confused how to modify it. It would be great if you can help me or give me a pointer to some similar things.
#lang eopl
;;;;;;;;;;;;;;;; top level and tests ;;;;;;;;;;;;;;;;
(define run
(lambda (string)
(eval-program (scan&parse string))))
;; needed for testing
(define equal-external-reps? equal?)
;;;;;;;;;;;;;;;; grammatical specification ;;;;;;;;;;;;;;;;
(define the-lexical-spec
'((whitespace (whitespace) skip)
(comment ("%" (arbno (not #\newline))) skip)
(identifier
(letter (arbno (or letter digit "_" "-" "?")))
symbol)
(number (digit (arbno digit)) number)))
(define the-grammar
'((program (expression) a-program)
(expression (number) lit-exp)
(expression (identifier) var-exp)
(expression
(primitive "(" (separated-list expression ",") ")")
primapp-exp)
(expression
("if" expression "then" expression "else" expression)
if-exp)
(expression
("let" (arbno identifier "=" expression) "in" expression)
let-exp)
(expression
("proc" "(" (separated-list identifier ",") ")" expression)
proc-exp)
(expression
("(" expression (arbno expression) ")")
app-exp)
(expression
("begin" expression (arbno ";" expression) "end")
begin-exp)
(primitive ("+") add-prim)
(primitive ("-") subtract-prim)
(primitive ("*") mult-prim)
(primitive ("add1") incr-prim)
(primitive ("sub1") decr-prim)
(primitive ("zero?") zero-test-prim)
))
(sllgen:make-define-datatypes the-lexical-spec the-grammar)
(define show-the-datatypes
(lambda () (sllgen:list-define-datatypes the-lexical-spec the-grammar)))
(define scan&parse
(sllgen:make-string-parser the-lexical-spec the-grammar))
(define just-scan
(sllgen:make-string-scanner the-lexical-spec the-grammar))
;;;;;;;;;;;;;;;; the interpreter ;;;;;;;;;;;;;;;;
(define eval-program
(lambda (pgm)
(cases program pgm
(a-program (body)
(eval-expression body (init-env))))))
(define eval-expression
(lambda (exp env)
(cases expression exp
(lit-exp (datum) datum)
(var-exp (id) (apply-env env id))
(primapp-exp (prim rands)
(let ((args (eval-rands rands env)))
(apply-primitive prim args)))
(if-exp (test-exp true-exp false-exp) ;\new4
(if (true-value? (eval-expression test-exp env))
(eval-expression true-exp env)
(eval-expression false-exp env)))
(begin-exp (exp1 exps)
(let loop ((acc (eval-expression exp1 env))
(exps exps))
(if (null? exps) acc
(loop (eval-expression (car exps) env) (cdr exps)))))
(let-exp (ids rands body) ;\new3
(let ((args (eval-rands rands env)))
(eval-expression body (extend-env ids args env))))
(proc-exp (ids body) (closure ids body env)) ;\new1
(app-exp (rator rands) ;\new7
(let ((proc (eval-expression rator env))
(args (eval-rands rands env)))
(if (procval? proc)
(apply-procval proc args)
(eopl:error 'eval-expression
"Attempt to apply non-procedure ~s" proc))))
;&
(else (eopl:error 'eval-expression "Not here:~s" exp))
)))
;;;; Right now a prefix must appear earlier in the file.
(define eval-rands
(lambda (rands env)
(map (lambda (x) (eval-rand x env)) rands)))
(define eval-rand
(lambda (rand env)
(eval-expression rand env)))
(define apply-primitive
(lambda (prim args)
(cases primitive prim
(add-prim () (+ (car args) (cadr args)))
(subtract-prim () (- (car args) (cadr args)))
(mult-prim () (* (car args) (cadr args)))
(incr-prim () (+ (car args) 1))
(decr-prim () (- (car args) 1))
;&
(zero-test-prim () (if (zero? (car args)) 1 0))
)))
(define init-env
(lambda ()
(extend-env
'(i v x)
'(1 5 10)
(empty-env))))
;;;;;;;;;;;;;;;; booleans ;;;;;;;;;;;;;;;;
(define true-value?
(lambda (x)
(not (zero? x))))
;;;;;;;;;;;;;;;; procedures ;;;;;;;;;;;;;;;;
(define-datatype procval procval?
(closure
(ids (list-of symbol?))
(body expression?)
(env environment?)))
(define apply-procval
(lambda (proc args)
(cases procval proc
(closure (ids body env)
(eval-expression body (extend-env ids args env))))))
;;;;;;;;;;;;;;;; environments ;;;;;;;;;;;;;;;;
(define-datatype environment environment?
(empty-env-record)
(extended-env-record
(syms (list-of symbol?))
(vec vector?) ; can use this for anything.
(env environment?))
)
(define empty-env
(lambda ()
(empty-env-record)))
(define extend-env
(lambda (syms vals env)
(extended-env-record syms (list->vector vals) env)))
(define apply-env
(lambda (env sym)
(cases environment env
(empty-env-record ()
(eopl:error 'apply-env "No binding for ~s" sym))
(extended-env-record (syms vals env)
(let ((position (rib-find-position sym syms)))
(if (number? position)
(vector-ref vals position)
(apply-env env sym)))))))
(define rib-find-position
(lambda (sym los)
(list-find-position sym los)))
(define list-find-position
(lambda (sym los)
(list-index (lambda (sym1) (eqv? sym1 sym)) los)))
(define list-index
(lambda (pred ls)
(cond
((null? ls) #f)
((pred (car ls)) 0)
(else (let ((list-index-r (list-index pred (cdr ls))))
(if (number? list-index-r)
(+ list-index-r 1)
#f))))))
(define iota
(lambda (end)
(let loop ((next 0))
(if (>= next end) '()
(cons next (loop (+ 1 next)))))))
(define difference
(lambda (set1 set2)
(cond
((null? set1) '())
((memv (car set1) set2)
(difference (cdr set1) set2))
(else (cons (car set1) (difference (cdr set1) set2))))))
You could change as follows:
(define apply-primitive
[... part of code ...]
(decr-prim () (if (and (= (length args) 1) (number? (car args)))
(- (car args) 1)
0))
[... rest of code ...]
I assume the other primitives should be changed accordingly, this will only change sub1.
I am trying to write an interpreter for scheme. So far i implemented define, if and some arithmetic expressions. Here is the grammar for my interpreter:
<s6> -> <expr>
| <define>
<expr> -> NUMBER
| IDENT
| <if>
| <let>
<define> -> ( define IDENT <expr> )
<if> -> ( if <expr> <expr> <expr> )
<let> -> ( let ( <var_binding_list> ) <expr> )
<var_binding_list> -> ( IDENT <expr> ) <var_binding_list>
| ( IDENT <expr> )
Here is the code i have written so far:
(define get-operator (lambda (op-symbol)
(cond
((equal? op-symbol '+) +)
((equal? op-symbol '-) -)
((equal? op-symbol '*) *)
((equal? op-symbol '/) /)
(else (error "s6-interpret: operator not implemented -->" op-symbol)))))
(define let-stmt? (lambda (e)
(and (list? e) (equal? (car e) 'let) (= (length e) 3))))
(define if-stmt? (lambda (e)
(and (list? e) (equal? (car e) 'if) (= (length e) 4))))
(define define-stmt? (lambda (e)
(and (list? e) (equal? (car e) 'define) (symbol? (cadr e)) (= (length e) 3))))
(define get-value (lambda (var env)
(cond
((null? env) (error "s6-interpret: unbound variable -->" var))
((equal? (caar env) var) (cdar env))
(else (get-value var (cdr env))))))
(define extend-env (lambda (var val old-env)
(cons (cons var val) old-env)))
(define repl (lambda (env)
(let* (
(dummy1 (display "cs305> "))
(expr (read))
(new-env (if (define-stmt? expr)
(extend-env (cadr expr) (s6-interpret (caddr expr) env) env)env))
(val (if (define-stmt? expr)
(cadr expr)
(s6-interpret expr env)))
(dummy2 (display "cs305: "))
(dummy3 (display val))
(dummy4 (newline))
(dummy4 (newline)))
(repl new-env))))
(define s6-interpret (lambda (e env)
(cond
((number? e) e)
((symbol? e) (get-value e env))
((not (list? e)) (error "s6-interpret: cannot evaluate -->" e))
((if-stmt? e) (if (eq? (cadr e) 0) ( s6-interpret (cadddr e) env) ( s6-interpret(caddr e) env)))
((let-stmt? e) (apply let (map s6-interpret (cdr e))))
(else
(let ((operands (map s6-interpret (cdr e) (make-list (length (cdr e)) env)))
(operator (get-operator (car e))))
(apply operator operands))))))
(define cs305-interpreter (lambda () (repl '())))
Everything i have written except "let" works fine. let-stmt? procedure also works as i want but the part of the code ((let-stmt? e) (apply let (map s6-interpret (cdr e)))) in s6-interpret does not work fine, it gives me an error saying that "syntactic keyword may not be used as an expression". Can anyone help me with the implementation of the interpreter for "let" statement as given in the grammar?
Thank you
You can't apply the special form let. The error is clear: it's not a procedure, it's syntax (a macro). One possible solution would be to implement a syntactic transformation at the evaluator level, once a let is detected, transform it into a lambda expression and evaluate it.
Take a look at exercise 4.6 in SICP, look for the topic "Derived Expressions". The key idea here is that if you find an expression such as this one:
(let ((x 1)
(y 2))
(+ x y))
You must transform it into this expression, that can be easily evaluated:
((lambda (x y)
(+ x y))
1 2)
It is easy to implement let without worrying about lambda as let just extends the environment and evaluates the body in the newly extended environment. As so:
...
((let-stmt? e)
(let ((names (map car (cadr e)))
(inits (map cadr (cadr e))))
;; Evaluate inits in env
(let ((vals (map (lambda (init) (s6-interpret init env)) inits)))
;; Extend env with names+vals
(let ((new-env (append (map cons names vals) env)))
;; Eval body in new env
(s6-interpret (caddr e) new-env))))) ; assumes 'body' is one form...
...
You also get to avoid worrying about general function calls using this approach.