;;;; Compiler
+(/debug "loading compiler.lisp!")
+
;;; Translate the Lisp code to Javascript. It will compile the special
;;; forms. Some primitive functions are compiled as special forms
;;; too. The respective real functions are defined in the target (see
;;; Wrap X with a Javascript code to convert the result from
;;; Javascript generalized booleans to T or NIL.
(defun js!bool (x)
- `(code "(" ,x "?" ,(ls-compile t) ": " ,(ls-compile nil) ")"))
+ `(if ,x ,(ls-compile t) ,(ls-compile nil)))
;;; Concatenate the arguments and wrap them with a self-calling
;;; Javascript anonymous function. It is used to make some Javascript
;;; It could be defined as function, but we could do some
;;; preprocessing in the future.
(defmacro js!selfcall (&body body)
- ``(code "(function(){" (code ,,@body) "})()"))
+ ``(call (function nil (code ,,@body))))
+
+(defmacro js!selfcall* (&body body)
+ ``(call (function nil ,,@body)))
+
;;; Like CODE, but prefix each line with four spaces. Two versions
;;; of this function are available, because the Ecmalisp version is
(defun gvarname (symbol)
(declare (ignore symbol))
- `(code "v" ,(incf *variable-counter*)))
+ (code "v" (incf *variable-counter*)))
(defun translate-variable (symbol)
(awhen (lookup-in-lexenv symbol *environment* 'variable)
*compilations*))
(define-compilation if (condition true &optional false)
- `(code "(" ,(ls-compile condition) " !== " ,(ls-compile nil)
- " ? " ,(ls-compile true *multiple-value-p*)
- " : " ,(ls-compile false *multiple-value-p*)
- ")"))
+ `(if (!== ,(ls-compile condition) ,(ls-compile nil))
+ ,(ls-compile true *multiple-value-p*)
+ ,(ls-compile false *multiple-value-p*)))
(defvar *ll-keywords* '(&optional &rest &key))
(ll-optional-arguments-canonical lambda-list))))
(remove nil (mapcar #'third args))))
-(defun lambda-name/docstring-wrapper (name docstring &rest code)
+(defun lambda-name/docstring-wrapper (name docstring code)
(if (or name docstring)
- (js!selfcall
- "var func = " `(code ,code) ";"
- (when name
- `(code "func.fname = " ,(js-escape-string name) ";"))
- (when docstring
- `(code "func.docstring = " ,(js-escape-string docstring) ";"))
- "return func;")
- `(code ,@code)))
+ (js!selfcall*
+ `(var (func ,code))
+ (when name `(= (get func |fname|) ,name))
+ (when docstring `(= (get func |docstring|) ,docstring))
+ `(return func))
+ `(code ,code)))
(defun lambda-check-argument-count
(n-required-arguments n-optional-arguments rest-p)
,(flet ((parse-keyword (keyarg)
;; ((keyword-name var) init-form)
`(code "for (i=" ,(+ n-required-arguments n-optional-arguments)
- "; i<nargs; i+=2){"
- (code
- "if (arguments[i+2] === " ,(ls-compile (caar keyarg)) "){"
- (code ,(translate-variable (cadr (car keyarg)))
- " = arguments[i+3];"
-
- ,(let ((svar (third keyarg)))
- (when svar
- `(code ,(translate-variable svar) " = " ,(ls-compile t) ";" )))
- "break;" )
- "}" )
- "}"
+ "; 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){"
- (code ,(translate-variable (cadr (car keyarg)))
- " = "
- ,(ls-compile (cadr keyarg))
- ";" )
- "}" )))
+ "if (i == nargs){"
+ ,(translate-variable (cadr (car keyarg)))
+ " = "
+ ,(ls-compile (cadr keyarg))
+ ";"
+ "}")))
(when keyword-arguments
- `(code "var i;"
+ `(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){"
- (code "throw 'Odd number of keyword arguments';" )
- "}"
- "for (i = start; i<nargs; i+=2){"
- (code "if ("
- ,(interleave (mapcar (lambda (x)
- `(code "arguments[i+2] !== " ,(ls-compile (caar x))))
- keyword-arguments)
- " && ")
- ")"
- (code
- "throw 'Unknown keyword argument ' + xstring(arguments[i+2].name);" ))
+ `(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);"
"}" )))))
(defun parse-lambda-list (ll)
(mapcar #'translate-variable
(append required-arguments optional-arguments)))
",")
- "){"
+ "){"
;; Check number of arguments
,(lambda-check-argument-count n-required-arguments
n-optional-arguments
(if block
(ls-compile-block `((block ,block ,@body)) t)
(ls-compile-block body t)))
- "})"))))))
+ "})"))))))
(defun setq-pair (var val)
(eq (binding-type b) 'variable)
(not (member 'special (binding-declarations b)))
(not (member 'constant (binding-declarations b))))
- `(code ,(binding-value b) " = " ,(ls-compile val)))
+ ;; TODO: Unnecesary make-symbol when codegen migration is
+ ;; finished.
+ `(= ,(make-symbol (binding-value b)) ,(ls-compile val)))
((and b (eq (binding-type b) 'macro))
(ls-compile `(setf ,var ,val)))
(t
((null (cdr pairs))
(error "Odd pairs in SETQ"))
(t
- (push `(code ,(setq-pair (car pairs) (cadr pairs))
- ,(if (null (cddr pairs)) "" ", "))
- result)
+ (push `,(setq-pair (car pairs) (cadr pairs)) result)
(setq pairs (cddr pairs)))))
- `(code "(" ,@(reverse result) ")")))
+ `(progn ,@(reverse result))))
;;; Compilation of literals an object dumping
(defvar *literal-counter* 0)
(defun genlit ()
- `(code "l" ,(incf *literal-counter*)))
+ (code "l" (incf *literal-counter*)))
(defun dump-symbol (symbol)
#-jscl
(let ((package (symbol-package symbol)))
(if (eq package (find-package "KEYWORD"))
- `(code "(new Symbol(" ,(dump-string (symbol-name symbol)) ", " ,(dump-string (package-name package)) "))")
- `(code "(new Symbol(" ,(dump-string (symbol-name symbol)) "))")))
+ `(new (call |Symbol| ,(dump-string (symbol-name symbol)) ,(dump-string (package-name package))))
+ `(new (call |Symbol| ,(dump-string (symbol-name symbol))))))
#+jscl
(let ((package (symbol-package symbol)))
(if (null package)
- `(code "(new Symbol(" ,(dump-string (symbol-name symbol)) "))")
+ `(new (call |Symbol| ,(dump-string (symbol-name symbol))))
(ls-compile `(intern ,(symbol-name symbol) ,(package-name package))))))
(defun dump-cons (cons)
(let ((head (butlast cons))
(tail (last cons)))
- `(code "QIList("
- ,@(interleave (mapcar (lambda (x) (literal x t)) head) ",")
- ,(literal (car tail) t)
- ","
- ,(literal (cdr tail) t)
- ")")))
+ `(call |QIList|
+ ,@(mapcar (lambda (x) `(code ,(literal x t))) head)
+ (code ,(literal (car tail) t))
+ (code ,(literal (cdr tail) t)))))
(defun dump-array (array)
(let ((elements (vector-to-list array)))
- `(code "[" ,(join (mapcar #'literal elements) ", ") "]")))
+ (list-to-vector (mapcar (lambda (x) `(code ,(literal x)))
+ elements))))
(defun dump-string (string)
- `(code "make_lisp_string(" ,(js-escape-string string) ")"))
+ `(call |make_lisp_string| ,string))
(defun literal (sexp &optional recursive)
(cond
(literal sexp))
(define-compilation %while (pred &rest body)
- (js!selfcall
- "while(" (ls-compile pred) " !== " (ls-compile nil) "){"
- `(code ,(ls-compile-block body))
- "}"
- "return " (ls-compile nil) ";" ))
+ (js!selfcall*
+ `(while (!== ,(ls-compile pred) ,(ls-compile nil))
+ 0 ; TODO: Force
+ ; braces. Unnecesary when code
+ ; is gone
+ ,(ls-compile-block body))
+ `(return ,(ls-compile nil))))
(define-compilation function (x)
(cond
'function)))
`(code "(function("
,@(interleave (mapcar #'translate-function fnames) ",")
- "){"
+ "){"
,(ls-compile-block body t)
- "})(" ,@cfuncs ")")))
+ "})(" ,@(interleave cfuncs ",") ")")))
(define-compilation labels (definitions &rest body)
(let* ((fnames (mapcar #'car definitions))
(define-compilation progn (&rest body)
(if (null (cdr body))
(ls-compile (car body) *multiple-value-p*)
- `(code "("
- ,@(interleave
- (append (mapcar #'ls-compile (butlast body))
- (list (ls-compile (car (last body)) t)))
- ",")
- ")")))
+ `(progn
+ ,@(append (mapcar #'ls-compile (butlast body))
+ (list (ls-compile (car (last body)) t))))))
(define-compilation macrolet (definitions &rest body)
(let ((*environment* (copy-lexenv *environment*)))
(when (null bindings)
(return-from let-binding-wrapper body))
`(code
- "try {"
- (code "var tmp;"
+ "try {"
+ (code "var tmp;"
,@(mapcar
(lambda (b)
(let ((s (ls-compile `(quote ,(car b)))))
- `(code "tmp = " ,s ".value;"
- ,s ".value = " ,(cdr b) ";"
+ `(code "tmp = " ,s ".value;"
+ ,s ".value = " ,(cdr b) ";"
,(cdr b) " = tmp;" )))
bindings)
,body
)
- "}"
- "finally {"
+ "}"
+ "finally {"
(code
,@(mapcar (lambda (b)
(let ((s (ls-compile `(quote ,(car b)))))
(translate-variable x)))
variables)
",")
- "){"
+ "){"
,(let ((body (ls-compile-block body t t)))
`(code ,(let-binding-wrapper dynamic-bindings body)))
"})(" ,@(interleave cvalues ",") ")")))
(let ((store (mapcar (lambda (s) (cons s (gvarname s)))
(remove-if-not #'special-variable-p symbols))))
`(code
- "try {"
+ "try {"
(code
,@(mapcar (lambda (b)
(let ((s (ls-compile `(quote ,(car b)))))
`(code "var " ,(cdr b) " = " ,s ".value;" )))
store)
,body)
- "}"
- "finally {"
+ "}"
+ "finally {"
(code
,@(mapcar (lambda (b)
(let ((s (ls-compile `(quote ,(car b)))))
(cbody (ls-compile-block body t)))
(if (member 'used (binding-declarations b))
(js!selfcall
- "try {"
- "var " idvar " = [];"
+ "try {"
+ "var " idvar " = [];"
`(code ,cbody)
- "}"
- "catch (cf){"
- " if (cf.type == 'block' && cf.id == " idvar ")"
+ "}"
+ "catch (cf){"
+ " if (cf.type == 'block' && cf.id == " idvar ")"
(if *multiple-value-p*
" return values.apply(this, forcemv(cf.values));"
" return cf.values;")
-
- " else"
- " throw cf;"
+
+ " else"
+ " throw cf;"
"}" )
(js!selfcall cbody)))))
"})")))
(define-compilation catch (id &rest body)
- (js!selfcall
- "var id = " (ls-compile id) ";"
- "try {"
- `(code ,(ls-compile-block body t))
- "}"
- "catch (cf){"
- " if (cf.type == 'catch' && cf.id == id)"
- (if *multiple-value-p*
- " return values.apply(this, forcemv(cf.values));"
- " return pv.apply(this, forcemv(cf.values));")
-
- " else"
- " throw cf;"
- "}" ))
+ (js!selfcall*
+ `(var (|id| ,(ls-compile id)))
+ `(try
+ ,(ls-compile-block body t))
+ `(catch (|cf|)
+ (if (and (== (get |cf| |type|) "catch")
+ (== (get |cf| |id|) |id|))
+ ,(if *multiple-value-p*
+ `(return (call (get |values| |apply|)
+ this
+ (call |forcemv| (get |cf| |values|))))
+ `(return (call (get |pv| |apply|)
+ this
+ (call |forcemv| (get |cf| |values|)))))
+ (throw |cf|)))))
(define-compilation throw (id value)
- (js!selfcall
- "var values = mv;"
- "throw ({"
- "type: 'catch', "
- "id: " (ls-compile id) ", "
- "values: " (ls-compile value t) ", "
- "message: 'Throw uncatched.'"
- "})"))
+ (js!selfcall*
+ `(var (|values| |mv|))
+ `(throw (object
+ |type| "catch"
+ |id| ,(ls-compile id)
+ |values| ,(ls-compile value t)
+ |message| "Throw uncatched."))))
(defun go-tag-p (x)
(or (integerp x) (symbolp x)))
(setq initag (second (binding-value b))))
(js!selfcall
;; TAGBODY branch to take
- "var " branch " = " initag ";"
- "var " tbidx " = [];"
- "tbloop:"
- "while (true) {"
- `(code "try {"
+ "var " branch " = " initag ";"
+ "var " tbidx " = [];"
+ "tbloop:"
+ "while (true) {"
+ `(code "try {"
,(let ((content nil))
- `(code "switch(" ,branch "){"
- "case " ,initag ":"
+ `(code "switch(" ,branch "){"
+ "case " ,initag ":"
,@(dolist (form (cdr body) (reverse content))
(push (if (not (go-tag-p form))
`(code ,(ls-compile form) ";" )
(let ((b (lookup-in-lexenv form *environment* 'gotag)))
`(code "case " ,(second (binding-value b)) ":" )))
content))
- "default:"
- " break tbloop;"
+ "default:"
+ " break tbloop;"
"}" ))
- "}"
- "catch (jump) {"
- " if (jump.type == 'tagbody' && jump.id == " ,tbidx ")"
- " " ,branch " = jump.label;"
- " else"
- " throw(jump);"
+ "}"
+ "catch (jump) {"
+ " if (jump.type == 'tagbody' && jump.id == " ,tbidx ")"
+ " " ,branch " = jump.label;"
+ " else"
+ " throw(jump);"
"}" )
- "}"
+ "}"
"return " (ls-compile nil) ";" ))))
(define-compilation go (label)
"})" )))
(define-compilation unwind-protect (form &rest clean-up)
- (js!selfcall
- "var ret = " (ls-compile nil) ";"
- "try {"
- `(code "ret = " ,(ls-compile form) ";" )
- "} finally {"
- `(code ,(ls-compile-block clean-up))
- "}"
- "return ret;" ))
+ (js!selfcall*
+ `(var (|ret| ,(ls-compile nil)))
+ `(try
+ (= |ret| ,(ls-compile form)))
+ `(finally
+ ,(ls-compile-block clean-up))
+ `(return |ret|)))
(define-compilation multiple-value-call (func-form &rest forms)
(js!selfcall
- "var func = " (ls-compile func-form) ";"
- "var args = [" (if *multiple-value-p* "values" "pv") ", 0];"
+ "var func = " (ls-compile func-form) ";"
+ "var args = [" (if *multiple-value-p* "values" "pv") ", 0];"
"return "
(js!selfcall
- "var values = mv;"
- "var vs;"
+ "var values = mv;"
+ "var vs;"
`(code
,@(mapcar (lambda (form)
- `(code "vs = " ,(ls-compile form t) ";"
- "if (typeof vs === 'object' && 'multiple-value' in vs)"
- (code "args = args.concat(vs);" )
- "else"
+ `(code "vs = " ,(ls-compile form t) ";"
+ "if (typeof vs === 'object' && 'multiple-value' in vs)"
+ (code " args = args.concat(vs);" )
+ " else "
(code "args.push(vs);" )))
forms))
- "args[1] = args.length-2;"
+ "args[1] = args.length-2;"
"return func.apply(window, args);" ) ";" ))
(define-compilation multiple-value-prog1 (first-form &rest forms)
(js!selfcall
- "var args = " (ls-compile first-form *multiple-value-p*) ";"
+ "var args = " (ls-compile first-form *multiple-value-p*) ";"
(ls-compile-block forms)
"return args;" ))
,@(mapcar (lambda (decl)
`(let ((name ,(first decl))
(type ,(second decl)))
- `(code "if (typeof " ,name " != '" ,type "')"
+ `(code "if (typeof " ,name " != '" ,type "')"
(code "throw 'The value ' + "
,name
" + ' is not a type "
(prelude '()))
(dolist (x args)
(cond
- ((floatp x) (push (float-to-string x) fargs))
- ((numberp x) (push (integer-to-string x) fargs))
- (t (let ((v (code "x" (incf counter))))
+ ((or (floatp x) (numberp x)) (push x fargs))
+ (t (let ((v (make-symbol (code "x" (incf counter)))))
(push v fargs)
- (push `(code "var " ,v " = " ,(ls-compile x) ";"
- "if (typeof " v " !== 'number') throw 'Not a number!';"
- )
+ (push `(code "var " ,v " = " ,(ls-compile x) ";"
+ "if (typeof " ,v " !== 'number') throw 'Not a number!';")
prelude)))))
(js!selfcall
`(code ,@(reverse prelude))
(define-raw-builtin + (&rest numbers)
(if (null numbers)
- "0"
+ 0
(variable-arity numbers
- `(code ,@(interleave numbers "+")))))
+ `(+ ,@numbers))))
(define-raw-builtin - (x &rest others)
(let ((args (cons x others)))
- (variable-arity args
- (if (null others)
- `(code "-" ,(car args))
- `(code ,@(interleave args "-"))))))
+ (variable-arity args `(- ,@args))))
(define-raw-builtin * (&rest numbers)
(if (null numbers)
- "1"
- (variable-arity numbers
- `(code ,@(interleave numbers "*")))))
+ 1
+ (variable-arity numbers `(* ,@numbers))))
(define-raw-builtin / (x &rest others)
(let ((args (cons x others)))
(variable-arity args
(if (null others)
- `(code "1 /" ,(car args))
- `(code ,@(interleave args "/"))))))
+ `(/ 1 ,(car args))
+ (reduce (lambda (x y) `(/ ,x ,y))
+ args)))))
(define-builtin mod (x y) (num-op-num x "%" y))
(defun comparison-conjuntion (vars op)
(cond
((null (cdr vars))
- "true")
+ 'true)
((null (cddr vars))
- `(code ,(car vars) ,op ,(cadr vars)))
+ `(,op ,(car vars) ,(cadr vars)))
(t
- `(code ,(car vars) ,op ,(cadr vars)
- " && "
- ,(comparison-conjuntion (cdr vars) op)))))
+ `(and (,op ,(car vars) ,(cadr vars))
+ ,(comparison-conjuntion (cdr vars) op)))))
(defmacro define-builtin-comparison (op sym)
`(define-raw-builtin ,op (x &rest args)
(let ((args (cons x args)))
(variable-arity args
- (js!bool (comparison-conjuntion args ,sym))))))
+ (js!bool (comparison-conjuntion args ',sym))))))
-(define-builtin-comparison > ">")
-(define-builtin-comparison < "<")
-(define-builtin-comparison >= ">=")
-(define-builtin-comparison <= "<=")
-(define-builtin-comparison = "==")
-(define-builtin-comparison /= "!=")
+(define-builtin-comparison > >)
+(define-builtin-comparison < <)
+(define-builtin-comparison >= >=)
+(define-builtin-comparison <= <=)
+(define-builtin-comparison = ==)
+(define-builtin-comparison /= !=)
(define-builtin numberp (x)
- (js!bool `(code "(typeof (" ,x ") == \"number\")")))
+ (js!bool `(== (typeof ,x) "number")))
(define-builtin floor (x)
(type-check (("x" "number" x))
"make_lisp_string(x.toString())"))
(define-builtin cons (x y)
- (code "({car: " x ", cdr: " y "})"))
+ `(object "car" ,x "cdr" ,y))
(define-builtin consp (x)
(js!bool
(js!selfcall
- "var tmp = " x ";"
+ "var tmp = " x ";"
"return (typeof tmp == 'object' && 'car' in tmp);" )))
(define-builtin car (x)
- (js!selfcall
- "var tmp = " x ";"
- "return tmp === " (ls-compile nil)
- "? " (ls-compile nil)
- ": tmp.car;" ))
+ (js!selfcall*
+ `(var (tmp ,x))
+ `(return (if (=== tmp ,(ls-compile nil))
+ ,(ls-compile nil)
+ (get tmp "car")))))
(define-builtin cdr (x)
- (js!selfcall
- "var tmp = " x ";"
- "return tmp === " (ls-compile nil) "? "
- (ls-compile nil)
- ": tmp.cdr;" ))
+ (js!selfcall*
+ `(var (tmp ,x))
+ `(return (if (=== tmp ,(ls-compile nil))
+ ,(ls-compile nil)
+ (get tmp "cdr")))))
(define-builtin rplaca (x new)
(type-check (("x" "object" x))
`(code "(x.cdr = " ,new ", x)")))
(define-builtin symbolp (x)
- (js!bool `(code "(" ,x " instanceof Symbol)")))
+ (js!bool `(instanceof ,x |Symbol|)))
(define-builtin make-symbol (name)
- `(code "(new Symbol(" ,name "))"))
+ `(new (call |Symbol| ,name)))
(define-builtin symbol-name (x)
- `(code "(" ,x ").name"))
+ `(get ,x "name"))
(define-builtin set (symbol value)
- `(code "(" ,symbol ").value = " ,value))
+ `(= (get ,symbol "value") ,value))
(define-builtin fset (symbol value)
- `(code "(" ,symbol ").fvalue = " ,value))
+ `(= (get ,symbol "fvalue") ,value))
(define-builtin boundp (x)
- (js!bool `(code "(" ,x ".value !== undefined)")))
+ (js!bool `(!== (get ,x "value") undefined)))
(define-builtin fboundp (x)
- (js!bool `(code "(" ,x ".fvalue !== undefined)")))
+ (js!bool `(!== (get ,x "fvalue") undefined)))
(define-builtin symbol-value (x)
- (js!selfcall
- "var symbol = " x ";"
- "var value = symbol.value;"
- "if (value === undefined) throw \"Variable `\" + xstring(symbol.name) + \"' is unbound.\";"
- "return value;" ))
+ (js!selfcall*
+ `(var (symbol ,x)
+ (value (get symbol "value")))
+ `(if (=== value undefined)
+ (throw (+ "Variable `" (call |xstring| (get symbol "name")) "' is unbound.")))
+ `(return value)))
(define-builtin symbol-function (x)
- (js!selfcall
- "var symbol = " x ";"
- "var func = symbol.fvalue;"
- "if (func === undefined) throw \"Function `\" + xstring(symbol.name) + \"' is undefined.\";"
- "return func;" ))
+ (js!selfcall*
+ `(var (symbol ,x)
+ (func (get symbol "fvalue")))
+ `(if (=== func undefined)
+ (throw (+ "Function `" (call |xstring| (get symbol "name")) "' is undefined.")))
+ `(return func)))
(define-builtin symbol-plist (x)
- `(code "((" ,x ").plist || " ,(ls-compile nil) ")"))
+ `(or (get ,x "plist") ,(ls-compile nil)))
(define-builtin lambda-code (x)
- `(code "make_lisp_string((" ,x ").toString())"))
+ `(call |make_lisp_string| (call (get ,x "toString"))))
(define-builtin eq (x y)
- (js!bool `(code "(" ,x " === " ,y ")")))
+ (js!bool `(=== ,x ,y)))
(define-builtin char-code (x)
(type-check (("x" "string" x))
(define-builtin characterp (x)
(js!bool
(js!selfcall
- "var x = " x ";"
+ "var x = " x ";"
"return (typeof(" x ") == \"string\") && (x.length == 1 || x.length == 2);")))
(define-builtin char-upcase (x)
- `(code "safe_char_upcase(" ,x ")"))
+ `(call |safe_char_upcase| ,x))
(define-builtin char-downcase (x)
- `(code "safe_char_downcase(" ,x ")"))
+ `(call |safe_char_downcase| ,x))
(define-builtin stringp (x)
(js!bool
(js!selfcall
- "var x = " x ";"
+ "var x = " x ";"
"return typeof(x) == 'object' && 'length' in x && x.stringp == 1;")))
(define-raw-builtin funcall (func &rest args)
(js!selfcall
- "var f = " (ls-compile func) ";"
+ "var f = " (ls-compile func) ";"
"return (typeof f === 'function'? f: f.fvalue)("
`(code
,@(interleave (list* (if *multiple-value-p* "values" "pv")
(let ((args (butlast args))
(last (car (last args))))
(js!selfcall
- "var f = " (ls-compile func) ";"
+ "var f = " (ls-compile func) ";"
"var args = [" `(code
,@(interleave (list* (if *multiple-value-p* "values" "pv")
(integer-to-string (length args))
(mapcar #'ls-compile args))
", "))
- "];"
- "var tail = (" (ls-compile last) ");"
- "while (tail != " (ls-compile nil) "){"
- " args.push(tail.car);"
- " args[1] += 1;"
- " tail = tail.cdr;"
- "}"
+ "];"
+ "var tail = (" (ls-compile last) ");"
+ "while (tail != " (ls-compile nil) "){"
+ " args.push(tail.car);"
+ " args[1] += 1;"
+ " tail = tail.cdr;"
+ "}"
"return (typeof f === 'function'? f : f.fvalue).apply(this, args);" ))))
(define-builtin js-eval (string)
(if *multiple-value-p*
(js!selfcall
- "var v = globalEval(xstring(" string "));"
+ "var v = globalEval(xstring(" string "));"
"return values.apply(this, forcemv(v));" )
`(code "globalEval(xstring(" ,string "))")))
(define-builtin %throw (string)
- (js!selfcall "throw " string ";" ))
+ (js!selfcall* `(throw ,string)))
(define-builtin functionp (x)
- (js!bool `(code "(typeof " ,x " == 'function')")))
+ (js!bool `(=== (typeof ,x) "function")))
(define-builtin %write-string (x)
- `(code "lisp.write(" ,x ")"))
+ `(call (get |lisp| "write") ,x))
+
+(define-builtin /debug (x)
+ `(call (get |console| "log") (call |xstring| ,x)))
;;; Storage vectors. They are used to implement arrays and (in the
(define-builtin storage-vector-p (x)
(js!bool
- (js!selfcall
- "var x = " x ";"
- "return typeof x === 'object' && 'length' in x;")))
+ (js!selfcall*
+ `(var (x ,x))
+ `(return (and (=== (typeof x) "object") (in "length" x))))))
(define-builtin make-storage-vector (n)
- (js!selfcall
- "var r = [];"
- "r.length = " n ";"
- "return r;" ))
+ (js!selfcall*
+ `(var (r #()))
+ `(= (get r "length") ,n)
+ `(return r)))
(define-builtin storage-vector-size (x)
- `(code ,x ".length"))
+ `(get ,x "length"))
(define-builtin resize-storage-vector (vector new-size)
- `(code "(" ,vector ".length = " ,new-size ")"))
+ `(= (get ,vector "length") ,new-size))
(define-builtin storage-vector-ref (vector n)
- (js!selfcall
- "var x = " "(" vector ")[" n "];"
- "if (x === undefined) throw 'Out of range';"
- "return x;" ))
+ (js!selfcall*
+ `(var (x (get ,vector ,n)))
+ `(if (=== x undefined) (throw "Out of range."))
+ `(return x)))
(define-builtin storage-vector-set (vector n value)
- (js!selfcall
- "var x = " vector ";"
- "var i = " n ";"
- "if (i < 0 || i >= x.length) throw 'Out of range';"
- "return x[i] = " value ";" ))
+ (js!selfcall*
+ `(var (x ,vector))
+ `(var (i ,n))
+ `(if (or (< i 0) (>= i (get x "length")))
+ (throw "Out of range."))
+ `(return (= (property x i) ,value))))
(define-builtin concatenate-storage-vector (sv1 sv2)
- (js!selfcall
- "var sv1 = " sv1 ";"
- "var r = sv1.concat(" sv2 ");"
- "r.type = sv1.type;"
- "r.stringp = sv1.stringp;"
- "return r;" ))
+ (js!selfcall*
+ `(var (sv1 ,sv1))
+ `(var (r (call (get sv1 "concat") ,sv2)))
+ `(= (get r "type") (get sv1 "type"))
+ `(= (get r "stringp") (get sv1 "stringp"))
+ `(return r)))
(define-builtin get-internal-real-time ()
"(new Date()).getTime()")
(define-raw-builtin oget* (object key &rest keys)
(js!selfcall
- "var tmp = (" (ls-compile object) ")[xstring(" (ls-compile key) ")];"
+ "var tmp = (" (ls-compile object) ")[xstring(" (ls-compile key) ")];"
`(code
,@(mapcar (lambda (key)
- `(code "if (tmp === undefined) return " ,(ls-compile nil) ";"
+ `(code "if (tmp === undefined) return " ,(ls-compile nil) ";"
"tmp = tmp[xstring(" ,(ls-compile key) ")];" ))
keys))
"return tmp === undefined? " (ls-compile nil) " : tmp;" ))
(define-raw-builtin oset* (value object key &rest keys)
(let ((keys (cons key keys)))
(js!selfcall
- "var obj = " (ls-compile object) ";"
+ "var obj = " (ls-compile object) ";"
`(code ,@(mapcar (lambda (key)
`(code "obj = obj[xstring(" ,(ls-compile key) ")];"
"if (obj === undefined) throw 'Impossible to set Javascript property.';" ))
(butlast keys)))
- "var tmp = obj[xstring(" (ls-compile (car (last keys))) ")] = " (ls-compile value) ";"
+ "var tmp = obj[xstring(" (ls-compile (car (last keys))) ")] = " (ls-compile value) ";"
"return tmp === undefined? " (ls-compile nil) " : tmp;" )))
(define-raw-builtin oget (object key &rest keys)
- `(code "js_to_lisp(" ,(ls-compile `(oget* ,object ,key ,@keys)) ")"))
+ `(call |js_to_lisp| ,(ls-compile `(oget* ,object ,key ,@keys))))
(define-raw-builtin oset (value object key &rest keys)
(ls-compile `(oset* (lisp-to-js ,value) ,object ,key ,@keys)))
(define-builtin objectp (x)
- (js!bool `(code "(typeof (" ,x ") === 'object')")))
+ (js!bool `(=== (typeof ,x) "object")))
-(define-builtin lisp-to-js (x) `(code "lisp_to_js(" ,x ")"))
-(define-builtin js-to-lisp (x) `(code "js_to_lisp(" ,x ")"))
+(define-builtin lisp-to-js (x) `(call |lisp_to_js| ,x))
+(define-builtin js-to-lisp (x) `(call |js_to_lisp| ,x))
(define-builtin in (key object)
- (js!bool `(code "(xstring(" ,key ") in (" ,object "))")))
+ (js!bool `(in (call |xstring| ,key) ,object)))
(define-builtin map-for-in (function object)
(js!selfcall
- "var f = " function ";"
- "var g = (typeof f === 'function' ? f : f.fvalue);"
- "var o = " object ";"
- "for (var key in o){"
+ "var f = " function ";"
+ "var g = (typeof f === 'function' ? f : f.fvalue);"
+ "var o = " object ";"
+ "for (var key in o){"
`(code "g(" ,(if *multiple-value-p* "values" "pv") ", 1, o[key]);" )
"}"
" return " (ls-compile nil) ";" ))
`(code ,(ls-compile-block (butlast sexps) nil decls-allowed-p)
"return " ,(ls-compile (car (last sexps)) *multiple-value-p*) ";")
`(code
- ,@(mapcar #'ls-compile sexps)
- ";"))))
+ ,@(interleave (mapcar #'ls-compile sexps) ";
+" *newline*)
+ ";" ,*newline*))))
-(defun ls-compile (sexp &optional multiple-value-p)
+(defun ls-compile* (sexp &optional multiple-value-p)
(multiple-value-bind (sexp expandedp) (!macroexpand-1 sexp)
(when expandedp
- (return-from ls-compile (ls-compile sexp multiple-value-p)))
+ (return-from ls-compile* (ls-compile sexp multiple-value-p)))
;; The expression has been macroexpanded. Now compile it!
(let ((*multiple-value-p* multiple-value-p))
(cond
(t
(error "How should I compile `~S'?" sexp))))))
+(defun ls-compile (sexp &optional multiple-value-p)
+ `(code "(" ,(ls-compile* sexp multiple-value-p) ")"))
+
(defvar *compile-print-toplevels* nil)
(defun convert-toplevel (sexp &optional multiple-value-p)
(let ((*toplevel-compilations* nil))
(cond
- ((and (consp sexp) (eq (car sexp) 'progn))
- (mapcar (lambda (s)
- (ls-compile-toplevel s t))
- (cdr sexp)))
+ ;; Non-empty toplevel progn
+ ((and (consp sexp)
+ (eq (car sexp) 'progn)
+ (cdr sexp))
+ `(progn
+ ,@(mapcar (lambda (s) (convert-toplevel s t))
+ (cdr sexp))))
(t
(when *compile-print-toplevels*
(let ((form-string (prin1-to-string sexp)))
(format t "Compiling ~a..." (truncate-string form-string))))
(let ((code (ls-compile sexp multiple-value-p)))
`(code
- ,@(interleave (get-toplevel-compilations) ";" t)
+ ,@(interleave (get-toplevel-compilations) ";
+" t)
,(when code
`(code ,code ";"))))))))
(defun ls-compile-toplevel (sexp &optional multiple-value-p)
- (js (convert-toplevel sexp multiple-value-p)))
+ (with-output-to-string (*standard-output*)
+ (js (convert-toplevel sexp multiple-value-p))))