(/debug "loading compiler.lisp!")
-(define-js-macro selfcall (&body body)
- `(call (function () ,@body)))
-
-(define-js-macro bool (expr)
- `(if ,expr ,(convert t) ,(convert nil)))
-
;;; 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
;;; the beginning of this file) as well as some primitive functions.
-(defun interleave (list element &optional after-last-p)
- (unless (null list)
- (with-collect
- (collect (car list))
- (dolist (x (cdr list))
- (collect element)
- (collect x))
- (when after-last-p
- (collect element)))))
+(define-js-macro selfcall (&body body)
+ `(call (function () ,@body)))
+
+(define-js-macro bool (expr)
+ `(if ,expr ,(convert t) ,(convert nil)))
-;;; Like CODE, but prefix each line with four spaces. Two versions
-;;; of this function are available, because the Ecmalisp version is
-;;; very slow and bootstraping was annoying.
+(define-js-macro method-call (x method &rest args)
+ `(call (get ,x ,method) ,@args))
;;; A Form can return a multiple values object calling VALUES, like
;;; values(arg1, arg2, ...). It will work in any context, as well as
(defun gvarname (symbol)
(declare (ignore symbol))
(incf *variable-counter*)
- (concat "v" (integer-to-string *variable-counter*)))
+ (make-symbol (concat "v" (integer-to-string *variable-counter*))))
(defun translate-variable (symbol)
(awhen (lookup-in-lexenv symbol *environment* 'variable)
(dotimes (idx n-optional-arguments)
(let ((arg (nth idx optional-arguments)))
(collect `(case ,(+ idx n-required-arguments)))
- (collect `(= ,(make-symbol (translate-variable (car arg)))
+ (collect `(= ,(translate-variable (car arg))
,(convert (cadr arg))))
(collect (when (third arg)
- `(= ,(make-symbol (translate-variable (third arg)))
+ `(= ,(translate-variable (third arg))
,(convert nil))))))
(collect 'default)
(collect '(break)))))))
(n-optional-arguments (length (ll-optional-arguments ll)))
(rest-argument (ll-rest-argument ll)))
(when rest-argument
- (let ((js!rest (make-symbol (translate-variable rest-argument))))
+ (let ((js!rest (translate-variable rest-argument)))
`(progn
(var (,js!rest ,(convert nil)))
(var i)
(destructuring-bind ((keyword-name var) &optional initform svar)
keyword-argument
(declare (ignore keyword-name initform))
- (collect `(var ,(make-symbol (translate-variable var))))
+ (collect `(var ,(translate-variable var)))
(when svar
(collect
- `(var (,(make-symbol (translate-variable svar))
+ `(var (,(translate-variable svar)
,(convert nil))))))))
;; Parse keywords
(if (=== (property |arguments| (+ i 2))
,(convert keyword-name))
(progn
- (= ,(make-symbol (translate-variable var))
+ (= ,(translate-variable var)
(property |arguments| (+ i 3)))
- ,(when svar `(= ,(make-symbol (translate-variable svar))
+ ,(when svar `(= ,(translate-variable svar)
,(convert t)))
(break))))
(if (== i |nargs|)
- (= ,(make-symbol (translate-variable var))
- ,(convert initform)))))))
+ (= ,(translate-variable var) ,(convert initform)))))))
(when keyword-arguments
`(progn
(var i)
(ll-svars ll)))))
(lambda-name/docstring-wrapper name documentation
`(function (|values| |nargs| ,@(mapcar (lambda (x)
- (make-symbol (translate-variable x)))
+ (translate-variable x))
(append required-arguments optional-arguments)))
;; Check number of arguments
,(lambda-check-argument-count n-required-arguments
(eq (binding-type b) 'variable)
(not (member 'special (binding-declarations b)))
(not (member 'constant (binding-declarations b))))
- ;; TODO: Unnecesary make-symbol when codegen migration is
- ;; finished.
- `(= ,(make-symbol (binding-value b)) ,(convert val)))
+ `(= ,(binding-value b) ,(convert val)))
((and b (eq (binding-type b) 'macro))
(convert `(setf ,var ,val)))
(t
(defun genlit ()
(incf *literal-counter*)
- (concat "l" (integer-to-string *literal-counter*)))
+ (make-symbol (concat "l" (integer-to-string *literal-counter*))))
(defun dump-symbol (symbol)
#-jscl
(cond
((integerp sexp) sexp)
((floatp sexp) sexp)
- ((characterp sexp)
- ;; TODO: Remove selfcall after migration
- `(selfcall (return ,(string sexp))))
+ ((characterp sexp) (string sexp))
(t
(or (cdr (assoc sexp *literal-table* :test #'eql))
(let ((dumped (typecase sexp
(if (and recursive (not (symbolp sexp)))
dumped
(let ((jsvar (genlit)))
- (push (cons sexp (make-symbol jsvar)) *literal-table*)
- (toplevel-compilation `(var (,(make-symbol jsvar) ,dumped)))
+ (push (cons sexp jsvar) *literal-table*)
+ (toplevel-compilation `(var (,jsvar ,dumped)))
(when (keywordp sexp)
- (toplevel-compilation `(= ,(get (make-symbol jsvar) "value") ,(make-symbol jsvar))))
- (make-symbol jsvar))))))))
+ (toplevel-compilation `(= ,(get jsvar "value") ,jsvar)))
+ jsvar)))))))
(define-compilation quote (sexp)
(define-compilation %while (pred &rest body)
`(selfcall
(while (!== ,(convert pred) ,(convert nil))
- 0 ; TODO: Force
- ; braces. Unnecesary when code
- ; is gone
,(convert-block body))
(return ,(convert nil))))
((symbolp x)
(let ((b (lookup-in-lexenv x *environment* 'function)))
(if b
- (make-symbol (binding-value b))
+ (binding-value b)
(convert `(symbol-function ',x)))))))
(defun make-function-binding (fname)
(extend-lexenv (mapcar #'make-function-binding fnames)
*environment*
'function)))
- `(call (function ,(mapcar #'make-symbol (mapcar #'translate-function fnames))
+ `(call (function ,(mapcar #'translate-function fnames)
,(convert-block body t))
,@cfuncs)))
'function)))
`(selfcall
,@(mapcar (lambda (func)
- `(var (,(make-symbol (translate-function (car func)))
+ `(var (,(translate-function (car func))
,(compile-lambda (cadr func)
`((block ,(car func) ,@(cddr func)))))))
definitions)
`(call (function ,(mapcar (lambda (x)
(if (special-variable-p x)
(let ((v (gvarname x)))
- (push (cons x (make-symbol v)) dynamic-bindings)
- (make-symbol v))
- (make-symbol (translate-variable x))))
+ (push (cons x v) dynamic-bindings)
+ v)
+ (translate-variable x)))
variables)
,(let ((body (convert-block body t t)))
`,(let-binding-wrapper dynamic-bindings body)))
(convert `(setq ,var ,value))
(let* ((v (gvarname var))
(b (make-binding :name var :type 'variable :value v)))
- (prog1 `(var (,(make-symbol v) ,(convert value)))
+ (prog1 `(var (,v ,(convert value)))
(push-to-lexenv b *environment* 'variable))))))
;;; Wrap BODY to restore the symbol values of SYMBOLS after body. It
(try
,@(mapcar (lambda (b)
(let ((s (convert `(quote ,(car b)))))
- `(var (,(make-symbol (cdr b)) (get ,s "value")))))
+ `(var (,(cdr b) (get ,s "value")))))
store)
,body)
(finally
,@(mapcar (lambda (b)
(let ((s (convert `(quote ,(car b)))))
- `(= (get ,s "value") ,(make-symbol (cdr b)))))
+ `(= (get ,s "value") ,(cdr b))))
store)))))
(define-compilation let* (bindings &rest body)
(if (member 'used (binding-declarations b))
`(selfcall
(try
- (var (,(make-symbol idvar) #()))
+ (var (,idvar #()))
,cbody)
(catch (cf)
(if (and (== (get cf "type") "block")
- (== (get cf "id") ,(make-symbol idvar)))
+ (== (get cf "id") ,idvar))
,(if *multiple-value-p*
- `(return (call (get |values| "apply") this (call |forcemv| (get cf "values"))))
+ `(return (method-call |values| "apply" this (call |forcemv| (get cf "values"))))
`(return (get cf "values")))
(throw cf))))
- ;; TODO: is selfcall necessary here?
`(selfcall ,cbody)))))
(define-compilation return-from (name &optional value)
(throw
(object
"type" "block"
- "id" ,(make-symbol (binding-value b))
+ "id" ,(binding-value b)
"values" ,(convert value multiple-value-p)
"message" ,(concat "Return from unknown block '" (symbol-name name) "'."))))))
(define-compilation catch (id &rest body)
`(selfcall
- (var (|id| ,(convert id)))
+ (var (id ,(convert id)))
(try
,(convert-block body t))
(catch (|cf|)
(if (and (== (get |cf| "type") "catch")
- (== (get |cf| "id") |id|))
+ (== (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")))))
+ `(return (method-call |values| "apply" this (call |forcemv| (get |cf| "values"))))
+ `(return (method-call |pv| "apply" this (call |forcemv| (get |cf| "values")))))
(throw |cf|)))))
(define-compilation throw (id value)
`(selfcall
(var (|values| |mv|))
(throw (object
- |type| "catch"
- |id| ,(convert id)
- |values| ,(convert value t)
- |message| "Throw uncatched."))))
+ "type" "catch"
+ "id" ,(convert id)
+ "values" ,(convert value t)
+ "message" "Throw uncatched."))))
(defun go-tag-p (x)
(or (integerp x) (symbolp x)))
(setq initag (second (binding-value b))))
`(selfcall
;; TAGBODY branch to take
- (var (,(make-symbol branch) ,initag))
- (var (,(make-symbol tbidx) #()))
+ (var (,branch ,initag))
+ (var (,tbidx #()))
(label tbloop
(while true
(try
- (switch ,(make-symbol branch)
+ (switch ,branch
,@(with-collect
(collect `(case ,initag))
(dolist (form (cdr body))
(break tbloop)))
(catch (jump)
(if (and (== (get jump "type") "tagbody")
- (== (get jump "id") ,(make-symbol tbidx)))
- (= ,(make-symbol branch) (get jump "label"))
+ (== (get jump "id") ,tbidx))
+ (= ,branch (get jump "label"))
(throw jump)))))
(return ,(convert nil))))))
(throw
(object
"type" "tagbody"
- "id" ,(make-symbol (first (binding-value b)))
+ "id" ,(first (binding-value b))
"label" ,(second (binding-value b))
"message" ,(concat "Attempt to GO to non-existing tag " n))))))
(define-compilation unwind-protect (form &rest clean-up)
`(selfcall
- (var (|ret| ,(convert nil)))
+ (var (ret ,(convert nil)))
(try
- (= |ret| ,(convert form)))
+ (= ret ,(convert form)))
(finally
,(convert-block clean-up))
- (return |ret|)))
+ (return ret)))
(define-compilation multiple-value-call (func-form &rest forms)
`(selfcall
(collect `(= vs ,(convert form t)))
(collect `(if (and (=== (typeof vs) "object")
(in "multiple-value" vs))
- (= args (call (get args "concat") vs))
- (call (get args "push") vs))))))
+ (= args (method-call args "concat" vs))
+ (method-call args "push" vs))))))
(= (property args 1) (- (property args "length") 2))
- (return (call (get func "apply") |window| args))))))
+ (return (method-call func "apply" |window| args))))))
(define-compilation multiple-value-prog1 (first-form &rest forms)
`(selfcall
(var (args ,(convert first-form *multiple-value-p*)))
- ;; TODO: Interleave is temporal
(progn ,@(mapcar #'convert forms))
(return args)))
`(bool (== (typeof ,x) "number")))
(define-builtin floor (x)
- `(call (get |Math| |floor|) ,x))
+ `(method-call |Math| "floor" ,x))
(define-builtin expt (x y)
- `(call (get |Math| |pow|) ,x ,y))
+ `(method-call |Math| "pow" ,x ,y))
(define-builtin float-to-string (x)
- `(call |make_lisp_string| (call (get ,x |toString|))))
+ `(call |make_lisp_string| (method-call ,x |toString|)))
(define-builtin cons (x y)
`(object "car" ,x "cdr" ,y))
`(or (get ,x "plist") ,(convert nil)))
(define-builtin lambda-code (x)
- `(call |make_lisp_string| (call (get ,x "toString"))))
+ `(call |make_lisp_string| (method-call ,x "toString")))
(define-builtin eq (x y)
`(bool (=== ,x ,y)))
(mapcar #'convert args)))))
(var (tail ,(convert last)))
(while (!= tail ,(convert nil))
- (call (get args "push") (get tail "car"))
+ (method-call args "push" (get tail "car"))
(post++ (property args 1))
(= tail (get tail "cdr")))
- (return (call (get (if (=== (typeof f) "function")
- f
- (get f "fvalue"))
- "apply")
- this
- args))))))
+ (return (method-call (if (=== (typeof f) "function")
+ f
+ (get f "fvalue"))
+ "apply"
+ this
+ args))))))
(define-builtin js-eval (string)
(if *multiple-value-p*
`(selfcall
(var (v (call |globalEval| (call |xstring| ,string))))
- (return (call (get |values| "apply") this (call |forcemv| v))))
+ (return (method-call |values| "apply" this (call |forcemv| v))))
`(call |globalEval| (call |xstring| ,string))))
(define-builtin %throw (string)
`(bool (=== (typeof ,x) "function")))
(define-builtin %write-string (x)
- `(call (get |lisp| "write") ,x))
+ `(method-call |lisp| "write" ,x))
(define-builtin /debug (x)
- `(call (get |console| "log") (call |xstring| ,x)))
+ `(method-call |console| "log" (call |xstring| ,x)))
;;; Storage vectors. They are used to implement arrays and (in the
(define-builtin concatenate-storage-vector (sv1 sv2)
`(selfcall
(var (sv1 ,sv1))
- (var (r (call (get sv1 "concat") ,sv2)))
+ (var (r (method-call sv1 "concat" ,sv2)))
(= (get r "type") (get sv1 "type"))
(= (get r "stringp") (get sv1 "stringp"))
(return r)))
(define-builtin get-internal-real-time ()
- `(call (get (new (call |Date|)) "getTime")))
+ `(method-call (new (call |Date|)) "getTime"))
(define-builtin values-array (array)
(if *multiple-value-p*
- `(call (get |values| "apply") this ,array)
- `(call (get |pv| "apply") this ,array)))
+ `(method-call |values| "apply" this ,array)
+ `(method-call |pv| "apply" this ,array)))
(define-raw-builtin values (&rest args)
(if *multiple-value-p*
(error "Bad function designator `~S'" function))
(cond
((translate-function function)
- `(call ,(make-symbol (translate-function function)) ,@arglist))
+ `(call ,(translate-function function) ,@arglist))
((and (symbolp function)
#+jscl (eq (symbol-package function) (find-package "COMMON-LISP"))
#-jscl t)
- `(call (get ,(convert `',function) "fvalue") ,@arglist))
+ `(method-call ,(convert `',function) "fvalue" ,@arglist))
#+jscl((symbolp function)
`(call ,(convert `#',function) ,@arglist))
((and (consp function) (eq (car function) 'lambda))
`(call ,(convert `#',function) ,@arglist))
((and (consp function) (eq (car function) 'oget))
- `(call ,(convert function) ,@arglist))
+ `(call |js_to_lisp|
+ (call ,(reduce (lambda (obj p)
+ `(property ,obj (call |xstring| ,p)))
+ (mapcar #'convert (cdr function)))
+ ,@(mapcar (lambda (s)
+ `(call |lisp_to_js| ,s))
+ args))))
(t
(error "Bad function descriptor")))))
(return ,(convert (car (last sexps)) *multiple-value-p*)))
`(progn ,@(mapcar #'convert sexps)))))
-(defun convert* (sexp &optional multiple-value-p)
+(defun convert (sexp &optional multiple-value-p)
(multiple-value-bind (sexp expandedp) (!macroexpand-1 sexp)
(when expandedp
- (return-from convert* (convert sexp multiple-value-p)))
+ (return-from convert (convert sexp multiple-value-p)))
;; The expression has been macroexpanded. Now compile it!
(let ((*multiple-value-p* multiple-value-p))
(cond
(let ((b (lookup-in-lexenv sexp *environment* 'variable)))
(cond
((and b (not (member 'special (binding-declarations b))))
- (make-symbol (binding-value b)))
+ (binding-value b))
((or (keywordp sexp)
(and b (member 'constant (binding-declarations b))))
`(get ,(convert `',sexp) "value"))
(t
(error "How should I compile `~S'?" sexp))))))
-(defun convert (sexp &optional multiple-value-p)
- (convert* sexp multiple-value-p))
-
(defvar *compile-print-toplevels* nil)