(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).
+ ;; ((keyword-name var) init-form svar).
(let ((arg (ensure-list keyarg)))
(cons (if (listp (car arg))
(car arg)
(block nil
;; Special case: a positive exact number of arguments.
(when (and (< 0 min) (eql min max))
- (return `(code "checkArgs(nargs, " ,min ");")))
+ (return `(call |checkArgs| |nargs| ,min)))
;; General case:
- `(code
- ,(when (< 0 min)
- `(code "checkArgsAtLeast(nargs, " ,min ");"))
- ,(when (numberp max)
- `(code "checkArgsAtMost(nargs, " ,max ");"))))))
+ `(progn
+ ,(when (< 0 min) `(call |checkArgsAtLeast| |nargs| ,min))
+ ,(when (numberp max) `(call |checkArgsAtMost| |nargs| ,max))))))
(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(nargs){"
- ,(let ((cases nil)
- (idx 0))
- (progn
- (while (< idx n-optional-arguments)
- (let ((arg (nth idx optional-arguments)))
- (push `(code "case " ,(+ idx n-required-arguments) ":"
- (code ,(translate-variable (car arg))
- "="
- ,(ls-compile (cadr arg)) ";")
- ,(when (third arg)
- `(code ,(translate-variable (third arg))
- "="
- ,(ls-compile nil)
- ";")))
- cases)
- (incf idx)))
- (push `(code "default: break;") cases)
- `(code ,@(reverse cases))))
- "}"))))
+ `(switch |nargs|
+ ,@(with-collect
+ (dotimes (idx n-optional-arguments)
+ (let ((arg (nth idx optional-arguments)))
+ (collect `(,(+ idx n-required-arguments)
+ (= ,(make-symbol (translate-variable (car arg)))
+ ,(ls-compile (cadr arg)))
+ ,(when (third arg)
+ `(= ,(make-symbol (translate-variable (third arg)))
+ ,(ls-compile nil)))))))
+ (collect `(default (break))))))))
(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) ";"
- "for (var i = nargs-1; i>=" ,(+ n-required-arguments n-optional-arguments)
- "; i--)"
- (code ,js!rest " = {car: arguments[i+2], cdr: " ,js!rest "};"))))))
+ (let ((js!rest (make-symbol (translate-variable rest-argument))))
+ `(progn
+ (var (,js!rest ,(ls-compile nil)))
+ (var i)
+ (for ((= i (- |nargs| 1))
+ (>= i ,(+ n-required-arguments n-optional-arguments))
+ (post-- i))
+ (= ,js!rest (object "car" (property |arguments| (+ i 2))
+ "cdr" ,js!rest))))))))
(defun compile-lambda-parse-keywords (ll)
(let ((n-required-arguments
(length (ll-optional-arguments ll)))
(keyword-arguments
(ll-keyword-arguments-canonical ll)))
- `(code
- ;; Declare variables
- ,@(mapcar (lambda (arg)
- (let ((var (second (car arg))))
- `(code "var " ,(translate-variable var) "; "
- ,(when (third arg)
- `(code "var " ,(translate-variable (third arg))
- " = " ,(ls-compile nil)
- ";" )))))
- keyword-arguments)
- ;; Parse keywords
- ,(flet ((parse-keyword (keyarg)
- ;; ((keyword-name var) init-form)
- `(code "for (i=" ,(+ n-required-arguments n-optional-arguments)
- "; i<nargs; i+=2){"
- "if (arguments[i+2] === " ,(ls-compile (caar keyarg)) "){"
- ,(translate-variable (cadr (car keyarg)))
- " = arguments[i+3];"
- ,(let ((svar (third keyarg)))
- (when svar
- `(code ,(translate-variable svar) " = " ,(ls-compile t) ";" )))
- "break;"
- "}"
- "}"
- ;; Default value
- "if (i == nargs){"
- ,(translate-variable (cadr (car keyarg)))
- " = "
- ,(ls-compile (cadr keyarg))
- ";"
- "}")))
- (when keyword-arguments
- `(code "var i;"
- ,@(mapcar #'parse-keyword keyword-arguments))))
- ;; Check for unknown keywords
- ,(when keyword-arguments
- `(code "var start = " ,(+ n-required-arguments n-optional-arguments) ";"
- "if ((nargs - start) % 2 == 1){"
- "throw 'Odd number of keyword arguments';"
- "}"
- "for (i = start; i<nargs; i+=2){"
- "if ("
- ,@(interleave (mapcar (lambda (x)
- `(code "arguments[i+2] !== " ,(ls-compile (caar x))))
- keyword-arguments)
- " && ")
- ")"
- "throw 'Unknown keyword argument ' + xstring(arguments[i+2].name);"
- "}" )))))
+ `(progn
+ ;; Declare variables
+ ,@(with-collect
+ (dolist (keyword-argument keyword-arguments)
+ (destructuring-bind ((keyword-name var) &optional initform svar)
+ keyword-argument
+ (declare (ignore keyword-name initform))
+ (collect `(var ,(make-symbol (translate-variable var))))
+ (when svar
+ (collect
+ `(var (,(make-symbol (translate-variable svar))
+ ,(ls-compile nil))))))))
+
+ ;; Parse keywords
+ ,(flet ((parse-keyword (keyarg)
+ (destructuring-bind ((keyword-name var) &optional initform svar) keyarg
+ ;; ((keyword-name var) init-form svar)
+ `(progn
+ (for ((= i ,(+ n-required-arguments n-optional-arguments))
+ (< i |nargs|)
+ (+= i 2))
+ ;; ....
+ (if (=== (property |arguments| (+ i 2))
+ ,(ls-compile keyword-name))
+ (progn
+ (= ,(make-symbol (translate-variable var))
+ (property |arguments| (+ i 3)))
+ ,(when svar `(= ,(make-symbol (translate-variable svar))
+ ,(ls-compile t)))
+ (break))))
+ (if (== i |nargs|)
+ (= ,(make-symbol (translate-variable var))
+ ,(ls-compile initform)))))))
+ (when keyword-arguments
+ `(progn
+ (var i)
+ ,@(mapcar #'parse-keyword keyword-arguments))))
+
+ ;; Check for unknown keywords
+ ,(when keyword-arguments
+ `(progn
+ (var (start ,(+ n-required-arguments n-optional-arguments)))
+ (if (== (% (- |nargs| start) 2) 1)
+ (throw "Odd number of keyword arguments."))
+ (for ((= i start) (< i |nargs|) (+= i 2))
+ (if (and ,@(mapcar (lambda (keyword-argument)
+ (destructuring-bind ((keyword-name var) &optional initform svar)
+ keyword-argument
+ (declare (ignore var initform svar))
+ `(!== (property |arguments| (+ i 2)) ,(ls-compile keyword-name))))
+ keyword-arguments))
+ (throw (+ "Unknown keyword argument "
+ (call |xstring|
+ (property
+ (property |arguments| (+ i 2))
+ "name")))))))))))
(defun parse-lambda-list (ll)
(values (ll-required-arguments ll)
keyword-arguments
(ll-svars ll)))))
(lambda-name/docstring-wrapper name documentation
- `(code
- "(function ("
- ,(join (list* "values"
- "nargs"
- (mapcar #'translate-variable
- (append required-arguments optional-arguments)))
- ",")
- "){"
- ;; Check number of arguments
- ,(lambda-check-argument-count n-required-arguments
- n-optional-arguments
- (or rest-argument keyword-arguments))
- ,(compile-lambda-optional ll)
- ,(compile-lambda-rest ll)
- ,(compile-lambda-parse-keywords ll)
- ,(let ((*multiple-value-p* t))
- (if block
- (ls-compile-block `((block ,block ,@body)) t)
- (ls-compile-block body t)))
- "})"))))))
+ `(function (|values| |nargs| ,@(mapcar (lambda (x)
+ (make-symbol (translate-variable x)))
+ (append required-arguments optional-arguments)))
+ ;; Check number of arguments
+ ,(lambda-check-argument-count n-required-arguments
+ n-optional-arguments
+ (or rest-argument keyword-arguments))
+ ,(compile-lambda-optional ll)
+ ,(compile-lambda-rest ll)
+ ,(compile-lambda-parse-keywords ll)
+
+ ,(let ((*multiple-value-p* t))
+ (if block
+ (ls-compile-block `((block ,block ,@body)) t)
+ (ls-compile-block body t)))))))))
(defun setq-pair (var val)
(defun let-binding-wrapper (bindings body)
(when (null bindings)
(return-from let-binding-wrapper body))
- `(code
- "try {"
- (code "var tmp;"
- ,@(mapcar
- (lambda (b)
- (let ((s (ls-compile `(quote ,(car b)))))
- `(code "tmp = " ,s ".value;"
- ,s ".value = " ,(cdr b) ";"
- ,(cdr b) " = tmp;" )))
- bindings)
- ,body
- )
- "}"
- "finally {"
- (code
- ,@(mapcar (lambda (b)
- (let ((s (ls-compile `(quote ,(car b)))))
- `(code ,s ".value" " = " ,(cdr b) ";" )))
- bindings))
- "}" ))
+ `(progn
+ (try (var tmp)
+ ,@(with-collect
+ (dolist (b bindings)
+ (let ((s (ls-compile `',(car b))))
+ (collect `(= tmp (get ,s "value")))
+ (collect `(= (get ,s "value") ,(cdr b)))
+ (collect `(= ,(cdr b) tmp)))))
+ ,body)
+ (finally
+ ,@(with-collect
+ (dolist (b bindings)
+ (let ((s (ls-compile `(quote ,(car b)))))
+ (collect `(= (get ,s "value") ,(cdr b)))))))))
(define-compilation let (bindings &rest body)
(let* ((bindings (mapcar #'ensure-list bindings))
`(call (function ,(mapcar (lambda (x)
(if (special-variable-p x)
(let ((v (gvarname x)))
- (push (cons x v) dynamic-bindings)
+ (push (cons x (make-symbol v)) dynamic-bindings)
(make-symbol v))
(make-symbol (translate-variable x))))
variables)
,(let ((body (ls-compile-block body t t)))
- `(code ,(let-binding-wrapper dynamic-bindings body))))
+ `,(let-binding-wrapper dynamic-bindings body)))
,@cvalues)))
;; unique identifier of the block as exception. We can't use the
;; variable name itself, because it could not to be unique, so we
;; capture it in a closure.
- (js!selfcall
- (when multiple-value-p `(code "var values = mv;" ))
- "throw ({"
- "type: 'block', "
- "id: " (binding-value b) ", "
- "values: " (ls-compile value multiple-value-p) ", "
- "message: 'Return from unknown block " (symbol-name name) ".'"
- "})")))
+ (js!selfcall*
+ (when multiple-value-p
+ `(var (|values| |mv|)))
+ `(throw
+ (object
+ "type" "block"
+ "id" ,(make-symbol (binding-value b))
+ "values" ,(ls-compile value multiple-value-p)
+ "message" ,(concat "Return from unknown block '" (symbol-name name) "'."))))))
(define-compilation catch (id &rest body)
(js!selfcall*