1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3;
18
19 import org.apache.commons.jexl3.internal.Closure;
20 import org.apache.commons.jexl3.internal.Script;
21 import org.junit.Assert;
22 import org.junit.Test;
23
24 import java.util.Arrays;
25 import java.util.List;
26 import java.util.Set;
27 import java.util.concurrent.Callable;
28
29
30
31
32 @SuppressWarnings({"AssertEqualsBetweenInconvertibleTypes"})
33 public class LambdaTest extends JexlTestCase {
34
35 public LambdaTest() {
36 super("LambdaTest");
37 }
38
39 @Test
40 public void testScriptArguments() {
41 final JexlEngine jexl = createEngine();
42 final JexlScript s = jexl.createScript(" x + x ", "x");
43 final JexlScript s42 = jexl.createScript("s(21)", "s");
44 final Object result = s42.execute(null, s);
45 Assert.assertEquals(42, result);
46 }
47
48 @Test
49 public void testScriptContext() {
50 final JexlEngine jexl = createEngine();
51 final JexlScript s = jexl.createScript("function(x) { x + x }");
52 final String fsstr = s.getParsedText(0);
53 Assert.assertEquals("(x)->{ x + x; }", fsstr);
54 Assert.assertEquals(42, s.execute(null, 21));
55 JexlScript s42 = jexl.createScript("s(21)");
56 final JexlContext ctxt = new JexlEvalContext();
57 ctxt.set("s", s);
58 Object result = s42.execute(ctxt);
59 Assert.assertEquals(42, result);
60 result = s42.execute(ctxt);
61 Assert.assertEquals(42, result);
62 s42 = jexl.createScript("x-> { x + x }");
63 result = s42.execute(ctxt, 21);
64 Assert.assertEquals(42, result);
65 }
66
67 @Test
68 public void testLambda() {
69 final JexlEngine jexl = createEngine();
70 String strs = "var s = function(x) { x + x }; s(21)";
71 JexlScript s42 = jexl.createScript(strs);
72 Object result = s42.execute(null);
73 Assert.assertEquals(42, result);
74 strs = "var s = function(x, y) { x + y }; s(15, 27)";
75 s42 = jexl.createScript(strs);
76 result = s42.execute(null);
77 Assert.assertEquals(42, result);
78 }
79
80 @Test
81 public void testLambdaClosure() {
82 final JexlEngine jexl = createEngine();
83 String strs = "var t = 20; var s = function(x, y) { x + y + t}; s(15, 7)";
84 JexlScript s42 = jexl.createScript(strs);
85 Object result = s42.execute(null);
86 Assert.assertEquals(42, result);
87 strs = "var t = 19; var s = function(x, y) { var t = 20; x + y + t}; s(15, 7)";
88 s42 = jexl.createScript(strs);
89 result = s42.execute(null);
90 Assert.assertEquals(42, result);
91 strs = "var t = 20; var s = function(x, y) {x + y + t}; t = 54; s(15, 7)";
92 s42 = jexl.createScript(strs);
93 result = s42.execute(null);
94 Assert.assertEquals(42, result);
95 strs = "var t = 19; var s = function(x, y) { var t = 20; x + y + t}; t = 54; s(15, 7)";
96 s42 = jexl.createScript(strs);
97 result = s42.execute(null);
98 Assert.assertEquals(42, result);
99 }
100
101 @Test
102 public void testLambdaLambda() {
103 final JexlEngine jexl = createEngine();
104 String strs = "var t = 19; ( (x, y)->{ var t = 20; x + y + t} )(15, 7);";
105 JexlScript s42 = jexl.createScript(strs);
106 Object result = s42.execute(null);
107 Assert.assertEquals(42, result);
108
109 strs = "( (x, y)->{ ( (xx, yy)->{xx + yy } )(x, y) } )(15, 27)";
110 s42 = jexl.createScript(strs);
111 result = s42.execute(null);
112 Assert.assertEquals(42, result);
113
114 strs = "var t = 19; var s = (x, y)->{ var t = 20; x + y + t}; t = 54; s(15, 7)";
115 s42 = jexl.createScript(strs);
116 result = s42.execute(null);
117 Assert.assertEquals(42, result);
118 }
119
120 @Test
121 public void testNestLambda() {
122 final JexlEngine jexl = createEngine();
123 final String strs = "( (x)->{ (y)->{ x + y } })(15)(27)";
124 final JexlScript s42 = jexl.createScript(strs);
125 final Object result = s42.execute(null);
126 Assert.assertEquals(42, result);
127 }
128
129 @Test
130 public void testNestLambada() throws Exception {
131 final JexlEngine jexl = createEngine();
132 final String strs = "(x)->{ (y)->{ x + y } }";
133 final JexlScript s42 = jexl.createScript(strs);
134 final JexlScript s42b = jexl.createScript(s42.toString());
135 Assert.assertEquals(s42.hashCode(), s42b.hashCode());
136 Assert.assertEquals(s42, s42b);
137 Object result = s42.execute(null, 15);
138 Assert.assertTrue(result instanceof JexlScript);
139 final Object resultb = s42.execute(null, 15);
140 Assert.assertEquals(result.hashCode(), resultb.hashCode());
141 Assert.assertEquals(result, resultb);
142 Assert.assertEquals(result, jexl.createScript(resultb.toString(), "x").execute(null, 15));
143 final JexlScript s15 = (JexlScript) result;
144 final Callable<Object> s15b = s15.callable(null, 27);
145 result = s15.execute(null, 27);
146 Assert.assertEquals(42, result);
147 result = s15b.call();
148 Assert.assertEquals(42, result);
149 }
150
151 @Test
152 public void testCompareLambdaRecurse() throws Exception {
153 final JexlEngine jexl = createEngine();
154 final String factSrc = "function fact(x) { x < 2? 1 : x * fact(x - 1) }";
155 final JexlScript fact0 = jexl.createScript(factSrc);
156 final JexlScript fact1 = jexl.createScript(fact0.toString());
157 Assert.assertEquals(fact0, fact1);
158 Closure r0 = (Closure) fact0.execute(null);
159 Closure r1 = (Closure) fact1.execute(null);
160 Assert.assertEquals(720, r0.execute(null, 6));
161 Assert.assertEquals(720, r1.execute(null, 6));
162 Assert.assertEquals(r0, r1);
163 Assert.assertEquals(r1, r0);
164
165 Assert.assertEquals(720, r0.execute(null, 6));
166 Assert.assertEquals(720, r1.execute(null, 6));
167 }
168
169 @Test
170 public void testHoistLambda() {
171 final JexlEngine jexl = createEngine();
172 final JexlEvalContext ctx = new JexlEvalContext();
173 ctx.getEngineOptions().setLexical(false);
174 JexlScript s42;
175 Object result;
176 JexlScript s15;
177 String[] localv;
178 Set<List<String>> hvars;
179 String strs;
180
181
182 strs = "(x)->{ (y)->{ x + y } }";
183 s42 = jexl.createScript(strs);
184 result = s42.execute(ctx, 15);
185 Assert.assertTrue(result instanceof JexlScript);
186 s15 = (JexlScript) result;
187 localv = s15.getLocalVariables();
188 Assert.assertEquals(0, localv.length);
189 hvars = s15.getVariables();
190 Assert.assertEquals(1, hvars.size());
191
192
193
194
195 strs = "(x)->{ (y)->{ var z = 169; var x; x + y } }";
196 s42 = jexl.createScript(strs);
197 result = s42.execute(ctx, 15);
198 Assert.assertTrue(result instanceof JexlScript);
199 s15 = (JexlScript) result;
200 localv = s15.getLocalVariables();
201 Assert.assertNotNull(localv);
202 Assert.assertEquals(1, localv.length);
203 hvars = s15.getVariables();
204 Assert.assertEquals(1, hvars.size());
205
206 result = s15.execute(ctx, 27);
207 Assert.assertEquals(42, result);
208 }
209
210 @Test
211 public void testRecurse() {
212 final JexlEngine jexl = createEngine();
213 final JexlContext jc = new MapContext();
214 final JexlScript script = jexl.createScript("var fact = (x)->{ if (x <= 1) 1; else x * fact(x - 1) }; fact(5)");
215 final int result = (Integer) script.execute(jc);
216 Assert.assertEquals(120, result);
217 }
218
219 @Test
220 public void testRecurse1() {
221 final JexlEngine jexl = createEngine();
222 final JexlContext jc = new MapContext();
223 String src = "var fact = (x)-> x <= 1? 1 : x * fact(x - 1);\nfact(5);\n";
224 final JexlScript script = jexl.createScript(src);
225 final int result = (Integer) script.execute(jc);
226 Assert.assertEquals(120, result);
227 String parsed = script.getParsedText();
228 Assert.assertEquals(src, parsed);
229 }
230
231 @Test
232 public void testRecurse2() {
233 final JexlEngine jexl = createEngine();
234 final JexlContext jc = new MapContext();
235
236 final JexlScript script = jexl.createScript(
237 "var y = 1; var z = 1; "
238 +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) }; fact(6)");
239 final int result = (Integer) script.execute(jc);
240 Assert.assertEquals(720, result);
241 }
242
243 @Test
244 public void testRecurse2b() {
245 final JexlEngine jexl = createEngine();
246 final JexlContext jc = new MapContext();
247
248 final JexlScript fact = jexl.createScript(
249 "var y = 1; var z = 1; "
250 +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) };" +
251 "fact");
252 Script func = (Script) fact.execute(jc);
253 String[] captured = func.getCapturedVariables();
254 Assert.assertEquals(3, captured.length);
255 Assert.assertTrue(Arrays.asList(captured).containsAll(Arrays.asList("z", "y", "fact")));
256 final int result = (Integer) func.execute(jc, 6);
257 Assert.assertEquals(720, result);
258 }
259
260 @Test
261 public void testRecurse3() {
262 final JexlEngine jexl = createEngine();
263 final JexlContext jc = new MapContext();
264
265 final JexlScript script = jexl.createScript(
266 "var y = 1; var z = 1;var foo = (x)->{y + z}; "
267 +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) }; fact(6)");
268 final int result = (Integer) script.execute(jc);
269 Assert.assertEquals(720, result);
270 }
271
272 @Test
273 public void testIdentity() {
274 final JexlEngine jexl = createEngine();
275 JexlScript script;
276 Object result;
277
278 script = jexl.createScript("(x)->{ x }");
279 Assert.assertArrayEquals(new String[]{"x"}, script.getParameters());
280 result = script.execute(null, 42);
281 Assert.assertEquals(42, result);
282 }
283
284 @Test
285 public void testCurry1() {
286 final JexlEngine jexl = createEngine();
287 JexlScript script;
288 Object result;
289 String[] parms;
290
291 final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
292 parms = base.getUnboundParameters();
293 Assert.assertEquals(3, parms.length);
294 script = base.curry(5);
295 parms = script.getUnboundParameters();
296 Assert.assertEquals(2, parms.length);
297 script = script.curry(15);
298 parms = script.getUnboundParameters();
299 Assert.assertEquals(1, parms.length);
300 script = script.curry(22);
301 parms = script.getUnboundParameters();
302 Assert.assertEquals(0, parms.length);
303 result = script.execute(null);
304 Assert.assertEquals(42, result);
305 }
306
307 @Test
308 public void testCurry2() {
309 final JexlEngine jexl = createEngine();
310 JexlScript script;
311 Object result;
312 String[] parms;
313
314 final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
315 script = base.curry(5, 15);
316 parms = script.getUnboundParameters();
317 Assert.assertEquals(1, parms.length);
318 script = script.curry(22);
319 result = script.execute(null);
320 Assert.assertEquals(42, result);
321 }
322
323 @Test
324 public void testCurry3() {
325 final JexlEngine jexl = createEngine();
326 JexlScript script;
327 Object result;
328
329 final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
330 script = base.curry(5, 15);
331 result = script.execute(null, 22);
332 Assert.assertEquals(42, result);
333 }
334
335 @Test
336 public void testCurry4() {
337 final JexlEngine jexl = createEngine();
338 JexlScript script;
339 Object result;
340
341 final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
342 script = base.curry(5);
343 result = script.execute(null, 15, 22);
344 Assert.assertEquals(42, result);
345 }
346
347 @Test
348 public void testCurry5() {
349 final JexlEngine jexl = createEngine();
350 JexlScript script;
351 Object result;
352
353 final JexlScript base = jexl.createScript("var t = x + y + z; return t", "x", "y", "z");
354 script = base.curry(5);
355 result = script.execute(null, 15, 22);
356 Assert.assertEquals(42, result);
357 }
358
359 @Test
360 public void test270() {
361 final JexlEngine jexl = createEngine();
362 final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
363 final String text = base.toString();
364 JexlScript script = base.curry(5, 15);
365 Assert.assertEquals(text, script.toString());
366
367 final JexlContext ctxt = new JexlEvalContext();
368 ctxt.set("s", base);
369 script = jexl.createScript("return s");
370 Object result = script.execute(ctxt);
371 Assert.assertEquals(text, result.toString());
372
373 script = jexl.createScript("return s.curry(1)");
374 result = script.execute(ctxt);
375 Assert.assertEquals(text, result.toString());
376 }
377
378 @Test
379 public void test271a() {
380 final JexlEngine jexl = createEngine();
381 final JexlScript base = jexl.createScript("var base = 1; var x = (a)->{ var y = (b) -> {base + b}; return base + y(a)}; x(40)");
382 final Object result = base.execute(null);
383 Assert.assertEquals(42, result);
384 }
385
386 @Test
387 public void test271b() {
388 final JexlEngine jexl = createEngine();
389 final JexlScript base = jexl.createScript("var base = 2; var sum = (x, y, z)->{ base + x + y + z }; var y = sum.curry(1); y(2,3)");
390 final Object result = base.execute(null);
391 Assert.assertEquals(8, result);
392 }
393
394 @Test
395 public void test271c() {
396 final JexlEngine jexl = createEngine();
397 final JexlScript base = jexl.createScript("(x, y, z)->{ 2 + x + y + z };");
398 final JexlScript y = base.curry(1);
399 final Object result = y.execute(null, 2, 3);
400 Assert.assertEquals(8, result);
401 }
402
403 @Test
404 public void test271d() {
405 final JexlEngine jexl = createEngine();
406 final JexlScript base = jexl.createScript("var base = 2; (x, y, z)->base + x + y + z;");
407 final JexlScript y = ((JexlScript) base.execute(null)).curry(1);
408 final Object result = y.execute(null, 2, 3);
409 Assert.assertEquals(8, result);
410 }
411
412
413
414 @Test
415 public void test271e() {
416 JexlEngine jexl = createEngine();
417 JexlScript base = jexl.createScript("var base = 1000; var f = (x, y)->{ var base = x + y + (base?:-1000); base; }; f(100, 20)");
418 Object result = base.execute(null);
419 Assert.assertEquals(1120, result);
420 }
421
422 @Test public void testFatFact0() {
423 JexlFeatures features = new JexlFeatures();
424 features.fatArrow(true);
425 String src = "function (a) { const fact = (x)=>{ x <= 1? 1 : x * fact(x - 1) }; fact(a) }";
426 JexlEngine jexl = createEngine(features);
427 JexlScript script = jexl.createScript(src);
428 Object result = script.execute(null, 6);
429 Assert.assertEquals(720, result);
430 }
431
432 @Test public void testFatFact1() {
433 String src = "function (a) { const fact = (x)=> x <= 1? 1 : x * fact(x - 1) ; fact(a) }";
434 JexlFeatures features = new JexlFeatures();
435 features.fatArrow(true);
436 JexlEngine jexl = createEngine(features);
437 JexlScript script = jexl.createScript(src);
438 Object result = script.execute(null, 6);
439 Assert.assertEquals(720, result);
440 features.fatArrow(false);
441 jexl = createEngine(features);
442 try {
443 script = jexl.createScript(src);
444 } catch(JexlException.Feature xfeature) {
445 Assert.assertTrue(xfeature.getMessage().contains("fat-arrow"));
446 }
447 }
448
449 @Test public void testNamedFunc() {
450 String src = "(let a)->{ function fact(const x) { x <= 1? 1 : x * fact(x - 1); } fact(a); }";
451 JexlEngine jexl = createEngine();
452 JexlScript script = jexl.createScript(src);
453 Object result = script.execute(null, 6);
454 Assert.assertEquals(720, result);
455 String parsed = simpleWhitespace(script.getParsedText());
456 Assert.assertEquals(simpleWhitespace(src), parsed);
457 }
458
459 @Test public void testNamedFuncIsConst() {
460 String src = "function foo(x) { x + x }; var foo ='nonononon'";
461 JexlEngine jexl = createEngine();
462 try {
463 JexlScript script = jexl.createScript(src);
464 Assert.fail("should fail, foo is already defined");
465 } catch(JexlException.Parsing xparse) {
466 Assert.assertTrue(xparse.getMessage().contains("foo"));
467 }
468 }
469 @Test
470 public void testFailParseFunc0() {
471 String src = "if (false) function foo(x) { x + x }; var foo = 1";
472 JexlEngine jexl = createEngine();
473 try {
474 JexlScript script = jexl.createScript(src);
475 } catch(JexlException.Parsing xparse) {
476 Assert.assertTrue(xparse.getMessage().contains("function"));
477 }
478 }
479
480 @Test
481 public void testFailParseFunc1() {
482 String src = "if (false) let foo = (x) { x + x }; var foo = 1";
483 JexlEngine jexl = createEngine();
484 try {
485 JexlScript script = jexl.createScript(src);
486 } catch(JexlException.Parsing xparse) {
487 Assert.assertTrue(xparse.getMessage().contains("let"));
488 }
489 }
490
491 @Test public void testLambdaExpr0() {
492 String src = "(x, y) -> x + y";
493 JexlEngine jexl = createEngine();
494 JexlScript script = jexl.createScript(src);
495 Object result = script.execute(null, 11, 31);
496 Assert.assertEquals(42, result);
497 }
498
499 @Test public void testLambdaExpr1() {
500 String src = "x -> x + x";
501 JexlEngine jexl = createEngine();
502 JexlScript script = jexl.createScript(src);
503 Object result = script.execute(null, 21);
504 Assert.assertEquals(42, result);
505 }
506
507 @Test public void testLambdaExpr10() {
508 String src = "(a)->{ var x = x -> x + x; x(a) }";
509 JexlEngine jexl = createEngine();
510 JexlScript script = jexl.createScript(src);
511 Object result = script.execute(null, 21);
512 Assert.assertEquals(42, result);
513 }
514
515 @Test public void testLambdaExpr2() {
516 String src = "x -> { { x + x } }";
517 JexlEngine jexl = createEngine();
518 JexlScript script = jexl.createScript(src);
519 Object result = script.execute(null, 21);
520 Assert.assertTrue(result instanceof Set);
521 Set<?> set = (Set<?>) result;
522 Assert.assertEquals(1, set.size());
523 Assert.assertTrue(set.contains(42));
524 }
525
526 @Test public void testLambdaExpr3() {
527 String src = "x -> ( { x + x } )";
528 JexlEngine jexl = createEngine();
529 JexlScript script = jexl.createScript(src);
530 Object result = script.execute(null, 21);
531 Assert.assertTrue(result instanceof Set);
532 Set<?> set = (Set<?>) result;
533 Assert.assertEquals(1, set.size());
534 Assert.assertTrue(set.contains(42));
535 }
536 }