(defun null (x)
(eq x nil))
+ (defun endp (x)
+ (if (null x)
+ t
+ (if (consp x)
+ nil
+ (error "type-error"))))
+
(defmacro return (&optional value)
`(return-from nil ,value))
(defun cadr (x) (car (cdr x)))
(defun cdar (x) (cdr (car x)))
(defun cddr (x) (cdr (cdr x)))
+ (defun cadar (x) (car (cdr (car x))))
(defun caddr (x) (car (cdr (cdr x))))
(defun cdddr (x) (cdr (cdr (cdr x))))
(defun cadddr (x) (car (cdr (cdr (cdr x)))))
`(setq ,x (- ,x ,delta)))
(defmacro push (x place)
- `(setq ,place (cons ,x ,place)))
+ (multiple-value-bind (dummies vals newval setter getter)
+ (get-setf-expansion place)
+ (let ((g (gensym)))
+ `(let* ((,g ,x)
+ ,@(mapcar #'list dummies vals)
+ (,(car newval) (cons ,g ,getter))
+ ,@(cdr newval))
+ ,setter))))
(defmacro dolist (iter &body body)
(let ((var (first iter))
x
(list x)))
-(defun !reduce (func list initial)
+(defun !reduce (func list &key initial-value)
(if (null list)
- initial
+ initial-value
(!reduce func
(cdr list)
- (funcall func initial (car list)))))
+ :initial-value (funcall func initial-value (car list)))))
;;; Go on growing the Lisp language in Ecmalisp, with more high
;;; level utilities as well as correct versions of other
(append (cdr list1) list2))))
(defun append (&rest lists)
- (!reduce #'append-two lists '()))
+ (!reduce #'append-two lists))
(defun revappend (list1 list2)
(while list1
(setq assignments (reverse assignments))
;;
`(let ,(mapcar #'cdr assignments)
- (setq ,@(!reduce #'append (mapcar #'butlast assignments) '())))))
+ (setq ,@(!reduce #'append (mapcar #'butlast assignments))))))
(defmacro do (varlist endlist &body body)
`(block nil
(let ,(mapcar (lambda (x) (list (first x) (second x))) varlist)
(while t
(when ,(car endlist)
- (return (progn ,(cdr endlist))))
+ (return (progn ,@(cdr endlist))))
(tagbody ,@body)
(psetq
,@(apply #'append
(let* ,(mapcar (lambda (x) (list (first x) (second x))) varlist)
(while t
(when ,(car endlist)
- (return (progn ,(cdr endlist))))
+ (return (progn ,@(cdr endlist))))
(tagbody ,@body)
(setq
,@(apply #'append
(defun map1 (func list)
(with-collect
- (while list
- (collect (funcall func (car list)))
- (setq list (cdr list)))))
+ (while list
+ (collect (funcall func (car list)))
+ (setq list (cdr list)))))
(defmacro loop (&body body)
`(while t ,@body))
(defun mapcar (func list &rest lists)
(let ((lists (cons list lists)))
(with-collect
- (block loop
- (loop
- (let ((elems (map1 #'car lists)))
- (do ((tail lists (cdr tail)))
- ((null tail))
- (when (null (car tail)) (return-from loop))
- (rplaca tail (cdar tail)))
- (collect (apply func elems))))))))
+ (block loop
+ (loop
+ (let ((elems (map1 #'car lists)))
+ (do ((tail lists (cdr tail)))
+ ((null tail))
+ (when (null (car tail)) (return-from loop))
+ (rplaca tail (cdar tail)))
+ (collect (apply func elems))))))))
(defun identity (x) x)
(defun copy-list (x)
(mapcar #'identity x))
+ (defun list* (arg &rest others)
+ (cond ((null others) arg)
+ ((null (cdr others)) (cons arg (car others)))
+ (t (do ((x others (cdr x)))
+ ((null (cddr x)) (rplacd x (cadr x))))
+ (cons arg others))))
+
(defun code-char (x) x)
(defun char-code (x) x)
(defun char= (x y) (= x y))
((funcall func (car list))
(remove-if func (cdr list)))
(t
+ ;;
(cons (car list) (remove-if func (cdr list))))))
(defun remove-if-not (func list)
(t
(error "Unsupported argument."))))
+ (defmacro do-sequence (iteration &body body)
+ (let ((seq (gensym))
+ (index (gensym)))
+ `(let ((,seq ,(second iteration)))
+ (cond
+ ;; Strings
+ ((stringp ,seq)
+ (let ((,index 0))
+ (dotimes (,index (length ,seq))
+ (let ((,(first iteration)
+ (char ,seq ,index)))
+ ,@body))))
+ ;; Lists
+ ((listp ,seq)
+ (dolist (,(first iteration) ,seq)
+ ,@body))
+ (t
+ (error "type-error!"))))))
+
(defun some (function seq)
- (cond
- ((stringp seq)
- (let ((index 0)
- (size (length seq)))
- (while (< index size)
- (when (funcall function (char seq index))
- (return-from some t))
- (incf index))
- nil))
- ((listp seq)
- (dolist (x seq nil)
- (when (funcall function x)
- (return t))))
- (t
- (error "Unknown sequence."))))
+ (do-sequence (elt seq)
+ (when (funcall function elt)
+ (return-from some t))))
(defun every (function seq)
- (cond
- ((stringp seq)
- (let ((index 0)
- (size (length seq)))
- (while (< index size)
- (unless (funcall function (char seq index))
- (return-from every nil))
- (incf index))
- t))
- ((listp seq)
- (dolist (x seq t)
- (unless (funcall function x)
- (return))))
- (t
- (error "Unknown sequence."))))
+ (do-sequence (elt seq)
+ (unless (funcall function elt)
+ (return-from every nil)))
+ t)
(defun assoc (x alist)
(while alist
`(multiple-value-call #'list ,value-from))
- ;;; Generalized references (SETF)
+;;; Generalized references (SETF)
(defvar *setf-expanders* nil)
`(,value)
`(setq ,place ,value)
place))
- (let* ((access-fn (car place))
- (expander (cdr (assoc access-fn *setf-expanders*))))
- (when (null expander)
- (error "Unknown generalized reference."))
- (apply expander (cdr place)))))
+ (let ((place (ls-macroexpand-1 place)))
+ (let* ((access-fn (car place))
+ (expander (cdr (assoc access-fn *setf-expanders*))))
+ (when (null expander)
+ (error "Unknown generalized reference."))
+ (apply expander (cdr place))))))
(defmacro define-setf-expander (access-fn lambda-list &body body)
(unless (symbolp access-fn)
`(progn (rplacd ,cons ,new-value) ,new-value)
`(car ,cons))))
- ;;; Packages
+ ;; Incorrect typecase, but used in NCONC.
+ (defmacro typecase (x &rest clausules)
+ (let ((value (gensym)))
+ `(let ((,value ,x))
+ (cond
+ ,@(mapcar (lambda (c)
+ (if (eq (car c) t)
+ `((t ,@(rest c)))
+ `((,(ecase (car c)
+ (integer 'integerp)
+ (cons 'consp)
+ (string 'stringp)
+ (atom 'atom)
+ (null 'null))
+ ,value)
+ ,@(or (rest c)
+ (list nil)))))
+ clausules)))))
+
+ ;; The NCONC function is based on the SBCL's one.
+ (defun nconc (&rest lists)
+ (flet ((fail (object)
+ (error "type-error in nconc")))
+ (do ((top lists (cdr top)))
+ ((null top) nil)
+ (let ((top-of-top (car top)))
+ (typecase top-of-top
+ (cons
+ (let* ((result top-of-top)
+ (splice result))
+ (do ((elements (cdr top) (cdr elements)))
+ ((endp elements))
+ (let ((ele (car elements)))
+ (typecase ele
+ (cons (rplacd (last splice) ele)
+ (setf splice ele))
+ (null (rplacd (last splice) nil))
+ (atom (if (cdr elements)
+ (fail ele)
+ (rplacd (last splice) ele))))))
+ (return result)))
+ (null)
+ (atom
+ (if (cdr top)
+ (fail top-of-top)
+ (return top-of-top))))))))
+
+ (defun nreconc (x y)
+ (do ((1st (cdr x) (if (endp 1st) 1st (cdr 1st)))
+ (2nd x 1st) ; 2nd follows first down the list.
+ (3rd y 2nd)) ;3rd follows 2nd down the list.
+ ((atom 2nd) 3rd)
+ (rplacd 2nd 3rd)))
+
+ (defun notany (fn seq)
+ (not (some fn seq)))
+
+
+ ;; Packages
(defvar *package-list* nil)
(defun list-all-packages ()
*package-list*)
- (defun make-package (name &optional use)
+ (defun make-package (name &key use)
(let ((package (new))
(use (mapcar #'find-package-or-fail use)))
(oset package "packageName" name)
(make-package "CL"))
(defvar *user-package*
- (make-package "CL-USER" (list *common-lisp-package*)))
+ (make-package "CL-USER" :use (list *common-lisp-package*)))
(defvar *keyword-package*
(make-package "KEYWORD"))
(defvar *newline* (string (code-char 10)))
(defun concat (&rest strs)
- (!reduce #'concat-two strs ""))
+ (!reduce #'concat-two strs :initial-value ""))
(defmacro concatf (variable &body form)
`(setq ,variable (concat ,variable (progn ,@form))))
(concat (prin1-to-string (car last)) " . " (prin1-to-string (cdr last)))))
")"))
((arrayp form)
- (concat "#" (prin1-to-string (vector-to-list form))))
+ (concat "#" (if (zerop (length form))
+ "()"
+ (prin1-to-string (vector-to-list form)))))
((packagep form)
(concat "#<PACKAGE " (package-name form) ">"))))
x))
+
;;;; Reader
;;; The Lisp reader, parse strings and return Lisp objects. The main
" : " (ls-compile false *multiple-value-p*)
")"))
-(defvar *lambda-list-keywords* '(&optional &rest &key))
+(defvar *ll-keywords* '(&optional &rest &key))
(defun list-until-keyword (list)
- (if (or (null list) (member (car list) *lambda-list-keywords*))
+ (if (or (null list) (member (car list) *ll-keywords*))
nil
(cons (car list) (list-until-keyword (cdr list)))))
-(defun lambda-list-section (keyword lambda-list)
- (list-until-keyword (cdr (member keyword lambda-list))))
+(defun ll-section (keyword ll)
+ (list-until-keyword (cdr (member keyword ll))))
-(defun lambda-list-required-arguments (lambda-list)
- (list-until-keyword lambda-list))
+(defun ll-required-arguments (ll)
+ (list-until-keyword ll))
-(defun lambda-list-optional-arguments-with-default (lambda-list)
- (mapcar #'ensure-list (lambda-list-section '&optional lambda-list)))
+(defun ll-optional-arguments-canonical (ll)
+ (mapcar #'ensure-list (ll-section '&optional ll)))
-(defun lambda-list-optional-arguments (lambda-list)
- (mapcar #'car (lambda-list-optional-arguments-with-default lambda-list)))
+(defun ll-optional-arguments (ll)
+ (mapcar #'car (ll-optional-arguments-canonical ll)))
-(defun lambda-list-rest-argument (lambda-list)
- (let ((rest (lambda-list-section '&rest lambda-list)))
+(defun ll-rest-argument (ll)
+ (let ((rest (ll-section '&rest ll)))
(when (cdr rest)
(error "Bad lambda-list"))
(car rest)))
-(defun lambda-list-keyword-arguments-canonical (lambda-list)
- (flet ((canonalize (keyarg)
+(defun ll-keyword-arguments-canonical (ll)
+ (flet ((canonicalize (keyarg)
;; Build a canonical keyword argument descriptor, filling
;; the optional fields. The result is a list of the form
;; ((keyword-name var) init-form).
- (let* ((arg (ensure-list keyarg))
- (init-form (cadr arg))
- var
- keyword-name)
- (if (listp (car arg))
- (setq var (cadr (car arg))
- keyword-name (car (car arg)))
- (setq var (car arg)
- keyword-name (intern (symbol-name (car arg)) "KEYWORD")))
- `((,keyword-name ,var) ,init-form))))
- (mapcar #'canonalize (lambda-list-section '&key lambda-list))))
-
-(defun lambda-list-keyword-arguments (lambda-list)
+ (let ((arg (ensure-list keyarg)))
+ (cons (if (listp (car arg))
+ (car arg)
+ (list (intern (symbol-name (car arg)) "KEYWORD") (car arg)))
+ (cdr arg)))))
+ (mapcar #'canonicalize (ll-section '&key ll))))
+
+(defun ll-keyword-arguments (ll)
(mapcar (lambda (keyarg) (second (first keyarg)))
- (lambda-list-keyword-arguments-canonical lambda-list)))
+ (ll-keyword-arguments-canonical ll)))
+
+(defun ll-svars (lambda-list)
+ (let ((args
+ (append
+ (ll-keyword-arguments-canonical lambda-list)
+ (ll-optional-arguments-canonical lambda-list))))
+ (remove nil (mapcar #'third args))))
(defun lambda-docstring-wrapper (docstring &rest strs)
(if docstring
(when (numberp max)
(code "checkArgsAtMost(arguments, " max ");" *newline*))))))
-(defun compile-lambda-optional (lambda-list)
- (let* ((optional-arguments (lambda-list-optional-arguments lambda-list))
- (n-required-arguments (length (lambda-list-required-arguments lambda-list)))
+(defun compile-lambda-optional (ll)
+ (let* ((optional-arguments (ll-optional-arguments-canonical ll))
+ (n-required-arguments (length (ll-required-arguments ll)))
(n-optional-arguments (length optional-arguments)))
(when optional-arguments
- (code "switch(arguments.length-1){" *newline*
- (let ((optional-and-defaults
- (lambda-list-optional-arguments-with-default lambda-list))
- (cases nil)
+ (code (mapconcat (lambda (arg)
+ (code "var " (translate-variable (first arg)) "; " *newline*
+ (when (third arg)
+ (code "var " (translate-variable (third arg))
+ " = " (ls-compile t)
+ "; " *newline*))))
+ optional-arguments)
+ "switch(arguments.length-1){" *newline*
+ (let ((cases nil)
(idx 0))
(progn
(while (< idx n-optional-arguments)
- (let ((arg (nth idx optional-and-defaults)))
+ (let ((arg (nth idx optional-arguments)))
(push (code "case " (+ idx n-required-arguments) ":" *newline*
- (translate-variable (car arg))
- "="
- (ls-compile (cadr arg))
- ";" *newline*)
+ (indent (translate-variable (car arg))
+ "="
+ (ls-compile (cadr arg)) ";" *newline*)
+ (when (third arg)
+ (indent (translate-variable (third arg))
+ "="
+ (ls-compile nil)
+ ";" *newline*)))
cases)
(incf idx)))
(push (code "default: break;" *newline*) cases)
(join (reverse cases))))
"}" *newline*))))
-(defun compile-lambda-rest (lambda-list)
- (let ((n-required-arguments (length (lambda-list-required-arguments lambda-list)))
- (n-optional-arguments (length (lambda-list-optional-arguments lambda-list)))
- (rest-argument (lambda-list-rest-argument lambda-list)))
+(defun compile-lambda-rest (ll)
+ (let ((n-required-arguments (length (ll-required-arguments ll)))
+ (n-optional-arguments (length (ll-optional-arguments ll)))
+ (rest-argument (ll-rest-argument ll)))
(when rest-argument
(let ((js!rest (translate-variable rest-argument)))
(code "var " js!rest "= " (ls-compile nil) ";" *newline*
(indent js!rest " = {car: arguments[i], cdr: ") js!rest "};"
*newline*)))))
-(defun compile-lambda-parse-keywords (lambda-list)
+(defun compile-lambda-parse-keywords (ll)
(let ((n-required-arguments
- (length (lambda-list-required-arguments lambda-list)))
+ (length (ll-required-arguments ll)))
(n-optional-arguments
- (length (lambda-list-optional-arguments lambda-list)))
+ (length (ll-optional-arguments ll)))
(keyword-arguments
- (lambda-list-keyword-arguments-canonical lambda-list)))
+ (ll-keyword-arguments-canonical ll)))
(code
;; Declare variables
(mapconcat (lambda (arg)
(let ((var (second (car arg))))
- (code "var " (translate-variable var) "; " *newline*)))
+ (code "var " (translate-variable var) "; " *newline*
+ (when (third arg)
+ (code "var " (translate-variable (third arg))
+ " = " (ls-compile nil)
+ ";" *newline*)))))
keyword-arguments)
;; Parse keywords
(flet ((parse-keyword (keyarg)
(indent (translate-variable (cadr (car keyarg)))
" = arguments[i+1];"
*newline*
+ (let ((svar (third keyarg)))
+ (when svar
+ (code (translate-variable svar) " = " (ls-compile t) ";" *newline*)))
"break;" *newline*)
"}" *newline*)
"}" *newline*
;; Default value
"if (i == arguments.length){" *newline*
- (indent
- (translate-variable (cadr (car keyarg)))
- " = "
- (ls-compile (cadr keyarg))
- ";" *newline*)
+ (indent (translate-variable (cadr (car keyarg))) " = " (ls-compile (cadr keyarg)) ";" *newline*)
"}" *newline*)))
(when keyword-arguments
(code "var i;" *newline*
"throw 'Unknown keyword argument ' + arguments[i].name;" *newline*))
"}" *newline*)))))
-(defun compile-lambda (lambda-list body)
- (let ((required-arguments (lambda-list-required-arguments lambda-list))
- (optional-arguments (lambda-list-optional-arguments lambda-list))
- (keyword-arguments (lambda-list-keyword-arguments lambda-list))
- (rest-argument (lambda-list-rest-argument lambda-list))
+(defun compile-lambda (ll body)
+ (let ((required-arguments (ll-required-arguments ll))
+ (optional-arguments (ll-optional-arguments ll))
+ (keyword-arguments (ll-keyword-arguments ll))
+ (rest-argument (ll-rest-argument ll))
documentation)
;; Get the documentation string for the lambda function
(when (and (stringp (car body))
(append (ensure-list rest-argument)
required-arguments
optional-arguments
- keyword-arguments))))
+ keyword-arguments
+ (ll-svars ll)))))
(lambda-docstring-wrapper
documentation
"(function ("
(lambda-check-argument-count n-required-arguments
n-optional-arguments
(or rest-argument keyword-arguments))
- (compile-lambda-optional lambda-list)
- (compile-lambda-rest lambda-list)
- (compile-lambda-parse-keywords lambda-list)
+ (compile-lambda-optional ll)
+ (compile-lambda-rest ll)
+ (compile-lambda-parse-keywords ll)
(let ((*multiple-value-p* t))
(ls-compile-block body t)))
"})"))))
(ls-compile-block body t))))
-
(defvar *compiling-file* nil)
(define-compilation eval-when-compile (&rest body)
(if *compiling-file*
"return args;" *newline*))
-
-;;; A little backquote implementation without optimizations of any
-;;; kind for ecmalisp.
-(defun backquote-expand-1 (form)
- (cond
- ((symbolp form)
- (list 'quote form))
- ((atom form)
- form)
- ((eq (car form) 'unquote)
- (car form))
- ((eq (car form) 'backquote)
- (backquote-expand-1 (backquote-expand-1 (cadr form))))
- (t
- (cons 'append
- (mapcar (lambda (s)
- (cond
- ((and (listp s) (eq (car s) 'unquote))
- (list 'list (cadr s)))
- ((and (listp s) (eq (car s) 'unquote-splicing))
- (cadr s))
- (t
- (list 'list (backquote-expand-1 s)))))
- form)))))
-
-(defun backquote-expand (form)
- (if (and (listp form) (eq (car form) 'backquote))
- (backquote-expand-1 (cadr form))
- form))
-
-(defmacro backquote (form)
- (backquote-expand-1 form))
+;;; Backquote implementation.
+;;;
+;;; Author: Guy L. Steele Jr. Date: 27 December 1985
+;;; Tested under Symbolics Common Lisp and Lucid Common Lisp.
+;;; This software is in the public domain.
+
+;;; The following are unique tokens used during processing.
+;;; They need not be symbols; they need not even be atoms.
+(defvar *comma* 'unquote)
+(defvar *comma-atsign* 'unquote-splicing)
+
+(defvar *bq-list* (make-symbol "BQ-LIST"))
+(defvar *bq-append* (make-symbol "BQ-APPEND"))
+(defvar *bq-list** (make-symbol "BQ-LIST*"))
+(defvar *bq-nconc* (make-symbol "BQ-NCONC"))
+(defvar *bq-clobberable* (make-symbol "BQ-CLOBBERABLE"))
+(defvar *bq-quote* (make-symbol "BQ-QUOTE"))
+(defvar *bq-quote-nil* (list *bq-quote* nil))
+
+;;; BACKQUOTE is an ordinary macro (not a read-macro) that processes
+;;; the expression foo, looking for occurrences of #:COMMA,
+;;; #:COMMA-ATSIGN, and #:COMMA-DOT. It constructs code in strict
+;;; accordance with the rules on pages 349-350 of the first edition
+;;; (pages 528-529 of this second edition). It then optionally
+;;; applies a code simplifier.
+
+;;; If the value of *BQ-SIMPLIFY* is non-NIL, then BACKQUOTE
+;;; processing applies the code simplifier. If the value is NIL,
+;;; then the code resulting from BACKQUOTE is exactly that
+;;; specified by the official rules.
+(defparameter *bq-simplify* t)
+
+(defmacro backquote (x)
+ (bq-completely-process x))
+
+;;; Backquote processing proceeds in three stages:
+;;;
+;;; (1) BQ-PROCESS applies the rules to remove occurrences of
+;;; #:COMMA, #:COMMA-ATSIGN, and #:COMMA-DOT corresponding to
+;;; this level of BACKQUOTE. (It also causes embedded calls to
+;;; BACKQUOTE to be expanded so that nesting is properly handled.)
+;;; Code is produced that is expressed in terms of functions
+;;; #:BQ-LIST, #:BQ-APPEND, and #:BQ-CLOBBERABLE. This is done
+;;; so that the simplifier will simplify only list construction
+;;; functions actually generated by BACKQUOTE and will not involve
+;;; any user code in the simplification. #:BQ-LIST means LIST,
+;;; #:BQ-APPEND means APPEND, and #:BQ-CLOBBERABLE means IDENTITY
+;;; but indicates places where "%." was used and where NCONC may
+;;; therefore be introduced by the simplifier for efficiency.
+;;;
+;;; (2) BQ-SIMPLIFY, if used, rewrites the code produced by
+;;; BQ-PROCESS to produce equivalent but faster code. The
+;;; additional functions #:BQ-LIST* and #:BQ-NCONC may be
+;;; introduced into the code.
+;;;
+;;; (3) BQ-REMOVE-TOKENS goes through the code and replaces
+;;; #:BQ-LIST with LIST, #:BQ-APPEND with APPEND, and so on.
+;;; #:BQ-CLOBBERABLE is simply eliminated (a call to it being
+;;; replaced by its argument). #:BQ-LIST* is replaced by either
+;;; LIST* or CONS (the latter is used in the two-argument case,
+;;; purely to make the resulting code a tad more readable).
+
+(defun bq-completely-process (x)
+ (let ((raw-result (bq-process x)))
+ (bq-remove-tokens (if *bq-simplify*
+ (bq-simplify raw-result)
+ raw-result))))
+
+(defun bq-process (x)
+ (cond ((atom x)
+ (list *bq-quote* x))
+ ((eq (car x) 'backquote)
+ (bq-process (bq-completely-process (cadr x))))
+ ((eq (car x) *comma*) (cadr x))
+ ((eq (car x) *comma-atsign*)
+ ;; (error ",@~S after `" (cadr x))
+ (error "ill-formed"))
+ ;; ((eq (car x) *comma-dot*)
+ ;; ;; (error ",.~S after `" (cadr x))
+ ;; (error "ill-formed"))
+ (t (do ((p x (cdr p))
+ (q '() (cons (bracket (car p)) q)))
+ ((atom p)
+ (cons *bq-append*
+ (nreconc q (list (list *bq-quote* p)))))
+ (when (eq (car p) *comma*)
+ (unless (null (cddr p))
+ ;; (error "Malformed ,~S" p)
+ (error "Malformed"))
+ (return (cons *bq-append*
+ (nreconc q (list (cadr p))))))
+ (when (eq (car p) *comma-atsign*)
+ ;; (error "Dotted ,@~S" p)
+ (error "Dotted"))
+ ;; (when (eq (car p) *comma-dot*)
+ ;; ;; (error "Dotted ,.~S" p)
+ ;; (error "Dotted"))
+ ))))
+
+;;; This implements the bracket operator of the formal rules.
+(defun bracket (x)
+ (cond ((atom x)
+ (list *bq-list* (bq-process x)))
+ ((eq (car x) *comma*)
+ (list *bq-list* (cadr x)))
+ ((eq (car x) *comma-atsign*)
+ (cadr x))
+ ;; ((eq (car x) *comma-dot*)
+ ;; (list *bq-clobberable* (cadr x)))
+ (t (list *bq-list* (bq-process x)))))
+
+;;; This auxiliary function is like MAPCAR but has two extra
+;;; purposes: (1) it handles dotted lists; (2) it tries to make
+;;; the result share with the argument x as much as possible.
+(defun maptree (fn x)
+ (if (atom x)
+ (funcall fn x)
+ (let ((a (funcall fn (car x)))
+ (d (maptree fn (cdr x))))
+ (if (and (eql a (car x)) (eql d (cdr x)))
+ x
+ (cons a d)))))
+
+;;; This predicate is true of a form that when read looked
+;;; like %@foo or %.foo.
+(defun bq-splicing-frob (x)
+ (and (consp x)
+ (or (eq (car x) *comma-atsign*)
+ ;; (eq (car x) *comma-dot*)
+ )))
+
+;;; This predicate is true of a form that when read
+;;; looked like %@foo or %.foo or just plain %foo.
+(defun bq-frob (x)
+ (and (consp x)
+ (or (eq (car x) *comma*)
+ (eq (car x) *comma-atsign*)
+ ;; (eq (car x) *comma-dot*)
+ )))
+
+;;; The simplifier essentially looks for calls to #:BQ-APPEND and
+;;; tries to simplify them. The arguments to #:BQ-APPEND are
+;;; processed from right to left, building up a replacement form.
+;;; At each step a number of special cases are handled that,
+;;; loosely speaking, look like this:
+;;;
+;;; (APPEND (LIST a b c) foo) => (LIST* a b c foo)
+;;; provided a, b, c are not splicing frobs
+;;; (APPEND (LIST* a b c) foo) => (LIST* a b (APPEND c foo))
+;;; provided a, b, c are not splicing frobs
+;;; (APPEND (QUOTE (x)) foo) => (LIST* (QUOTE x) foo)
+;;; (APPEND (CLOBBERABLE x) foo) => (NCONC x foo)
+(defun bq-simplify (x)
+ (if (atom x)
+ x
+ (let ((x (if (eq (car x) *bq-quote*)
+ x
+ (maptree #'bq-simplify x))))
+ (if (not (eq (car x) *bq-append*))
+ x
+ (bq-simplify-args x)))))
+
+(defun bq-simplify-args (x)
+ (do ((args (reverse (cdr x)) (cdr args))
+ (result
+ nil
+ (cond ((atom (car args))
+ (bq-attach-append *bq-append* (car args) result))
+ ((and (eq (caar args) *bq-list*)
+ (notany #'bq-splicing-frob (cdar args)))
+ (bq-attach-conses (cdar args) result))
+ ((and (eq (caar args) *bq-list**)
+ (notany #'bq-splicing-frob (cdar args)))
+ (bq-attach-conses
+ (reverse (cdr (reverse (cdar args))))
+ (bq-attach-append *bq-append*
+ (car (last (car args)))
+ result)))
+ ((and (eq (caar args) *bq-quote*)
+ (consp (cadar args))
+ (not (bq-frob (cadar args)))
+ (null (cddar args)))
+ (bq-attach-conses (list (list *bq-quote*
+ (caadar args)))
+ result))
+ ((eq (caar args) *bq-clobberable*)
+ (bq-attach-append *bq-nconc* (cadar args) result))
+ (t (bq-attach-append *bq-append*
+ (car args)
+ result)))))
+ ((null args) result)))
+
+(defun null-or-quoted (x)
+ (or (null x) (and (consp x) (eq (car x) *bq-quote*))))
+
+;;; When BQ-ATTACH-APPEND is called, the OP should be #:BQ-APPEND
+;;; or #:BQ-NCONC. This produces a form (op item result) but
+;;; some simplifications are done on the fly:
+;;;
+;;; (op '(a b c) '(d e f g)) => '(a b c d e f g)
+;;; (op item 'nil) => item, provided item is not a splicable frob
+;;; (op item 'nil) => (op item), if item is a splicable frob
+;;; (op item (op a b c)) => (op item a b c)
+(defun bq-attach-append (op item result)
+ (cond ((and (null-or-quoted item) (null-or-quoted result))
+ (list *bq-quote* (append (cadr item) (cadr result))))
+ ((or (null result) (equal result *bq-quote-nil*))
+ (if (bq-splicing-frob item) (list op item) item))
+ ((and (consp result) (eq (car result) op))
+ (list* (car result) item (cdr result)))
+ (t (list op item result))))
+
+;;; The effect of BQ-ATTACH-CONSES is to produce a form as if by
+;;; `(LIST* ,@items ,result) but some simplifications are done
+;;; on the fly.
+;;;
+;;; (LIST* 'a 'b 'c 'd) => '(a b c . d)
+;;; (LIST* a b c 'nil) => (LIST a b c)
+;;; (LIST* a b c (LIST* d e f g)) => (LIST* a b c d e f g)
+;;; (LIST* a b c (LIST d e f g)) => (LIST a b c d e f g)
+(defun bq-attach-conses (items result)
+ (cond ((and (every #'null-or-quoted items)
+ (null-or-quoted result))
+ (list *bq-quote*
+ (append (mapcar #'cadr items) (cadr result))))
+ ((or (null result) (equal result *bq-quote-nil*))
+ (cons *bq-list* items))
+ ((and (consp result)
+ (or (eq (car result) *bq-list*)
+ (eq (car result) *bq-list**)))
+ (cons (car result) (append items (cdr result))))
+ (t (cons *bq-list** (append items (list result))))))
+
+;;; Removes funny tokens and changes (#:BQ-LIST* a b) into
+;;; (CONS a b) instead of (LIST* a b), purely for readability.
+(defun bq-remove-tokens (x)
+ (cond ((eq x *bq-list*) 'list)
+ ((eq x *bq-append*) 'append)
+ ((eq x *bq-nconc*) 'nconc)
+ ((eq x *bq-list**) 'list*)
+ ((eq x *bq-quote*) 'quote)
+ ((atom x) x)
+ ((eq (car x) *bq-clobberable*)
+ (bq-remove-tokens (cadr x)))
+ ((and (eq (car x) *bq-list**)
+ (consp (cddr x))
+ (null (cdddr x)))
+ (cons 'cons (maptree #'bq-remove-tokens (cdr x))))
+ (t (maptree #'bq-remove-tokens x))))
(define-transformation backquote (form)
- (backquote-expand-1 form))
+ (bq-completely-process form))
+
;;; Primitives
*builtins*))
(defmacro define-builtin (name args &body body)
- `(progn
- (define-raw-builtin ,name ,args
- (let ,(mapcar (lambda (arg) `(,arg (ls-compile ,arg))) args)
- ,@body))))
+ `(define-raw-builtin ,name ,args
+ (let ,(mapcar (lambda (arg) `(,arg (ls-compile ,arg))) args)
+ ,@body)))
;;; DECLS is a list of (JSVARNAME TYPE LISPFORM) declarations.
(defmacro type-check (decls &body body)
"string1.concat(string2)"))
(define-raw-builtin funcall (func &rest args)
- (code "(" (ls-compile func) ")("
- (join (cons (if *multiple-value-p* "values" "pv")
- (mapcar #'ls-compile args))
- ", ")
- ")"))
+ (js!selfcall
+ "var f = " (ls-compile func) ";" *newline*
+ "return (typeof f === 'function'? f: f.fvalue)("
+ (join (cons (if *multiple-value-p* "values" "pv")
+ (mapcar #'ls-compile args))
+ ", ")
+ ")"))
(define-raw-builtin apply (func &rest args)
(if (null args)
" args.push(tail.car);" *newline*
" tail = tail.cdr;" *newline*
"}" *newline*
- "return f.apply(this, args);" *newline*))))
+ "return (typeof f === 'function'? f : f.fvalue).apply(this, args);" *newline*))))
(define-builtin js-eval (string)
(type-check (("string" "string" string))
(ls-compile (ls-macroexpand-1 sexp) multiple-value-p)
(compile-funcall name args))))))
(t
- (error "How should I compile this?")))))
+ (error (concat "How should I compile " (prin1-to-string sexp) "?"))))))
(defun ls-compile-toplevel (sexp &optional multiple-value-p)
(let ((*toplevel-compilations* nil))
(defun eval (x)
(js-eval (ls-compile-toplevel x t)))
- (export '(&rest &key &optional &body * *gensym-counter* *package* +
- - / 1+ 1- < <= = = > >= and append apply aref arrayp assoc
- atom block boundp boundp butlast caar cadddr caddr cadr
- car car case catch cdar cdddr cddr cdr cdr char char-code
- char= code-char cond cons consp constantly copy-list decf
- declaim defconstant defparameter defun defmacro defvar
- digit-char digit-char-p disassemble do do* documentation
- dolist dotimes ecase eq eql equal error eval every export
- fdefinition find-package find-symbol first flet fourth
- fset funcall function functionp gensym get-universal-time
- go identity if in-package incf integerp integerp intern
- keywordp labels lambda last length let let*
- list-all-packages list listp make-array make-package
- make-symbol mapcar member minusp mod multiple-value-bind
- multiple-value-call multiple-value-list
- multiple-value-prog1 nil not nth nthcdr null numberp or
- package-name package-use-list packagep parse-integer plusp
- prin1-to-string print proclaim prog1 prog2 progn psetq
- push quote remove remove-if remove-if-not return
- return-from revappend reverse rplaca rplacd second set setf
- setq some string-upcase string string= stringp subseq
- symbol-function symbol-name symbol-package symbol-plist
- symbol-value symbolp t tagbody third throw truncate unless
- unwind-protect values values-list variable warn when
- write-line write-string zerop))
+ (export '(&rest &key &optional &body * *gensym-counter* *package* + - / 1+ 1- <
+ <= = = > >= and append apply aref arrayp assoc atom block boundp
+ boundp butlast caar cadddr caddr cadr car car case catch cdar cdddr
+ cddr cdr cdr char char-code fdefinition find-package find-symbol first
+ flet fourth fset funcall function functionp gensym get-setf-expansion
+ get-universal-time go identity if in-package incf integerp integerp
+ intern keywordp labels lambda last length let let* char= code-char
+ cond cons consp constantly copy-list decf declaim define-setf-expander
+ defconstant defparameter defun defmacro defvar digit-char digit-char-p
+ disassemble do do* documentation dolist dotimes ecase eq eql equal
+ error eval every export list-all-packages list list* listp loop make-array
+ make-package make-symbol mapcar member minusp mod multiple-value-bind
+ multiple-value-call multiple-value-list multiple-value-prog1 nconc nil not
+ nth nthcdr null numberp or package-name package-use-list packagep
+ parse-integer plusp prin1-to-string print proclaim prog1 prog2 progn
+ psetq push quote nreconc remove remove-if remove-if-not return return-from
+ revappend reverse rplaca rplacd second set setf setq some
+ string-upcase string string= stringp subseq symbol-function
+ symbol-name symbol-package symbol-plist symbol-value symbolp t tagbody
+ third throw truncate unless unwind-protect values values-list variable
+ warn when write-line write-string zerop))
(setq *package* *user-package*)