1 | import {
|
2 | AST_Array,
|
3 | AST_Atom,
|
4 | AST_Await,
|
5 | AST_BigInt,
|
6 | AST_Binary,
|
7 | AST_Block,
|
8 | AST_Call,
|
9 | AST_Catch,
|
10 | AST_Chain,
|
11 | AST_Class,
|
12 | AST_ClassProperty,
|
13 | AST_ConciseMethod,
|
14 | AST_Conditional,
|
15 | AST_Debugger,
|
16 | AST_Definitions,
|
17 | AST_Destructuring,
|
18 | AST_Directive,
|
19 | AST_Do,
|
20 | AST_Dot,
|
21 | AST_EmptyStatement,
|
22 | AST_Expansion,
|
23 | AST_Export,
|
24 | AST_Finally,
|
25 | AST_For,
|
26 | AST_ForIn,
|
27 | AST_ForOf,
|
28 | AST_If,
|
29 | AST_Import,
|
30 | AST_ImportMeta,
|
31 | AST_Jump,
|
32 | AST_LabeledStatement,
|
33 | AST_Lambda,
|
34 | AST_LoopControl,
|
35 | AST_NameMapping,
|
36 | AST_NewTarget,
|
37 | AST_Node,
|
38 | AST_Number,
|
39 | AST_Object,
|
40 | AST_ObjectGetter,
|
41 | AST_ObjectKeyVal,
|
42 | AST_ObjectProperty,
|
43 | AST_ObjectSetter,
|
44 | AST_PrefixedTemplateString,
|
45 | AST_PropAccess,
|
46 | AST_RegExp,
|
47 | AST_Sequence,
|
48 | AST_SimpleStatement,
|
49 | AST_String,
|
50 | AST_Super,
|
51 | AST_Switch,
|
52 | AST_SwitchBranch,
|
53 | AST_Symbol,
|
54 | AST_TemplateSegment,
|
55 | AST_TemplateString,
|
56 | AST_This,
|
57 | AST_Toplevel,
|
58 | AST_Try,
|
59 | AST_Unary,
|
60 | AST_VarDef,
|
61 | AST_While,
|
62 | AST_With,
|
63 | AST_Yield
|
64 | } from "./ast.js";
|
65 |
|
66 | const shallow_cmp = (node1, node2) => {
|
67 | return (
|
68 | node1 === null && node2 === null
|
69 | || node1.TYPE === node2.TYPE && node1.shallow_cmp(node2)
|
70 | );
|
71 | };
|
72 |
|
73 | export const equivalent_to = (tree1, tree2) => {
|
74 | if (!shallow_cmp(tree1, tree2)) return false;
|
75 | const walk_1_state = [tree1];
|
76 | const walk_2_state = [tree2];
|
77 |
|
78 | const walk_1_push = walk_1_state.push.bind(walk_1_state);
|
79 | const walk_2_push = walk_2_state.push.bind(walk_2_state);
|
80 |
|
81 | while (walk_1_state.length && walk_2_state.length) {
|
82 | const node_1 = walk_1_state.pop();
|
83 | const node_2 = walk_2_state.pop();
|
84 |
|
85 | if (!shallow_cmp(node_1, node_2)) return false;
|
86 |
|
87 | node_1._children_backwards(walk_1_push);
|
88 | node_2._children_backwards(walk_2_push);
|
89 |
|
90 | if (walk_1_state.length !== walk_2_state.length) {
|
91 |
|
92 | return false;
|
93 | }
|
94 | }
|
95 |
|
96 | return walk_1_state.length == 0 && walk_2_state.length == 0;
|
97 | };
|
98 |
|
99 |
|
100 | const mkshallow = (props) => {
|
101 | const comparisons = Object
|
102 | .keys(props)
|
103 | .map(key => {
|
104 | if (props[key] === "eq") {
|
105 | return `this.${key} === other.${key}`;
|
106 | } else if (props[key] === "exist") {
|
107 | return `(this.${key} == null ? other.${key} == null : this.${key} === other.${key})`;
|
108 | } else {
|
109 | throw new Error(`mkshallow: Unexpected instruction: ${props[key]}`);
|
110 | }
|
111 | })
|
112 | .join(" && ");
|
113 |
|
114 | return new Function("other", "return " + comparisons);
|
115 | };
|
116 |
|
117 | const pass_through = () => true;
|
118 |
|
119 | AST_Node.prototype.shallow_cmp = function () {
|
120 | throw new Error("did not find a shallow_cmp function for " + this.constructor.name);
|
121 | };
|
122 |
|
123 | AST_Debugger.prototype.shallow_cmp = pass_through;
|
124 |
|
125 | AST_Directive.prototype.shallow_cmp = mkshallow({ value: "eq" });
|
126 |
|
127 | AST_SimpleStatement.prototype.shallow_cmp = pass_through;
|
128 |
|
129 | AST_Block.prototype.shallow_cmp = pass_through;
|
130 |
|
131 | AST_EmptyStatement.prototype.shallow_cmp = pass_through;
|
132 |
|
133 | AST_LabeledStatement.prototype.shallow_cmp = mkshallow({ "label.name": "eq" });
|
134 |
|
135 | AST_Do.prototype.shallow_cmp = pass_through;
|
136 |
|
137 | AST_While.prototype.shallow_cmp = pass_through;
|
138 |
|
139 | AST_For.prototype.shallow_cmp = mkshallow({
|
140 | init: "exist",
|
141 | condition: "exist",
|
142 | step: "exist"
|
143 | });
|
144 |
|
145 | AST_ForIn.prototype.shallow_cmp = pass_through;
|
146 |
|
147 | AST_ForOf.prototype.shallow_cmp = pass_through;
|
148 |
|
149 | AST_With.prototype.shallow_cmp = pass_through;
|
150 |
|
151 | AST_Toplevel.prototype.shallow_cmp = pass_through;
|
152 |
|
153 | AST_Expansion.prototype.shallow_cmp = pass_through;
|
154 |
|
155 | AST_Lambda.prototype.shallow_cmp = mkshallow({
|
156 | is_generator: "eq",
|
157 | async: "eq"
|
158 | });
|
159 |
|
160 | AST_Destructuring.prototype.shallow_cmp = mkshallow({
|
161 | is_array: "eq"
|
162 | });
|
163 |
|
164 | AST_PrefixedTemplateString.prototype.shallow_cmp = pass_through;
|
165 |
|
166 | AST_TemplateString.prototype.shallow_cmp = pass_through;
|
167 |
|
168 | AST_TemplateSegment.prototype.shallow_cmp = mkshallow({
|
169 | "value": "eq"
|
170 | });
|
171 |
|
172 | AST_Jump.prototype.shallow_cmp = pass_through;
|
173 |
|
174 | AST_LoopControl.prototype.shallow_cmp = pass_through;
|
175 |
|
176 | AST_Await.prototype.shallow_cmp = pass_through;
|
177 |
|
178 | AST_Yield.prototype.shallow_cmp = mkshallow({
|
179 | is_star: "eq"
|
180 | });
|
181 |
|
182 | AST_If.prototype.shallow_cmp = mkshallow({
|
183 | alternative: "exist"
|
184 | });
|
185 |
|
186 | AST_Switch.prototype.shallow_cmp = pass_through;
|
187 |
|
188 | AST_SwitchBranch.prototype.shallow_cmp = pass_through;
|
189 |
|
190 | AST_Try.prototype.shallow_cmp = mkshallow({
|
191 | bcatch: "exist",
|
192 | bfinally: "exist"
|
193 | });
|
194 |
|
195 | AST_Catch.prototype.shallow_cmp = mkshallow({
|
196 | argname: "exist"
|
197 | });
|
198 |
|
199 | AST_Finally.prototype.shallow_cmp = pass_through;
|
200 |
|
201 | AST_Definitions.prototype.shallow_cmp = pass_through;
|
202 |
|
203 | AST_VarDef.prototype.shallow_cmp = mkshallow({
|
204 | value: "exist"
|
205 | });
|
206 |
|
207 | AST_NameMapping.prototype.shallow_cmp = pass_through;
|
208 |
|
209 | AST_Import.prototype.shallow_cmp = mkshallow({
|
210 | imported_name: "exist",
|
211 | imported_names: "exist"
|
212 | });
|
213 |
|
214 | AST_ImportMeta.prototype.shallow_cmp = pass_through;
|
215 |
|
216 | AST_Export.prototype.shallow_cmp = mkshallow({
|
217 | exported_definition: "exist",
|
218 | exported_value: "exist",
|
219 | exported_names: "exist",
|
220 | module_name: "eq",
|
221 | is_default: "eq",
|
222 | });
|
223 |
|
224 | AST_Call.prototype.shallow_cmp = pass_through;
|
225 |
|
226 | AST_Sequence.prototype.shallow_cmp = pass_through;
|
227 |
|
228 | AST_PropAccess.prototype.shallow_cmp = pass_through;
|
229 |
|
230 | AST_Chain.prototype.shallow_cmp = pass_through;
|
231 |
|
232 | AST_Dot.prototype.shallow_cmp = mkshallow({
|
233 | property: "eq"
|
234 | });
|
235 |
|
236 | AST_Unary.prototype.shallow_cmp = mkshallow({
|
237 | operator: "eq"
|
238 | });
|
239 |
|
240 | AST_Binary.prototype.shallow_cmp = mkshallow({
|
241 | operator: "eq"
|
242 | });
|
243 |
|
244 | AST_Conditional.prototype.shallow_cmp = pass_through;
|
245 |
|
246 | AST_Array.prototype.shallow_cmp = pass_through;
|
247 |
|
248 | AST_Object.prototype.shallow_cmp = pass_through;
|
249 |
|
250 | AST_ObjectProperty.prototype.shallow_cmp = pass_through;
|
251 |
|
252 | AST_ObjectKeyVal.prototype.shallow_cmp = mkshallow({
|
253 | key: "eq"
|
254 | });
|
255 |
|
256 | AST_ObjectSetter.prototype.shallow_cmp = mkshallow({
|
257 | static: "eq"
|
258 | });
|
259 |
|
260 | AST_ObjectGetter.prototype.shallow_cmp = mkshallow({
|
261 | static: "eq"
|
262 | });
|
263 |
|
264 | AST_ConciseMethod.prototype.shallow_cmp = mkshallow({
|
265 | static: "eq",
|
266 | is_generator: "eq",
|
267 | async: "eq",
|
268 | });
|
269 |
|
270 | AST_Class.prototype.shallow_cmp = mkshallow({
|
271 | name: "exist",
|
272 | extends: "exist",
|
273 | });
|
274 |
|
275 | AST_ClassProperty.prototype.shallow_cmp = mkshallow({
|
276 | static: "eq"
|
277 | });
|
278 |
|
279 | AST_Symbol.prototype.shallow_cmp = mkshallow({
|
280 | name: "eq"
|
281 | });
|
282 |
|
283 | AST_NewTarget.prototype.shallow_cmp = pass_through;
|
284 |
|
285 | AST_This.prototype.shallow_cmp = pass_through;
|
286 |
|
287 | AST_Super.prototype.shallow_cmp = pass_through;
|
288 |
|
289 | AST_String.prototype.shallow_cmp = mkshallow({
|
290 | value: "eq"
|
291 | });
|
292 |
|
293 | AST_Number.prototype.shallow_cmp = mkshallow({
|
294 | value: "eq"
|
295 | });
|
296 |
|
297 | AST_BigInt.prototype.shallow_cmp = mkshallow({
|
298 | value: "eq"
|
299 | });
|
300 |
|
301 | AST_RegExp.prototype.shallow_cmp = function (other) {
|
302 | return (
|
303 | this.value.flags === other.value.flags
|
304 | && this.value.source === other.value.source
|
305 | );
|
306 | };
|
307 |
|
308 | AST_Atom.prototype.shallow_cmp = pass_through;
|