+ (apply #'code strs)))
+
+(defun lambda-check-argument-count
+ (n-required-arguments n-optional-arguments rest-p)
+ ;; Note: Remember that we assume that the number of arguments of a
+ ;; call is at least 1 (the values argument).
+ (let ((min (1+ n-required-arguments))
+ (max (if rest-p 'n/a (+ 1 n-required-arguments n-optional-arguments))))
+ (block nil
+ ;; Special case: a positive exact number of arguments.
+ (when (and (< 1 min) (eql min max))
+ (return (code "checkArgs(arguments, " min ");" *newline*)))
+ ;; General case:
+ (code
+ (when (< 1 min)
+ (code "checkArgsAtLeast(arguments, " min ");" *newline*))
+ (when (numberp max)
+ (code "checkArgsAtMost(arguments, " max ");" *newline*))))))
+
+(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 (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-arguments)))
+ (push (code "case " (+ idx n-required-arguments) ":" *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 (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*
+ "for (var i = arguments.length-1; i>="
+ (+ 1 n-required-arguments n-optional-arguments)
+ "; i--)" *newline*
+ (indent js!rest " = {car: arguments[i], cdr: ") js!rest "};"
+ *newline*)))))
+
+(defun compile-lambda-parse-keywords (ll)
+ (let ((n-required-arguments
+ (length (ll-required-arguments ll)))
+ (n-optional-arguments
+ (length (ll-optional-arguments ll)))
+ (keyword-arguments
+ (ll-keyword-arguments-canonical ll)))
+ (code
+ ;; Declare variables
+ (mapconcat (lambda (arg)
+ (let ((var (second (car arg))))
+ (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)
+ ;; ((keyword-name var) init-form)
+ (code "for (i=" (+ 1 n-required-arguments n-optional-arguments)
+ "; i<arguments.length; i+=2){" *newline*
+ (indent
+ "if (arguments[i] === " (ls-compile (caar keyarg)) "){" *newline*
+ (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*)
+ "}" *newline*)))
+ (when keyword-arguments
+ (code "var i;" *newline*
+ (mapconcat #'parse-keyword keyword-arguments))))
+ ;; Check for unknown keywords
+ (when keyword-arguments
+ (code "for (i=" (+ 1 n-required-arguments n-optional-arguments)
+ "; i<arguments.length; i+=2){" *newline*
+ (indent "if ("
+ (join (mapcar (lambda (x)
+ (concat "arguments[i] !== " (ls-compile (caar x))))
+ keyword-arguments)
+ " && ")
+ ")" *newline*
+ (indent
+ "throw 'Unknown keyword argument ' + arguments[i].name;" *newline*))
+ "}" *newline*)))))
+
+(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))