REPL

Constant REPL 

Source
pub const REPL: &str = "; The prompt, as a task: it reads and prints by calling the console, and sends\n; itself the message asking for the next turn, so nothing here blocks. The\n; environment is the task\'s state - a file\'s definitions nest into the body that\n; follows them (`src/surface.rs`, `body`), and a line has no body to nest into.\n\n; The language without this file\'s own helpers, since a `define` binds only the\n; rest of the body and none of them is in scope yet.\n(define base (environment))\n\n; `(define name expr)` or `(define (f a b) body...)` as name and expression, or\n; `()` for a form that is not a definition. The split `src/surface.rs` makes,\n; made again because that one needs a body to nest into.\n(define (definition form)\n  (if (tagged? form \'define)\n      (let ((head (cadr form)))\n        (if (pair? head)\n            (cons (car head)\n                  (cons \'lambda\n                        (cons (car head) (cons (cdr head) (cdr (cdr form))))))\n            (cons head (caddr form))))\n      \'()))\n\n; What an import is worth showing: the value is a list of closures.\n(define (names frame)\n  (if (nil? frame) \'() (cons (car (car frame)) (names (cdr frame)))))\n\n; A line\'s forms, as the value to show and the environment the next line runs\n; in. A trailing definition or import is the only case that moves the\n; environment. A definition shows its name rather than its value, which is\n; usually a closure; an import shows the names it brought.\n(define (evaluate env forms)\n  (let ((one (if (nil? (cdr forms)) (car forms) \'())))\n    (let ((d (definition one)))\n      (cond ((pair? d)\n             (let ((v (eval-in env (desugar-body (list (cdr d))))))\n               (cons (car d) (env-extend env (car d) v))))\n            ((tagged? one \'import)\n             (let ((f (eval-in env (desugar-body (list (cadr one))))))\n               (cons (names f) (cons f env))))\n            (else (cons (eval-in env (desugar-body forms)) env))))))\n\n; One shape, `(tag v . env)`, so the loop reads the environment out of either.\n; The tag is in the outer `car`, where no value of the user\'s can reach it.\n(define (shown v env) (cons \'show (cons v env)))\n(define (quiet env) (cons \'quiet (cons \'() env)))\n\n; A throw anywhere in reading, rewriting or evaluating is shown in place of a\n; value and the environment left as it was, which is what makes a prompt survive\n; a mistake. An empty line reads to no forms, which `evaluate` has no answer for\n; - `(car \'())` throws - so it is decided before evaluating rather than after.\n(define (turn env line)\n  (let ((got (attempt (lambda ()\n                        (let ((forms (read line)))\n                          (if (nil? forms) \'() (evaluate env forms)))))))\n    (if (tagged? got \'ok)\n        (let ((out (cdr got)))\n          (if (nil? out) (quiet env) (shown (car out) (cdr out))))\n        (shown (cons \'error (cdr got)) env))))\n\n; Nothing is read out of the message: any message is a request for one more\n; line, so the prompt is driven by a mailbox like everything else. End of input\n; ends the task, which is what closes the world.\n(task-loop self\n           (lambda h (env msg)\n             (let ((line (call \'(host stdin) (list \'read \"narju> \"))))\n               (if (eq? line \'eof)\n                   (cons \'stop \'bye)\n                   (let ((out (turn env line)))\n                     (begin\n                       (if (eq? (car out) \'quiet)\n                           \'ok\n                           (call \'(host stdout) (list \'line (show (cadr out)))))\n                       (begin (send self \'more) (cdr (cdr out))))))))\n           base)\n";
Expand description

The prompt. An object program and a task like any other: nothing about a REPL needs to be built into the host.