Advertisement
Guest User

Untitled

a guest
Mar 26th, 2015
240
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.37 KB | None | 0 0
  1. ;; ** The TOY interpreter
  2.  
  3. #lang pl 12
  4.  
  5. ;;; ==================================================================
  6. ;;; Syntax
  7.  
  8. #| The BNF:
  9. <TOY> ::= <num>
  10. | <id>
  11. | { set! <id> <TOY> }
  12. | { bind {{ <id> <TOY> } ... } <TOY> <TOY> ... }
  13. | { bindrec {{ <id> <TOY> } ... } <TOY> <TOY> ... }
  14. | { fun { <id> ... } <TOY> <TOY> ... }
  15. | { rfun { <id> ... } <TOY> <TOY> ... }
  16. | { if <TOY> <TOY> <TOY> }
  17. | { <TOY> <TOY> ... }
  18. |#
  19.  
  20. ;; A matching abstract syntax tree datatype:
  21. (define-type TOY
  22. [Num Number]
  23. [Id Symbol]
  24. [Set Symbol TOY]
  25. [Bind (Listof Symbol) (Listof TOY) (Listof TOY)]
  26. [BindRec (Listof Symbol) (Listof TOY) (Listof TOY)]
  27. [Fun (Listof Symbol) (Listof TOY)]
  28. [RFun (Listof Symbol) (Listof TOY)]
  29. [Call TOY (Listof TOY)]
  30. [If TOY TOY TOY])
  31.  
  32. (: unique-list? : (Listof Any) -> Boolean)
  33. ;; Tests whether a list is unique, used to guard Bind and Fun values.
  34. (define (unique-list? xs)
  35. (or (null? xs)
  36. (and (not (member (first xs) (rest xs)))
  37. (unique-list? (rest xs)))))
  38.  
  39. (: parse-sexpr : Sexpr -> TOY)
  40. ;; to convert s-expressions into TOYs
  41. (define (parse-sexpr sexpr)
  42. (match sexpr
  43. [(number: n) (Num n)]
  44. [(symbol: name) (Id name)]
  45. [(cons 'set! more)
  46. (match sexpr
  47. [(list 'set! (symbol: name) new) (Set name (parse-sexpr new))]
  48. [else (error 'parse-sexpr "bad `set!' syntax in ~s" sexpr)])]
  49. [(cons (and binder (or 'bind 'bindrec)) more)
  50. (match sexpr
  51. [(list _ (list (list (symbol: names) (sexpr: nameds)) ...)
  52. (sexpr: body0) (sexpr: body) ...)
  53. (if (unique-list? names)
  54. ((if (eq? 'bind binder) Bind BindRec)
  55. names
  56. (map parse-sexpr nameds)
  57. (map parse-sexpr (cons body0 body)))
  58. (error 'parse-sexpr
  59. "`~s' got duplicate names: ~s" binder names))]
  60. [else (error 'parse-sexpr
  61. "bad `~s' syntax in ~s" binder sexpr)])]
  62. [(cons (and funner (or 'fun 'rfun)) more)
  63. (match sexpr
  64. [(list _ (list (symbol: names) ...)
  65. (sexpr: body0) (sexpr: body) ...)
  66. (if (unique-list? names)
  67. ((if (eq? 'fun funner) Fun RFun)
  68. names
  69. (map parse-sexpr (cons body0 body)))
  70. (error 'parse-sexpr
  71. "`~s' got duplicate names: ~s" funner names))]
  72. [else (error 'parse-sexpr
  73. "bad `~s' syntax in ~s" funner sexpr)])]
  74. [(cons 'if more)
  75. (match sexpr
  76. [(list 'if cond then else)
  77. (If (parse-sexpr cond) (parse-sexpr then) (parse-sexpr else))]
  78. [else (error 'parse-sexpr "bad `if' syntax in ~s" sexpr)])]
  79. [(list fun (sexpr: args) ...) ; other lists are applications
  80. (Call (parse-sexpr fun)
  81. (map parse-sexpr args))]
  82. [else (error 'parse-sexpr "bad syntax in ~s" sexpr)]))
  83.  
  84. (: parse : String -> TOY)
  85. ;; Parses a string containing an TOY expression to a TOY AST.
  86. (define (parse str)
  87. (parse-sexpr (string->sexpr str)))
  88.  
  89. ;;; ==================================================================
  90. ;;; Values and environments
  91.  
  92. (define-type ENV
  93. [EmptyEnv]
  94. [FrameEnv FRAME ENV])
  95.  
  96. (define-type VAL
  97. [BogusV]
  98. [RktV Any]
  99. [FunV (Listof Symbol) (Listof TOY) ENV Boolean]
  100. [PrimV ((Listof VAL) -> VAL)])
  101.  
  102. ;; a frame is an association list of names and values.
  103. (define-type FRAME = (Listof (List Symbol (Boxof VAL))))
  104.  
  105. ;; a single bogus value to use wherever needed
  106. (define the-bogus-value (BogusV))
  107.  
  108.  
  109. (: raw-extend : (Listof Symbol) (Listof (Boxof VAL)) ENV -> ENV)
  110. ;; extends an environment with a new frame, given names and value
  111. ;; boxes
  112. (define (raw-extend names boxed-values env)
  113. (if (= (length names) (length boxed-values))
  114. (FrameEnv (map (lambda ([name : Symbol] [boxed-val : (Boxof VAL)])
  115. (list name boxed-val))
  116. names boxed-values)
  117. env)
  118. (error 'raw-extend "arity mismatch for names: ~s" names)))
  119.  
  120. (: extend : (Listof Symbol) (Listof VAL) ENV -> ENV)
  121. ;; extends an environment with a new frame (given plain values).
  122. (define (extend names values env)
  123. (raw-extend names (map (inst box VAL) values) env))
  124.  
  125. (: extend-rec : (Listof Symbol) (Listof TOY) ENV -> ENV)
  126. ;; extends an environment with a new recursive frame.
  127. (define (extend-rec names exprs env)
  128. (let* ([bvals (build-list
  129. (length names)
  130. (lambda (n)
  131. (box the-bogus-value)))]
  132. [benv (raw-extend names bvals env)]
  133. [vals (map (lambda ([expr : TOY]) (eval expr benv)) exprs)])
  134. (for-each (lambda ([val : VAL] [bogus-val : (Boxof VAL)])
  135. (set-box! bogus-val val))
  136. vals
  137. bvals)
  138. benv))
  139.  
  140. (: lookup : Symbol ENV -> (Boxof VAL))
  141. ;; lookup a symbol in an environment, frame by frame, return its value
  142. ;; or throw an error if it isn't bound
  143. (define (lookup name env)
  144. (cases env
  145. [(EmptyEnv) (error 'lookup "no binding for ~s" name)]
  146. [(FrameEnv frame rest)
  147. (let ([cell (assq name frame)])
  148. (if cell
  149. (second cell)
  150. (lookup name rest)))]))
  151.  
  152. (: unwrap-rktv : VAL -> Any)
  153. ;; helper for `racket-func->prim-val': unwrap a RktV wrapper in
  154. ;; preparation to be sent to the primitive function
  155. (define (unwrap-rktv x)
  156. (cases x
  157. [(RktV v) v]
  158. [else (error 'racket-func "bad input: ~s" x)]))
  159.  
  160. (: racket-func->prim-val : Function -> (Boxof VAL))
  161. ;; converts a racket function to a primitive evaluator function which
  162. ;; is a PrimV holding a ((Listof VAL) -> VAL) function. (the
  163. ;; resulting function will use the list function as is, and it is the
  164. ;; list function's responsibility to throw an error if it's given a
  165. ;; bad number of arguments or bad input types.)
  166. (define (racket-func->prim-val racket-func)
  167. (define list-func (make-untyped-list-function racket-func))
  168. (box (PrimV (lambda (args)
  169. (RktV (list-func (map unwrap-rktv args)))))))
  170.  
  171. ;; The global environment has a few primitives:
  172. (: global-environment : ENV)
  173. (define global-environment
  174. (FrameEnv (list (list '+ (racket-func->prim-val +))
  175. (list '- (racket-func->prim-val -))
  176. (list '* (racket-func->prim-val *))
  177. (list '/ (racket-func->prim-val /))
  178. (list '< (racket-func->prim-val <))
  179. (list '> (racket-func->prim-val >))
  180. (list '= (racket-func->prim-val =))
  181. ;; values
  182. (list 'true (box (RktV #t)))
  183. (list 'false (box (RktV #f))))
  184. (EmptyEnv)))
  185.  
  186. ;;; ==================================================================
  187. ;;; Evaluation
  188.  
  189.  
  190. (: get-boxes : (Listof TOY) ENV -> (Listof (Boxof VAL)))
  191. ;; helper function for run that consumes expressions and returns
  192. ;; a suitable list of boxes (references)
  193. (define (get-boxes toys env)
  194. (: get-box : TOY -> (Boxof VAL))
  195. (define (get-box toy)
  196. (cases toy
  197. [(Id name) (lookup name env)]
  198. [else (error 'rfun "non-identifier, got: ~s" toy)]))
  199. (map get-box toys))
  200.  
  201. (: eval-body : (Listof TOY) ENV -> VAL)
  202. ;; evaluates a list of expressions, returns the last value.
  203. (define (eval-body exprs env)
  204. (foldl (lambda ([expr : TOY]
  205. [prev-val : VAL])
  206. (eval expr env))
  207. the-bogus-value exprs))
  208.  
  209. (: eval : TOY ENV -> VAL)
  210. ;; evaluates TOY expressions.
  211. (define (eval expr env)
  212. ;; convenient helper
  213. (: eval* : TOY -> VAL)
  214. (define (eval* expr) (eval expr env))
  215. (cases expr
  216. [(Num n) (RktV n)]
  217. [(Id name) (unbox (lookup name env))]
  218. [(Bind names exprs bound-body)
  219. (eval-body bound-body (extend names (map eval* exprs) env))]
  220. [(BindRec names exprs bound-body)
  221. (eval-body bound-body (extend-rec names exprs env))]
  222. [(Fun names bound-body)
  223. (FunV names bound-body env false)]
  224. [(RFun names bound-body)
  225. (FunV names bound-body env true)]
  226. [(Call fun-expr arg-exprs)
  227. (let ([fval (eval* fun-expr)]
  228. [arg-vals (map eval* arg-exprs)])
  229. (cases fval
  230. [(PrimV proc) (proc arg-vals)]
  231. [(FunV names body fun-env by-ref?)
  232. (if by-ref?
  233. (eval-body body (raw-extend names
  234. (get-boxes arg-exprs env)
  235. fun-env))
  236. (eval-body body (extend names (map eval* arg-exprs) fun-env)))]
  237. [else (error 'eval "function call with a non-function: ~s"
  238. fval)]))]
  239. [(If cond-expr then-expr else-expr)
  240. (eval* (if (cases (eval* cond-expr)
  241. [(RktV v) v] ; Racket value => use as boolean
  242. [else #t]) ; other values are always true
  243. then-expr
  244. else-expr))]
  245. [(Set name expr)
  246. (set-box! (lookup name env) (eval* expr))
  247. the-bogus-value]))
  248.  
  249. (: run : String -> Any)
  250. ;; evaluate a TOY program contained in a string
  251. (define (run str)
  252. (let ([result (eval (parse str) global-environment)])
  253. (cases result
  254. [(RktV v) v]
  255. [else (error 'run
  256. "evaluation returned a bad value: ~s" result)])))
  257.  
  258. ;;; ==================================================================
  259. ;;; Tests
  260.  
  261.  
  262. (test (run "{{fun {x} {+ x 1}} 4}")
  263. => 5)
  264. (test (run "{bind {{add3 {fun {x} {+ x 3}}}} {add3 1}}")
  265. => 4)
  266. (test (run "{bind {{add3 {fun {x} {+ x 3}}}
  267. {add1 {fun {x} {+ x 1}}}}
  268. {bind {{x 3}} {add1 {add3 x}}}}")
  269. => 7)
  270. (test (run "{bind {{identity {fun {x} x}}
  271. {foo {fun {x} {+ x 1}}}}
  272. {{identity foo} 123}}")
  273. => 124)
  274. (test (run "{bind {{x 3}}
  275. {bind {{f {fun {y} {+ x y}}}}
  276. {bind {{x 5}}
  277. {f 4}}}}")
  278. => 7)
  279. (test (run "{{{fun {x} {x 1}}
  280. {fun {x} {fun {y} {+ x y}}}}
  281. 123}")
  282. => 124)
  283.  
  284. ;; More tests for complete coverage
  285. (test (run "{bind x 5 x}") =error> "bad `bind' syntax")
  286. (test (run "{fun x x}") =error> "bad `fun' syntax")
  287. (test (run "{if x}") =error> "bad `if' syntax")
  288. (test (run "{}") =error> "bad syntax")
  289. (test (run "{bind {{x 5} {x 5}} x}") =error> "bind* duplicate names")
  290. (test (run "{fun {x x} x}") =error> "fun* duplicate names")
  291. (test (run "{+ x 1}") =error> "no binding for")
  292. (test (run "{+ 1 {fun {x} x}}") =error> "bad input")
  293. (test (run "{+ 1 {fun {x} x}}") =error> "bad input")
  294. (test (run "{1 2}") =error> "with a non-function")
  295. (test (run "{{fun {x} x}}") =error> "arity mismatch")
  296. (test (run "{if {< 4 5} 6 7}") => 6)
  297. (test (run "{if {< 5 4} 6 7}") => 7)
  298. (test (run "{if + 6 7}") => 6)
  299. (test (run "{fun {x} x}") =error> "returned a bad value")
  300.  
  301. ;; assignment tests
  302. (test (run "{set! {+ x 1} x}") =error> "bad `set!' syntax")
  303. (test (run "{bind {{x 1}} {set! x {+ x 1}} x}") => 2)
  304.  
  305. ;; `bindrec' tests
  306. (test (run "{bindrec {x 6} x}") =error> "bad `bindrec' syntax")
  307. (test (run "{bindrec {{fact {fun {n}
  308. {if {= 0 n}
  309. 1
  310. {* n {fact {- n 1}}}}}}}
  311. {fact 5}}")
  312. => 120)
  313.  
  314. ;; tests for multiple expressions and assignment
  315. (test (run "{bind {{make-counter
  316. {fun {}
  317. {bind {{c 0}}
  318. {fun {}
  319. {set! c {+ 1 c}}
  320. c}}}}}
  321. {bind {{c1 {make-counter}}
  322. {c2 {make-counter}}}
  323. {* {c1} {c1} {c2} {c1}}}}")
  324. => 6)
  325. (test (run "{bindrec {{foo {fun {}
  326. {set! foo {fun {} 2}}
  327. 1}}}
  328. {+ {foo} {* 10 {foo}}}}")
  329. => 21)
  330. (test (run "{bind {{c 0}}
  331. {{fun {}
  332. {set! c {+ 1 c}}
  333. {set! c {+ 1 c}}
  334. {set! c {+ 1 c}}
  335. c}}}")
  336. => 3)
  337.  
  338. ;; `rfun' tests
  339. (test (run "{{rfun {x} x} 4}") =error> "non-identifier")
  340. (test (run "{bind {{swap! {rfun {x y}
  341. {bind {{tmp x}}
  342. {set! x y}
  343. {set! y tmp}}}}
  344. {a 1}
  345. {b 2}}
  346. {swap! a b}
  347. {+ a {* 10 b}}}")
  348. => 12)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement