4 (%compile-defmacro 'defmacro
5 (lambda (name args &rest body)
7 (%compile-defmacro ',name (lambda ,args ,@body))))))
9 (defmacro defvar (name value)
12 (%compile-defvar ',name))
15 (defmacro defun (name args &rest body)
18 (%compile-defun ',name))
19 (fsetq ,name (lambda ,args ,@body))))
21 (defun = (x y) (= x y))
22 (defun + (x y) (+ x y))
23 (defun - (x y) (- x y))
24 (defun * (x y) (* x y))
25 (defun / (x y) (/ x y))
26 (defun 1+ (x) (+ x 1))
27 (defun 1- (x) (- x 1))
28 (defun cons (x y ) (cons x y))
29 (defun car (x) (car x))
30 (defun cdr (x) (cdr x))
32 (defun append (list1 list2)
36 (append (cdr list1) list2))))
38 (defun reverse-aux (list acc)
41 (reverse-aux (cdr list) (cons (car list) acc))))
44 (reverse-aux list '()))
46 (defun mapcar (func list)
49 (cons (funcall func (car list))
50 (mapcar func (cdr list))))))
53 (defun !reduce (func list initial)
58 (funcall func initial (car list)))))
65 (defmacro while (condition &body body)
71 (defun concat-two (s1 s2)
72 (concatenate 'string s1 s2)))
77 (defun concat (&rest strs)
78 (!reduce (lambda (s1 s2) (concat-two s1 s2))
82 ;;; Concatenate a list of strings, with a separator
83 (defun join (list separator)
92 (join (cdr list) separator)))))
94 (defun join-trailing (list separator)
97 (concat (car list) separator (join-trailing (cdr list) separator))))
99 (defun integer-to-string (x)
104 (push (mod x 10) digits)
105 (setq x (truncate x 10)))
106 (join (mapcar (lambda (d) (string (char "0123456789" d)))
112 ;;; It is a basic Lisp reader. It does not use advanced stuff
113 ;;; intentionally, because we want to use it to bootstrap a simple
114 ;;; Lisp. The main entry point is the function `ls-read', which
115 ;;; accepts a strings as argument and return the Lisp expression.
116 (defun make-string-stream (string)
119 (defun %peek-char (stream)
121 (peek-char nil stream nil)
122 (and (< (cdr stream) (length (car stream)))
123 (char (car stream) (cdr stream)))))
125 (defun %read-char (stream)
127 (read-char stream nil)
128 (and (< (cdr stream) (length (car stream)))
129 (prog1 (char (car stream) (cdr stream))
130 (incf (cdr stream))))))
132 (defun whitespacep (ch)
133 (or (char= ch #\space) (char= ch #\newline) (char= ch #\tab)))
135 (defun skip-whitespaces (stream)
137 (setq ch (%peek-char stream))
138 (while (and ch (whitespacep ch))
140 (setq ch (%peek-char stream)))))
142 (defun terminalp (ch)
143 (or (null ch) (whitespacep ch) (char= #\) ch) (char= #\( ch)))
145 (defun read-until (stream func)
148 (setq ch (%peek-char stream))
149 (while (not (funcall func ch))
150 (setq string (concat string (string ch)))
152 (setq ch (%peek-char stream)))
155 (defun skip-whitespaces-and-comments (stream)
157 (skip-whitespaces stream)
158 (setq ch (%peek-char stream))
159 (while (and ch (eql ch #\;))
160 (read-until stream (lambda (x) (eql x #\newline)))
161 (skip-whitespaces stream)
162 (setq ch (%peek-char stream)))))
164 (defun %read-list (stream)
165 (skip-whitespaces-and-comments stream)
166 (let ((ch (%peek-char stream)))
173 (skip-whitespaces-and-comments stream)
174 (prog1 (ls-read stream)
175 (unless (char= (%read-char stream) #\))
176 (error "')' was expected."))))
178 (cons (ls-read stream) (%read-list stream))))))
180 (defvar *eof* (make-symbol "EOF"))
181 (defun ls-read (stream)
182 (skip-whitespaces-and-comments stream)
183 (let ((ch (%peek-char stream)))
192 (list 'quote (ls-read stream)))
195 (list 'backquote (ls-read stream)))
198 (prog1 (read-until stream (lambda (ch) (char= ch #\")))
199 (%read-char stream)))
202 (if (eql (%peek-char stream) #\@)
203 (progn (%read-char stream) (list 'unquote-splicing (ls-read stream)))
204 (list 'unquote (ls-read stream))))
207 (ecase (%read-char stream)
209 (list 'function (ls-read stream)))
211 (let ((feature (read-until stream #'terminalp)))
213 ((string= feature "common-lisp")
214 (ls-read stream);ignore
216 ((string= feature "lispstrack")
219 (error "Unknown reader form.")))))))
221 (let ((string (read-until stream #'terminalp)))
222 (if (every #'digit-char-p string)
223 (parse-integer string)
224 (intern (string-upcase string))))))))
226 (defun ls-read-from-string (string)
227 (ls-read (make-string-stream string)))
233 (defun make-var-binding (symbol)
234 (cons symbol (concat "v" (integer-to-string (incf counter))))))
237 (defun make-func-binding (symbol)
238 (cons symbol (concat "f" (integer-to-string (incf counter))))))
240 (defvar *compilations* nil)
242 (defun ls-compile-block (sexps env fenv)
243 (join-trailing (mapcar (lambda (x)
244 (ls-compile x env fenv))
249 (defun extend-env (args env)
250 (append (mapcar #'make-var-binding args) env))
252 (defparameter *env* '())
253 (defparameter *fenv* '())
255 (defun ls-lookup (symbol env)
256 (let ((binding (assoc symbol env)))
257 (and binding (cdr binding))))
259 (defun lookup-variable (symbol env)
260 (or (ls-lookup symbol env)
261 (ls-lookup symbol *env*)
262 (error "Undefined variable `~a'" symbol)))
264 (defun lookup-function (symbol env)
265 (or (ls-lookup symbol env)
266 (ls-lookup symbol *fenv*)
267 (error "Undefined function `~a'" symbol)))
269 (defmacro define-compilation (name args &body body)
270 ;; Creates a new primitive `name' with parameters args and
271 ;; @body. The body can access to the local environment through the
273 `(push (list ',name (lambda (env fenv ,@args) ,@body))
276 (defvar *toplevel-compilations*)
278 (define-compilation if (condition true false)
280 (ls-compile condition env fenv)
282 (ls-compile true env fenv)
284 (ls-compile false env fenv)
287 ;;; Return the required args of a lambda list
288 (defun lambda-list-required-argument (lambda-list)
289 (if (or (null lambda-list) (eq (car lambda-list) '&rest))
291 (cons (car lambda-list) (lambda-list-required-argument (cdr lambda-list)))))
293 (defun lambda-list-rest-argument (lambda-list)
294 (second (member '&rest lambda-list)))
296 (define-compilation lambda (lambda-list &rest body)
297 (let ((required-arguments (lambda-list-required-argument lambda-list))
298 (rest-argument (lambda-list-rest-argument lambda-list)))
299 (let ((new-env (extend-env (cons rest-argument required-arguments) env)))
300 (concat "(function ("
301 (join (mapcar (lambda (x) (lookup-variable x new-env))
307 (concat "var " (lookup-variable rest-argument new-env)
308 " = arguments.slice("
309 (prin1-to-string (length required-arguments))
313 (concat (ls-compile-block (butlast body) new-env fenv)
314 "return " (ls-compile (car (last body)) new-env fenv) ";")
318 (define-compilation fsetq (var val)
319 (concat (lookup-function var fenv)
321 (ls-compile val env fenv)))
323 (define-compilation setq (var val)
324 (concat (lookup-variable var env)
326 (ls-compile val env fenv)))
331 (defun literal->js (sexp)
333 ((null sexp) "undefined")
334 ((integerp sexp) (integer-to-string sexp))
335 ((stringp sexp) (concat "\"" sexp "\""))
336 ((symbolp sexp) (concat "{name: \"" (symbol-name sexp) "\"}"))
337 ((consp sexp) (concat "{car: "
338 (literal->js (car sexp))
340 (literal->js (cdr sexp)) "}"))))
343 (defun literal (form)
344 (let ((var (concat "l" (integer-to-string (incf counter)))))
345 (push (concat "var " var " = " (literal->js form)) *toplevel-compilations*)
348 (define-compilation quote (sexp)
351 (define-compilation debug (form)
352 (concat "console.log(" (ls-compile form env fenv) ")"))
354 (define-compilation while (pred &rest body)
355 (concat "(function(){ while("
356 (ls-compile pred env fenv)
358 (ls-compile-block body env fenv)
361 (define-compilation function (x)
363 ((and (listp x) (eq (car x) 'lambda))
364 (ls-compile x env fenv))
366 (lookup-function x fenv))))
369 (defmacro eval-when-compile (&body body)
370 `(eval-when (:compile-toplevel :execute)
373 (defvar *eval-when-compilations*)
374 (define-compilation eval-when-compile (&rest body)
375 (eval (cons 'progn body))
378 (defmacro define-transformation (name args form)
379 `(define-compilation ,name ,args
380 (ls-compile ,form env fenv)))
382 (define-transformation progn (&rest body)
383 `((lambda () ,@body)))
385 (define-transformation let (bindings &rest body)
386 `((lambda ,(mapcar 'car bindings) ,@body)
387 ,@(mapcar 'cadr bindings)))
389 ;;; A little backquote implementation without optimizations of any
390 ;;; kind for lispstrack.
391 (defun backquote-expand-1 (form)
397 ((eq (car form) 'unquote)
399 ((eq (car form) 'backquote)
400 (backquote-expand-1 (backquote-expand-1 (cadr form))))
405 ((and (listp s) (eq (car s) 'unquote))
406 (list 'list (cadr s)))
407 ((and (listp s) (eq (car s) 'unquote-splicing))
410 (list 'list (backquote-expand-1 s)))))
413 (defun backquote-expand (form)
414 (if (and (listp form) (eq (car form) 'backquote))
415 (backquote-expand-1 (cadr form))
418 (defmacro backquote (form)
419 (backquote-expand-1 form))
421 (define-transformation backquote (form)
422 (backquote-expand-1 form))
426 (define-compilation + (x y)
427 (concat "((" (ls-compile x env fenv) ") + (" (ls-compile y env fenv) "))"))
429 (define-compilation - (x y)
430 (concat "((" (ls-compile x env fenv) ") - (" (ls-compile y env fenv) "))"))
432 (define-compilation * (x y)
433 (concat "((" (ls-compile x env fenv) ") * (" (ls-compile y env fenv) "))"))
435 (define-compilation / (x y)
436 (concat "((" (ls-compile x env fenv) ") / (" (ls-compile y env fenv) "))"))
438 (define-compilation = (x y)
439 (concat "((" (ls-compile x env fenv) ") == (" (ls-compile y env fenv) "))"))
441 (define-compilation null (x)
442 (concat "(" (ls-compile x env fenv) "== undefined)"))
444 (define-compilation cons (x y)
445 (concat "{car: " (ls-compile x env fenv) ", cdr: " (ls-compile y env fenv) "}"))
447 (define-compilation car (x)
448 (concat "(" (ls-compile x env fenv) ").car"))
450 (define-compilation cdr (x)
451 (concat "(" (ls-compile x env fenv) ").cdr"))
453 (define-compilation symbol-name (x)
454 (concat "(" (ls-compile x env fenv) ").name"))
456 (define-compilation eq (x y)
457 (concat "(" (ls-compile x env fenv) " === " (ls-compile y env fenv) ")"))
459 (define-compilation eql (x y)
460 (concat "(" (ls-compile x env fenv) " == " (ls-compile y env fenv) ")"))
462 (define-compilation code-char (x)
463 (concat "String.fromCharCode( " (ls-compile x env fenv) ")"))
465 (define-compilation char (string index)
467 (ls-compile string env fenv)
469 (ls-compile index env fenv)
472 (define-compilation concat-two (string1 string2)
474 (ls-compile string1 env fenv)
476 (ls-compile string2 env fenv)
479 (define-compilation funcall (func &rest args)
481 (ls-compile func env fenv)
483 (join (mapcar (lambda (x)
484 (ls-compile x env fenv))
489 (defun %compile-defvar (name)
490 (push (make-var-binding name) *env*)
491 (push (concat "var " (lookup-variable name *env*)) *toplevel-compilations*))
493 (defun %compile-defun (name)
494 (push (make-func-binding name) *fenv*)
495 (push (concat "var " (lookup-variable name *fenv*)) *toplevel-compilations*))
497 (defun %compile-defmacro (name lambda)
498 (push (cons name (cons 'macro lambda)) *fenv*))
500 (defun ls-macroexpand-1 (form &optional env fenv)
501 (let ((function (cdr (assoc (car form) *fenv*))))
502 (if (and (listp function) (eq (car function) 'macro))
503 (apply (eval (cdr function)) (cdr form))
506 (defun compile-funcall (function args env fenv)
509 (concat (lookup-function function fenv)
511 (join (mapcar (lambda (x) (ls-compile x env fenv)) args)
514 ((and (listp function) (eq (car function) 'lambda))
515 (concat "(" (ls-compile function env fenv) ")("
516 (join (mapcar (lambda (x) (ls-compile x env fenv)) args)
520 (error "Invalid function designator ~a." function))))
522 (defun ls-compile (sexp &optional env fenv)
524 ((symbolp sexp) (lookup-variable sexp env))
525 ((integerp sexp) (integer-to-string sexp))
526 ((stringp sexp) (concat "\"" sexp "\""))
528 (let ((sexp (ls-macroexpand-1 sexp env fenv)))
529 (let ((compiler-func (second (assoc (car sexp) *compilations*))))
531 (apply compiler-func env fenv (cdr sexp))
532 (compile-funcall (car sexp) (cdr sexp) env fenv)))))))
534 (defun ls-compile-toplevel (sexp)
535 (setq *toplevel-compilations* nil)
536 (let ((code (ls-compile sexp)))
538 (concat (join (mapcar (lambda (x) (concat x ";" *newline*))
539 *toplevel-compilations*)
542 (setq *toplevel-compilations* nil))))
546 (defun ls-compile-file (filename output)
547 (with-open-file (in filename)
548 (with-open-file (out output :direction :output :if-exists :supersede)
552 for compilation = (ls-compile-toplevel x)
553 when compilation do (write-line (concat compilation "; ") out)))))
555 (ls-compile-file "lispstrack.lisp" "lispstrack.js")))