delorean_lang 1.0.0 → 1.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.
@@ -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:',