yukigo-wollok-parser 0.1.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.
@@ -0,0 +1,384 @@
1
+ import { expect } from "chai";
2
+ import * as Yu from "yukigo-ast";
3
+ import { WollokToYukigoTransformer } from "../src/transformer";
4
+ import {
5
+ Assignment,
6
+ Body,
7
+ Catch,
8
+ Class,
9
+ Field,
10
+ If,
11
+ Import,
12
+ Literal,
13
+ Method,
14
+ Mixin,
15
+ New,
16
+ Node,
17
+ Package,
18
+ Parameter,
19
+ Reference,
20
+ Return,
21
+ Send,
22
+ Singleton,
23
+ Super,
24
+ Throw,
25
+ Try,
26
+ Variable,
27
+ } from "wollok-ts";
28
+
29
+ describe("WollokToYukigoTransformer", () => {
30
+ let transformer: WollokToYukigoTransformer;
31
+
32
+ beforeEach(() => {
33
+ transformer = new WollokToYukigoTransformer();
34
+ });
35
+
36
+ const p = (members, imports?, sourceMap?) =>
37
+ new Package({ name: "example", imports, members, sourceMap });
38
+ const lit = (value, sourceMap?) => new Literal({ value, sourceMap });
39
+ const ref = (name) => new Reference<any>({ name });
40
+ const send = (receiver, message, args) =>
41
+ new Send({ receiver, message, args });
42
+ const met = (name, parameters: Parameter[], body) =>
43
+ new Method({ name, parameters, body });
44
+ const param = (name) => new Parameter({ name });
45
+ const unguardedBody = (sentences) => new Body({ sentences });
46
+ const singleton = (name, members) => new Singleton({ name, members });
47
+ const wollokClass = (name, members) => new Class({ name, members });
48
+ const ret = (value) => new Return({ value });
49
+ const imp = (name) => new Import({ entity: ref(name) });
50
+ const mixin = (name, members) => new Mixin({ name, members });
51
+ const field = (name, isConstant: boolean, value?) =>
52
+ new Field({ name, value, isConstant });
53
+ const variable = (name, isConstant: boolean, value) =>
54
+ new Variable({ name, value, isConstant });
55
+ const assign = (variable, value) => new Assignment({ variable, value });
56
+ const newExpr = (className, args) =>
57
+ new New({ instantiated: ref(className), args });
58
+ const ifStmt = (condition, thenBody, elseBody?) =>
59
+ new If({ condition, thenBody, elseBody });
60
+ const throwStmt = (exception) => new Throw({ exception });
61
+ const catchClause = (parameter, body) => new Catch({ parameter, body });
62
+ const tryStmt = (body, catches, always?) =>
63
+ new Try({ body, catches, always });
64
+ const superInv = (args) => new Super({ args });
65
+
66
+ const transformNode = (node: Node) => {
67
+ const pkg = p([node]);
68
+ const resultAST = transformer.transform(pkg);
69
+ return resultAST[0];
70
+ };
71
+
72
+ describe("Primitives/Literals", () => {
73
+ it("should transform String literals to YuString", () => {
74
+ const input = lit("Hello World");
75
+ const result = transformNode(input) as Yu.StringPrimitive;
76
+
77
+ expect(result).to.be.instanceOf(Yu.StringPrimitive);
78
+ expect(result.value).to.equal("Hello World");
79
+ });
80
+
81
+ it("should transform Number literals to YuNumber", () => {
82
+ const input = lit(42);
83
+ const result = transformNode(input) as Yu.NumberPrimitive;
84
+
85
+ expect(result).to.be.instanceOf(Yu.NumberPrimitive);
86
+ expect(result.value).to.equal(42);
87
+ });
88
+
89
+ it("should transform Boolean literals to YuBoolean", () => {
90
+ const input = lit(true);
91
+ const result = transformNode(input) as Yu.BooleanPrimitive;
92
+
93
+ expect(result).to.be.instanceOf(Yu.BooleanPrimitive);
94
+ expect(result.value).to.be.true;
95
+ });
96
+
97
+ it("should transform null values to YuNil", () => {
98
+ const input = lit(null);
99
+ const result = transformNode(input) as Yu.NilPrimitive;
100
+
101
+ expect(result).to.be.instanceOf(Yu.NilPrimitive);
102
+ });
103
+ });
104
+
105
+ describe("References", () => {
106
+ it("should transform References to SymbolPrimitive", () => {
107
+ const input = ref("console");
108
+ const result = transformNode(input) as Yu.SymbolPrimitive;
109
+
110
+ expect(result).to.be.instanceOf(Yu.SymbolPrimitive);
111
+ expect(result.value).to.equal("console");
112
+ });
113
+ });
114
+
115
+ describe("Expressions", () => {
116
+ it("should transform a Send (method call) correctly", () => {
117
+ const input = send(ref("console"), "println", [lit("Hi")]);
118
+ const result = transformNode(input) as Yu.Send;
119
+
120
+ expect(result).to.be.instanceOf(Yu.Send);
121
+ expect(result.receiver).to.be.instanceOf(Yu.SymbolPrimitive);
122
+ expect((result.receiver as Yu.SymbolPrimitive).value).to.equal("console");
123
+
124
+ expect(result.selector).to.be.instanceOf(Yu.SymbolPrimitive);
125
+ expect((result.selector as Yu.SymbolPrimitive).value).to.equal("println");
126
+
127
+ expect(result.args).to.have.lengthOf(1);
128
+ expect((result.args[0] as Yu.StringPrimitive).value).to.equal("Hi");
129
+ });
130
+ });
131
+
132
+ describe("Methods & Parameters", () => {
133
+ it("should transform a Method into an Equation with UnguardedBody", () => {
134
+ const input = met(
135
+ "greet",
136
+ [param("name"), param("age")],
137
+ unguardedBody([send(ref("self"), "call", [])])
138
+ );
139
+
140
+ const result = transformNode(input) as Yu.Method;
141
+ expect(result).to.be.instanceOf(Yu.Method);
142
+ expect(result.identifier.value).to.equal("greet");
143
+
144
+ expect(result.equations).to.have.lengthOf(1);
145
+ const equation = result.equations[0];
146
+
147
+ expect(equation.patterns).to.have.lengthOf(2);
148
+ expect((equation.patterns[0] as Yu.VariablePattern).name.value).to.equal(
149
+ "name"
150
+ );
151
+ expect((equation.patterns[1] as Yu.VariablePattern).name.value).to.equal(
152
+ "age"
153
+ );
154
+
155
+ expect(equation.body).to.be.instanceOf(Yu.UnguardedBody);
156
+ const body = equation.body as Yu.UnguardedBody;
157
+ expect(body.sequence).to.be.instanceOf(Yu.Sequence);
158
+ });
159
+ });
160
+
161
+ describe("Object/Class", () => {
162
+ it("should transform a Singleton (Object) with members", () => {
163
+ const input = singleton("MyObject", [
164
+ met("doSomething", [], unguardedBody([])),
165
+ ]);
166
+
167
+ const result = transformNode(input) as Yu.Object;
168
+
169
+ expect(result).to.be.instanceOf(Yu.Object);
170
+ expect(result.identifier.value).to.equal("MyObject");
171
+
172
+ expect(result.expression).to.be.instanceOf(Yu.Sequence);
173
+ const seq = result.expression as Yu.Sequence;
174
+ expect(seq.statements).to.have.lengthOf(1);
175
+ expect(seq.statements[0]).to.be.instanceOf(Yu.Method);
176
+ });
177
+
178
+ it("should transform a Class", () => {
179
+ const input = wollokClass("MyClass", []);
180
+ const result = transformNode(input) as Yu.Class;
181
+ expect(result).to.be.instanceOf(Yu.Class);
182
+ expect(result.identifier.value).to.equal("MyClass");
183
+ expect(result.expression).to.be.instanceOf(Yu.Sequence);
184
+ });
185
+ });
186
+
187
+ describe("SourceMap/SourceLocation", () => {
188
+ it("should map SourceMap to SourceLocation", () => {
189
+ const input = lit(100, {
190
+ start: { line: 10, column: 5 },
191
+ end: { line: 10, column: 8 },
192
+ });
193
+
194
+ const result = transformNode(input);
195
+
196
+ expect(result.loc).to.not.be.undefined;
197
+ expect(result.loc?.line).to.equal(10);
198
+ expect(result.loc?.column).to.equal(5);
199
+ });
200
+ });
201
+
202
+ describe("Package", () => {
203
+ it("should return an array of Statements from a Package", () => {
204
+ const pkg = p([wollokClass("A", []), singleton("B", [])]);
205
+
206
+ const result = transformer.transform(pkg);
207
+
208
+ expect(result).to.be.an("array");
209
+ expect(result).to.have.lengthOf(2);
210
+ expect(result[0]).to.be.instanceOf(Yu.Class);
211
+ expect(result[1]).to.be.instanceOf(Yu.Object);
212
+ });
213
+ });
214
+
215
+ describe("Return Methods", () => {
216
+ it("should transform method with parameters, explicit return and binary operations", () => {
217
+ const input = met(
218
+ "calculation",
219
+ [param("a")],
220
+ unguardedBody([ret(send(ref("a"), "+", [lit(2)]))])
221
+ );
222
+
223
+ const result = transformNode(input) as Yu.Method;
224
+
225
+ expect(result.identifier.value).to.equal("calculation");
226
+
227
+ const equation = result.equations[0];
228
+ expect(equation.patterns).to.have.lengthOf(1);
229
+ expect(equation.patterns[0]).to.be.instanceOf(Yu.VariablePattern);
230
+ expect((equation.patterns[0] as Yu.VariablePattern).name.value).to.equal(
231
+ "a"
232
+ );
233
+
234
+ const bodySeq = (equation.body as Yu.UnguardedBody).sequence;
235
+ expect(bodySeq.statements).to.have.lengthOf(1);
236
+
237
+ const returnStmt = bodySeq.statements[0] as Yu.Return;
238
+ expect(returnStmt).to.be.instanceOf(Yu.Return);
239
+
240
+ const binaryOp = returnStmt.body as Yu.ArithmeticBinaryOperation;
241
+ expect(binaryOp).to.be.instanceOf(Yu.ArithmeticBinaryOperation);
242
+
243
+ expect(binaryOp.operator).to.equal("Plus");
244
+
245
+ expect(binaryOp.left).to.be.instanceOf(Yu.SymbolPrimitive);
246
+ expect((binaryOp.left as Yu.SymbolPrimitive).value).to.equal("a");
247
+
248
+ expect(binaryOp.right).to.be.instanceOf(Yu.NumberPrimitive);
249
+ expect((binaryOp.right as Yu.NumberPrimitive).value).to.equal(2);
250
+ });
251
+ });
252
+
253
+ describe("Structural Extensions (Import, Mixin, Field)", () => {
254
+ it("should transform Import -> Include", () => {
255
+ const input = p([], [imp("wollok.game")]);
256
+ const result = transformNode(input) as Yu.Include;
257
+ const importStmt = result[0];
258
+ expect(importStmt).to.be.instanceOf(Yu.Include);
259
+ expect(importStmt.identifier.value).to.equal("wollok.game");
260
+ });
261
+
262
+ it("should transform Mixin -> Class", () => {
263
+ const input = mixin("FlyingAbility", []);
264
+ const result = transformNode(input) as Yu.Class;
265
+
266
+ expect(result).to.be.instanceOf(Yu.Class);
267
+ expect(result.identifier.value).to.equal("FlyingAbility");
268
+ });
269
+
270
+ it("should transform Field -> Attribute (with value)", () => {
271
+ const input = field("energy", false, lit(100));
272
+ const result = transformNode(input) as Yu.Attribute;
273
+
274
+ expect(result).to.be.instanceOf(Yu.Attribute);
275
+ expect(result.identifier.value).to.equal("energy");
276
+ expect((result.expression as Yu.NumberPrimitive).value).to.equal(100);
277
+ });
278
+
279
+ it("should transform Field -> Attribute (without value defaults to Nil)", () => {
280
+ const input = field("target", false);
281
+ const result = transformNode(input) as Yu.Attribute;
282
+
283
+ expect(result).to.be.instanceOf(Yu.Attribute);
284
+ expect(result.expression).to.be.instanceOf(Yu.NilPrimitive);
285
+ });
286
+ });
287
+
288
+ describe("State & Flow (Vars, Assignments, If)", () => {
289
+ it("should transform Variable Declaration", () => {
290
+ const input = variable("count", false, lit(0));
291
+ const result = transformNode(input) as Yu.Variable;
292
+
293
+ expect(result).to.be.instanceOf(Yu.Variable);
294
+ expect(result.identifier.value).to.equal("count");
295
+ expect((result.expression as Yu.NumberPrimitive).value).to.equal(0);
296
+ });
297
+
298
+ it("should transform Assignment", () => {
299
+ const input = assign(ref("count"), lit(5));
300
+ const result = transformNode(input) as Yu.Assignment;
301
+
302
+ expect(result).to.be.instanceOf(Yu.Assignment);
303
+ expect(result.identifier.value).to.equal("count");
304
+ expect((result.expression as Yu.NumberPrimitive).value).to.equal(5);
305
+ });
306
+
307
+ it("should transform If/Else", () => {
308
+ const input = ifStmt(lit(true), unguardedBody([]), unguardedBody([]));
309
+ const result = transformNode(input) as Yu.If;
310
+
311
+ expect(result).to.be.instanceOf(Yu.If);
312
+ expect(result.condition).to.be.instanceOf(Yu.BooleanPrimitive);
313
+ expect(result.then).to.be.instanceOf(Yu.Sequence);
314
+ expect(result.elseExpr).to.be.instanceOf(Yu.Sequence);
315
+ });
316
+
317
+ it("should transform If (without Else injects empty Sequence)", () => {
318
+ const input = ifStmt(lit(false), unguardedBody([]));
319
+ const result = transformNode(input) as Yu.If;
320
+
321
+ expect(result).to.be.instanceOf(Yu.If);
322
+ expect(result.elseExpr).to.be.instanceOf(Yu.Sequence);
323
+ expect((result.elseExpr as Yu.Sequence).statements).to.be.empty;
324
+ });
325
+ });
326
+
327
+ describe("Object Oriented Features (Self, Super, New)", () => {
328
+ it("should transform Reference('self') -> Self", () => {
329
+ const input = ref("self");
330
+ const result = transformNode(input) as Yu.Self;
331
+
332
+ expect(result).to.be.instanceOf(Yu.Self);
333
+ expect(result).to.not.have.property("value");
334
+ });
335
+
336
+ it("should transform New -> Yu.New", () => {
337
+ const input = newExpr("Bird", [lit("Tweety")]);
338
+ const result = transformNode(input) as Yu.New;
339
+
340
+ expect(result).to.be.instanceOf(Yu.New);
341
+ expect(result.identifier.value).to.equal("Bird");
342
+ expect(result.args).to.have.lengthOf(1);
343
+ });
344
+
345
+ it("should transform SuperInvocation -> Send(receiver='super')", () => {
346
+ const input = superInv([]);
347
+ const result = transformNode(input) as Yu.Super;
348
+
349
+ expect(result).to.be.instanceOf(Yu.Super);
350
+ });
351
+ });
352
+
353
+ describe("Exception Handling (Throw, Try/Catch)", () => {
354
+ it("should transform Throw -> Raise", () => {
355
+ const input = throwStmt(newExpr("Error", []));
356
+ const result = transformNode(input) as Yu.Raise;
357
+
358
+ expect(result).to.be.instanceOf(Yu.Raise);
359
+ expect(result.body).to.be.instanceOf(Yu.New);
360
+ });
361
+
362
+ it("should transform Try/Catch/Always", () => {
363
+ const input = tryStmt(
364
+ unguardedBody([]),
365
+ [catchClause(param("e"), unguardedBody([]))],
366
+ unguardedBody([])
367
+ );
368
+
369
+ const result = transformNode(input) as Yu.Try;
370
+
371
+ expect(result).to.be.instanceOf(Yu.Try);
372
+ expect(result.body).to.be.instanceOf(Yu.Sequence);
373
+
374
+ expect(result.catchExpr).to.have.lengthOf(1);
375
+ const catchBlock = result.catchExpr[0];
376
+ expect(catchBlock.patterns).to.have.lengthOf(1);
377
+ expect(
378
+ (catchBlock.patterns[0] as Yu.VariablePattern).name.value
379
+ ).to.equal("e");
380
+
381
+ expect(result.finallyExpr).to.be.instanceOf(Yu.Sequence);
382
+ });
383
+ });
384
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2024",
4
+ "module": "nodenext",
5
+ "moduleResolution": "nodenext",
6
+ "lib": ["ES2024"],
7
+ "outDir": "./dist",
8
+ "declaration": true,
9
+ "esModuleInterop": true,
10
+ "allowSyntheticDefaultImports": true,
11
+ "sourceMap": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "skipLibCheck": true
14
+ },
15
+ "include": ["src"],
16
+ "exclude": ["dist/**/*"]
17
+ }