Commit | Line | Data |
---|---|---|
cc4f1551 RD |
1 | (in-package :parenscript) |
2 | ||
9da682ca RD |
3 | (defgeneric script-equal (compiled-ast-node1 compiled-ast-node2) |
4 | (:documentation "Determines if the AST nodes are equal.")) | |
5 | ||
cc4f1551 | 6 | ;;; AST node equality |
9da682ca | 7 | (defmethod script-equal ((obj1 list) (obj2 list)) |
cc4f1551 | 8 | (and (= (length obj1) (length obj2)) |
9da682ca | 9 | (every #'script-equal obj1 obj2))) |
cc4f1551 | 10 | |
9da682ca | 11 | (defmethod script-equal ((obj1 t) (obj2 t)) |
cc4f1551 RD |
12 | (equal obj1 obj2)) |
13 | ||
9da682ca | 14 | (defmacro defscriptclass (name superclasses slots &rest class-options) |
cc4f1551 RD |
15 | (let ((slot-names (mapcar #'(lambda (slot) (if (atom slot) slot (first slot))) slots))) |
16 | `(progn | |
17 | (defclass ,name ,superclasses | |
18 | ,slots ,@class-options) | |
9da682ca | 19 | (defmethod script-equal ((obj1 ,name) (obj2 ,name)) |
cc4f1551 | 20 | (every #'(lambda (slot) |
9da682ca | 21 | (script-equal (slot-value obj1 slot) |
cc4f1551 RD |
22 | (slot-value obj2 slot))) |
23 | ',slot-names))))) | |
24 | ||
5aa10005 RD |
25 | (in-package :parenscript.javascript) |
26 | ||
27 | (defgeneric expression-precedence (expression) | |
28 | (:documentation "Returns the precedence of an enscript-javascript expression")) | |
29 | ||
30 | ;;;; define Javascript language types | |
cc4f1551 RD |
31 | (defclass statement () |
32 | ((value :initarg :value :accessor value :initform nil)) | |
33 | (:documentation "A Javascript entity without a value.")) | |
34 | ||
35 | (defclass expression (statement) | |
36 | () | |
37 | (:documentation "A Javascript entity with a value.")) | |
38 | ||
39 | ;;; array literals | |
9da682ca | 40 | (defscriptclass array-literal (expression) |
cc4f1551 RD |
41 | ((values :initarg :values :accessor array-values))) |
42 | ||
5aa10005 | 43 | (defscriptclass js-aref (expression) |
cc4f1551 RD |
44 | ((array :initarg :array |
45 | :accessor aref-array) | |
46 | (index :initarg :index | |
47 | :accessor aref-index))) | |
48 | ||
49 | ;;; object literals (maps and hash-tables) | |
9da682ca | 50 | (defscriptclass object-literal (expression) |
cc4f1551 RD |
51 | ((values :initarg :values :accessor object-values))) |
52 | ||
53 | ;;; string literals | |
9da682ca | 54 | (defscriptclass string-literal (expression) |
cc4f1551 RD |
55 | (value)) |
56 | ||
57 | ||
58 | ;;; number literals | |
9da682ca | 59 | (defscriptclass number-literal (expression) |
cc4f1551 RD |
60 | (value)) |
61 | ||
62 | ;;; variables | |
5aa10005 | 63 | (defscriptclass js-variable (expression) |
cc4f1551 RD |
64 | (value)) |
65 | ||
cc4f1551 | 66 | ;;; operators |
9da682ca | 67 | (defscriptclass op-form (expression) |
cc4f1551 RD |
68 | ((operator :initarg :operator :accessor operator) |
69 | (args :initarg :args :accessor op-args))) | |
70 | ||
71 | (eval-when (:compile-toplevel :load-toplevel :execute) | |
cc4f1551 RD |
72 | (defparameter *op-precedence-hash* (make-hash-table :test #'equal)) |
73 | ||
74 | ;;; generate the operator precedences from *OP-PRECEDENCES* | |
75 | (let ((precedence 1)) | |
76 | (dolist (ops '((aref) | |
77 | (slot-value) | |
78 | (! not ~) | |
79 | (* / %) | |
80 | (+ -) | |
81 | (<< >>) | |
82 | (>>>) | |
83 | (< > <= >=) | |
84 | (in if) | |
85 | (eql == != =) | |
86 | (=== !==) | |
87 | (&) | |
88 | (^) | |
89 | (\|) | |
90 | (\&\& and) | |
91 | (\|\| or) | |
92 | (setf *= /= %= += -= <<= >>= >>>= \&= ^= \|=) | |
93 | (comma))) | |
94 | (dolist (op ops) | |
95 | (let ((op-name (symbol-name op))) | |
96 | (setf (gethash op-name *op-precedence-hash*) precedence))) | |
97 | (incf precedence))) | |
98 | ||
99 | (defun op-precedence (op) | |
100 | (gethash (if (symbolp op) | |
101 | (symbol-name op) | |
102 | op) | |
103 | *op-precedence-hash*))) | |
104 | ||
9da682ca | 105 | (defscriptclass one-op (expression) |
cc4f1551 RD |
106 | ((pre-p :initarg :pre-p |
107 | :initform nil | |
108 | :accessor one-op-pre-p) | |
109 | (op :initarg :op | |
110 | :accessor one-op))) | |
111 | ||
112 | ;;; function calls | |
9da682ca | 113 | (defscriptclass function-call (expression) |
cc4f1551 RD |
114 | ((function :initarg :function :accessor f-function) |
115 | (args :initarg :args :accessor f-args))) | |
116 | ||
9da682ca | 117 | (defscriptclass method-call (expression) |
cc4f1551 RD |
118 | ((method :initarg :method :accessor m-method) |
119 | (object :initarg :object :accessor m-object) | |
120 | (args :initarg :args :accessor m-args))) | |
121 | ||
122 | ;;; body forms | |
5aa10005 RD |
123 | (defscriptclass js-block (expression) |
124 | ((statements :initarg :statements :accessor block-statements) | |
125 | (indent :initarg :indent :initform "" :accessor block-indent))) | |
cc4f1551 | 126 | |
5aa10005 | 127 | (defmethod initialize-instance :after ((block js-block) &rest initargs) |
cc4f1551 | 128 | (declare (ignore initargs)) |
5aa10005 | 129 | (let* ((statements (block-statements block)) |
9da682ca | 130 | (last (last statements)) |
cc4f1551 | 131 | (last-stmt (car last))) |
5aa10005 RD |
132 | (when (typep last-stmt 'js-block) |
133 | (setf (block-statements block) | |
9da682ca | 134 | (nconc (butlast statements) |
5aa10005 | 135 | (block-statements last-stmt)))))) |
cc4f1551 | 136 | |
5aa10005 | 137 | (defscriptclass js-sub-block (js-block) |
9da682ca | 138 | (statements indent)) |
cc4f1551 RD |
139 | |
140 | ;;; function definition | |
5aa10005 | 141 | (defscriptclass js-lambda (expression) |
cc4f1551 RD |
142 | ((args :initarg :args :accessor lambda-args) |
143 | (body :initarg :body :accessor lambda-body))) | |
144 | ||
5aa10005 | 145 | (defscriptclass js-defun (js-lambda) |
cc4f1551 RD |
146 | ((name :initarg :name :accessor defun-name))) |
147 | ||
148 | ;;; object creation | |
5aa10005 | 149 | (defscriptclass js-object (expression) |
cc4f1551 RD |
150 | ((slots :initarg :slots |
151 | :accessor o-slots))) | |
152 | ||
5aa10005 | 153 | (defscriptclass js-slot-value (expression) |
cc4f1551 RD |
154 | ((object :initarg :object |
155 | :accessor sv-object) | |
156 | (slot :initarg :slot | |
157 | :accessor sv-slot))) | |
158 | ||
159 | ;;; cond | |
5aa10005 | 160 | (defscriptclass js-cond (expression) |
cc4f1551 RD |
161 | ((tests :initarg :tests |
162 | :accessor cond-tests) | |
163 | (bodies :initarg :bodies | |
164 | :accessor cond-bodies))) | |
165 | ||
5aa10005 | 166 | (defscriptclass js-if (expression) |
cc4f1551 RD |
167 | ((test :initarg :test |
168 | :accessor if-test) | |
169 | (then :initarg :then | |
170 | :accessor if-then) | |
171 | (else :initarg :else | |
172 | :accessor if-else))) | |
173 | ||
5aa10005 | 174 | (defmethod initialize-instance :after ((if js-if) &rest initargs) |
cc4f1551 RD |
175 | (declare (ignore initargs)) |
176 | (when (and (if-then if) | |
5aa10005 RD |
177 | (typep (if-then if) 'js-sub-block)) |
178 | (change-class (if-then if) 'js-block)) | |
cc4f1551 | 179 | (when (and (if-else if) |
5aa10005 RD |
180 | (typep (if-else if) 'js-sub-block)) |
181 | (change-class (if-else if) 'js-block))) | |
cc4f1551 RD |
182 | |
183 | ;;; switch | |
5aa10005 | 184 | (defscriptclass js-switch (statement) |
cc4f1551 RD |
185 | ((value :initarg :value :accessor case-value) |
186 | (clauses :initarg :clauses :accessor case-clauses))) | |
187 | ||
188 | ;;; assignment | |
189 | ||
5aa10005 | 190 | (defscriptclass js-setf (expression) |
cc4f1551 RD |
191 | ((lhs :initarg :lhs :accessor setf-lhs) |
192 | (rhsides :initarg :rhsides :accessor setf-rhsides))) | |
193 | ||
194 | ;;; defvar | |
5aa10005 | 195 | (defscriptclass js-defvar (statement) |
cc4f1551 RD |
196 | ((names :initarg :names :accessor var-names) |
197 | (value :initarg :value :accessor var-value))) | |
198 | ||
199 | ;;; iteration | |
5aa10005 | 200 | (defscriptclass js-for (statement) |
cc4f1551 RD |
201 | ((vars :initarg :vars :accessor for-vars) |
202 | (steps :initarg :steps :accessor for-steps) | |
203 | (check :initarg :check :accessor for-check) | |
204 | (body :initarg :body :accessor for-body))) | |
205 | ||
9da682ca | 206 | (defscriptclass for-each (statement) |
cc4f1551 RD |
207 | ((name :initarg :name :accessor fe-name) |
208 | (value :initarg :value :accessor fe-value) | |
209 | (body :initarg :body :accessor fe-body))) | |
210 | ||
5aa10005 | 211 | (defscriptclass js-while (statement) |
cc4f1551 RD |
212 | ((check :initarg :check :accessor while-check) |
213 | (body :initarg :body :accessor while-body))) | |
214 | ||
215 | ;;; with | |
5aa10005 | 216 | (defscriptclass js-with (statement) |
cc4f1551 RD |
217 | ((obj :initarg :obj :accessor with-obj) |
218 | (body :initarg :body :accessor with-body))) | |
219 | ||
220 | ;;; try-catch | |
5aa10005 | 221 | (defscriptclass js-try (statement) |
cc4f1551 RD |
222 | ((body :initarg :body :accessor try-body) |
223 | (catch :initarg :catch :accessor try-catch) | |
224 | (finally :initarg :finally :accessor try-finally))) | |
225 | ||
226 | ;;; regular expressions | |
9da682ca | 227 | (defscriptclass regex (expression) |
cc4f1551 RD |
228 | (value)) |
229 | ||
230 | ;;; conditional compilation | |
9da682ca | 231 | (defscriptclass cc-if () |
cc4f1551 RD |
232 | ((test :initarg :test :accessor cc-if-test) |
233 | (body :initarg :body :accessor cc-if-body))) | |
234 | ||
235 | ;; TODO this may not be the best integrated implementation of | |
236 | ;; instanceof into the rest of the code | |
5aa10005 | 237 | (defscriptclass js-instanceof (expression) |
cc4f1551 RD |
238 | ((value) |
239 | (type :initarg :type))) | |
240 | ||
5aa10005 RD |
241 | (defmacro define-js-single-op (name &optional (superclass 'expression)) |
242 | (let ((js-name (intern (concatenate 'string "JS-" (symbol-name name)) #.*package*))) | |
cc4f1551 | 243 | `(progn |
5aa10005 | 244 | (defscriptclass ,js-name (,superclass) |
cc4f1551 RD |
245 | (value))))) |
246 | ||
5aa10005 RD |
247 | (define-js-single-op return statement) |
248 | (define-js-single-op throw statement) | |
249 | (define-js-single-op delete) | |
250 | (define-js-single-op void) | |
251 | (define-js-single-op typeof) | |
252 | (define-js-single-op new) |