University stuff.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

evaluator.scm 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. ;;; "Metacircular evaluator", basert på koden i seksjon 4.1.1-4.1.4 i SICP.
  2. ;;; Del av innlevering 3b i INF2810, vår 2017.
  3. ;;
  4. ;; Last hele filen inn i Scheme. For å starte read-eval-print loopen og
  5. ;; initialisere den globale omgivelsen, kjør:
  6. ;; (set! the-global-environment (setup-environment))
  7. ;; (read-eval-print-loop)
  8. ;;
  9. ;; Merk at det visse steder i koden, som i `special-form?', vanligvis
  10. ;; ville være mere naturlig å bruke `or' enn `cond'. Evaluatoren er
  11. ;; skrevet helt uten bruk av `and' / `or' for å vise at disse likevel
  12. ;; kan støttes i det implementerte språket selv om de ikke brukes i
  13. ;; implementeringsspråket. (Se oppgave 3a for mer om dette.)
  14. ;; hack for å etterlikne SICPs feilmeldinger:
  15. (define exit-to-toplevel 'dummy)
  16. (call-with-current-continuation
  17. (lambda (cont) (set! exit-to-toplevel cont)))
  18. (define (error reason . args)
  19. (display "ERROR: ")
  20. (display reason)
  21. (for-each (lambda (arg)
  22. (display " ")
  23. (write arg))
  24. args)
  25. (newline)
  26. (exit-to-toplevel))
  27. ;;; Selve kjernen i evaluatoren (seksjon 4.1.1, SICP):
  28. ;;; -----------------------------------------------------------------------
  29. ;; Merk at vi skiller ut evaluering av special forms i en egen prosedyre.
  30. (define (mc-eval exp env) ;; tilsvarer eval i SICP
  31. (cond ((self-evaluating? exp) exp)
  32. ((variable? exp) (lookup-variable-value exp env))
  33. ((special-form? exp) (eval-special-form exp env))
  34. ((application? exp)
  35. (mc-apply (mc-eval (operator exp) env)
  36. (list-of-values (operands exp) env)))
  37. (else
  38. (error "Unknown expression type -- mc-eval:" exp))))
  39. (define (mc-apply proc args) ;; tilsvarer apply i SICP
  40. (cond ((primitive-procedure? proc)
  41. (apply-primitive-procedure proc args))
  42. ((compound-procedure? proc)
  43. (eval-sequence
  44. (procedure-body proc)
  45. (extend-environment
  46. (procedure-parameters proc)
  47. args
  48. (procedure-environment proc))))
  49. (else
  50. (error
  51. "Unknown procedure type -- mc-apply:" proc))))
  52. (define (eval-special-form exp env)
  53. (cond ((quoted? exp) (text-of-quotation exp))
  54. ((assignment? exp) (eval-assignment exp env))
  55. ((definition? exp) (eval-definition exp env))
  56. ((if? exp) (eval-if exp env))
  57. ((lambda? exp)
  58. (make-procedure (lambda-parameters exp)
  59. (lambda-body exp)
  60. env))
  61. ((begin? exp)
  62. (eval-sequence (begin-actions exp) env))
  63. ((cond? exp) (mc-eval (cond->if exp) env))))
  64. (define (special-form? exp)
  65. (cond ((quoted? exp) #t)
  66. ((assignment? exp) #t)
  67. ((definition? exp) #t)
  68. ((if? exp) #t)
  69. ((lambda? exp) #t)
  70. ((begin? exp) #t)
  71. ((cond? exp) #t)
  72. (else #f)))
  73. (define (list-of-values exps env)
  74. (if (no-operands? exps)
  75. '()
  76. (cons (mc-eval (first-operand exps) env)
  77. (list-of-values (rest-operands exps) env))))
  78. (define (eval-if exp env)
  79. (if (true? (mc-eval (if-predicate exp) env))
  80. (mc-eval (if-consequent exp) env)
  81. (mc-eval (if-alternative exp) env)))
  82. (define (eval-sequence exps env)
  83. (cond ((last-exp? exps) (mc-eval (first-exp exps) env))
  84. (else (mc-eval (first-exp exps) env)
  85. (eval-sequence (rest-exps exps) env))))
  86. (define (eval-assignment exp env)
  87. (set-variable-value! (assignment-variable exp)
  88. (mc-eval (assignment-value exp) env)
  89. env)
  90. 'ok)
  91. (define (eval-definition exp env)
  92. (define-variable! (definition-variable exp)
  93. (mc-eval (definition-value exp) env)
  94. env)
  95. 'ok)
  96. ;;; Selektorene / aksessorene som definerer syntaksen til uttrykk i språket
  97. ;;; (seksjon 4.1.2, SICP)
  98. ;;; -----------------------------------------------------------------------
  99. (define (self-evaluating? exp)
  100. (cond ((number? exp) #t)
  101. ((string? exp) #t)
  102. ((boolean? exp) #t)
  103. (else #f)))
  104. (define (tagged-list? exp tag)
  105. (if (pair? exp)
  106. (eq? (car exp) tag)
  107. #f))
  108. (define (quoted? exp)
  109. (tagged-list? exp 'quote))
  110. (define (text-of-quotation exp) (cadr exp))
  111. (define (variable? exp) (symbol? exp))
  112. (define (assignment? exp)
  113. (tagged-list? exp 'set!))
  114. (define (assignment-variable exp) (cadr exp))
  115. (define (assignment-value exp) (caddr exp))
  116. (define (definition? exp)
  117. (tagged-list? exp 'define))
  118. (define (definition-variable exp)
  119. (if (symbol? (cadr exp))
  120. (cadr exp)
  121. (caadr exp)))
  122. (define (definition-value exp)
  123. (if (symbol? (cadr exp))
  124. (caddr exp)
  125. (make-lambda (cdadr exp)
  126. (cddr exp))))
  127. (define (lambda? exp) (tagged-list? exp 'lambda))
  128. (define (lambda-parameters exp) (cadr exp))
  129. (define (lambda-body exp) (cddr exp))
  130. (define (make-lambda parameters body)
  131. (cons 'lambda (cons parameters body)))
  132. (define (if? exp) (tagged-list? exp 'if))
  133. (define (if-predicate exp) (cadr exp))
  134. (define (if-consequent exp) (caddr exp))
  135. (define (if-alternative exp)
  136. (if (not (null? (cdddr exp)))
  137. (cadddr exp)
  138. 'false))
  139. (define (make-if predicate consequent alternative)
  140. (list 'if predicate consequent alternative))
  141. (define (begin? exp) (tagged-list? exp 'begin))
  142. (define (begin-actions exp) (cdr exp))
  143. (define (last-exp? seq) (null? (cdr seq)))
  144. (define (first-exp seq) (car seq))
  145. (define (rest-exps seq) (cdr seq))
  146. (define (sequence->exp seq)
  147. (cond ((null? seq) seq)
  148. ((last-exp? seq) (first-exp seq))
  149. (else (make-begin seq))))
  150. (define (make-begin seq) (cons 'begin seq))
  151. (define (application? exp) (pair? exp))
  152. (define (operator exp) (car exp))
  153. (define (operands exp) (cdr exp))
  154. (define (no-operands? ops) (null? ops))
  155. (define (first-operand ops) (car ops))
  156. (define (rest-operands ops) (cdr ops))
  157. (define (cond? exp) (tagged-list? exp 'cond))
  158. (define (cond-clauses exp) (cdr exp))
  159. (define (cond-else-clause? clause)
  160. (eq? (cond-predicate clause) 'else))
  161. (define (cond-predicate clause) (car clause))
  162. (define (cond-actions clause) (cdr clause))
  163. (define (cond->if exp)
  164. (expand-clauses (cond-clauses exp)))
  165. (define (expand-clauses clauses)
  166. (if (null? clauses)
  167. 'false ; no else clause
  168. (let ((first (car clauses))
  169. (rest (cdr clauses)))
  170. (if (cond-else-clause? first)
  171. (if (null? rest)
  172. (sequence->exp (cond-actions first))
  173. (error "ELSE clause isn't last -- COND->IF:"
  174. clauses))
  175. (make-if (cond-predicate first)
  176. (sequence->exp (cond-actions first))
  177. (expand-clauses rest))))))
  178. ;;; Evaluatorens interne datastrukturer for å representere omgivelser,
  179. ;;; prosedyrer, osv (seksjon 4.1.3, SICP):
  180. ;;; -----------------------------------------------------------------------
  181. (define (false? x)
  182. (cond ((eq? x 'false) #t)
  183. ((eq? x #f) #t)
  184. (else #f)))
  185. (define (true? x)
  186. (not (false? x)))
  187. ;; (som i SICP-Scheme'en vi tar med true/false som boolske verdier.)
  188. (define (make-procedure parameters body env)
  189. (list 'procedure parameters body env))
  190. (define (compound-procedure? p)
  191. (tagged-list? p 'procedure))
  192. (define (procedure-parameters p) (cadr p))
  193. (define (procedure-body p) (caddr p))
  194. (define (procedure-environment p) (cadddr p))
  195. (define (enclosing-environment env) (cdr env))
  196. (define (first-frame env) (car env))
  197. (define the-empty-environment '())
  198. ;; En ramme er et par der car er variablene
  199. ;; og cdr er verdiene:
  200. (define (make-frame variables values)
  201. (cons variables values))
  202. (define (frame-variables frame) (car frame))
  203. (define (frame-values frame) (cdr frame))
  204. (define (add-binding-to-frame! var val frame)
  205. (set-car! frame (cons var (car frame)))
  206. (set-cdr! frame (cons val (cdr frame))))
  207. (define (extend-environment vars vals base-env)
  208. (if (= (length vars) (length vals))
  209. (cons (make-frame vars vals) base-env)
  210. (if (< (length vars) (length vals))
  211. (error "Too many arguments supplied:" vars vals)
  212. (error "Too few arguments supplied:" vars vals))))
  213. ;; Søker gjennom listene av variabel-bindinger i første ramme og
  214. ;; så bakover i den omsluttende omgivelsen. (Moro; to nivåer av
  215. ;; interne definisjoner med gjensidig rekursjon.)
  216. (define (lookup-variable-value var env)
  217. (define (env-loop env)
  218. (define (scan vars vals)
  219. ; paralell rekursjon på listene av symboler og verdier
  220. (cond ((null? vars)
  221. (env-loop (enclosing-environment env)))
  222. ((eq? var (car vars))
  223. (car vals))
  224. (else (scan (cdr vars) (cdr vals)))))
  225. (if (eq? env the-empty-environment)
  226. (error "Unbound variable:" var)
  227. (let ((frame (first-frame env)))
  228. (scan (frame-variables frame)
  229. (frame-values frame)))))
  230. (env-loop env))
  231. ;; Endrer bindingen av 'var' til 'val' i en omgivelse
  232. ;; (gir feil dersom 'var' ikke er bundet):
  233. (define (set-variable-value! var val env)
  234. (define (env-loop env)
  235. (define (scan vars vals)
  236. (cond ((null? vars)
  237. (env-loop (enclosing-environment env)))
  238. ((eq? var (car vars))
  239. (set-car! vals val))
  240. (else (scan (cdr vars) (cdr vals)))))
  241. (if (eq? env the-empty-environment)
  242. (error "Unbound variable -- SET!:" var)
  243. (let ((frame (first-frame env)))
  244. (scan (frame-variables frame)
  245. (frame-values frame)))))
  246. (env-loop env))
  247. ;; define-variable! legger til en ny binding mellom 'var' og 'val'
  248. ;; i den første rammen i omgivelsen 'env':
  249. (define (define-variable! var val env)
  250. (let ((frame (first-frame env)))
  251. (define (scan vars vals)
  252. (cond ((null? vars)
  253. (add-binding-to-frame! var val frame))
  254. ((eq? var (car vars))
  255. (set-car! vals val))
  256. (else (scan (cdr vars) (cdr vals)))))
  257. (scan (frame-variables frame)
  258. (frame-values frame))))
  259. ;;; Håndtering av primitiver og den globale omgivelsen (SICP seksjon 4.1.4)
  260. ;;; -----------------------------------------------------------------------
  261. (define (setup-environment)
  262. (let ((initial-env
  263. (extend-environment (primitive-procedure-names)
  264. (primitive-procedure-objects)
  265. the-empty-environment)))
  266. (define-variable! 'true 'true initial-env)
  267. (define-variable! 'false 'false initial-env)
  268. (define-variable! 'nil '() initial-env)
  269. initial-env))
  270. (define the-global-environment the-empty-environment)
  271. ;; For initialisering av den globale omgivelsen, se kommentar til slutt i fila.
  272. (define (primitive-procedure? proc)
  273. (tagged-list? proc 'primitive))
  274. (define (primitive-implementation proc) (cadr proc))
  275. (define primitive-procedures
  276. (list (list 'car car)
  277. (list 'cdr cdr)
  278. (list 'cons cons)
  279. (list 'null? null?)
  280. (list 'not not)
  281. (list '+ +)
  282. (list '- -)
  283. (list '* *)
  284. (list '/ /)
  285. (list '= =)
  286. (list 'eq? eq?)
  287. (list 'equal? equal?)
  288. (list 'display
  289. (lambda (x) (display x) 'ok))
  290. (list 'newline
  291. (lambda () (newline) 'ok))
  292. ;; her kan vi legge til flere primitiver.
  293. ))
  294. (define (primitive-procedure-names)
  295. (map car
  296. primitive-procedures))
  297. (define (primitive-procedure-objects)
  298. (map (lambda (proc) (list 'primitive (cadr proc)))
  299. primitive-procedures))
  300. (define apply-in-underlying-scheme apply)
  301. (define (apply-primitive-procedure proc args)
  302. (apply-in-underlying-scheme
  303. (primitive-implementation proc) args))
  304. ;;; Hjelpeprosedyrer for REPL-interaksjon (SICP seksjon 4.1.4)
  305. ;;; -----------------------------------------------------------------------
  306. (define input-prompt ";;; MC-Eval input:")
  307. (define output-prompt ";;; MC-Eval value:")
  308. (define (read-eval-print-loop) ;;tilsvarer driver-loop i SICP
  309. (prompt-for-input input-prompt)
  310. (let ((input (read)))
  311. (let ((output (mc-eval input the-global-environment)))
  312. (announce-output output-prompt)
  313. (user-print output)))
  314. (read-eval-print-loop))
  315. (define (prompt-for-input string)
  316. (newline) (newline) (display string) (newline))
  317. (define (announce-output string)
  318. (newline) (display string) (newline))
  319. (define (user-print object)
  320. (if (compound-procedure? object)
  321. (display (list 'compound-procedure
  322. (procedure-parameters object)
  323. (procedure-body object)
  324. '<procedure-env>))
  325. (display object)))
  326. 'METACIRCULAR-EVALUATOR-LOADED
  327. ;;; For å starte read-eval-print loopen og initialisere
  328. ;;; den globale omgivelsen, kjør:
  329. ;;; (set! the-global-environment (setup-environment))
  330. ;;; (read-eval-print-loop)