rebirth: Make quote form recursive, add quasiquote

This was pretty much a prerequisite for introducing macros (in commits to
follow)---the code would otherwise be far too verbose.

Note that this does _not_ introduce the shorthand forms ("'", "`", ",",
",@"), as implementing those in a reasonable manner would require
preprocesing the AST, which is not what I want to focus on right now.  So
macros will still be a bit verbose, using the full
"(quasiquote ... (unquote-splicing ...))" and such instead of simply
"`(... ,@(...))"

* build-aux/bootstrap/rebirth.scm
  (quote-sexp, quasiquote-sexp): Add procedures.
  (fnmap)[quote]: Use `quote-sexp'.
    [quasiquote]: Add special form.
master
Mike Gerwitz 2017-12-06 22:40:22 -05:00
parent d881345fe6
commit a839301a12
Signed by: mikegerwitz
GPG Key ID: 8C917B7F5DC51BA2
1 changed files with 74 additions and 6 deletions

View File

@ -556,6 +556,76 @@
"function " id "(" params ")\n{\n" body "\n};")))
;; Quote an expression
;;
;; If SEXP is a token, produce an ECMAScript Symbol. Otherwise,
;; recursively apply to each element in the list.
;;
;; TODO: This implementation isn't wholly correct---numbers, for example,
;; should not be converted to symbols, as they already are one.
(define (quote-sexp sexp)
(if (token? sexp)
(string-append "Symbol.for('" (sexp->es sexp) "')")
(string-append
"[" (join "," (map quote-sexp sexp)) "]")))
;; Quasiquote an expression
;;
;; A quasiquoted expression acts just like a quoted expression with one
;; notable exception---quoting can be escaped using special forms. For
;; example, each of these are equivalent:
;;
;; (quasiquote (a 1 2 (unquote (eq? 3 4))))
;; (list (quote a) 1 2 (eq? 3 4))
;; (quasiquote (a (unquote-splicing (list 1 2)) (unquote (eq? 3 4))))
;;
;; TODO/WARNING: Normally "(quasiquote a (unquote-splicing b))" would
;; produce "(a . b)" in a proper Lisp, but we do not yet support proper
;; pairs at the time that this procedure was written; all cdrs are assumed
;; to be lists. So do not do that---always splice lists.
(define (quasiquote-sexp sexp)
;; get type of token at car of pair, unless not a pair
(define (-sexp-maybe-type sexp)
(and (pair? sexp)
(token? (car sexp))
(token-value (car sexp))))
;; recursively process the sexp, handling various types of unquoting
(define (-quote-maybe sexp delim)
(if (pair? sexp)
(let* ((item (car sexp))
(rest (cdr sexp))
(type (-sexp-maybe-type item))
(add-delim (not (string=? type "unquote-splicing"))))
(string-append
(case type
;; escape quoting, nest within
(("unquote")
(string-append (if delim "," "")
(sexp->es (cadr item))))
;; escape quoting, splice list into parent expression
;; (lazy kluge warning)
(("unquote-splicing")
(string-append
"]).concat(" (sexp->es (cadr item)) ").concat(["))
;; anything else, we're still quasiquoting recursively
(else (string-append (if delim "," "")
(quasiquote-sexp item))))
;; continue processing this list
(-quote-maybe rest add-delim)))
""))
;; tokens fall back to normal quoting
(if (token? sexp)
(quote-sexp sexp)
(string-append
"([" (-quote-maybe sexp #f) "])")))
;; Function/procedure aliases and special forms
;;
;; And here we have what is probably the most grotesque part of this file.
@ -606,12 +676,10 @@
"})(false)")))
(("js:break") "__whilebrk=true")
;; fortunately ES6+ has native symbol support :)
;; we don't (yet?) need list quoting in Prebirth
(("quote")
(if (pair? (cdr args))
(error "quoting lists is not yet supported; sorry!")
(string-append "Symbol.for('" (sexp->es args) "')")))
;; note that the unquote forms are only valid within a quasiquote; see
;; that procedure for the handling of those forms
(("quote") (quote-sexp (car args)))
(("quasiquote") (quasiquote-sexp (car args)))
(("define") (cdfn t))