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.
- package/.mocharc.json +4 -0
- package/CHANGELOG.md +24 -0
- package/README.md +0 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/transformer.d.ts +26 -0
- package/dist/transformer.js +285 -0
- package/dist/transformer.js.map +1 -0
- package/package.json +35 -0
- package/src/index.ts +26 -0
- package/src/transformer.ts +427 -0
- package/tests/operations.spec.ts +154 -0
- package/tests/transformer.spec.ts +384 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import * as Yu from "yukigo-ast;
|
|
2
|
+
import {
|
|
3
|
+
Assignment,
|
|
4
|
+
Body,
|
|
5
|
+
Catch,
|
|
6
|
+
Class,
|
|
7
|
+
Field,
|
|
8
|
+
If,
|
|
9
|
+
Import,
|
|
10
|
+
Literal,
|
|
11
|
+
Method,
|
|
12
|
+
Mixin,
|
|
13
|
+
New,
|
|
14
|
+
Node,
|
|
15
|
+
Package,
|
|
16
|
+
Parameter,
|
|
17
|
+
Reference,
|
|
18
|
+
Return,
|
|
19
|
+
Send,
|
|
20
|
+
Singleton,
|
|
21
|
+
Super,
|
|
22
|
+
Throw,
|
|
23
|
+
Try,
|
|
24
|
+
Variable,
|
|
25
|
+
} from "wollok-ts";
|
|
26
|
+
|
|
27
|
+
const ARITHMETIC_BINARY_OPS: Record<string, Yu.ArithmeticBinaryOperator> = {
|
|
28
|
+
"+": "Plus",
|
|
29
|
+
"-": "Minus",
|
|
30
|
+
"*": "Multiply",
|
|
31
|
+
"/": "Divide",
|
|
32
|
+
"%": "Modulo",
|
|
33
|
+
"**": "Power",
|
|
34
|
+
max: "Max",
|
|
35
|
+
min: "Min",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const COMPARISON_OPS: Record<string, Yu.ComparisonOperatorType> = {
|
|
39
|
+
"==": "Equal",
|
|
40
|
+
"!=": "NotEqual",
|
|
41
|
+
"===": "Same",
|
|
42
|
+
"!==": "NotSame",
|
|
43
|
+
">=": "GreaterOrEqualThan",
|
|
44
|
+
">": "GreaterThan",
|
|
45
|
+
"<=": "LessOrEqualThan",
|
|
46
|
+
"<": "LessThan",
|
|
47
|
+
like: "Similar",
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const LOGICAL_BINARY_OPS: Record<string, Yu.LogicalBinaryOperator> = {
|
|
51
|
+
"&&": "And",
|
|
52
|
+
and: "And",
|
|
53
|
+
"||": "Or",
|
|
54
|
+
or: "Or",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const BITWISE_BINARY_OPS: Record<string, Yu.BitwiseBinaryOperator> = {
|
|
58
|
+
"|": "BitwiseOr",
|
|
59
|
+
"&": "BitwiseAnd",
|
|
60
|
+
"^": "BitwiseXor",
|
|
61
|
+
"<<": "BitwiseLeftShift",
|
|
62
|
+
">>": "BitwiseRightShift",
|
|
63
|
+
">>>": "BitwiseUnsignedRightShift",
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function mapLocation(wollokNode: Node): Yu.SourceLocation | undefined {
|
|
67
|
+
if (wollokNode.sourceMap && wollokNode.sourceMap.start) {
|
|
68
|
+
return new Yu.SourceLocation(
|
|
69
|
+
wollokNode.sourceMap.start.line,
|
|
70
|
+
wollokNode.sourceMap.start.column
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export class WollokToYukigoTransformer {
|
|
77
|
+
public transform(root: Package): Yu.AST {
|
|
78
|
+
const result = this.visit(root);
|
|
79
|
+
return Array.isArray(result) ? result : [result];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private visit(node: Node): any {
|
|
83
|
+
const nodeType = node.constructor ? node.constructor.name : "Unknown";
|
|
84
|
+
switch (nodeType) {
|
|
85
|
+
case "Package":
|
|
86
|
+
return this.visitPackage(node as Package);
|
|
87
|
+
case "Singleton":
|
|
88
|
+
return this.visitSingleton(node as Singleton);
|
|
89
|
+
case "Mixin":
|
|
90
|
+
case "Class":
|
|
91
|
+
return this.visitClass(node as Class | Mixin);
|
|
92
|
+
case "Method":
|
|
93
|
+
return this.visitMethod(node as Method);
|
|
94
|
+
case "Body":
|
|
95
|
+
return this.visitBody(node as Body);
|
|
96
|
+
case "Send":
|
|
97
|
+
return this.visitSend(node as Send);
|
|
98
|
+
case "Literal":
|
|
99
|
+
return this.visitLiteral(node as Literal);
|
|
100
|
+
case "Reference":
|
|
101
|
+
return this.visitReference(node as Reference<any>);
|
|
102
|
+
case "Parameter":
|
|
103
|
+
return this.visitParameter(node as Parameter);
|
|
104
|
+
case "Return":
|
|
105
|
+
return this.visitReturn(node as Return);
|
|
106
|
+
case "Variable":
|
|
107
|
+
return this.visitVariable(node as Variable);
|
|
108
|
+
case "New":
|
|
109
|
+
return this.visitNew(node as New);
|
|
110
|
+
case "If":
|
|
111
|
+
return this.visitIf(node as If);
|
|
112
|
+
case "Field":
|
|
113
|
+
return this.visitField(node as Field);
|
|
114
|
+
case "Assignment":
|
|
115
|
+
return this.visitAssignment(node as Assignment);
|
|
116
|
+
case "Throw":
|
|
117
|
+
return this.visitThrow(node as Throw);
|
|
118
|
+
case "Try":
|
|
119
|
+
return this.visitTry(node as Try);
|
|
120
|
+
case "Catch":
|
|
121
|
+
return this.visitCatch(node as Catch);
|
|
122
|
+
case "Super":
|
|
123
|
+
return this.visitSuper(node as Super);
|
|
124
|
+
default:
|
|
125
|
+
throw new Error(`Nodo Wollok desconocido o no soportado: ${nodeType}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private visitPackage(node: Package): Yu.Statement[] {
|
|
130
|
+
return [
|
|
131
|
+
...node.imports.map((imp) => this.visitImport(imp)),
|
|
132
|
+
...node.members.map((member: Node) => this.visit(member)),
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private visitImport(node: Import): Yu.Include {
|
|
137
|
+
const identifier = new Yu.SymbolPrimitive(
|
|
138
|
+
node.entity.name,
|
|
139
|
+
mapLocation(node)
|
|
140
|
+
);
|
|
141
|
+
return new Yu.Include(identifier, mapLocation(node));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private visitField(node: Field): Yu.Attribute {
|
|
145
|
+
const identifier = new Yu.SymbolPrimitive(node.name, mapLocation(node));
|
|
146
|
+
|
|
147
|
+
let value: Yu.Expression;
|
|
148
|
+
if (node.value) {
|
|
149
|
+
value = this.visit(node.value);
|
|
150
|
+
} else {
|
|
151
|
+
// default to Nil if no initial value provided
|
|
152
|
+
value = new Yu.NilPrimitive(null, mapLocation(node));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return new Yu.Attribute(identifier, value, mapLocation(node));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private visitVariable(node: Variable): Yu.Variable {
|
|
159
|
+
const identifier = new Yu.SymbolPrimitive(node.name, mapLocation(node));
|
|
160
|
+
|
|
161
|
+
let expression: Yu.Expression;
|
|
162
|
+
if (node.value) {
|
|
163
|
+
expression = this.visit(node.value);
|
|
164
|
+
} else {
|
|
165
|
+
expression = new Yu.NilPrimitive(null, mapLocation(node));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return new Yu.Variable(
|
|
169
|
+
identifier,
|
|
170
|
+
expression,
|
|
171
|
+
undefined,
|
|
172
|
+
mapLocation(node)
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private visitAssignment(node: Assignment): Yu.Assignment {
|
|
177
|
+
const refName = node.variable.name || node.variable;
|
|
178
|
+
const identifier = new Yu.SymbolPrimitive(
|
|
179
|
+
refName.toString(),
|
|
180
|
+
mapLocation(node)
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const expression = this.visit(node.value);
|
|
184
|
+
|
|
185
|
+
return new Yu.Assignment(identifier, expression, mapLocation(node));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private visitNew(node: New): Yu.New {
|
|
189
|
+
const className = new Yu.SymbolPrimitive(
|
|
190
|
+
node.instantiated.name,
|
|
191
|
+
mapLocation(node)
|
|
192
|
+
);
|
|
193
|
+
const args = (node.args || []).map((arg: any) => this.visit(arg));
|
|
194
|
+
|
|
195
|
+
return new Yu.New(className, args, mapLocation(node));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private visitIf(node: If): Yu.If {
|
|
199
|
+
const condition = this.visit(node.condition);
|
|
200
|
+
const thenExpr = this.visit(node.thenBody);
|
|
201
|
+
|
|
202
|
+
let elseExpr: Yu.Expression;
|
|
203
|
+
if (node.elseBody) {
|
|
204
|
+
elseExpr = this.visit(node.elseBody);
|
|
205
|
+
} else {
|
|
206
|
+
elseExpr = new Yu.Sequence([], mapLocation(node));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return new Yu.If(condition, thenExpr, elseExpr, mapLocation(node));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private visitThrow(node: Throw): Yu.Raise {
|
|
213
|
+
const exception = this.visit(node.exception);
|
|
214
|
+
return new Yu.Raise(exception, mapLocation(node));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private visitTry(node: Try): Yu.Try {
|
|
218
|
+
const body = this.visit(node.body);
|
|
219
|
+
|
|
220
|
+
const catchExprs: Yu.Catch[] = (node.catches || []).map((c: any) =>
|
|
221
|
+
this.visitCatch(c)
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
let finallyExpr: Yu.Expression;
|
|
225
|
+
if (node.always) {
|
|
226
|
+
finallyExpr = this.visit(node.always);
|
|
227
|
+
} else {
|
|
228
|
+
finallyExpr = new Yu.Sequence([], mapLocation(node));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return new Yu.Try(body, catchExprs, finallyExpr, mapLocation(node));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private visitCatch(node: Catch): Yu.Catch {
|
|
235
|
+
const paramName = node.parameter.name;
|
|
236
|
+
const pattern = new Yu.VariablePattern(
|
|
237
|
+
new Yu.SymbolPrimitive(paramName, mapLocation(node.parameter)),
|
|
238
|
+
mapLocation(node.parameter)
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
const body = this.visit(node.body);
|
|
242
|
+
|
|
243
|
+
return new Yu.Catch([pattern], body, mapLocation(node));
|
|
244
|
+
}
|
|
245
|
+
private visitSuper(node: Super): Yu.Super {
|
|
246
|
+
return new Yu.Super(mapLocation(node));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private visitSingleton(node: Singleton): Yu.Object {
|
|
250
|
+
const identifier = new Yu.SymbolPrimitive(node.name, mapLocation(node));
|
|
251
|
+
|
|
252
|
+
const members = node.members.map((m: Node) => this.visit(m));
|
|
253
|
+
const bodyExpression = new Yu.Sequence(members, mapLocation(node));
|
|
254
|
+
|
|
255
|
+
return new Yu.Object(identifier, bodyExpression, mapLocation(node));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private visitClass(node: Class | Mixin): Yu.Class {
|
|
259
|
+
const identifier = new Yu.SymbolPrimitive(node.name, mapLocation(node));
|
|
260
|
+
|
|
261
|
+
const members = node.members.map((m: any) => this.visit(m));
|
|
262
|
+
const bodyExpression = new Yu.Sequence(members, mapLocation(node));
|
|
263
|
+
const classInstance = new Yu.Class(
|
|
264
|
+
identifier,
|
|
265
|
+
undefined,
|
|
266
|
+
undefined,
|
|
267
|
+
bodyExpression,
|
|
268
|
+
mapLocation(node)
|
|
269
|
+
);
|
|
270
|
+
return classInstance;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private visitMethod(node: Method): Yu.Method {
|
|
274
|
+
const identifier = new Yu.SymbolPrimitive(node.name, mapLocation(node));
|
|
275
|
+
|
|
276
|
+
const patterns: Yu.Pattern[] = (node.parameters || []).map((param) =>
|
|
277
|
+
this.visit(param)
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
if (node.body === "native")
|
|
281
|
+
throw Error("Native methods not supported yet.");
|
|
282
|
+
|
|
283
|
+
const bodySequence = this.visit(node.body);
|
|
284
|
+
const unguardedBody = new Yu.UnguardedBody(
|
|
285
|
+
bodySequence,
|
|
286
|
+
mapLocation(node.body)
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
const equation = new Yu.Equation(
|
|
290
|
+
patterns,
|
|
291
|
+
unguardedBody,
|
|
292
|
+
undefined,
|
|
293
|
+
mapLocation(node)
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
return new Yu.Method(identifier, [equation], mapLocation(node));
|
|
297
|
+
}
|
|
298
|
+
private visitParameter(node: Parameter): Yu.VariablePattern {
|
|
299
|
+
const nameSymbol = new Yu.SymbolPrimitive(node.name, mapLocation(node));
|
|
300
|
+
return new Yu.VariablePattern(nameSymbol, mapLocation(node));
|
|
301
|
+
}
|
|
302
|
+
private visitReturn(node: Return): Yu.Return {
|
|
303
|
+
const expression = this.visit(node.value);
|
|
304
|
+
return new Yu.Return(expression, mapLocation(node));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private visitBody(node: Body): Yu.Sequence {
|
|
308
|
+
const statements = node.sentences.map((s: any) => this.visit(s));
|
|
309
|
+
return new Yu.Sequence(statements, mapLocation(node));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private visitSend(node: Send): Yu.Expression {
|
|
313
|
+
const receiver = this.visit(node.receiver);
|
|
314
|
+
const args = (node.args || []).map((arg: any) => this.visit(arg));
|
|
315
|
+
const op = node.message;
|
|
316
|
+
const loc = mapLocation(node);
|
|
317
|
+
|
|
318
|
+
if (args.length === 1) {
|
|
319
|
+
const binaryExpr = transformBinary(op, receiver, args[0], loc);
|
|
320
|
+
if (binaryExpr) return binaryExpr;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (args.length === 0) {
|
|
324
|
+
const unaryExpr = transformUnary(op, receiver, loc);
|
|
325
|
+
// note that binaryExpr and unaryExpr can be null if it transform functions cant match any operation to the selector
|
|
326
|
+
if (unaryExpr) return unaryExpr;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const selector = new Yu.SymbolPrimitive(op, loc);
|
|
330
|
+
return new Yu.Send(receiver, selector, args, loc);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private visitReference(node: Reference<any>): Yu.SymbolPrimitive | Yu.Self {
|
|
334
|
+
if (node.name === "self") return new Yu.Self(mapLocation(node));
|
|
335
|
+
return new Yu.SymbolPrimitive(node.name, mapLocation(node));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
private visitLiteral(node: Literal): Yu.Primitive {
|
|
339
|
+
const val = node.value;
|
|
340
|
+
const loc = mapLocation(node);
|
|
341
|
+
|
|
342
|
+
const isCollection =
|
|
343
|
+
Array.isArray(val) &&
|
|
344
|
+
"name" in val[0] &&
|
|
345
|
+
WollokListTypes.includes(val[0].name);
|
|
346
|
+
|
|
347
|
+
if (isCollection)
|
|
348
|
+
return new Yu.ListPrimitive(
|
|
349
|
+
val[1].map((element) => this.visit(element)),
|
|
350
|
+
loc
|
|
351
|
+
);
|
|
352
|
+
if (typeof val === "number") return new Yu.NumberPrimitive(val, loc);
|
|
353
|
+
if (typeof val === "boolean") return new Yu.BooleanPrimitive(val, loc);
|
|
354
|
+
if (val === null) return new Yu.NilPrimitive(null, loc);
|
|
355
|
+
|
|
356
|
+
return new Yu.StringPrimitive(String(val), loc);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const WollokListTypes = ["wollok.lang.List", "wollok.lang.Set"];
|
|
361
|
+
|
|
362
|
+
function transformBinary(
|
|
363
|
+
op: string,
|
|
364
|
+
left: Yu.Expression,
|
|
365
|
+
right: Yu.Expression,
|
|
366
|
+
loc: Yu.SourceLocation | undefined
|
|
367
|
+
): Yu.Expression | null {
|
|
368
|
+
// RangeExpression
|
|
369
|
+
if (op === "..") return new Yu.RangeExpression(left, right, undefined, loc);
|
|
370
|
+
|
|
371
|
+
// Arithmetic
|
|
372
|
+
if (op in ARITHMETIC_BINARY_OPS)
|
|
373
|
+
return new Yu.ArithmeticBinaryOperation(
|
|
374
|
+
ARITHMETIC_BINARY_OPS[op],
|
|
375
|
+
left,
|
|
376
|
+
right,
|
|
377
|
+
loc
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
// Comparison
|
|
381
|
+
if (op in COMPARISON_OPS)
|
|
382
|
+
return new Yu.ComparisonOperation(COMPARISON_OPS[op], left, right, loc);
|
|
383
|
+
|
|
384
|
+
// Logical
|
|
385
|
+
if (op in LOGICAL_BINARY_OPS)
|
|
386
|
+
return new Yu.LogicalBinaryOperation(
|
|
387
|
+
LOGICAL_BINARY_OPS[op],
|
|
388
|
+
left,
|
|
389
|
+
right,
|
|
390
|
+
loc
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
// Bitwise
|
|
394
|
+
if (op in BITWISE_BINARY_OPS)
|
|
395
|
+
return new Yu.BitwiseBinaryOperation(
|
|
396
|
+
BITWISE_BINARY_OPS[op],
|
|
397
|
+
left,
|
|
398
|
+
right,
|
|
399
|
+
loc
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
// String Concatenation
|
|
403
|
+
if (op === "++" || op === "concat")
|
|
404
|
+
return new Yu.StringOperation("Concat", left, right, loc);
|
|
405
|
+
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function transformUnary(
|
|
410
|
+
op: string,
|
|
411
|
+
receiver: Yu.Expression,
|
|
412
|
+
loc: Yu.SourceLocation | undefined
|
|
413
|
+
): Yu.Expression | null {
|
|
414
|
+
// Logical Negation
|
|
415
|
+
if (["!", "negate"].includes(op))
|
|
416
|
+
return new Yu.LogicalUnaryOperation("Negation", receiver, loc);
|
|
417
|
+
|
|
418
|
+
// Arithmetic Negation
|
|
419
|
+
if (["invert", "-"].includes(op))
|
|
420
|
+
return new Yu.ArithmeticUnaryOperation("Negation", receiver, loc);
|
|
421
|
+
|
|
422
|
+
// Bitwise Negation
|
|
423
|
+
if (["~", "bitwiseNot"].includes(op))
|
|
424
|
+
return new Yu.BitwiseUnaryOperation("BitwiseNot", receiver, loc);
|
|
425
|
+
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import * as Yu from "yukigo-ast";
|
|
3
|
+
import { WollokToYukigoTransformer } from "../src/transformer";
|
|
4
|
+
import {
|
|
5
|
+
Send,
|
|
6
|
+
Reference,
|
|
7
|
+
Literal,
|
|
8
|
+
Package,
|
|
9
|
+
Method,
|
|
10
|
+
Body,
|
|
11
|
+
Parameter,
|
|
12
|
+
Return,
|
|
13
|
+
Singleton,
|
|
14
|
+
} from "wollok-ts";
|
|
15
|
+
|
|
16
|
+
describe("WollokToYukigoTransformer - Native Operations", () => {
|
|
17
|
+
let transformer: WollokToYukigoTransformer;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
transformer = new WollokToYukigoTransformer();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const transformExpression = (node: any) => {
|
|
24
|
+
const pkg = new Package({
|
|
25
|
+
name: "test",
|
|
26
|
+
members: [
|
|
27
|
+
new Singleton({
|
|
28
|
+
members: [
|
|
29
|
+
new Method({
|
|
30
|
+
name: "t",
|
|
31
|
+
parameters: [],
|
|
32
|
+
body: new Body({ sentences: [new Return({ value: node })] }),
|
|
33
|
+
}),
|
|
34
|
+
],
|
|
35
|
+
}),
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
const res = transformer.transform(pkg) as Yu.AST;
|
|
39
|
+
const obj = res[0] as Yu.Object;
|
|
40
|
+
const seq = obj.expression as Yu.Sequence;
|
|
41
|
+
const method = seq.statements[0] as Yu.Method;
|
|
42
|
+
const body = method.equations[0].body as Yu.UnguardedBody;
|
|
43
|
+
const returnExpr = body.sequence.statements[0] as Yu.Return;
|
|
44
|
+
return returnExpr.body;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const ref = (n) => new Reference({ name: n });
|
|
48
|
+
const lit = (v) => new Literal({ value: v });
|
|
49
|
+
const send = (rec, msg, args = []) =>
|
|
50
|
+
new Send({ receiver: rec, message: msg, args });
|
|
51
|
+
|
|
52
|
+
describe("Arithmetic Operations", () => {
|
|
53
|
+
it("should transform '+' to ArithmeticPlus", () => {
|
|
54
|
+
const input = send(ref("a"), "+", [lit(1)]);
|
|
55
|
+
const result = transformExpression(input) as Yu.ArithmeticBinaryOperation;
|
|
56
|
+
|
|
57
|
+
expect(result).to.be.instanceOf(Yu.ArithmeticBinaryOperation);
|
|
58
|
+
expect(result.operator).to.equal("Plus");
|
|
59
|
+
expect((result.left as Yu.SymbolPrimitive).value).to.equal("a");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should transform '/' to ArithmeticDivide", () => {
|
|
63
|
+
const input = send(lit(10), "/", [lit(2)]);
|
|
64
|
+
const result = transformExpression(input) as Yu.ArithmeticBinaryOperation;
|
|
65
|
+
|
|
66
|
+
expect(result).to.be.instanceOf(Yu.ArithmeticBinaryOperation);
|
|
67
|
+
expect(result.operator).to.equal("Divide");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should transform '**' to ArithmeticPower", () => {
|
|
71
|
+
const input = send(ref("x"), "**", [lit(2)]);
|
|
72
|
+
const result = transformExpression(input) as Yu.ArithmeticBinaryOperation;
|
|
73
|
+
|
|
74
|
+
expect(result).to.be.instanceOf(Yu.ArithmeticBinaryOperation);
|
|
75
|
+
expect(result.operator).to.equal("Power");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("should transform unary '-' (invert) to Arithmetic Negation", () => {
|
|
79
|
+
const input = send(ref("x"), "invert", []); // Assuming 'invert' or '-' for unary
|
|
80
|
+
const result = transformExpression(input) as Yu.ArithmeticUnaryOperation;
|
|
81
|
+
|
|
82
|
+
expect(result).to.be.instanceOf(Yu.ArithmeticUnaryOperation);
|
|
83
|
+
expect(result.operator).to.equal("Negation");
|
|
84
|
+
expect((result.operand as Yu.SymbolPrimitive).value).to.equal("x");
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("Comparison Operations", () => {
|
|
89
|
+
it("should transform '==' to ComparisonEqual", () => {
|
|
90
|
+
const input = send(ref("a"), "==", [ref("b")]);
|
|
91
|
+
const result = transformExpression(input) as Yu.ComparisonOperation;
|
|
92
|
+
|
|
93
|
+
expect(result).to.be.instanceOf(Yu.ComparisonOperation);
|
|
94
|
+
expect(result.operator).to.equal("Equal");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should transform '>=' to GreaterOrEqualThan", () => {
|
|
98
|
+
const input = send(ref("age"), ">=", [lit(18)]);
|
|
99
|
+
const result = transformExpression(input) as Yu.ComparisonOperation;
|
|
100
|
+
|
|
101
|
+
expect(result).to.be.instanceOf(Yu.ComparisonOperation);
|
|
102
|
+
expect(result.operator).to.equal("GreaterOrEqualThan");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe("Logical Operations", () => {
|
|
107
|
+
it("should transform '&&' to LogicalAnd", () => {
|
|
108
|
+
const input = send(ref("valid"), "&&", [ref("ready")]);
|
|
109
|
+
const result = transformExpression(input) as Yu.LogicalBinaryOperation;
|
|
110
|
+
|
|
111
|
+
expect(result).to.be.instanceOf(Yu.LogicalBinaryOperation);
|
|
112
|
+
expect(result.operator).to.equal("And");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should transform 'or' keyword to LogicalOr", () => {
|
|
116
|
+
const input = send(ref("x"), "or", [ref("y")]);
|
|
117
|
+
const result = transformExpression(input) as Yu.LogicalBinaryOperation;
|
|
118
|
+
|
|
119
|
+
expect(result).to.be.instanceOf(Yu.LogicalBinaryOperation);
|
|
120
|
+
expect(result.operator).to.equal("Or");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should transform unary 'negate' to LogicalNegation", () => {
|
|
124
|
+
const input = send(ref("found"), "negate", []);
|
|
125
|
+
const result = transformExpression(input) as Yu.LogicalUnaryOperation;
|
|
126
|
+
|
|
127
|
+
expect(result).to.be.instanceOf(Yu.LogicalUnaryOperation);
|
|
128
|
+
expect(result.operator).to.equal("Negation");
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe("Bitwise Operations", () => {
|
|
133
|
+
it("should transform '|' to BitwiseOr", () => {
|
|
134
|
+
const input = send(lit(1), "|", [lit(2)]);
|
|
135
|
+
const result = transformExpression(input) as Yu.BitwiseBinaryOperation;
|
|
136
|
+
|
|
137
|
+
expect(result).to.be.instanceOf(Yu.BitwiseBinaryOperation);
|
|
138
|
+
expect(result.operator).to.equal("BitwiseOr");
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe("Fallback", () => {
|
|
143
|
+
it("should treat unknown operators as standard Send", () => {
|
|
144
|
+
// 'customOp' is not in our maps
|
|
145
|
+
const input = send(ref("a"), "customOp", [ref("b")]);
|
|
146
|
+
const result = transformExpression(input) as Yu.Send;
|
|
147
|
+
|
|
148
|
+
expect(result).to.be.instanceOf(Yu.Send);
|
|
149
|
+
expect((result.selector as Yu.SymbolPrimitive).value).to.equal(
|
|
150
|
+
"customOp"
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|