/hertfordstreet/schemes/evaluator_with_proper_defines.ss

https://github.com/GunioRobot/hobby-code · Scheme · 313 lines · 250 code · 47 blank · 16 comment · 1 complexity · ecef92ccd4943cd7247fea4f1c54bf99 MD5 · raw file

  1. #lang scheme
  2. (require scheme/mpair)
  3. (require (lib "trace.ss"))
  4. ;the heart of the evaluator
  5. ;dynamic scope
  6. (define (dynamic-eval exp env)
  7. (cond ((number? exp) exp)
  8. ((eq? exp 'true) #t)
  9. ((eq? exp 'false) #f)
  10. ((eq? exp 'nil) '())
  11. ((symbol? exp) (lookup exp env))
  12. ((eq? (car exp) 'define) (define-variable! (cadr exp) (dynamic-eval (caddr exp) env) env))
  13. ((eq? (car exp) 'quote) (cadr exp))
  14. ((or (eq? (car exp) 'λ) (eq? (car exp) 'lambda))
  15. (list 'closure (cdr exp) env))
  16. ((eq? (car exp) 'cond) (evcond (cdr exp) env))
  17. (else (dynamic-apply (dynamic-eval (car exp) env) (evlist (cdr exp) env) env))))
  18. (define (dynamic-apply proc args env)
  19. (cond ((primitive? proc) (apply-primop proc args))
  20. ((eq? (car proc) 'closure)
  21. (dynamic-eval (cadadr proc) (bind (caadr proc) args env)))
  22. (else (error (format "dynamic-apply error: trying to apply ~a to ~a in environment ~a" proc args env)))))
  23. ;lexical scope
  24. (define (lexical-eval exp env)
  25. (cond ((number? exp) exp)
  26. ((eq? exp 'true) #t)
  27. ((eq? exp 'false) #f)
  28. ((eq? exp 'nil) '())
  29. ((symbol? exp) (lookup exp env))
  30. ((eq? (car exp) 'define) (define-variable! (cadr exp) (lexical-eval (caddr exp) env) env))
  31. ((eq? (car exp) 'quote) (cadr exp))
  32. ((or (eq? (car exp) 'λ) (eq? (car exp) 'lambda))
  33. (list 'closure (cdr exp) env))
  34. ((eq? (car exp) 'cond)
  35. (evcond (cdr exp) env))
  36. (else (lexical-apply (lexical-eval (car exp) env) (evlist (cdr exp) env)))))
  37. (define (lexical-apply proc args)
  38. (cond ((primitive? proc) (apply-primop proc args))
  39. ((eq? (car proc) 'closure)
  40. (lexical-eval (cadadr proc) (bind (caadr proc) args (caddr proc))))
  41. (else (error (format "lexical-apply error: trying to apply ~a to ~a" proc args)))))
  42. ;choose which one to use
  43. (define eval lexical-eval)
  44. ;evaluator common procedures
  45. (define (evlist explist env)
  46. (if (null? explist) '()
  47. (cons (eval (car explist) env) (evlist (cdr explist) env))))
  48. (define (evcond clauses env)
  49. (cond ((null? clauses) (error "cond with no true clauses (missing else?)"))
  50. ((eq? (caar clauses) 'else)
  51. (eval (cadar clauses) env))
  52. ((false? (eval (caar clauses) env))
  53. (evcond (cdr clauses) env))
  54. (else (eval (cadar clauses) env))))
  55. ;construction of environments (Sussman version)
  56. (define (bind vars vals env) (mcons (pair-up vars vals) env))
  57. (define (pair-up vars vals)
  58. (cond
  59. ((eq? vars '()) (cond ((eq? vals '()) '())
  60. (else (error 'TMA))))
  61. ((symbol? vars) (mcons (mcons vars vals) '()))
  62. ((eq? vals '()) (error 'TFA))
  63. (else
  64. (mcons (mcons (car vars)
  65. (car vals))
  66. (pair-up (cdr vars)
  67. (cdr vals))))))
  68. (define (lookup sym env)
  69. (cond ((eq? env '()) (error (format "unbound variable: ~a" sym)))
  70. (else
  71. ((lambda (vcell)
  72. (cond ((eq? vcell '())
  73. (lookup sym
  74. (mcdr env)))
  75. (else (mcdr vcell))))
  76. (new-assq sym (mcar env))))))
  77. (define (new-assq sym alist)
  78. (cond ((eq? alist '()) '())
  79. ((eq? sym (mcar (mcar alist)))
  80. (mcar alist))
  81. (else (new-assq sym (mcdr alist)))))
  82. (define (define-variable! var val env)
  83. (let ((frame (mcar env)))
  84. (let ((vcell (new-assq var frame)))
  85. (if (null? vcell)
  86. (let ((newvcell (mcons var val))
  87. (newlink (mcons (mcar frame) (mcdr frame))))
  88. (set-mcar! frame newvcell)
  89. (set-mcdr! frame newlink)
  90. (format "defined ~a" var))
  91. (begin (set-mcdr! vcell val) (format "mutated ~a!!" var))))))
  92. (define (environmentchangetests)
  93. (let* ((littleenv (bind '(y z) '(10 20) (bind '(z w) '(100 200) '())))
  94. (bigenv (bind '(x y) '(1 2) littleenv)))
  95. (define-variable! 'x 1000 bigenv)
  96. (define-variable! 'u 2000 bigenv)
  97. (define-variable! 'z 3000 bigenv)
  98. (list
  99. (test (lookup 'x bigenv) 1000)
  100. (test (map (λ(x) (lookup x bigenv)) '(x y z w u)) '(1000 2 3000 200 2000))
  101. (test (map (λ(x) (lookup x littleenv)) '(y z w)) '(10 20 200))
  102. (failtest (lookup 'a bigenv))
  103. )))
  104. ;pre-defined primitive ops
  105. (define (primitive? op) (or (eq? op 'prim-times)
  106. (eq? op 'prim-plus)
  107. (eq? op 'prim-equals)
  108. (eq? op 'prim-car)
  109. (eq? op 'prim-cdr)
  110. (eq? op 'prim-cons)
  111. (eq? op 'prim-minus)
  112. (eq? op 'prim-lessthan)
  113. (eq? op 'prim-eq?)))
  114. (define (apply-primop op args)
  115. (cond ((eq? op 'prim-times) (apply * args))
  116. ((eq? op 'prim-plus) (apply + args))
  117. ((eq? op 'prim-equals) (apply = args))
  118. ((eq? op 'prim-car) (apply car args))
  119. ((eq? op 'prim-cdr) (apply cdr args))
  120. ((eq? op 'prim-cons) (apply cons args))
  121. ((eq? op 'prim-lessthan) (apply < args))
  122. ((eq? op 'prim-minus) (apply - args))
  123. ((eq? op 'prim-eq?) (apply eq? args))
  124. ))
  125. (define init-env (bind '(* + = car cdr cons < - eq? ) '(prim-times prim-plus prim-equals prim-car prim-cdr prim-cons prim-lessthan prim-minus prim-eq? )'()))
  126. ;tests
  127. (define-syntax-rule (test expression expected-result)
  128. (let ((expstring (format "~a" (quote expression))))
  129. (with-handlers (((λ(v) #t) (λ(v) (list #f (format "~a failed with [~a]" expstring v)))))
  130. (let* ((result expression)
  131. (success (equal? result expected-result)))
  132. (list success (format "~a -> ~a (should be ~a)" expstring result expected-result))))))
  133. (define-syntax-rule (failtest expression)
  134. (let ((expstring (format "~a" (quote expression))))
  135. (with-handlers (((λ(v) #t) (λ(v) (list #t (format "~a failed with [~a]" expstring v)))))
  136. (let ((result expression))
  137. (list #f (format "~a should have failed, but completed, returning ~a" expstring result))))))
  138. (define (test-testing-tests)
  139. (list
  140. (failtest (lookup 'z (bind '(x y z) '(1 2 3) '())))
  141. (test (lookup 'z (bind '(x y z) '(1 2 3) '())) 4)
  142. (test (lookup 'w (bind '(x y z) '(1 2 3) '())) 3)
  143. ))
  144. (let ((ttt (test-testing-tests)))
  145. (if (for/or ((i ttt)) (car i))
  146. (error (format "test-testing-tests are broken ~a" (map cdr (filter car ttt))))
  147. "tests tested successfully"))
  148. (define xyz123env (bind '(x y z) '(1 2 3) '()))
  149. (define (environmenttests)
  150. (list
  151. (test (lookup 'z xyz123env) 3)
  152. (failtest (lookup 'w xyz123env))
  153. (test (map (λ (q) (lookup q (bind '(w z) '(20 50) xyz123env))) '(x y z w)) '(1 2 50 20))
  154. (failtest (lookup 'u (bind '(w z) '(20 50) xyz123env)))
  155. (test (map (λ (x) (lookup x (bind '() '() (bind '(x z) '(10 2) (bind '(x . y) '(1 2 3 4) init-env))))) '(x z y * =))
  156. '(10 2 (2 3 4) prim-times prim-equals))
  157. (failtest (bind '(x) '(1 2) init-env))
  158. (failtest (bind '(x y) '(1) init-env))))
  159. (define (applytests)
  160. (list
  161. (failtest (dynamic-apply '(closure 'a 'b 'c) '() '()))
  162. (failtest (lexical-apply '(lambda 'a 'b 'c) '()))))
  163. (define (evaltests)
  164. (list
  165. (test (eval '2 '()) 2)
  166. (test (eval '(quote x) '()) 'x)
  167. (test (eval 'x (bind 'x '4 '())) 4)
  168. (test (eval '((λ (x) (* x x)) 2) init-env) 4)
  169. (test (eval '((lambda (x) (* x x)) 2) init-env) 4)
  170. (test (eval '(cond ((= x 1) 'one) ((= x 2) 'two) (else 'other)) (bind '(x) '(2) init-env)) 'two)
  171. (test (eval '(cond ((= x 1) 'one) ((= x 2) 'two) (else 'other)) (bind '(x) '(1) init-env)) 'one)
  172. (test (eval '(cond ((= x 1) 'one) ((= x 2) 'two) (else 'other)) (bind '(x) '(3) init-env)) 'other)
  173. (test (eval '((λ (x) (cond ((= x 1) 'one) ((= x 2) 'two) (else 'other))) 1) init-env) 'one)
  174. (test (eval '((λ (x) (cond ((= x 1) 'one) ((= x 2) 'two) (else 'other))) 2) init-env) 'two)
  175. (test (eval '((λ (x) (cond ((= x 1) 'one) ((= x 2) 'two) (else 'other))) 3) init-env) 'other)
  176. (test (eval '((λ (x) (* x x)) 2) init-env) 4)
  177. (test (eval '((λ (x . y) (* x (* (car y) (car (cdr y))))) 1 2 3 4) init-env) 6)
  178. (test (eval '((λ y (* (car y) (car (cdr y)) (car (cdr (cdr y))))) 1 2 3 4) init-env) 6)
  179. (test (eval '((λ x x) 1 2 3 4 5) '()) '(1 2 3 4 5))
  180. (test (eval '((λ x (* (car x) (car (cdr x)))) 3 4) init-env) 12)
  181. (test (eval '((λ () (* x (* (car y) (car (cdr y)))))) (bind '(x y) '(10 (2 3)) init-env)) 60)
  182. (test (eval '((lambda(x) (cons (cdr x)(car x))) (cons 'a 'b)) init-env) '(b . a))
  183. (test (eval '((lambda (list) (list 1 2 3))(lambda l l)) init-env) '(1 2 3))
  184. (test (eval '((lambda (n)
  185. ((lambda (fact) (fact fact n))
  186. (lambda (ft k) (cond ((< k 2) k) (else (* k (ft ft (- k 1))))))))
  187. 10) init-env) 3628800)
  188. (test (eval '(cond (true 'hi)) init-env) 'hi)
  189. (failtest (eval '(cond (false 'hi)) init-env))
  190. ))
  191. (define cuteexp '((λ (f) ((λ (x) (f)) 'dynamic)) ((λ (x) (λ () x)) 'lexical)))
  192. (define (dynamic-binding-tests)
  193. (list
  194. (test (eval '((λ (p y) (p 5)) (λ (x) (* x y)) 10) init-env) 50)
  195. (test (eval '((λ (p y) (p 5)) (λ (x) (* x y)) 10) init-env) 50)
  196. (failtest (eval '(((λ (y) (λ (x) (* y x))) 2) 3) init-env))
  197. (test (eval '((λ (p y) (p 5)) ((λ (y) (λ (x) (* x y))) 100) 10) init-env) 50)
  198. (test (eval '((λ (f) (* ((λ (x) (f)) 3)((λ (x) (f)) 4))) ((λ (x) (λ () x)) 2)) init-env) 12)
  199. (test (eval cuteexp init-env) 'dynamic)))
  200. (define (lexical-binding-tests)
  201. (list
  202. (failtest (eval '((λ (p y) (p 5)) (λ (x) (* x y)) 10) init-env))
  203. (failtest (eval '((λ (p y) (p 5)) (λ (x) (* x y)) 10) init-env))
  204. (test (eval '(((λ (y) (λ (x) (* y x))) 2) 3) init-env) 6 )
  205. (test (eval '((λ (p y) (p 5)) ((λ (y) (λ (x) (* x y))) 100) 10) init-env) 500)
  206. (test (eval '((λ (f) (* ((λ (x) (f)) 3)((λ (x) (f)) 4))) ((λ (x) (λ () x)) 2)) init-env) 4)
  207. (test (eval cuteexp init-env) 'lexical)
  208. ))
  209. (define (dotests . testprocs)
  210. (let* ((t (append-map (λ(p)(p)) testprocs ))
  211. (success (for/and ((i t)) (car i)))
  212. (failingtests (map (λ(s) (cadr s))(filter (λ(s) (not (car s))) t))))
  213. (if success "all tests pass" (list "!!!!! TEST FAILURE!!!!!->" failingtests))))
  214. (define (lexical-binding)
  215. (set! eval lexical-eval)
  216. (printf "evaluating with lexical binding: ~n"))
  217. (define (dynamic-binding)
  218. (set! eval dynamic-eval)
  219. (printf "evaluating with dynamic binding: ~n"))
  220. (dynamic-binding)
  221. (dotests environmentchangetests environmenttests evaltests applytests dynamic-binding-tests)
  222. (lexical-binding)
  223. (dotests environmentchangetests environmenttests evaltests applytests lexical-binding-tests)
  224. (lexical-binding)
  225. ;towards an interactive top level
  226. (define (toplevel)
  227. (let ((entered (read)))
  228. (printf "~a~n" (eval entered init-env))
  229. (toplevel)))
  230. (define (read-exp exp) (eval exp init-env))
  231. (define (read-exps explist)
  232. (for ((e explist)) (print (read-exp e))))
  233. (define prelude
  234. '((define square (lambda (x) (* x x)))
  235. (define factorial (lambda (n) (cond ((< n 2) n)(else (* n (factorial (- n 1)))))))
  236. (define not (lambda (x) (cond (x false) (else true))))
  237. (define even? (lambda (x) (cond ((= x 0) true) (else (odd? (- x 1))))))
  238. (define odd? (lambda (x) (cond ((= x 0) false) (else (even? (- x 1))))))
  239. (define map (lambda (f lst) (cond ((eq? lst nil) nil) (else (cons (f (car lst)) (map f (cdr lst)))))))
  240. (define seq (lambda (a b) (cond ((= a b)(cons a nil)) (else (cons a (seq (+ a 1) b))))))))
  241. (define (preludetests)
  242. (list (test (eval '(map square (seq 0 10)) init-env) '(0 1 4 9 16 25 36 49 64 81 100))
  243. (test (eval '(map factorial (seq 0 10)) init-env) '(0 1 2 6 24 120 720 5040 40320 362880 3628800))
  244. (test (eval '(map even? (seq 0 10)) init-env) '(#t #f #t #f #t #f #t #f #t #f #t))
  245. (test (eval '(map odd? (seq 0 10)) init-env) '(#f #t #f #t #f #t #f #t #f #t #f))
  246. (test (eval '(not false) init-env) #t)
  247. ))
  248. (define (alltraceon) (trace dynamic-eval lexical-eval eval lexical-apply dynamic-apply)); bind pair-up lookup evlist evcond))
  249. (define (alltraceoff) (untrace dynamic-eval lexical-eval eval lexical-apply dynamic-apply bind pair-up lookup evlist evcond))
  250. ;now turn all tracing on so that we can watch evaluation at the repl
  251. (alltraceoff)
  252. (printf "reading the prelude...")
  253. (read-exps prelude)
  254. (printf "read~n")
  255. (printf "testing the prelude...")
  256. (dotests preludetests)
  257. (alltraceon)
  258. (printf "try: ~a~n" '(eval '((λ (f) ((λ (x) (f)) 'dynamic)) ((λ (x) (λ () x)) 'lexical)) '()))
  259. (printf "use (set! eval dynamic-eval) and (set! eval lexical-eval) to change between the two philosophies")
  260. (printf " (toplevel) for the REPL~n")
  261. #|
  262. (eval '((lambda (n)
  263. ((lambda (fact) (fact fact n))
  264. (lambda (ft k) (cond ((< k 2) k) (else (* k (ft ft (- k 1))))))))
  265. 0) '((* . prim-times)(- . prim-minus)(< . prim-lessthan)))
  266. |#