yukigo 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.json +3 -3
- package/CHANGELOG.md +26 -0
- package/README.md +193 -199
- package/dist/analyzer/GraphBuilder.d.ts +29 -0
- package/dist/analyzer/GraphBuilder.js +99 -0
- package/dist/analyzer/index.d.ts +11 -23
- package/dist/analyzer/index.js +100 -58
- package/dist/analyzer/inspections/functional/functional.d.ts +44 -0
- package/dist/analyzer/inspections/functional/functional.js +149 -0
- package/dist/analyzer/inspections/functional/smells.d.ts +16 -0
- package/dist/analyzer/inspections/functional/smells.js +98 -0
- package/dist/analyzer/inspections/{generic.d.ts → generic/generic.d.ts} +70 -43
- package/dist/analyzer/inspections/generic/generic.js +604 -0
- package/dist/analyzer/inspections/generic/smells.d.ts +61 -0
- package/dist/analyzer/inspections/generic/smells.js +349 -0
- package/dist/analyzer/inspections/imperative/imperative.d.ts +35 -0
- package/dist/analyzer/inspections/imperative/imperative.js +109 -0
- package/dist/analyzer/inspections/imperative/smells.d.ts +16 -0
- package/dist/analyzer/inspections/imperative/smells.js +58 -0
- package/dist/analyzer/inspections/logic/logic.d.ts +32 -0
- package/dist/analyzer/inspections/logic/logic.js +96 -0
- package/dist/analyzer/inspections/logic/smells.d.ts +15 -0
- package/dist/analyzer/inspections/logic/smells.js +60 -0
- package/dist/analyzer/inspections/object/object.d.ts +88 -0
- package/dist/analyzer/inspections/object/object.js +319 -0
- package/dist/analyzer/inspections/object/smells.d.ts +30 -0
- package/dist/analyzer/inspections/object/smells.js +135 -0
- package/dist/analyzer/utils.d.ts +26 -4
- package/dist/analyzer/utils.js +71 -13
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/interpreter/components/EnvBuilder.d.ts +9 -5
- package/dist/interpreter/components/EnvBuilder.js +100 -30
- package/dist/interpreter/components/Operations.d.ts +4 -4
- package/dist/interpreter/components/Operations.js +17 -2
- package/dist/interpreter/components/PatternMatcher.d.ts +47 -17
- package/dist/interpreter/components/PatternMatcher.js +264 -119
- package/dist/interpreter/components/RuntimeContext.d.ts +35 -0
- package/dist/interpreter/components/RuntimeContext.js +93 -0
- package/dist/interpreter/components/TestRunner.d.ts +18 -0
- package/dist/interpreter/components/TestRunner.js +103 -0
- package/dist/interpreter/components/Visitor.d.ts +63 -57
- package/dist/interpreter/components/Visitor.js +508 -173
- package/dist/interpreter/components/logic/LogicEngine.d.ts +29 -0
- package/dist/interpreter/components/logic/LogicEngine.js +259 -0
- package/dist/interpreter/components/logic/LogicResolver.d.ts +53 -0
- package/dist/interpreter/components/logic/LogicResolver.js +471 -0
- package/dist/interpreter/components/logic/LogicTranslator.d.ts +14 -0
- package/dist/interpreter/components/logic/LogicTranslator.js +99 -0
- package/dist/interpreter/components/runtimes/FunctionRuntime.d.ts +12 -0
- package/dist/interpreter/components/runtimes/FunctionRuntime.js +147 -0
- package/dist/interpreter/components/runtimes/LazyRuntime.d.ts +19 -0
- package/dist/interpreter/components/runtimes/LazyRuntime.js +269 -0
- package/dist/interpreter/components/runtimes/ObjectRuntime.d.ts +35 -0
- package/dist/interpreter/components/runtimes/ObjectRuntime.js +126 -0
- package/dist/interpreter/entities.d.ts +105 -0
- package/dist/interpreter/entities.js +96 -0
- package/dist/interpreter/errors.d.ts +1 -1
- package/dist/interpreter/index.d.ts +4 -12
- package/dist/interpreter/index.js +10 -13
- package/dist/interpreter/trampoline.d.ts +17 -0
- package/dist/interpreter/trampoline.js +38 -0
- package/dist/interpreter/utils.d.ts +4 -7
- package/dist/interpreter/utils.js +25 -17
- package/dist/tester/index.d.ts +25 -0
- package/dist/tester/index.js +108 -0
- package/dist/utils/helpers.d.ts +0 -4
- package/dist/utils/helpers.js +20 -24
- package/package.json +2 -2
- package/src/analyzer/GraphBuilder.ts +142 -0
- package/src/analyzer/index.ts +185 -132
- package/src/analyzer/inspections/functional/functional.ts +121 -0
- package/src/analyzer/inspections/functional/smells.ts +102 -0
- package/src/analyzer/inspections/{generic.ts → generic/generic.ts} +581 -499
- package/src/analyzer/inspections/generic/smells.ts +365 -0
- package/src/analyzer/inspections/imperative/imperative.ts +101 -0
- package/src/analyzer/inspections/imperative/smells.ts +54 -0
- package/src/analyzer/inspections/logic/logic.ts +90 -0
- package/src/analyzer/inspections/logic/smells.ts +54 -0
- package/src/analyzer/inspections/{object.ts → object/object.ts} +264 -282
- package/src/analyzer/inspections/object/smells.ts +144 -0
- package/src/analyzer/utils.ts +109 -26
- package/src/index.ts +3 -2
- package/src/interpreter/components/EnvBuilder.ts +202 -97
- package/src/interpreter/components/Operations.ts +99 -81
- package/src/interpreter/components/PatternMatcher.ts +475 -254
- package/src/interpreter/components/RuntimeContext.ts +119 -0
- package/src/interpreter/components/TestRunner.ts +151 -0
- package/src/interpreter/components/Visitor.ts +1065 -493
- package/src/interpreter/components/logic/LogicEngine.ts +519 -0
- package/src/interpreter/components/logic/LogicResolver.ts +858 -0
- package/src/interpreter/components/logic/LogicTranslator.ts +149 -0
- package/src/interpreter/components/runtimes/FunctionRuntime.ts +227 -0
- package/src/interpreter/components/runtimes/LazyRuntime.ts +334 -0
- package/src/interpreter/components/runtimes/ObjectRuntime.ts +224 -0
- package/src/interpreter/errors.ts +47 -47
- package/src/interpreter/index.ts +52 -59
- package/src/interpreter/trampoline.ts +71 -0
- package/src/interpreter/utils.ts +84 -79
- package/src/tester/index.ts +128 -0
- package/src/utils/helpers.ts +67 -73
- package/tests/analyzer/functional.spec.ts +207 -221
- package/tests/analyzer/generic.spec.ts +178 -100
- package/tests/analyzer/helpers.spec.ts +83 -83
- package/tests/analyzer/logic.spec.ts +237 -292
- package/tests/analyzer/oop.spec.ts +323 -338
- package/tests/analyzer/transitive.spec.ts +166 -0
- package/tests/interpreter/EnvBuilder.spec.ts +183 -178
- package/tests/interpreter/FunctionRuntime.spec.ts +223 -234
- package/tests/interpreter/LazyRuntime.spec.ts +225 -190
- package/tests/interpreter/LogicEngine.spec.ts +327 -194
- package/tests/interpreter/LogicSubstitution.spec.ts +80 -0
- package/tests/interpreter/ObjectRuntime.spec.ts +606 -0
- package/tests/interpreter/Operations.spec.ts +220 -220
- package/tests/interpreter/PatternSystem.spec.ts +213 -189
- package/tests/interpreter/Tests.spec.ts +122 -0
- package/tests/interpreter/interpreter.spec.ts +991 -937
- package/tests/tester/Tester.spec.ts +153 -0
- package/tsconfig.build.json +15 -7
- package/tsconfig.json +25 -17
- package/dist/analyzer/inspections/functional.d.ts +0 -46
- package/dist/analyzer/inspections/functional.js +0 -123
- package/dist/analyzer/inspections/generic.js +0 -427
- package/dist/analyzer/inspections/imperative.d.ts +0 -37
- package/dist/analyzer/inspections/imperative.js +0 -105
- package/dist/analyzer/inspections/logic.d.ts +0 -49
- package/dist/analyzer/inspections/logic.js +0 -140
- package/dist/analyzer/inspections/object.d.ts +0 -83
- package/dist/analyzer/inspections/object.js +0 -235
- package/dist/interpreter/components/FunctionRuntime.d.ts +0 -8
- package/dist/interpreter/components/FunctionRuntime.js +0 -52
- package/dist/interpreter/components/LazyRuntime.d.ts +0 -7
- package/dist/interpreter/components/LazyRuntime.js +0 -75
- package/dist/interpreter/components/LogicEngine.d.ts +0 -21
- package/dist/interpreter/components/LogicEngine.js +0 -152
- package/dist/interpreter/components/LogicResolver.d.ts +0 -11
- package/dist/interpreter/components/LogicResolver.js +0 -87
- package/src/analyzer/inspections/functional.ts +0 -159
- package/src/analyzer/inspections/imperative.ts +0 -129
- package/src/analyzer/inspections/logic.ts +0 -166
- package/src/interpreter/components/FunctionRuntime.ts +0 -79
- package/src/interpreter/components/LazyRuntime.ts +0 -97
- package/src/interpreter/components/LogicEngine.ts +0 -227
- package/src/interpreter/components/LogicResolver.ts +0 -130
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import {
|
|
3
|
+
PrimitiveValue,
|
|
4
|
+
RuntimeFunction,
|
|
5
|
+
UnguardedBody,
|
|
6
|
+
Sequence,
|
|
7
|
+
Return,
|
|
8
|
+
SymbolPrimitive,
|
|
9
|
+
NumberPrimitive,
|
|
10
|
+
VariablePattern,
|
|
11
|
+
RuntimeObject,
|
|
12
|
+
RuntimeClass,
|
|
13
|
+
StringPrimitive,
|
|
14
|
+
Primitive,
|
|
15
|
+
Super,
|
|
16
|
+
ArithmeticBinaryOperation,
|
|
17
|
+
EnvStack,
|
|
18
|
+
} from "yukigo-ast";
|
|
19
|
+
import { createGlobalEnv } from "../../src/interpreter/utils.js";
|
|
20
|
+
import {
|
|
21
|
+
Continuation,
|
|
22
|
+
idContinuation,
|
|
23
|
+
Thunk,
|
|
24
|
+
trampoline,
|
|
25
|
+
} from "../../src/interpreter/trampoline.js";
|
|
26
|
+
import { RuntimeContext } from "../../src/interpreter/components/RuntimeContext.js";
|
|
27
|
+
|
|
28
|
+
const createEmptyEnv = () => ({ head: new Map(), tail: null });
|
|
29
|
+
|
|
30
|
+
const createMethodMap = (
|
|
31
|
+
methods: RuntimeFunction[],
|
|
32
|
+
): Map<string, RuntimeFunction> =>
|
|
33
|
+
new Map(methods.map((m) => [m.identifier, m]));
|
|
34
|
+
const createMethod = (name: string, returnVal: Primitive): RuntimeFunction => {
|
|
35
|
+
return {
|
|
36
|
+
type: "Function",
|
|
37
|
+
identifier: name,
|
|
38
|
+
arity: 0,
|
|
39
|
+
pendingArgs: [],
|
|
40
|
+
equations: [
|
|
41
|
+
{
|
|
42
|
+
patterns: [],
|
|
43
|
+
body: new UnguardedBody(new Sequence([new Return(returnVal)])),
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const createClass = (
|
|
50
|
+
name: string,
|
|
51
|
+
superclass?: string,
|
|
52
|
+
methodDefs: Map<string, RuntimeFunction> = new Map(),
|
|
53
|
+
mixins: string[] = [],
|
|
54
|
+
): RuntimeClass => {
|
|
55
|
+
return {
|
|
56
|
+
type: "Class",
|
|
57
|
+
identifier: name,
|
|
58
|
+
fields: new Map(),
|
|
59
|
+
methods: methodDefs,
|
|
60
|
+
superclass,
|
|
61
|
+
mixins,
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
describe("ctx.objRuntime", () => {
|
|
66
|
+
let objectInstance: RuntimeObject;
|
|
67
|
+
const className = "TestClass";
|
|
68
|
+
const initialFields = new Map<string, PrimitiveValue>([
|
|
69
|
+
["count", 10],
|
|
70
|
+
["name", "Yukigo"],
|
|
71
|
+
]);
|
|
72
|
+
const methods = new Map<string, RuntimeFunction>();
|
|
73
|
+
const classDef: RuntimeClass = {
|
|
74
|
+
type: "Class",
|
|
75
|
+
identifier: className,
|
|
76
|
+
fields: initialFields,
|
|
77
|
+
methods,
|
|
78
|
+
mixins: [],
|
|
79
|
+
superclass: undefined,
|
|
80
|
+
};
|
|
81
|
+
const env: EnvStack = createGlobalEnv();
|
|
82
|
+
env.head.set(className, classDef);
|
|
83
|
+
const ctx = new RuntimeContext();
|
|
84
|
+
ctx.setEnv(env);
|
|
85
|
+
beforeEach(() => {
|
|
86
|
+
objectInstance = ctx.objRuntime.instantiate(
|
|
87
|
+
className,
|
|
88
|
+
"obj",
|
|
89
|
+
initialFields,
|
|
90
|
+
methods,
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe("instantiate()", () => {
|
|
95
|
+
it("debe crear un objeto con la estructura correcta", () => {
|
|
96
|
+
expect(objectInstance.type).to.equal("Object");
|
|
97
|
+
expect(objectInstance.className).to.equal(className);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("debe clonar el mapa de campos (no usar la referencia original)", () => {
|
|
101
|
+
const fieldsDef = new Map([["x", 1]]);
|
|
102
|
+
const obj = ctx.objRuntime.instantiate("A", "objA", fieldsDef, new Map());
|
|
103
|
+
|
|
104
|
+
fieldsDef.set("x", 2);
|
|
105
|
+
|
|
106
|
+
expect(obj.fields.get("x")).to.equal(1);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe("Field Access (Get/Set)", () => {
|
|
111
|
+
it("getField debe devolver el valor de un campo existente", () => {
|
|
112
|
+
const val = ctx.objRuntime.getField(objectInstance, "count");
|
|
113
|
+
expect(val).to.equal(10);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("getField debe lanzar error si el campo no existe", () => {
|
|
117
|
+
expect(() => {
|
|
118
|
+
ctx.objRuntime.getField(objectInstance, "non_existent");
|
|
119
|
+
}).to.throw(/Field 'non_existent' not found/);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("getField debe lanzar error si el target no es un objeto", () => {
|
|
123
|
+
expect(() => {
|
|
124
|
+
ctx.objRuntime.getField(123 as any, "count");
|
|
125
|
+
}).to.throw(/Target is not an object/);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("setField debe actualizar el valor de un campo existente", () => {
|
|
129
|
+
ctx.objRuntime.setField(objectInstance, "count", 20);
|
|
130
|
+
expect(objectInstance.fields.get("count")).to.equal(20);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("setField debe lanzar error si intentas crear un campo nuevo (strict mode)", () => {
|
|
134
|
+
expect(() => {
|
|
135
|
+
ctx.objRuntime.setField(objectInstance, "newProp", 99);
|
|
136
|
+
}).to.throw(/Cannot set unknown field/);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("dispatch()", () => {
|
|
141
|
+
it("debe ejecutar un método que accede a 'self' (campos del objeto)", () => {
|
|
142
|
+
const getCountMethod: RuntimeFunction = {
|
|
143
|
+
type: "Function",
|
|
144
|
+
identifier: "getCount",
|
|
145
|
+
arity: 0,
|
|
146
|
+
pendingArgs: [],
|
|
147
|
+
equations: [
|
|
148
|
+
{
|
|
149
|
+
patterns: [],
|
|
150
|
+
body: new UnguardedBody(
|
|
151
|
+
new Sequence([new Return(new SymbolPrimitive("count"))]),
|
|
152
|
+
),
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
objectInstance.methods.set("getCount", getCountMethod);
|
|
158
|
+
|
|
159
|
+
const result = trampoline(
|
|
160
|
+
ctx.objRuntime.dispatch(
|
|
161
|
+
objectInstance,
|
|
162
|
+
"getCount",
|
|
163
|
+
[],
|
|
164
|
+
env,
|
|
165
|
+
|
|
166
|
+
idContinuation,
|
|
167
|
+
),
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
expect(result).to.equal(10);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("debe fallar si el método no existe", () => {
|
|
174
|
+
expect(() => {
|
|
175
|
+
trampoline(
|
|
176
|
+
ctx.objRuntime.dispatch(
|
|
177
|
+
objectInstance,
|
|
178
|
+
"unknownMethod",
|
|
179
|
+
[],
|
|
180
|
+
env,
|
|
181
|
+
|
|
182
|
+
idContinuation,
|
|
183
|
+
),
|
|
184
|
+
);
|
|
185
|
+
}).to.throw(/does not understand 'unknownMethod'/);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("debe fallar si el receiver no es un objeto", () => {
|
|
189
|
+
expect(() => {
|
|
190
|
+
trampoline(
|
|
191
|
+
ctx.objRuntime.dispatch(
|
|
192
|
+
"soy un string" as any,
|
|
193
|
+
"toString",
|
|
194
|
+
[],
|
|
195
|
+
createEmptyEnv() as any,
|
|
196
|
+
|
|
197
|
+
idContinuation,
|
|
198
|
+
),
|
|
199
|
+
);
|
|
200
|
+
}).to.throw(/is not an object/);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("debe permitir argumentos en el método", () => {
|
|
204
|
+
const returnArgAST = new Return(new SymbolPrimitive("val"));
|
|
205
|
+
const addMethod: RuntimeFunction = {
|
|
206
|
+
type: "Function",
|
|
207
|
+
identifier: "echo",
|
|
208
|
+
arity: 1,
|
|
209
|
+
pendingArgs: [],
|
|
210
|
+
equations: [
|
|
211
|
+
{
|
|
212
|
+
patterns: [new VariablePattern(new SymbolPrimitive("val"))],
|
|
213
|
+
body: new UnguardedBody(new Sequence([returnArgAST])),
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
objectInstance.methods.set("echo", addMethod);
|
|
219
|
+
|
|
220
|
+
const result = trampoline(
|
|
221
|
+
ctx.objRuntime.dispatch(
|
|
222
|
+
objectInstance,
|
|
223
|
+
"echo",
|
|
224
|
+
[999],
|
|
225
|
+
env,
|
|
226
|
+
|
|
227
|
+
idContinuation,
|
|
228
|
+
),
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
expect(result).to.equal(999);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
describe("Method Lookup", () => {
|
|
235
|
+
it("debe delegar a la superclase si el método no está en la instancia ni en la clase", () => {
|
|
236
|
+
env.head.set(
|
|
237
|
+
"Animal",
|
|
238
|
+
createClass(
|
|
239
|
+
"Animal",
|
|
240
|
+
undefined,
|
|
241
|
+
createMethodMap([createMethod("speak", new StringPrimitive("Guau"))]),
|
|
242
|
+
),
|
|
243
|
+
);
|
|
244
|
+
env.head.set("Perro", createClass("Perro", "Animal"));
|
|
245
|
+
|
|
246
|
+
const perro = ctx.objRuntime.instantiate(
|
|
247
|
+
"Perro",
|
|
248
|
+
"dogObj",
|
|
249
|
+
new Map(),
|
|
250
|
+
new Map(),
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const res = trampoline(
|
|
254
|
+
ctx.objRuntime.dispatch(
|
|
255
|
+
perro,
|
|
256
|
+
"speak",
|
|
257
|
+
[],
|
|
258
|
+
env,
|
|
259
|
+
|
|
260
|
+
idContinuation,
|
|
261
|
+
),
|
|
262
|
+
);
|
|
263
|
+
expect(res).to.equal("Guau");
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("debe subir múltiples niveles en la jerarquía (Abuelo -> Padre -> Hijo)", () => {
|
|
267
|
+
env.head.set(
|
|
268
|
+
"A",
|
|
269
|
+
createClass(
|
|
270
|
+
"A",
|
|
271
|
+
undefined,
|
|
272
|
+
createMethodMap([createMethod("id", new NumberPrimitive(1))]),
|
|
273
|
+
),
|
|
274
|
+
);
|
|
275
|
+
env.head.set("B", createClass("B", "A"));
|
|
276
|
+
env.head.set("C", createClass("C", "B"));
|
|
277
|
+
|
|
278
|
+
const objC = ctx.objRuntime.instantiate(
|
|
279
|
+
"C",
|
|
280
|
+
"objC",
|
|
281
|
+
new Map(),
|
|
282
|
+
new Map(),
|
|
283
|
+
);
|
|
284
|
+
expect(
|
|
285
|
+
trampoline(
|
|
286
|
+
ctx.objRuntime.dispatch(
|
|
287
|
+
objC,
|
|
288
|
+
"id",
|
|
289
|
+
[],
|
|
290
|
+
env,
|
|
291
|
+
|
|
292
|
+
idContinuation,
|
|
293
|
+
),
|
|
294
|
+
),
|
|
295
|
+
).to.equal(1);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("debe encontrar métodos definidos en un Mixin", () => {
|
|
299
|
+
env.head.set(
|
|
300
|
+
"Volador",
|
|
301
|
+
createClass(
|
|
302
|
+
"Volador",
|
|
303
|
+
undefined,
|
|
304
|
+
createMethodMap([createMethod("volar", new StringPrimitive("Wosh"))]),
|
|
305
|
+
),
|
|
306
|
+
);
|
|
307
|
+
env.head.set(
|
|
308
|
+
"Ave",
|
|
309
|
+
createClass("Ave", undefined, undefined, ["Volador"]),
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
const pepita = ctx.objRuntime.instantiate(
|
|
313
|
+
"Ave",
|
|
314
|
+
"birdObj",
|
|
315
|
+
new Map(),
|
|
316
|
+
new Map(),
|
|
317
|
+
);
|
|
318
|
+
expect(
|
|
319
|
+
trampoline(
|
|
320
|
+
ctx.objRuntime.dispatch(
|
|
321
|
+
pepita,
|
|
322
|
+
"volar",
|
|
323
|
+
[],
|
|
324
|
+
env,
|
|
325
|
+
|
|
326
|
+
idContinuation,
|
|
327
|
+
),
|
|
328
|
+
),
|
|
329
|
+
).to.equal("Wosh");
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it("debe soportar Mixines recursivos (Mixin incluye otro Mixin)", () => {
|
|
333
|
+
env.head.set(
|
|
334
|
+
"HabilidadA",
|
|
335
|
+
createClass(
|
|
336
|
+
"HabilidadA",
|
|
337
|
+
undefined,
|
|
338
|
+
createMethodMap([createMethod("skill", new StringPrimitive("Fire"))]),
|
|
339
|
+
),
|
|
340
|
+
);
|
|
341
|
+
env.head.set(
|
|
342
|
+
"HabilidadB",
|
|
343
|
+
createClass("HabilidadB", undefined, undefined, ["HabilidadA"]),
|
|
344
|
+
);
|
|
345
|
+
env.head.set(
|
|
346
|
+
"Heroe",
|
|
347
|
+
createClass("Heroe", undefined, undefined, ["HabilidadB"]),
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
const heroe = ctx.objRuntime.instantiate(
|
|
351
|
+
"Heroe",
|
|
352
|
+
"heroObj",
|
|
353
|
+
new Map(),
|
|
354
|
+
new Map(),
|
|
355
|
+
);
|
|
356
|
+
expect(
|
|
357
|
+
trampoline(
|
|
358
|
+
ctx.objRuntime.dispatch(
|
|
359
|
+
heroe,
|
|
360
|
+
"skill",
|
|
361
|
+
[],
|
|
362
|
+
env,
|
|
363
|
+
|
|
364
|
+
idContinuation,
|
|
365
|
+
),
|
|
366
|
+
),
|
|
367
|
+
).to.equal("Fire");
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it("Prioridad: La Clase Propia gana a Mixines y Superclase", () => {
|
|
371
|
+
env.head.set(
|
|
372
|
+
"Super",
|
|
373
|
+
createClass(
|
|
374
|
+
"Super",
|
|
375
|
+
undefined,
|
|
376
|
+
createMethodMap([createMethod("val", new NumberPrimitive(1))]),
|
|
377
|
+
),
|
|
378
|
+
);
|
|
379
|
+
env.head.set(
|
|
380
|
+
"Mixin",
|
|
381
|
+
createClass(
|
|
382
|
+
"Mixin",
|
|
383
|
+
undefined,
|
|
384
|
+
createMethodMap([createMethod("val", new NumberPrimitive(2))]),
|
|
385
|
+
),
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
env.head.set(
|
|
389
|
+
"Child",
|
|
390
|
+
createClass(
|
|
391
|
+
"Child",
|
|
392
|
+
"Super",
|
|
393
|
+
createMethodMap([createMethod("val", new NumberPrimitive(3))]),
|
|
394
|
+
["Mixin"],
|
|
395
|
+
),
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
const child = ctx.objRuntime.instantiate(
|
|
399
|
+
"Child",
|
|
400
|
+
"childObj",
|
|
401
|
+
new Map(),
|
|
402
|
+
new Map(),
|
|
403
|
+
);
|
|
404
|
+
expect(
|
|
405
|
+
trampoline(
|
|
406
|
+
ctx.objRuntime.dispatch(
|
|
407
|
+
child,
|
|
408
|
+
"val",
|
|
409
|
+
[],
|
|
410
|
+
env,
|
|
411
|
+
|
|
412
|
+
idContinuation,
|
|
413
|
+
),
|
|
414
|
+
),
|
|
415
|
+
).to.equal(3);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it("Prioridad: El Mixin gana a la Superclase", () => {
|
|
419
|
+
env.head.set(
|
|
420
|
+
"Super",
|
|
421
|
+
createClass(
|
|
422
|
+
"Super",
|
|
423
|
+
undefined,
|
|
424
|
+
createMethodMap([createMethod("val", new NumberPrimitive(1))]),
|
|
425
|
+
),
|
|
426
|
+
);
|
|
427
|
+
env.head.set(
|
|
428
|
+
"Mixin",
|
|
429
|
+
createClass(
|
|
430
|
+
"Mixin",
|
|
431
|
+
undefined,
|
|
432
|
+
createMethodMap([createMethod("val", new NumberPrimitive(2))]),
|
|
433
|
+
),
|
|
434
|
+
);
|
|
435
|
+
env.head.set(
|
|
436
|
+
"Child",
|
|
437
|
+
createClass("Child", "Super", undefined, ["Mixin"]),
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
const child = ctx.objRuntime.instantiate(
|
|
441
|
+
"Child",
|
|
442
|
+
"childObj",
|
|
443
|
+
new Map(),
|
|
444
|
+
new Map(),
|
|
445
|
+
);
|
|
446
|
+
expect(
|
|
447
|
+
trampoline(
|
|
448
|
+
ctx.objRuntime.dispatch(
|
|
449
|
+
child,
|
|
450
|
+
"val",
|
|
451
|
+
[],
|
|
452
|
+
env,
|
|
453
|
+
|
|
454
|
+
idContinuation,
|
|
455
|
+
),
|
|
456
|
+
),
|
|
457
|
+
).to.equal(2);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it("Prioridad: El último Mixin de la lista gana (Shadowing de derecha a izquierda)", () => {
|
|
461
|
+
env.head.set(
|
|
462
|
+
"MixinA",
|
|
463
|
+
createClass(
|
|
464
|
+
"MixinA",
|
|
465
|
+
undefined,
|
|
466
|
+
createMethodMap([createMethod("val", new NumberPrimitive(10))]),
|
|
467
|
+
),
|
|
468
|
+
);
|
|
469
|
+
env.head.set(
|
|
470
|
+
"MixinB",
|
|
471
|
+
createClass(
|
|
472
|
+
"MixinB",
|
|
473
|
+
undefined,
|
|
474
|
+
createMethodMap([createMethod("val", new NumberPrimitive(20))]),
|
|
475
|
+
),
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
env.head.set(
|
|
479
|
+
"Clase",
|
|
480
|
+
createClass("Clase", undefined, undefined, ["MixinA", "MixinB"]),
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
const obj = ctx.objRuntime.instantiate(
|
|
484
|
+
"Clase",
|
|
485
|
+
"objC",
|
|
486
|
+
new Map(),
|
|
487
|
+
new Map(),
|
|
488
|
+
);
|
|
489
|
+
expect(
|
|
490
|
+
trampoline(
|
|
491
|
+
ctx.objRuntime.dispatch(
|
|
492
|
+
obj,
|
|
493
|
+
"val",
|
|
494
|
+
[],
|
|
495
|
+
env,
|
|
496
|
+
|
|
497
|
+
idContinuation,
|
|
498
|
+
),
|
|
499
|
+
),
|
|
500
|
+
).to.equal(20);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it("Prioridad: Orden inverso de Mixines", () => {
|
|
504
|
+
env.head.set(
|
|
505
|
+
"MixinA",
|
|
506
|
+
createClass(
|
|
507
|
+
"MixinA",
|
|
508
|
+
undefined,
|
|
509
|
+
createMethodMap([createMethod("val", new NumberPrimitive(10))]),
|
|
510
|
+
),
|
|
511
|
+
);
|
|
512
|
+
env.head.set(
|
|
513
|
+
"MixinB",
|
|
514
|
+
createClass(
|
|
515
|
+
"MixinB",
|
|
516
|
+
undefined,
|
|
517
|
+
createMethodMap([createMethod("val", new NumberPrimitive(20))]),
|
|
518
|
+
),
|
|
519
|
+
);
|
|
520
|
+
|
|
521
|
+
env.head.set(
|
|
522
|
+
"Clase",
|
|
523
|
+
createClass("Clase", undefined, undefined, ["MixinB", "MixinA"]),
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
const obj = ctx.objRuntime.instantiate(
|
|
527
|
+
"Clase",
|
|
528
|
+
"obj",
|
|
529
|
+
new Map(),
|
|
530
|
+
new Map(),
|
|
531
|
+
);
|
|
532
|
+
expect(
|
|
533
|
+
trampoline(
|
|
534
|
+
ctx.objRuntime.dispatch(
|
|
535
|
+
obj,
|
|
536
|
+
"val",
|
|
537
|
+
[],
|
|
538
|
+
env,
|
|
539
|
+
|
|
540
|
+
idContinuation,
|
|
541
|
+
),
|
|
542
|
+
),
|
|
543
|
+
).to.equal(10);
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
describe("Super", () => {
|
|
547
|
+
it("debe invocar al método de la superclase y operar con el resultado", () => {
|
|
548
|
+
env.head.set(
|
|
549
|
+
"Base",
|
|
550
|
+
createClass(
|
|
551
|
+
"Base",
|
|
552
|
+
undefined,
|
|
553
|
+
createMethodMap([createMethod("calc", new NumberPrimitive(10))]),
|
|
554
|
+
),
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
const astBody = new UnguardedBody(
|
|
558
|
+
new Sequence([
|
|
559
|
+
new Return(
|
|
560
|
+
new ArithmeticBinaryOperation(
|
|
561
|
+
"Plus",
|
|
562
|
+
new Super([]),
|
|
563
|
+
new NumberPrimitive(5),
|
|
564
|
+
),
|
|
565
|
+
),
|
|
566
|
+
]),
|
|
567
|
+
);
|
|
568
|
+
|
|
569
|
+
const methodHijo: RuntimeFunction = {
|
|
570
|
+
type: "Function",
|
|
571
|
+
identifier: "calc",
|
|
572
|
+
arity: 0,
|
|
573
|
+
pendingArgs: [],
|
|
574
|
+
equations: [
|
|
575
|
+
{
|
|
576
|
+
patterns: [],
|
|
577
|
+
body: astBody,
|
|
578
|
+
},
|
|
579
|
+
],
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
const Hijo: RuntimeClass = {
|
|
583
|
+
type: "Class",
|
|
584
|
+
identifier: "Hijo",
|
|
585
|
+
fields: new Map(),
|
|
586
|
+
methods: new Map([["calc", methodHijo]]),
|
|
587
|
+
superclass: "Base",
|
|
588
|
+
mixins: [],
|
|
589
|
+
};
|
|
590
|
+
env.head.set("Hijo", Hijo);
|
|
591
|
+
|
|
592
|
+
const hijoInstance = ctx.objRuntime.instantiate(
|
|
593
|
+
"Hijo",
|
|
594
|
+
"childObj",
|
|
595
|
+
new Map(),
|
|
596
|
+
new Map(),
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
const result = trampoline(
|
|
600
|
+
ctx.objRuntime.dispatch(hijoInstance, "calc", [], env, idContinuation),
|
|
601
|
+
);
|
|
602
|
+
|
|
603
|
+
expect(result).to.equal(15);
|
|
604
|
+
});
|
|
605
|
+
});
|
|
606
|
+
});
|