X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=lispstrack.lisp;h=b67749166d74f6ceb14c319f37ab6353578c2b3d;hb=709f6ae9830e6bd5a4ac84da06083178af6bc7a3;hp=3b96c3552f958cdb392add3f8285b7c1b48f75bd;hpb=fde8e35af42d48b703fafc91df3982e332bc64b6;p=jscl.git
diff --git a/lispstrack.lisp b/lispstrack.lisp
index 3b96c35..b677491 100644
--- a/lispstrack.lisp
+++ b/lispstrack.lisp
@@ -1,5 +1,26 @@
-;;; Library
-
+;;; lispstrack.lisp ---
+
+;; Copyright (C) 2012 David Vazquez
+;; Copyright (C) 2012 Raimon Grau
+
+;; This program is free software: you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation, either version 3 of the
+;; License, or (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see .
+
+;;; This code is executed when lispstrack compiles this file
+;;; itself. The compiler provides compilation of some special forms,
+;;; as well as funcalls and macroexpansion, but no functions. So, we
+;;; define the Lisp world from scratch. This code has to define enough
+;;; language to the compiler to be able to run.
#+lispstrack
(progn
(eval-when-compile
@@ -8,18 +29,24 @@
`(eval-when-compile
(%compile-defmacro ',name '(lambda ,args ,@body))))))
- (defmacro defvar (name value)
+ (defmacro %defvar (name value)
`(progn
(eval-when-compile
(%compile-defvar ',name))
(setq ,name ,value)))
- (defmacro defun (name args &rest body)
+ (defmacro defvar (name value)
+ `(%defvar ,name ,value))
+
+ (defmacro %defun (name args &rest body)
`(progn
(eval-when-compile
(%compile-defun ',name))
(fsetq ,name (lambda ,args ,@body))))
+ (defmacro defun (name args &rest body)
+ `(%defun ,name ,args ,@body))
+
(defvar *package* (new))
(defvar nil (make-symbol "NIL"))
@@ -106,6 +133,16 @@
#+lispstrack
(progn
+ (defmacro defun (name args &rest body)
+ `(progn
+ (%defun ,name ,args ,@body)
+ ',name))
+
+ (defmacro defvar (name value)
+ `(progn
+ (%defvar ,name ,value)
+ ',name))
+
(defun append-two (list1 list2)
(if (null list1)
list2
@@ -305,6 +342,9 @@
(equal s1 s2)))
+;;; The compiler offers some primitives and special forms which are
+;;; not found in Common Lisp, for instance, while. So, we grow Common
+;;; Lisp a bit to it can execute the rest of the file.
#+common-lisp
(progn
(defmacro while (condition &body body)
@@ -312,6 +352,10 @@
((not ,condition))
,@body))
+ (defmacro eval-when-compile (&body body)
+ `(eval-when (:compile-toplevel :load-toplevel :execute)
+ ,@body))
+
(defun concat-two (s1 s2)
(concatenate 'string s1 s2))
@@ -321,6 +365,11 @@
(setf (cdr cons) new)))
+;;; At this point, no matter if Common Lisp or lispstrack is compiling
+;;; from here, this code will compile on both. We define some helper
+;;; functions now for string manipulation and so on. They will be
+;;; useful in the compiler, mostly.
+
(defvar *newline* (string (code-char 10)))
(defun concat (&rest strs)
@@ -356,13 +405,11 @@
digits)
""))))
-
;;;; Reader
-;;; It is a basic Lisp reader. It does not use advanced stuff
-;;; intentionally, because we want to use it to bootstrap a simple
-;;; Lisp. The main entry point is the function `ls-read', which
-;;; accepts a strings as argument and return the Lisp expression.
+;;; The Lisp reader, parse strings and return Lisp objects. The main
+;;; entry points are `ls-read' and `ls-read-from-string'.
+
(defun make-string-stream (string)
(cons string 0))
@@ -498,6 +545,11 @@
;;;; Compiler
+;;; 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.
+
(defvar *compilation-unit-checks* '())
(defvar *env* '())
@@ -514,7 +566,6 @@
(defun mark-binding-as-declared (b)
(setcar (cdddr b) t))
-
(defvar *variable-counter* 0)
(defun gvarname (symbol)
(concat "v" (integer-to-string (incf *variable-counter*))))
@@ -560,7 +611,6 @@
(defun lookup-function-translation (symbol env)
(binding-translation (lookup-function symbol env)))
-
(defvar *toplevel-compilations* nil)
(defun %compile-defvar (name)
@@ -576,7 +626,6 @@
(defun %compile-defmacro (name lambda)
(push (make-binding name 'macro lambda t) *fenv*))
-
(defvar *compilations* nil)
(defun ls-compile-block (sexps env fenv)
@@ -651,7 +700,6 @@
(ls-compile val env fenv)))
;;; Literals
-
(defun escape-string (string)
(let ((output "")
(index 0)
@@ -703,14 +751,9 @@
((symbolp x)
(lookup-function-translation x fenv))))
-#+common-lisp
-(defmacro eval-when-compile (&body body)
- `(eval-when (:compile-toplevel :load-toplevel :execute)
- ,@body))
-
(define-compilation eval-when-compile (&rest body)
(eval (cons 'progn body))
- nil)
+ "")
(defmacro define-transformation (name args form)
`(define-compilation ,name ,args
@@ -927,6 +970,10 @@
(compile-bool
(concat "(" (ls-compile key env fenv) " in " (ls-compile object env fenv) ")")))
+(define-compilation functionp (x)
+ (compile-bool
+ (concat "(typeof " (ls-compile x env fenv) " == 'function')")))
+
(defun macrop (x)
(and (symbolp x) (eq (binding-type (lookup-function x *fenv*)) 'macro)))
@@ -979,7 +1026,22 @@
code)
(setq *toplevel-compilations* nil))))
-;;; ----------------------------------------------------------
+
+;;; Once we have the compiler, we define the runtime environment and
+;;; interactive development (eval), which works calling the compiler
+;;; and evaluating the Javascript result globally.
+
+(defun print-to-string (form)
+ (cond
+ ((symbolp form) (symbol-name form))
+ ((integerp form) (integer-to-string form))
+ ((stringp form) (concat "\"" (escape-string form) "\""))
+ ((functionp form) (concat "#"))
+ ((listp form)
+ (concat "("
+ (join (mapcar #'print-to-string form)
+ " ")
+ ")"))))
#+lispstrack
(progn
@@ -999,7 +1061,6 @@
(ls-compile-toplevel x nil nil))))
(js-eval code)))
-
;; Set the initial global environment to be equal to the host global
;; environment at this point of the compilation.
(eval-when-compile
@@ -1014,6 +1075,7 @@
(js-eval
(concat "var lisp = {};"
"lisp.read = " (lookup-function-translation 'ls-read-from-string nil) ";" *newline*
+ "lisp.print = " (lookup-function-translation 'print-to-string nil) ";" *newline*
"lisp.eval = " (lookup-function-translation 'eval nil) ";" *newline*
"lisp.compile = " (lookup-function-translation 'ls-compile-toplevel nil) ";" *newline*
"lisp.evalString = function(str){" *newline*
@@ -1023,6 +1085,10 @@
" return lisp.compile(lisp.read(str));" *newline*
"}" *newline*)))
+
+;;; Finally, we provide a couple of functions to easily bootstrap
+;;; this. It just calls the compiler with this file as input.
+
#+common-lisp
(progn
(defun read-whole-file (filename)