delorean_lang 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Delorean
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.0'
5
5
  end
@@ -1342,4 +1342,394 @@ eof
1342
1342
  r = engine.evaluate('B', ['x', 'y', 'xx', 'yy'])
1343
1343
  expect(r).to eq [5, 2460, 128, 1230]
1344
1344
  end
1345
+
1346
+ describe 'blocks' do
1347
+ let(:default_node) do
1348
+ ['A:',
1349
+ ' array = [1, 2, 3]',
1350
+ " hash = {'a': 1, 'b': 2, 'c': 3}",
1351
+ ]
1352
+ end
1353
+
1354
+ it 'evaluates on arrays' do
1355
+ engine.parse defn(*default_node,
1356
+ ' b = array.any()',
1357
+ ' item =?',
1358
+ ' result = item > 10',
1359
+ ' c = array.any()',
1360
+ ' x =?',
1361
+ ' result = x > 1',
1362
+ ' d = array.select',
1363
+ ' x =?',
1364
+ ' result = x > 1',
1365
+ ' e = array.any',
1366
+ ' item =?',
1367
+ ' result = item > 10',
1368
+ ' f = array.any',
1369
+ ' x =?',
1370
+ ' result = x > 1',
1371
+ ' g = array.any?',
1372
+ ' result = nil',
1373
+ ' h = array.any',
1374
+ ' result = nil',
1375
+ )
1376
+
1377
+ r = engine.evaluate('A', 'b')
1378
+ expect(r).to eq(false)
1379
+
1380
+ r = engine.evaluate('A', 'c')
1381
+ expect(r).to eq(true)
1382
+
1383
+ r = engine.evaluate('A', 'd')
1384
+ expect(r).to eq([2, 3])
1385
+
1386
+ r = engine.evaluate('A', 'e')
1387
+ expect(r).to eq(false)
1388
+
1389
+ r = engine.evaluate('A', 'f')
1390
+ expect(r).to eq(true)
1391
+
1392
+ r = engine.evaluate('A', 'g')
1393
+ expect(r).to eq(false)
1394
+ end
1395
+
1396
+ it 'works with question mark in methods' do
1397
+ engine.parse defn(*default_node,
1398
+ ' b = array.any?',
1399
+ ' item =?',
1400
+ ' result = item > 10',
1401
+ )
1402
+ r = engine.evaluate('A', 'b')
1403
+ expect(r).to eq(false)
1404
+ end
1405
+
1406
+ it 'raises parse error if result formula is not present in block' do
1407
+ expect do
1408
+ engine.parse defn(*default_node,
1409
+ ' b = array.any?',
1410
+ ' item =?',
1411
+ ' wrong = item > 10',
1412
+ )
1413
+ end.to raise_error(
1414
+ Delorean::ParseError,
1415
+ /result formula is required in blocks/
1416
+ )
1417
+ end
1418
+
1419
+ # it 'chains method calls on block' do
1420
+ # engine.parse defn(*default_node,
1421
+ # ' b = array.select { |b| b > 2 }.last',
1422
+ # ' c = array.select { |b| ',
1423
+ # ' b > 2 ||',
1424
+ # ' b <= 1 ',
1425
+ # ' }.first',
1426
+ # )
1427
+ #
1428
+ # r = engine.evaluate('A', 'b')
1429
+ # expect(r).to eq(3)
1430
+ #
1431
+ # r = engine.evaluate('A', 'c')
1432
+ # expect(r).to eq(1)
1433
+ # end
1434
+
1435
+ it 'evaluates on hashes' do
1436
+ engine.parse defn(*default_node,
1437
+ ' b = hash.any()',
1438
+ ' key =?',
1439
+ ' val =?',
1440
+ ' result = val > 10',
1441
+ ' c = hash.any()',
1442
+ ' key =?',
1443
+ ' val =?',
1444
+ ' result = val > 2',
1445
+ ' d = hash.select()',
1446
+ ' key =?',
1447
+ ' val =?',
1448
+ " result = key == 'a' || val == 2",
1449
+ ' e = hash.select()',
1450
+ ' key =?',
1451
+ ' val =?',
1452
+ " result = key == 'c' || key == 'b'",
1453
+ )
1454
+
1455
+ r = engine.evaluate('A', 'b')
1456
+ expect(r).to eq(false)
1457
+
1458
+ r = engine.evaluate('A', 'c')
1459
+ expect(r).to eq(true)
1460
+
1461
+ r = engine.evaluate('A', 'd')
1462
+ expect(r).to eq('a' => 1, 'b' => 2)
1463
+
1464
+ r = engine.evaluate('A', 'e')
1465
+ expect(r).to eq('b' => 2, 'c' => 3)
1466
+ end
1467
+
1468
+ it 'whitelisting still works inside of block' do
1469
+ engine.parse defn(*default_node,
1470
+ ' b = array.any()',
1471
+ ' item =?',
1472
+ ' result = array[1] != 2',
1473
+ ' c = array.any()',
1474
+ ' x =?',
1475
+ ' result = ActiveRecord::Base.all()',
1476
+ ' d = array.reject',
1477
+ ' e = d.with_index()',
1478
+ ' v =?',
1479
+ ' result = nil',
1480
+ )
1481
+
1482
+ r = engine.evaluate('A', 'b')
1483
+ expect(r).to eq(false)
1484
+
1485
+ expect { engine.evaluate('A', 'c') }.to raise_error(
1486
+ RuntimeError, 'no such method all'
1487
+ )
1488
+
1489
+ expect { engine.evaluate('A', 'e') }.to raise_error(
1490
+ RuntimeError, 'no such method with_index'
1491
+ )
1492
+ end
1493
+
1494
+ it 'variables in blocks do not overwrite external variables' do
1495
+ engine.parse defn(*default_node,
1496
+ ' b = 1',
1497
+ ' c = 2',
1498
+ ' d = array.select',
1499
+ ' b =?',
1500
+ ' result = b + c',
1501
+ )
1502
+
1503
+ r = engine.evaluate('A', ['d', 'b', 'c'])
1504
+ expect(r).to eq([[1, 2, 3], 1, 2])
1505
+ end
1506
+
1507
+ it 'works with default block parameter values' do
1508
+ engine.parse defn(*default_node,
1509
+ ' b = 1',
1510
+ ' d = array.select',
1511
+ ' b =?',
1512
+ ' c =? 2',
1513
+ ' result = b > c',
1514
+ )
1515
+
1516
+ r = engine.evaluate('A', ['d', 'b'])
1517
+ expect(r).to eq([[3], 1])
1518
+ end
1519
+
1520
+ it 'works with syntax in blocks' do
1521
+ engine.parse defn(*default_node,
1522
+ ' b = 1',
1523
+ ' c = array.select ',
1524
+ ' b =? 1',
1525
+ ' result = b > 2',
1526
+ ' d = array.select ',
1527
+ ' b =?',
1528
+ ' result = b > 2',
1529
+ ' e = array.select ',
1530
+ ' result = b > 2',
1531
+ ' t = 1',
1532
+ ' f = 3'
1533
+ )
1534
+
1535
+ r = engine.evaluate('A', 'c')
1536
+ expect(r).to eq([3])
1537
+
1538
+ r = engine.evaluate('A', 'd')
1539
+ expect(r).to eq([3])
1540
+
1541
+ r = engine.evaluate('A', 'e')
1542
+ expect(r).to eq([])
1543
+
1544
+ r = engine.evaluate('A', ['e', 'b'])
1545
+ expect(r).to eq([[], 1])
1546
+ end
1547
+
1548
+ describe 'methods' do
1549
+ it 'all?' do
1550
+ engine.parse defn(*default_node,
1551
+ ' b = array.all?',
1552
+ ' num =?',
1553
+ ' result = num > 1',
1554
+ ' c = hash.all?',
1555
+ ' key =?',
1556
+ ' val =?',
1557
+ ' result = key.length() == 1',
1558
+ )
1559
+
1560
+ r = engine.evaluate('A', 'b')
1561
+ expect(r).to eq(false)
1562
+
1563
+ r = engine.evaluate('A', 'c')
1564
+ expect(r).to eq(true)
1565
+ end
1566
+
1567
+ it 'any?' do
1568
+ engine.parse defn(*default_node,
1569
+ ' b = array.any?',
1570
+ ' num =?',
1571
+ ' result = num > 4',
1572
+ ' c = hash.any?',
1573
+ ' key =?',
1574
+ ' val =?',
1575
+ ' result = val >= 2',
1576
+ )
1577
+
1578
+ r = engine.evaluate('A', 'b')
1579
+ expect(r).to eq(false)
1580
+
1581
+ r = engine.evaluate('A', 'c')
1582
+ expect(r).to eq(true)
1583
+ end
1584
+
1585
+ it 'find' do
1586
+ engine.parse defn(*default_node,
1587
+ ' b = array.find',
1588
+ ' num =?',
1589
+ ' result = num == 2',
1590
+ ' c = hash.find',
1591
+ ' key =?',
1592
+ ' val =?',
1593
+ ' result = key == "b"',
1594
+ )
1595
+
1596
+ r = engine.evaluate('A', 'b')
1597
+ expect(r).to eq(2)
1598
+
1599
+ r = engine.evaluate('A', 'c')
1600
+ expect(r).to eq(['b', 2])
1601
+ end
1602
+
1603
+ it 'max_by' do
1604
+ engine.parse defn(*default_node,
1605
+ ' b = array.max_by',
1606
+ ' num =?',
1607
+ ' result = - num',
1608
+ ' c = hash.max_by',
1609
+ ' key =?',
1610
+ ' val =?',
1611
+ ' result = val',
1612
+ )
1613
+
1614
+ r = engine.evaluate('A', 'b')
1615
+ expect(r).to eq(1)
1616
+
1617
+ r = engine.evaluate('A', 'c')
1618
+ expect(r).to eq(['c', 3])
1619
+ end
1620
+
1621
+ it 'min_by' do
1622
+ engine.parse defn(*default_node,
1623
+ ' b = array.min_by',
1624
+ ' num =?',
1625
+ ' result = - num',
1626
+ ' c = hash.min_by',
1627
+ ' key =?',
1628
+ ' val =?',
1629
+ ' result = val',
1630
+ )
1631
+
1632
+ r = engine.evaluate('A', 'b')
1633
+ expect(r).to eq(3)
1634
+
1635
+ r = engine.evaluate('A', 'c')
1636
+ expect(r).to eq(['a', 1])
1637
+ end
1638
+
1639
+ it 'none?' do
1640
+ engine.parse defn(*default_node,
1641
+ ' b = array.none?',
1642
+ ' num =?',
1643
+ ' result = num > 4',
1644
+ ' c = hash.none?',
1645
+ ' key =?',
1646
+ ' val =?',
1647
+ ' result = val >= 2',
1648
+ )
1649
+
1650
+ r = engine.evaluate('A', 'b')
1651
+ expect(r).to eq(true)
1652
+
1653
+ r = engine.evaluate('A', 'c')
1654
+ expect(r).to eq(false)
1655
+ end
1656
+
1657
+ it 'reduce' do
1658
+ engine.parse defn(*default_node,
1659
+ ' b = array.reduce(2)',
1660
+ ' sum =?',
1661
+ ' num =?',
1662
+ ' result = sum + num',
1663
+ ' b2= array.reduce',
1664
+ ' sum =?',
1665
+ ' num =?',
1666
+ ' result = sum + num',
1667
+ ' c = hash.reduce(2)',
1668
+ ' sum =?',
1669
+ ' key_val =?',
1670
+ ' result = sum + key_val[1]',
1671
+ ' c2 = hash.reduce([0])',
1672
+ ' sum =?',
1673
+ ' key_val =?',
1674
+ ' result = [sum.last + key_val[1]]',
1675
+ )
1676
+
1677
+ r = engine.evaluate('A', 'b')
1678
+ expect(r).to eq(8)
1679
+
1680
+ r = engine.evaluate('A', 'b2')
1681
+ expect(r).to eq(6)
1682
+
1683
+ r = engine.evaluate('A', 'c')
1684
+ expect(r).to eq(8)
1685
+
1686
+ r = engine.evaluate('A', 'c2')
1687
+ expect(r).to eq([6])
1688
+ end
1689
+
1690
+ it 'reject' do
1691
+ engine.parse defn(*default_node,
1692
+ ' b = array.reject',
1693
+ ' num =?',
1694
+ ' result = num >= 2',
1695
+ ' c = hash.reject',
1696
+ ' key =?',
1697
+ ' val =?',
1698
+ ' result = val > 2',
1699
+ )
1700
+
1701
+ r = engine.evaluate('A', 'b')
1702
+ expect(r).to eq([1])
1703
+
1704
+ r = engine.evaluate('A', 'c')
1705
+ expect(r).to eq('a' => 1, 'b' => 2)
1706
+ end
1707
+
1708
+ it 'uniq' do
1709
+ engine.parse defn(*default_node,
1710
+ ' b = array.uniq',
1711
+ ' num =?',
1712
+ ' result = (num / 3.0).round',
1713
+ ' b2 = array.uniq',
1714
+ ' c = hash.uniq',
1715
+ ' key =?',
1716
+ ' val =?',
1717
+ ' result = if key == "b" then "a" else key',
1718
+ ' c2 = hash.uniq',
1719
+ )
1720
+
1721
+ r = engine.evaluate('A', 'b')
1722
+ expect(r).to eq([1, 2])
1723
+
1724
+ r = engine.evaluate('A', 'b2')
1725
+ expect(r).to eq([1, 2, 3])
1726
+
1727
+ r = engine.evaluate('A', 'c')
1728
+ expect(r).to eq([['a', 1], ['c', 3]])
1729
+
1730
+ r = engine.evaluate('A', 'c2')
1731
+ expect(r).to eq([['a', 1], ['b', 2], ['c', 3]])
1732
+ end
1733
+ end
1734
+ end
1345
1735
  end
@@ -879,6 +879,89 @@ describe 'Delorean' do
879
879
  )
880
880
  end
881
881
 
882
+ it 'should parse question mark methods' do
883
+ engine.parse defn('A:',
884
+ " a = {'a': 1, 'b': 2, 'c': 3}",
885
+ ' b = a.any?',
886
+ ' key =?',
887
+ ' value =?',
888
+ ' result = value > 10',
889
+ )
890
+ end
891
+
892
+ it 'should not parse question mark variables' do
893
+ expect do
894
+ engine.parse defn('A:',
895
+ " a? = {'a': 1, 'b': 2, 'c': 3}",
896
+ )
897
+ end.to raise_error(Delorean::ParseError)
898
+ end
899
+
900
+ it 'should not parse question mark variables 2' do
901
+ expect do
902
+ engine.parse defn('A:',
903
+ " a?bc = {'a': 1, 'b': 2, 'c': 3}",
904
+ )
905
+ end.to raise_error(Delorean::ParseError)
906
+ end
907
+
908
+ describe 'blocks' do
909
+ it 'should not not work with default values' do
910
+ expect do
911
+ engine.parse defn('A:',
912
+ ' a = [1, 2, 3]',
913
+ ' b = a.any()',
914
+ ' item =?',
915
+ ' other =? ActiveRecord::Base.all',
916
+ ' result = item > other',
917
+ ' c = a.any() { |ActiveRecord::Base.all| true }',
918
+ ' item =? ActiveRecord::Base.all',
919
+ ' result = true',
920
+ ' d = a.any() { |item = 1| true }',
921
+ ' item =? 1',
922
+ ' result = true',
923
+ )
924
+ end.to raise_error(Delorean::ParseError)
925
+ end
926
+
927
+ it 'should raise parse error if result formula is not present in block' do
928
+ expect do
929
+ engine.parse defn('A:',
930
+ ' array = [1, 2, 3]',
931
+ ' b = array.any?',
932
+ ' item =?',
933
+ ' wrong = item > 10',
934
+ )
935
+ end.to raise_error(
936
+ Delorean::ParseError,
937
+ /result formula is required in blocks/
938
+ )
939
+ end
940
+
941
+ it 'should parse blocks' do
942
+ expect do
943
+ engine.parse defn('A:',
944
+ " a = {'a': 1, 'b': 2, 'c': 3}",
945
+ ' b = a.any()',
946
+ ' key =?',
947
+ ' value =?',
948
+ ' result = value > 10',
949
+ ' c = a.any()',
950
+ ' key =?',
951
+ ' value =?',
952
+ ' result = value > 2',
953
+ ' d = a.select()',
954
+ ' key =?',
955
+ ' value =?',
956
+ " result = key == 'a' || value == 2",
957
+ ' e = a.select()',
958
+ ' key =?',
959
+ " result = key == 'c' || key == 'b'",
960
+ )
961
+ end.to_not raise_error
962
+ end
963
+ end
964
+
882
965
  xit 'should parse ERR()' do
883
966
  # pending ... wrapping with parens -- (ERR()) works
884
967
  engine.parse defn('A:',