keyword/optional fixes, slot-value accepts many slot names
[clinton/parenscript.git] / src / compilation-interface.lisp
1 (in-package :parenscript)
2
3 (defmacro with-new-compilation-environment ((var) &body body)
4 `(let* ((,var (make-basic-compilation-environment))
5 (*compilation-environment* ,var))
6 ,@body))
7
8 (defun translate-ast (compiled-expr
9 &key
10 (comp-env *compilation-environment*)
11 (output-stream *standard-output*)
12 (output-spec :javascript)
13 (pretty-print t))
14 "Translates a compiled Parenscript program (compiled with COMPILE-PAREN-FORM)
15 to a Javascript string. Outputs to the stream OUTPUT-STREAM in the language given
16 by OUTPUT-SPEC, pretty printing if PRETTY-PRINT is non-null.
17
18 OUTPUT-SPEC must be :javascript at the moment."
19 (declare (ignore comp-env))
20 (when (not (eql :javascript output-spec))
21 (error "Unsupported output-spec for translation: ~A" output-spec))
22 (when (eql :javascript output-spec)
23 ; (if (not pretty-print)
24 ; (js-translate compiled-expr :statement output-stream)
25 (write-string (string-join
26 (ps-js::js-to-statement-strings compiled-expr 0)
27 (string #\Newline))
28 output-stream)))
29
30 (defun non-nil-comp-env ()
31 "Returns a sane compilation environment. Either the one currently bound or a new
32 one."
33 (or *compilation-environment*
34 (make-basic-compilation-environment)))
35
36
37 (defun compile-script (script-form
38 &key
39 (output-spec :javascript)
40 (pretty-print t)
41 (output-stream nil)
42 (toplevel-p t)
43 (comp-env (non-nil-comp-env)))
44 "Compiles the Parenscript form SCRIPT-FORM into the language specified by OUTPUT-SPEC.
45 Non-null PRETTY-PRINT values result in a pretty-printed output code. If OUTPUT-STREAM
46 is NIL, then the result is a string; otherwise code is output to the OUTPUT-STREAM stream.
47 COMP-ENV is the compilation environment in which to compile the form.
48
49 This is the main function used by Parenscript users to compile their code to Javascript (and
50 potentially other languages)."
51 (macrolet ((with-output-stream ((var) &body body)
52 `(if (null output-stream)
53 (with-output-to-string (,var)
54 ,@body)
55 (let ((,var output-stream))
56 ,@body))))
57 ;; we might want to bind this rather than set it
58 (setf (comp-env-compiling-toplevel-p comp-env) toplevel-p)
59 (with-output-stream (stream)
60 (let* ((*compilation-environment* comp-env)
61 (compiled
62 (progn
63 (let ((first-result
64 (compile-parenscript-form comp-env script-form)))
65 (if (not toplevel-p)
66 first-result
67 (progn
68 (setf (comp-env-compiling-toplevel-p comp-env) nil)
69 (compile-parenscript-form comp-env first-result)))))))
70 (translate-ast
71 compiled
72 ; (compile-script-form script-form :comp-env comp-env)
73 :comp-env comp-env
74 :output-stream stream
75 :output-spec output-spec
76 :pretty-print pretty-print)))))
77
78 (defun compile-script-file (source-file
79 &key
80 (output-spec :javascript)
81 (comp-env (non-nil-comp-env))
82 (pretty-print t)
83 (output-stream *standard-output*))
84 "Compiles the given Parenscript source file and outputs the results
85 to the given output stream."
86 (setf (comp-env-compiling-toplevel-p comp-env) t)
87 (with-open-file (input source-file :direction :input)
88 (let ((end-read-form '#:unique))
89 (flet ((read-form ()
90 (parenscript.reader:read input nil end-read-form)))
91 (macrolet ((with-output-stream ((var) &body body)
92 `(if (null output-stream)
93 (with-output-to-string (,var)
94 ,@body)
95 (let ((,var output-stream))
96 ,@body))))
97 (let* ((*compilation-environment* comp-env)
98 (compiled
99 (do ((form (read-form) (read-form))
100 (compiled-forms nil))
101 ((eql form end-read-form)
102 (progn
103 (setf (comp-env-compiling-toplevel-p comp-env) nil)
104 (compile-parenscript-form
105 comp-env
106 `(progn ,@(nreverse compiled-forms)))))
107 (let ((tl-compiled-form
108 (compile-parenscript-form comp-env form)))
109 (push tl-compiled-form compiled-forms)))))
110 (with-output-stream (output)
111 (translate-ast
112 compiled
113 :comp-env comp-env
114 :output-stream output
115 :output-spec output-spec
116 :pretty-print pretty-print))))))))
117
118 ;(defun compile-script-asdf-component (component
119 ; &key
120 ; (output-spec :javascript)
121 ; (pretty-print t)
122 ; (output-to-stream t)
123 ; (output-stream *standard-output*)
124 ; output-to-files ;; currently ignored
125 ; (comp-env (non-nil-comp-env)))
126 ; "Compiles any ASDF:COMPONENT and its dependencies "
127
128 (defun compile-script-system (system
129 &key
130 (output-spec :javascript)
131 (pretty-print t)
132 (output-to-stream t)
133 (output-stream *standard-output*)
134 output-to-files ;; currently ignored
135 (comp-env (non-nil-comp-env)))
136 "Compiles a collection of parenscripts as described by an ASDF system into files or
137 a specified output stream."
138 (asdf:operate 'asdf::parenscript-compile-op system
139 :output-spec output-spec
140 :pretty-print pretty-print
141 ; :output-to-stream t
142 :output-stream output-stream
143 :comp-env comp-env
144 :force-p t
145 ))
146
147
148 ;(defun compile-script-system-component (system-designator
149
150 ;(defun compile-script-file (script-src-file
151 ; &key
152 ; (output-spec :javascript)
153 ; (output-stream *standard-out*)
154 ; (comp-env *compilation-environment*))
155
156
157 ;;; old file compilation functions:
158 (defun compile-parenscript-file-to-string (source-file)
159 "Compile SOURCE-FILE (a parenscript file) to a javascript string. (in-package ...) forms
160 behave as expected and all other forms are evaluated according to the value of
161 EVAL-FORMS-P. If the result of the evaluation is not nil then it's compiled with
162 js:js* and written to the output."
163 (compile-script-file source-file :output-stream nil))
164
165 (defun compile-parenscript-file (source-file &rest args &key destination-file &allow-other-keys)
166 "Compile SOURCE-FILE (a parenscript file) to a javascript file with
167 compile-parenscript-file-to-string. When DESTINATION-FILE is omitted,
168 then it will be named the same as SOURCE-FILE but with js extension."
169 (setf args (copy-list args))
170 (remf args :destination-file)
171 (unless destination-file
172 (setf destination-file (merge-pathnames (make-pathname :type "js")
173 source-file)))
174 (with-open-file (output destination-file :if-exists :supersede :direction :output)
175 (write-string (apply #'compile-parenscript-file-to-string source-file args) output)))
176
177 (defun ps-to-string (expr)
178 "Given an AST node, compiles it to a Javascript string."
179 (string-join
180 (ps-js::js-to-statement-strings (compile-script-form expr) 0)
181 (string #\Newline)))
182
183 ;;; SEXPs -> Javascript string functionality
184 (defmacro script (&body body)
185 "A macro that returns a Javascript string of the supplied Parenscript forms."
186 `(script* '(progn ,@body)))
187
188 (defun script* (&rest body)
189 "Return the javascript string representing BODY.
190 Body is evaluated."
191 (compile-script `(progn ,@body)))
192
193 ;;; Handy synonyms
194 (defmacro ps (&body body)
195 `(script ,@body))
196
197 (defmacro ps* (&body body)
198 `(script* ,@body))
199
200 (defmacro js (&body body)
201 `(script ,@body))
202
203 (defmacro js* (&body body)
204 `(script* ,@body))