PageRenderTime 42ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/interpreter.lisp

https://github.com/Bike/kernel
Lisp | 134 lines | 77 code | 7 blank | 50 comment | 0 complexity | 8c6f8f585d62a5226ffbd3f305768663 MD5 | raw file
  1. (in-package #:kernel)
  2. ;;;; Interpreter for a bootstrap implementation of the Kernel language.
  3. (deftype k-object () '(or k-symbol k-cons k-ignore k-inert k-null k-environment k-combiner function))
  4. (defun interpreter ()
  5. "Interface to the Kernel interpreter."
  6. (with-simple-restart (abort "Exit the Kernel interpreter.")
  7. (let ((working-environment (make-k-environment :parents (list *ground-environment*)))
  8. (*package* (find-package 'kernel-primitives)))
  9. (loop
  10. (with-simple-restart (abort "Return to the Kernel interpreter.")
  11. (format t "~&kernel> ")
  12. (let ((sexp (cl->kernel (read))))
  13. (fresh-line)
  14. (interpret sexp working-environment #'princ)))))))
  15. (defun cl->kernel (obj)
  16. "Convert a CL structure to a Kernel list. Signals an error on unknown types. Does not halt on cyclic lists."
  17. (etypecase obj
  18. (cons (k-cons (cl->kernel (car obj)) (cl->kernel (cdr obj))))
  19. (null %nil)
  20. (symbol
  21. (string-case ((symbol-name obj) :default obj)
  22. ;; Some constant symbols.
  23. ;; At least for now, %t is CL:t, %f is CL:nil,
  24. ;; and the others are unforgeable constants, namely, instances of unique structs
  25. ;; (see types.lisp)
  26. ("%T" t)
  27. ("%F" nil)
  28. ("%INERT" %inert)
  29. ("%IGNORE" %ignore)))))
  30. (defun interpret (expression environment continuation)
  31. "Interpret a Kernel list in a Kernel environment and pass the result to cont, a CL function."
  32. (etypecase expression
  33. (k-boolean (funcall continuation expression))
  34. (k-symbol (funcall continuation (lookup expression environment)))
  35. (k-cons (interp-call expression environment continuation))
  36. (k-object (funcall continuation expression))))
  37. (defun interp-call (call env cont)
  38. "Interpret a Kernel call in env, passing its result to cont."
  39. ;; This function is somewhat confusing in CPS, so hang on.
  40. ;; First we evaluate the car (e.g. the function position) in env, and pass that
  41. ;; to a continuation.
  42. (interpret (k-car call) env
  43. #'(lambda (combiner)
  44. ;; This continuation works out what to do based on the combiner's type.
  45. (etypecase combiner
  46. (k-operative-compound
  47. ;; If it's a compound operative (e.g., it was ($vau ...) at some point)
  48. ;; then we're "done"; evaluate its code in an environment augmented
  49. ;; with the arguments, passing the result to the continuation.
  50. (interpret (combiner-code combiner)
  51. (augment-environment
  52. (k-cons (operative-envparam combiner) (operative-arglist combiner))
  53. (k-cons env (k-cdr call))
  54. (make-k-environment :parents (list (operative-static-env combiner)) :bindings nil))
  55. cont))
  56. (k-operative-primitive
  57. ;; If it's a primitive operative, call it on the operand tree with the environment and continuation.
  58. (funcall (combiner-code combiner) (k-cdr call) env cont))
  59. (k-applicative
  60. ;; Sanity check that it's an applicative; if it's not, etypecase signals
  61. ;; an error. Hm, why am I going through the effort of error signalling?
  62. ;; This is just supposed to make a self-hosting implementation possible...
  63. ;; </muse>
  64. ;; So anyway, it's an applicative of some sort, so evaluate the arguments
  65. ;; and pass them to a specifically constructed continuation.
  66. ;; We have to go deeper and all.
  67. (map-interp (k-cdr call)
  68. env
  69. (if (typep combiner 'k-applicative-compound)
  70. ;; If it's a compound applicative, the receiving continuation
  71. ;; simply evaluates (in Kernel terms)
  72. ;; (cons (unwrap combiner) args)
  73. ;; in the given environment, with the continuation of the whole call.
  74. #'(lambda (args)
  75. (interpret (k-cons (combiner-code combiner) args)
  76. env
  77. cont))
  78. ;; If it's primitive, call the continuation on its result on
  79. ;; the args and the environment.
  80. ;; Primitive applicatives get a copy of the environment too.
  81. ;; Perhaps I'm too generous. But they can just (declare ignore env).
  82. #'(lambda (args)
  83. (funcall (combiner-code combiner) args env cont)))))))))
  84. (defun map-interp (exps env cont)
  85. "Evaluate exps as Kernel expressions from left to right in env and pass the Kernel list of them to cont."
  86. ;; exps may be cyclic, so we have to be a bit tricky.
  87. (multiple-value-bind (exps mu lambda) (decycle exps)
  88. ;; decycle returns the length of the acyclic prefix (mu)
  89. ;; and cycle (lambda) so that we can return a cyclic
  90. ;; structure if one was passed.
  91. (labels ((helper (expressions cont)
  92. ;; Helper to actually do the evaluating,
  93. ;; so we can avoid doing (comparatively expensive)
  94. ;; cycle-handling BS more than once.
  95. ;; It's still continuation-passing, though. Hi confusion!
  96. (if (eq expressions %nil)
  97. ;; If we're out of expressions, we're done; "return" Kernel nil.
  98. (funcall cont %nil)
  99. ;; If not, interpret the first expression...
  100. (interpret (k-car expressions) env
  101. ;; ...passing the result to a continuation, which...
  102. #'(lambda (x)
  103. ;; ...recursively calls helper, continuating to...
  104. (helper (k-cdr expressions)
  105. #'(lambda (y)
  106. ;; ...a regular goddamn cons.
  107. (funcall cont (k-cons x y)))))))))
  108. ;; Well, that was a mouthful.
  109. ;; Now just call the helper function with a continuation that returns
  110. ;; a structure of the same shape. And by "return" I mean "more CPS bullshit";
  111. ;; there has got to be a better way to write in this style.
  112. (helper exps
  113. (if (zerop lambda)
  114. ;; If there's no cycle, fuck this shit.
  115. cont
  116. ;; Welp.
  117. #'(lambda (list)
  118. ;; Traverse to the (mu)th cons of the return value,
  119. ;; remember what it is, then keep going to the
  120. ;; (mu + lambda - 1)th, and set its cdr to that.
  121. (do ((pos 0 (1+ pos))
  122. (cur list (k-cdr cur))
  123. (cycle-start nil))
  124. ((= pos (+ mu lambda -1))
  125. (setf (k-cdr cur) cycle-start)
  126. (funcall cont list))
  127. (when (= pos mu) (setf cycle-start cur)))))))))