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.
- checksums.yaml +5 -5
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +4 -4
- data/Makefile +2 -0
- data/README.md +34 -0
- data/lib/delorean/base.rb +13 -5
- data/lib/delorean/delorean.rb +545 -35
- data/lib/delorean/delorean.treetop +24 -1
- data/lib/delorean/engine.rb +21 -1
- data/lib/delorean/nodes.rb +150 -0
- data/lib/delorean/ruby/whitelists/default.rb +166 -128
- data/lib/delorean/version.rb +1 -1
- data/spec/eval_spec.rb +390 -0
- data/spec/parse_spec.rb +83 -0
- metadata +4 -3
data/lib/delorean/version.rb
CHANGED
data/spec/eval_spec.rb
CHANGED
@@ -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
|
data/spec/parse_spec.rb
CHANGED
@@ -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:',
|