reorganized-code.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. ;; ============
  2. ;; INTRODUCTION
  3. ;; ============
  4. ;; This chapter deals with the writing of a simple interpreter for
  5. ;; S-expressions. The code is reorganized to be easier to understand.
  6. ;; =============
  7. ;; PREREQUISITES
  8. ;; =============
  9. ;; These are defined elsewhere in the book and simply used in this chapter.
  10. (define first car)
  11. (define rest cdr)
  12. (define second cadr)
  13. (define third caddr)
  14. (define build list)
  15. (define (atom? sth)
  16. (and (not (pair? sth))
  17. (not (null? sth))))
  18. ;; ===========================
  19. ;; DATA ABSTRACTION FOR TABLES
  20. ;; ===========================
  21. ;; Tables or better environments could be implemented in many ways. Here we use
  22. ;; lists for simplicity. Lists do, in this case, incur a performance cost when
  23. ;; looking up bindings in the environment of an expression, but not in the case
  24. ;; of extending an environment.
  25. ;; Ideally what one would like to have is a data structure, that has constant
  26. ;; time random access (O(1)) and constant time adding to the environment.
  27. ;; To abstract from the underlying data structure we define the interface in
  28. ;; terms of procedures. This will enable us to keep changes local, if we want to
  29. ;; change the underlying data structure.
  30. (define new-entry build)
  31. (define extend-table cons)
  32. (define empty-table '()) ; not defined in the book
  33. (define lookup-in-entry
  34. (lambda (name entry lookup-fallback-proc)
  35. (lookup-in-entry-helper name
  36. (first entry)
  37. (second entry)
  38. lookup-fallback-proc)))
  39. (define lookup-in-entry-helper
  40. (lambda (name keys values lookup-fallback-proc)
  41. (cond
  42. [(null? keys) (lookup-fallback-proc name)]
  43. [(eq? name (first keys)) (first values)]
  44. [else
  45. (lookup-in-entry-helper name
  46. (rest keys)
  47. (rest values)
  48. lookup-fallback-proc)])))
  49. (define lookup-in-empty-table ; not defined in the book
  50. (lambda (name)
  51. "Hopefully this code will never be run. It will result in an error."
  52. ;; Here is the original version of the code from the book:
  53. #;(car '())
  54. ;; Instead we are goint to raise a proper error. The book does not do this,
  55. ;; in order to not have to introduce exception handling as well.
  56. (throw 'identifier-not-in-table name)))
  57. (define new-table ; not defined in the book
  58. (lambda (entry)
  59. (extend-table entry empty-table)))
  60. (define lookup-in-table
  61. (lambda (name table lookup-failure-proc)
  62. (cond
  63. [(null? table) (lookup-failure-proc name)]
  64. [else
  65. (lookup-in-entry name
  66. (first table)
  67. ;; fallback will be to look up the name in the rest of
  68. ;; the table
  69. (lambda (name)
  70. (lookup-in-table name
  71. (rest table)
  72. ;; the failure proc is retained, to be
  73. ;; called when there are no more
  74. ;; entries to continue the lookup in
  75. lookup-failure-proc)))])))
  76. ;; ======================
  77. ;; EXPRESSIONS TO ACTIONS
  78. ;; ======================
  79. ;; An S-expression is either an atom or a list.
  80. ;; -> There are 2 cases to distinguish, when transforming an S-expression to an
  81. ;; action.
  82. (define expression-to-action
  83. (lambda (expr)
  84. "Having to evaluate an expression, one first has to know what to do
  85. next. This procedure delegates the decision what to do next to 2 procedures."
  86. (cond [(atom? expr) (atom-to-action expr)]
  87. [else (list-to-action expr)])))
  88. (define atom-to-action
  89. (lambda (#|atom|# a)
  90. ;; An atom is always a constant or it is an identifier, which is bound to
  91. ;; something else, which we hopefully find inside a table/an environment.
  92. (cond [(number? a) *const]
  93. [(eq? a #t) *const]
  94. [(eq? a #f) *const]
  95. [(eq? a 'cons) *const]
  96. [(eq? a 'car) *const]
  97. [(eq? a 'cdr) *const]
  98. [(eq? a 'null?) *const]
  99. [(eq? a 'eq?) *const]
  100. [(eq? a 'atom?) *const]
  101. [(eq? a 'zero?) *const]
  102. [(eq? a 'add1) *const]
  103. [(eq? a 'sub1) *const]
  104. [(eq? a 'number?) *const]
  105. [else *identifier])))
  106. (define list-to-action
  107. (lambda (#|list|# l)
  108. (cond [(atom? (car l))
  109. (cond [(eq? (car l) 'quote) *quote]
  110. [(eq? (car l) 'lambda) *lambda]
  111. [(eq? (car l) 'cond) *cond]
  112. [else *application])]
  113. [else *application])))
  114. ;; =================
  115. ;; VALUE AND MEANING
  116. ;; =================
  117. ;; The procedure `meaning` approximates `eval`.
  118. (define value
  119. (lambda (expr)
  120. "The procedure value evaluates an expression giving an empty environment."
  121. (meaning expr empty-table)))
  122. (define meaning
  123. (lambda (expr table)
  124. "The procedure meaning evaluates an expression given an environment. It does
  125. so by figuring out what the next action to take is and then applies that action
  126. to the expression and the given environment."
  127. ((expression-to-action expr) expr table)))
  128. ;; =====================
  129. ;; DEFINITION OF ACTIONS
  130. ;; =====================
  131. (define *const
  132. (lambda (expr table)
  133. "This interpreter does not deal with strings. There are only numbers,
  134. booleans and primitive procedures and compositions of those."
  135. (cond
  136. [(number? expr) expr]
  137. [(eq? expr #t) #t]
  138. [(eq? expr #f) #f]
  139. [else
  140. (build 'primitive expr)])))
  141. (define *quote
  142. (lambda (expr table)
  143. (text-of-quotation expr)))
  144. ;; Explanation: Something quoted has the form `'(quote something)`, so the second
  145. ;; part is the quoted thing. But why name it its "text"?
  146. (define *identifier
  147. (lambda (expr table)
  148. "An identifier's meaning depends on the environment it is defined in, so we
  149. look up its meaning there."
  150. (lookup-in-table expr
  151. table
  152. ;; Lookup in an empty table, which is the
  153. ;; lookup-failure-proc, will result in an error.
  154. lookup-in-empty-table)))
  155. (define *lambda
  156. (lambda (the-lambda table)
  157. "This will result in something like the following:
  158. (list 'non-primitive
  159. <table>
  160. <formals-of-lambda>
  161. <body-of-lambda>)"
  162. (build 'non-primitive
  163. (cons table
  164. ;; The cdr of a lambda expression is its argument list and a
  165. ;; body.
  166. (cdr the-lambda)))))
  167. (define *cond
  168. (lambda (cond-expr table)
  169. "Evaluating a cond expression is simple. One needs only to figure out which
  170. of the questions or conditions is true, starting from the first of the
  171. cond-cases. Once a true question or condition has been found one needs to
  172. evaluate its answer or consequent, using the given environment or table."
  173. (evaluate-cond-q-and-a-lines (cond-q-and-a-lines-of cond-expr) table)))
  174. (define *application
  175. (lambda (expr table)
  176. "An *application is always an application of a primitive (for example: car,
  177. cdr, ...) or application of a user defined procedure, a non-primitive."
  178. ;; The idea here is to first evaluate the procedure and its arguments,
  179. ;; before applying the procedure to its arguments.
  180. (apply
  181. ;; We get the definition for the procedure out of the given environment.
  182. (meaning (function-of-applicable-expr expr) table)
  183. ;; Then we evaluate the arguments of the procedure call using the given
  184. ;; environment.
  185. (evaluate-list (arguments-of expr) table))))
  186. ;; =============================
  187. ;; DATA ABSTRACTIONS FOR ACTIONS
  188. ;; =============================
  189. (define text-of-quotation second)
  190. (define table-of first)
  191. (define formals-of second)
  192. (define body-of third)
  193. (define question-of-cond-branch first)
  194. (define answer-of-cond-branch second)
  195. (define cond-q-and-a-lines-of cdr)
  196. (define function-of-applicable-expr car)
  197. (define arguments-of cdr)
  198. (define else?
  199. (lambda (something)
  200. ;; The book includes a check for `atom?`. I am not sure why this is
  201. ;; necessary, if we already check whether `something` is equal to the symbol
  202. ;; `else`.
  203. (eq? something 'else)))
  204. ;; The book distinguishes between primitives and non-primitives as types of
  205. ;; functions, where primitives are the ones, which need to already be defined in
  206. ;; our language to construct non-primitives from them. The functions are marked
  207. ;; as primitives or non-primitives by putting a tag (a symbol) in an expression,
  208. ;; which represents a primitive or a non-primitive. It is tagged data.
  209. ;; We can see this being put in our implementation of *lambda and *const. In the
  210. ;; predicates `primitive?` and `non-primitive?` we look for those markers.
  211. (define primitive?
  212. (lambda (sth)
  213. (eq? (first sth) 'primitive)))
  214. (define non-primitive?
  215. (lambda (sth)
  216. (eq? (first sth) 'non-primitive)))
  217. ;; =====================
  218. ;; EVALUATION PROCEDURES
  219. ;; =====================
  220. ;; Writing a function for evaluating a cond expression. This can be done,
  221. ;; because we now have code that evaluates code and thus is one level above the
  222. ;; evaluated code. This is why we do only need a normal function to evaluate a
  223. ;; cond expression. A macro is basically the same thing as such one level above
  224. ;; code, because it processes the code and can perform syntax transformations
  225. ;; before the code is run.
  226. ;; Also note, that cond is not an *application. As the book continues to
  227. ;; explain, in an *application expression, all the arguments must be evaluated,
  228. ;; before the application can be done. This must not be the case for a cond
  229. ;; expression. Instead, only parts of it shall be evaluated.
  230. (define evaluate-cond-q-and-a-lines
  231. (lambda (q-and-a-lines table)
  232. ;; When evaluating a cond expression, we in turn rely on cond in our
  233. ;; one-level-above language. This cond is not necessarily the same as the
  234. ;; cond in the code we are processing.
  235. (cond
  236. ;; Check for an else-branch. In this case evaluate the answer part.
  237. [(else? (question-of-cond-branch (car q-and-a-lines)))
  238. (meaning (answer-of-cond-branch (car q-and-a-lines))
  239. table)]
  240. ;; If there is a normal question, then it needs to be evaluated, to get its
  241. ;; boolean return value.
  242. [(meaning (question-of-cond-branch (car q-and-a-lines))
  243. table)
  244. (meaning (answer-of-cond-branch (car q-and-a-lines))
  245. table)]
  246. ;; Otherwise consider the next question answer pair.
  247. [else
  248. (evaluate-cond-q-and-a-lines (rest q-and-a-lines) table)])))
  249. (define evaluate-list
  250. (lambda (list-expression table)
  251. ;; This will "reduce" the list elements (arguments), so that they are ready
  252. ;; to have the function applied to them.
  253. (cond
  254. ;; If the list is empty, then the result is also empty.
  255. [(null? list-expression) '()]
  256. [else
  257. (cons (meaning (car list-expression) table)
  258. (evaluate-list (cdr list-expression) table))])))
  259. ;; =====
  260. ;; APPLY
  261. ;; =====
  262. ;; What follows are the procedures that, with the help of the procedures defined
  263. ;; before, apply procedures and primitives to expressions in the limited lisp
  264. ;; dialect we built.
  265. (define apply
  266. (lambda (func args)
  267. "apply receives the result of an evaluation of a procedure name, which is a "
  268. ;; Depending on whether a procedure is a primitive or a non-primitive, the
  269. ;; application happens in different ways.
  270. (cond
  271. [(primitive? func)
  272. ;; first of func is the tag for primitive or non-primitive.
  273. ;; '(primitive func-name args)
  274. (apply-primitive (second func) args)]
  275. [(non-primitive? func)
  276. (apply-closure (second func) args)]
  277. [else
  278. (throw 'wrong-call-to-apply func args)])))
  279. ;; Explanation: The whole time we were writing procedures to evaluate lists or
  280. ;; subsequently atoms, which represent programs. This `apply-primitive`
  281. ;; procedure is all about understanding, what kind of expression we have to
  282. ;; evaluate and then do the transformation, which leads to the evaluation result
  283. ;; in our one-level-above language. For example, if we find a `car`, then we
  284. ;; need to get the first element of the list that is in `vals`. This is what we
  285. ;; do in our one-level-above language. The result is returned as a value, which
  286. ;; is reinserted into the evaluated language's context.
  287. (define apply-primitive
  288. (lambda (name vals)
  289. (define :atom?
  290. (lambda (sth)
  291. (cond
  292. [(atom? sth) #t]
  293. ;; Why have more cases? Isn't the check for atom? sufficient? A: No,
  294. ;; we need to unwrap things. We tagged expressions with things like
  295. ;; 'primitive and 'non-primitive. Those could be atoms.
  296. [(null? sth) #f]
  297. ;; OK, primitives are countes as atoms then.
  298. [(eq? (car sth) 'primitive) #t]
  299. ;; Why #t?
  300. [(eq? (car sth) 'non-primitive) #t]
  301. [else #f])))
  302. (cond
  303. [(eq? name #| here was a gap |# 'cons)
  304. (cons (first vals) (second vals))]
  305. [(eq? name 'car)
  306. (car (first vals))]
  307. [(eq? name 'cdr)
  308. (#| here was a gap |# cdr (first vals))]
  309. [(eq? name 'null?)
  310. (null? (first vals))]
  311. [(eq? name 'eq?)
  312. (#| here was a gap |# eq? (first vals) #| here was a gap |# (second vals))]
  313. [(eq? name 'atom?)
  314. ;; Here we define the semantics of `atom?` in our evaluated language using
  315. ;; our meta language. Q: I don't know, why the semantics are different
  316. ;; from the ones we defined for `atom?` in our meta language. A: Now I
  317. ;; know: Because we need to unwrap for things we tagged as primitives, as
  318. ;; stated in the definition of `:atom?`.
  319. (#| here was a gap |# :atom? (first vals))]
  320. [(eq? name 'zero?)
  321. (zero? (first vals))]
  322. [(eq? name 'add1)
  323. (+ (first vals) 1)]
  324. [(eq? name 'sub1)
  325. (- (first vals) 1)]
  326. [(eq? name 'number?)
  327. (number? (first vals))])))
  328. (define apply-closure
  329. (lambda (closure vals)
  330. ;; Evaluate the closure with the extended environment.
  331. (meaning
  332. (body-of closure)
  333. ;; Add the arguments of the closure to its table, to be able to use
  334. ;; `meaning` on the body of the closure and the extended table
  335. ;; (environment).
  336. (extend-table (new-entry (formals-of closure) vals)
  337. (table-of closure)))))