symbol-function checks if the function is defined
[jscl.git] / ecmalisp.lisp
index 04b1cb6..cd831bf 100644 (file)
 
 #+ecmalisp
 (progn
+
+  'defmacro
   (eval-when-compile
     (%compile-defmacro 'defmacro
                        '(lambda (name args &rest body)
-                         `(eval-when-compile
-                            (%compile-defmacro ',name
-                                              '(lambda ,(mapcar (lambda (x)
-                                                                   (if (eq x '&body)
-                                                                       '&rest
-                                                                       x))
-                                                                 args)
-                                                 ,@body))))))
+                         `(progn
+                            (eval-when-compile
+                              (%compile-defmacro ',name
+                                                 '(lambda ,(mapcar (lambda (x)
+                                                                     (if (eq x '&body)
+                                                                         '&rest
+                                                                         x))
+                                                                   args)
+                                                   ,@body)))
+                            ',name))))
 
   (defmacro defvar (name value)
     `(progn
-       (eval-when-compile
-         (%compile-defvar ',name))
        (setq ,name ,value)
        ',name))
 
@@ -51,9 +53,7 @@
 
   (defmacro defun (name args &body body)
     `(progn
-       (eval-when-compile
-         (%compile-defun ',name))
-       (fsetq ,name (named-lambda ,(symbol-name name) ,args
+       (fset ',name (named-lambda ,(symbol-name name) ,args
                       (block ,name ,@body)))
        ',name))
 
          ,value)))
 
   (defmacro prog2 (form1 result &body body)
-    `(prog1 (progn ,form1 ,result) ,@body))
+    `(prog1 (progn ,form1 ,result) ,@body)))
 
-  )
 
 
 ;;; This couple of helper functions will be defined in both Common
     (write-string *newline*)
     x)
 
+  (defun warn (string)
+    (write-string "WARNING: ")
+    (write-line string))
+
   (defun print (x)
     (write-line (prin1-to-string x))
     x))
 (defun gvarname (symbol)
   (concat "v" (integer-to-string (incf *variable-counter*))))
 
-(defun lookup-variable (symbol env)
-  (or (lookup-in-lexenv symbol env 'variable)
-      (lookup-in-lexenv symbol *environment* 'variable)
-      (let ((name (symbol-name symbol))
-            (binding (make-binding symbol 'special-variable (gvarname symbol) nil)))
-        (push-to-lexenv binding *environment* 'variable)
-        (push (lambda ()
-               (let ((b (lookup-in-lexenv symbol *environment* 'variable)))
-                 (unless (binding-declared b)
-                     (error (concat "Undefined variable `" name "'")))))
-              *compilation-unit-checks*)
-        binding)))
-
-(defun lookup-variable-translation (symbol env)
-  (binding-translation (lookup-variable symbol env)))
+(defun translate-variable (symbol env)
+  (binding-translation (lookup-in-lexenv symbol env 'variable)))
 
 (defun extend-local-env (args env)
   (let ((new (copy-lexenv env)))
       (let ((b (make-binding symbol 'lexical-variable (gvarname symbol) t)))
         (push-to-lexenv b new 'variable)))))
 
-(defvar *function-counter* 0)
-(defun lookup-function (symbol env)
-  (or (lookup-in-lexenv symbol env 'function)
-      (lookup-in-lexenv symbol *environment* 'function)
-      (let ((name (symbol-name symbol))
-            (binding
-             (make-binding symbol
-                           'function
-                           (concat "f" (integer-to-string (incf *function-counter*)))
-                           nil)))
-        (push-to-lexenv binding *environment* 'function)
-        (push (lambda ()
-               (let ((b (lookup-in-lexenv symbol *environment* 'function)))
-                 (unless (binding-declared b)
-                   (error (concat "Undefined function `" name "'")))))
-              *compilation-unit-checks*)
-        binding)))
-
-(defun lookup-function-translation (symbol env)
-  (binding-translation (lookup-function symbol env)))
-
+;;; Toplevel compilations
 (defvar *toplevel-compilations* nil)
 
-(defun %compile-defvar (name)
-  (let ((b (lookup-variable name *environment*)))
-    (mark-binding-as-declared b)
-    (push (concat "var " (binding-translation b)) *toplevel-compilations*)))
+(defun toplevel-compilation (string)
+  (push string *toplevel-compilations*))
+
+(defun null-or-empty-p (x)
+  (zerop (length x)))
 
-(defun %compile-defun (name)
-  (let ((b (lookup-function name *environment*)))
-    (mark-binding-as-declared b)
-    (push (concat "var " (binding-translation b)) *toplevel-compilations*)))
+(defun get-toplevel-compilations ()
+  (reverse (remove-if #'null-or-empty-p *toplevel-compilations*)))
 
 (defun %compile-defmacro (name lambda)
   (push-to-lexenv (make-binding name 'macro lambda t) *environment* 'function))
 
 (defun ls-compile-block (sexps env)
   (join-trailing
-   (remove-if #'null
-              (mapcar (lambda (x) (ls-compile x env))  sexps))
+   (remove-if #'null-or-empty-p
+              (mapcar (lambda (x) (ls-compile x env)) sexps))
    (concat ";" *newline*)))
 
 (defmacro define-compilation (name args &body body)
                     env)))
       (concat "(function ("
               (join (mapcar (lambda (x)
-                              (lookup-variable-translation x new-env))
+                              (translate-variable x new-env))
                             (append required-arguments optional-arguments))
                     ",")
               "){" *newline*
                                  (let ((arg (nth idx optional-and-defaults)))
                                    (push (concat "case "
                                                  (integer-to-string (+ idx n-required-arguments)) ":" *newline*
-                                                 (lookup-variable-translation (car arg) new-env)
+                                                 (translate-variable (car arg) new-env)
                                                  "="
                                                  (ls-compile (cadr arg) new-env)
                                                  ";" *newline*)
                    "")
                ;; &rest/&body argument
                (if rest-argument
-                   (let ((js!rest (lookup-variable-translation rest-argument new-env)))
+                   (let ((js!rest (translate-variable rest-argument new-env)))
                      (concat "var " js!rest "= " (ls-compile nil) ";" *newline*
                              "for (var i = arguments.length-1; i>="
                              (integer-to-string (+ n-required-arguments n-optional-arguments))
                        "return " (ls-compile (car (last body)) new-env) ";")) *newline*
               "})"))))
 
-(define-compilation fsetq (var val)
-  (concat (lookup-function-translation var env)
-          " = "
-          (ls-compile val env)))
-
 (define-compilation setq (var val)
-  (let ((b (lookup-variable var env)))
-    (ecase (binding-type b)
-      (lexical-variable (concat (binding-translation b) " = " (ls-compile val env)))
-      (special-variable (ls-compile `(set ',var ,val) env)))))
+  (let ((b (lookup-in-lexenv var env 'variable)))
+    (if (eq (binding-type b) 'lexical-variable)
+        (concat (binding-translation b) " = " (ls-compile val env))
+        (ls-compile `(set ',var ,val) env))))
 
 ;;; FFI Variable accessors
 (define-compilation js-vref (var)
   var)
+
 (define-compilation js-vset (var val)
   (concat "(" var " = " (ls-compile val env) ")"))
 
         (let ((v (genlit))
               (s (concat "{name: \"" (escape-string (symbol-name sexp)) "\"}")))
           (push (cons sexp v) *literal-symbols*)
-          (push (concat "var " v " = " s) *toplevel-compilations*)
+          (toplevel-compilation (concat "var " v " = " s))
           v))
      #+ecmalisp
-     (let ((v (genlit)))
-       (push (concat "var " v " = " (ls-compile `(intern ,(symbol-name sexp))))
-             *toplevel-compilations*)
+     (let ((v (genlit))
+           (s (ls-compile `(intern ,(symbol-name sexp)))))
+       (toplevel-compilation (concat "var " v " = " s))
        v))
     ((consp sexp)
      (let ((c (concat "{car: " (literal (car sexp) t) ", "
        (if recursive
           c
           (let ((v (genlit)))
-            (push (concat "var " v " = " c) *toplevel-compilations*)
+            (toplevel-compilation (concat "var " v " = " c))
             v))))))
 
 (define-compilation quote (sexp)
     ((and (listp x) (eq (car x) 'lambda))
      (ls-compile x env))
     ((symbolp x)
-     (lookup-function-translation x env))))
+     (ls-compile `(symbol-function ',x))
+     ;; TODO: Add lexical functions
+     ;;(lookup-function-translation x env)
+     )))
 
 (define-compilation eval-when-compile (&rest body)
   (eval (cons 'progn body))
       (let ((new-env (extend-local-env variables env)))
         (concat "(function("
                 (join (mapcar (lambda (x)
-                                (lookup-variable-translation x new-env))
+                                (translate-variable x new-env))
                               variables)
                       ",")
                 "){" *newline*
 (define-builtin set (symbol value)
   (concat "(" symbol ").value =" value))
 
+(define-builtin fset (symbol value)
+  (concat "(" symbol ").function =" value))
+
 (define-builtin symbol-value (x)
-  (concat "(" x ").value"))
+  (js!selfcall
+    "var symbol = " x ";" *newline*
+    "var value = symbol.value;" *newline*
+    "if (value === undefined) throw \"Variable `\" + symbol.name + \"' is unbound.\";" *newline*
+    "return value;" *newline*))
 
 (define-builtin symbol-function (x)
-  (concat "(" x ").function"))
+  (js!selfcall
+    "var symbol = " x ";" *newline*
+    "var func = symbol.function;" *newline*
+    "if (func === undefined) throw \"Function `\" + symbol.name + \"' is undefined.\";" *newline*
+    "return func;" *newline*))
 
 (define-builtin eq    (x y) (js!bool (concat "(" x " === " y ")")))
 (define-builtin equal (x y) (js!bool (concat "(" x  " == " y ")")))
   (type-check (("x" "string" x))
     "lisp.write(x)"))
 
-(defun macrop (x)
-  (and (symbolp x) (eq (binding-type (lookup-function x *environment*)) 'macro)))
+(defun macro (x)
+  (and (symbolp x)
+       (let ((b (lookup-in-lexenv x *environment* 'function)))
+         (eq (binding-type b) 'macro)
+         b)))
 
-(defun ls-macroexpand-1 (form env)
-  (if (macrop (car form))
-      (let ((binding (lookup-function (car form) *environment*)))
-        (if (eq (binding-type binding) 'macro)
-            (apply (eval (binding-translation binding)) (cdr form))
-            form))
-      form))
+(defun ls-macroexpand-1 (form)
+  (let ((macro-binding (macro (car form))))
+    (if macro-binding
+        (apply (eval (binding-translation macro-binding)) (cdr form))
+        form)))
 
 (defun compile-funcall (function args env)
   (cond
     ((symbolp function)
-     (concat (lookup-function-translation function env)
-             "("
+     (concat (ls-compile `(quote ,function)) ".function("
              (join (mapcar (lambda (x) (ls-compile x env)) args)
                    ", ")
              ")"))
 (defun ls-compile (sexp &optional (env (make-lexenv)))
   (cond
     ((symbolp sexp)
-     (let ((b (lookup-variable sexp env)))
-       (ecase (binding-type b)
-        (lexical-variable
-         (binding-translation b))
-        (special-variable
-          (ls-compile `(symbol-value ',sexp) env)))))
+     (let ((b (lookup-in-lexenv sexp env 'variable)))
+       (if (eq (binding-type b) 'lexical-variable)
+           (binding-translation b)
+           (ls-compile `(symbol-value ',sexp) env))))
     ((integerp sexp) (integer-to-string sexp))
     ((stringp sexp) (concat "\"" (escape-string sexp) "\""))
     ((listp sexp)
      (if (assoc (car sexp) *compilations*)
          (let ((comp (second (assoc (car sexp) *compilations*))))
            (apply comp env (cdr sexp)))
-         (if (macrop (car sexp))
-             (ls-compile (ls-macroexpand-1 sexp env) env)
+         (if (macro (car sexp))
+             (ls-compile (ls-macroexpand-1 sexp) env)
              (compile-funcall (car sexp) (cdr sexp) env))))))
 
-(defun null-or-empty-p (x)
-  (zerop (length x)))
-
 (defun ls-compile-toplevel (sexp)
   (setq *toplevel-compilations* nil)
   (cond
     (t
      (let ((code (ls-compile sexp)))
        (prog1
-           (concat (join-trailing (remove-if #'null-or-empty-p *toplevel-compilations*)
-                                  (concat ";" *newline*))
+           (concat (join-trailing (get-toplevel-compilations) (concat ";" *newline*))
                    (if code
                        (concat code ";" *newline*)
                        ""))
   ;; Set the initial global environment to be equal to the host global
   ;; environment at this point of the compilation.
   (eval-when-compile
-    (let ((tmp (ls-compile
-                `(progn
-                  ,@(mapcar (lambda (s)
-                              `(oset *package* ,(symbol-name (car s))
-                                     (js-vref ,(cdr s))))
-                            *literal-symbols*)
-                   (setq *environment* ',*environment*)
-                   (setq *variable-counter* ,*variable-counter*)
-                   (setq *function-counter* ,*function-counter*)
-                   (setq *gensym-counter* ,*gensym-counter*)
-                   (setq *block-counter* ,*block-counter*)))))
-      (setq *toplevel-compilations*
-            (append *toplevel-compilations* (list tmp)))))
-  ;; KLUDGE:
+    (toplevel-compilation
+     (ls-compile
+      `(progn
+         ,@(mapcar (lambda (s)
+                     `(oset *package* ,(symbol-name (car s))
+                            (js-vref ,(cdr s))))
+                   *literal-symbols*)
+         (setq *environment* ',*environment*)
+         (setq *variable-counter* ,*variable-counter*)
+         (setq *gensym-counter* ,*gensym-counter*)
+         (setq *block-counter* ,*block-counter*)))))
+
   (eval-when-compile
-    (let ((tmp (ls-compile
-                `(setq *literal-counter* ,*literal-counter*))))
-      (setq *toplevel-compilations*
-            (append *toplevel-compilations* (list tmp))))))
+    (toplevel-compilation
+     (ls-compile `(setq *literal-counter* ,*literal-counter*)))))
 
 
 ;;; Finally, we provide a couple of functions to easily bootstrap
     (setq *literal-symbols* nil)
     (setq *variable-counter* 0
           *gensym-counter* 0
-          *function-counter* 0
           *literal-counter* 0
           *block-counter* 0)
     (ls-compile-file "ecmalisp.lisp" "ecmalisp.js")))