delorean_lang 0.4.2 → 0.4.4
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 +4 -4
- data/lib/delorean/base.rb +2 -7
- data/lib/delorean/delorean.rb +718 -250
- data/lib/delorean/delorean.treetop +13 -3
- data/lib/delorean/engine.rb +7 -0
- data/lib/delorean/nodes.rb +54 -48
- data/lib/delorean/version.rb +1 -1
- data/spec/eval_spec.rb +54 -0
- data/spec/parse_spec.rb +27 -0
- metadata +2 -2
@@ -50,7 +50,7 @@ grammar Delorean
|
|
50
50
|
/
|
51
51
|
'(' sp? al:kw_args? sp? ')' <NodeCall>
|
52
52
|
/
|
53
|
-
'.' sp? i:identifier '(' sp? al:
|
53
|
+
'.' sp? i:identifier '(' sp? al:fn_args? sp? ')' <Call>
|
54
54
|
/
|
55
55
|
'.' sp? i:(identifier / integer) <GetAttr>
|
56
56
|
end
|
@@ -127,12 +127,22 @@ grammar Delorean
|
|
127
127
|
end
|
128
128
|
|
129
129
|
rule hash_args
|
130
|
-
e0:expression sp?
|
130
|
+
splat:('**') e0:expression sp?
|
131
|
+
ifexp:('if' sp e3:expression sp?)?
|
132
|
+
args_rest:(sp? ',' sp? al:hash_args?)? <HashArgs>
|
133
|
+
/
|
134
|
+
e0:expression sp? ':' sp? e1:expression sp?
|
135
|
+
ifexp:('if' sp e3:expression sp?)?
|
131
136
|
args_rest:(sp? ',' sp? al:hash_args?)? <HashArgs>
|
132
137
|
end
|
133
138
|
|
134
139
|
rule kw_args
|
135
|
-
|
140
|
+
splat:('**') arg0:expression sp?
|
141
|
+
ifexp:('if' sp e3:expression sp?)?
|
142
|
+
args_rest:(sp? ',' sp? al:kw_args?)? <KwArgs>
|
143
|
+
/
|
144
|
+
k:(i:identifier sp? '=' sp?)? arg0:expression sp?
|
145
|
+
ifexp:('if' sp e3:expression sp?)?
|
136
146
|
args_rest:(sp? ',' sp? al:kw_args?)? <KwArgs>
|
137
147
|
end
|
138
148
|
|
data/lib/delorean/engine.rb
CHANGED
data/lib/delorean/nodes.rb
CHANGED
@@ -240,7 +240,7 @@ eos
|
|
240
240
|
raise "String interpolation not supported" if text_value =~ /\#\{.*\}/
|
241
241
|
|
242
242
|
# FIXME: syntax check?
|
243
|
-
text_value
|
243
|
+
text_value
|
244
244
|
end
|
245
245
|
end
|
246
246
|
|
@@ -248,7 +248,7 @@ eos
|
|
248
248
|
def rewrite(context)
|
249
249
|
# remove the quotes and requote. We don't want the likes of #{}
|
250
250
|
# evals to just pass through.
|
251
|
-
text_value[1..-2].inspect
|
251
|
+
text_value[1..-2].inspect
|
252
252
|
end
|
253
253
|
end
|
254
254
|
|
@@ -314,44 +314,36 @@ eos
|
|
314
314
|
|
315
315
|
class Call < SNode
|
316
316
|
def check(context, *)
|
317
|
-
al.check(context)
|
318
|
-
[]
|
317
|
+
al.text_value.empty? ? [] : al.check(context)
|
319
318
|
end
|
320
319
|
|
321
320
|
def rewrite(context, vcode)
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
args_str = args.reverse.join(',')
|
321
|
+
if al.text_value.empty?
|
322
|
+
args_str, arg_count = "", 0
|
323
|
+
else
|
324
|
+
args_str, arg_count = al.rewrite(context), al.arg_count
|
325
|
+
end
|
328
326
|
|
329
327
|
if vcode.is_a?(ClassText)
|
330
328
|
# ruby class call
|
331
329
|
class_name = vcode.text
|
332
|
-
context.parse_check_call_fn(i.text_value,
|
330
|
+
context.parse_check_call_fn(i.text_value, arg_count, class_name)
|
333
331
|
"#{class_name}.#{i.text_value}(#{args_str})"
|
334
332
|
else
|
335
333
|
"_instance_call(#{vcode}, '#{i.text_value}', [#{args_str}], _e)"
|
336
334
|
end
|
337
|
-
|
338
335
|
end
|
339
336
|
end
|
340
337
|
|
341
338
|
class NodeCall < SNode
|
342
339
|
def check(context, *)
|
343
|
-
al.check(context)
|
344
|
-
[]
|
340
|
+
al.text_value.empty? ? [] : al.check(context)
|
345
341
|
end
|
346
342
|
|
347
343
|
def rewrite(context, node_name)
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
(kw.map {|k, v| "'#{k}' => #{v}"} +
|
352
|
-
args.reverse.each_with_index.map {|v, i| "#{i} => #{v}"}).join(',')
|
353
|
-
|
354
|
-
"_node_call(#{node_name}, _e, {#{kw_str}})"
|
344
|
+
var = "_h#{context.hcount}"
|
345
|
+
res = al.text_value.empty? ? "" : al.rewrite(context, var)
|
346
|
+
"(#{var}={}; #{res}; _node_call(#{node_name}, _e, #{var}))"
|
355
347
|
end
|
356
348
|
end
|
357
349
|
|
@@ -410,7 +402,7 @@ eos
|
|
410
402
|
end
|
411
403
|
|
412
404
|
def rewrite(context)
|
413
|
-
"[" + (defined?(args) ? args.rewrite(context) : "") + "]
|
405
|
+
"[" + (defined?(args) ? args.rewrite(context) : "") + "]"
|
414
406
|
end
|
415
407
|
end
|
416
408
|
|
@@ -457,7 +449,7 @@ eos
|
|
457
449
|
|
458
450
|
res += ".select{|#{args_str}|(#{ifexp.e3.rewrite(context)})}" if
|
459
451
|
defined?(ifexp.e3)
|
460
|
-
res += ".map{|#{args_str}| (#{e2.rewrite(context)}) }
|
452
|
+
res += ".map{|#{args_str}| (#{e2.rewrite(context)}) }"
|
461
453
|
unpack_vars.each {|vname| context.parse_undef_var(vname)}
|
462
454
|
res
|
463
455
|
end
|
@@ -465,13 +457,13 @@ eos
|
|
465
457
|
|
466
458
|
class SetExpr < ListExpr
|
467
459
|
def rewrite(context)
|
468
|
-
"Set#{super}
|
460
|
+
"Set#{super}"
|
469
461
|
end
|
470
462
|
end
|
471
463
|
|
472
464
|
class SetComprehension < ListComprehension
|
473
465
|
def rewrite(context)
|
474
|
-
"Set[*#{super}]
|
466
|
+
"Set[*#{super}]"
|
475
467
|
end
|
476
468
|
end
|
477
469
|
|
@@ -514,7 +506,7 @@ eos
|
|
514
506
|
unpack_str = unpack_vars.count > 1 ? "(#{args_str})" : args_str
|
515
507
|
|
516
508
|
res += ".each_with_object({}){|#{unpack_str}, _h#{hid}| " +
|
517
|
-
"_h#{hid}[#{el.rewrite(context)}]=(#{er.rewrite(context)})}
|
509
|
+
"_h#{hid}[#{el.rewrite(context)}]=(#{er.rewrite(context)})}"
|
518
510
|
|
519
511
|
unpack_vars.each {|vname| context.parse_undef_var(vname)}
|
520
512
|
res
|
@@ -527,47 +519,61 @@ eos
|
|
527
519
|
end
|
528
520
|
|
529
521
|
def rewrite(context)
|
530
|
-
"{
|
522
|
+
return "{}" unless defined?(args)
|
523
|
+
var = "_h#{context.hcount}"
|
524
|
+
"(#{var}={}; " + args.rewrite(context, var) + "; #{var})"
|
531
525
|
end
|
532
526
|
end
|
533
527
|
|
534
528
|
class KwArgs < SNode
|
535
529
|
def check(context, *)
|
536
|
-
arg0.check(context)
|
537
|
-
|
538
|
-
|
530
|
+
[arg0.check(context),
|
531
|
+
(ifexp.e3.check(context) if defined?(ifexp.e3)),
|
532
|
+
(args_rest.al.check(context) if
|
533
|
+
defined?(args_rest.al) && !args_rest.al.empty?)
|
534
|
+
].compact.sum
|
539
535
|
end
|
540
536
|
|
541
|
-
def rewrite(context)
|
537
|
+
def rewrite(context, var, i=0)
|
542
538
|
arg0_rw = arg0.rewrite(context)
|
543
539
|
|
544
|
-
if defined?(
|
545
|
-
|
540
|
+
if defined?(splat)
|
541
|
+
res = "#{var}.merge!(#{arg0_rw})"
|
546
542
|
else
|
547
|
-
|
543
|
+
k_rw = defined?(k.i) ? "'#{k.i.text_value}'" : i.to_s
|
544
|
+
res = "#{var}[#{k_rw}]=(#{arg0_rw})"
|
545
|
+
i += 1 unless defined?(k.i)
|
548
546
|
end
|
549
547
|
|
550
|
-
if defined?(
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
[args, kw]
|
548
|
+
res += " if (#{ifexp.e3.rewrite(context)})" if defined?(ifexp.e3)
|
549
|
+
res += ";"
|
550
|
+
res += args_rest.al.rewrite(context, var, i) if
|
551
|
+
defined?(args_rest.al) && !args_rest.al.text_value.empty?
|
552
|
+
res
|
557
553
|
end
|
558
554
|
end
|
559
555
|
|
560
556
|
class HashArgs < SNode
|
561
557
|
def check(context, *)
|
562
|
-
e0.check(context)
|
563
|
-
|
564
|
-
|
558
|
+
[e0.check(context),
|
559
|
+
(e1.check(context) unless defined?(splat)),
|
560
|
+
(ifexp.e3.check(context) if defined?(ifexp.e3)),
|
561
|
+
(args_rest.al.check(context) if
|
562
|
+
defined?(args_rest.al) && !args_rest.al.empty?),
|
563
|
+
].compact.sum
|
565
564
|
end
|
566
565
|
|
567
|
-
def rewrite(context)
|
568
|
-
|
569
|
-
|
570
|
-
|
566
|
+
def rewrite(context, var)
|
567
|
+
if defined?(splat)
|
568
|
+
res = "#{var}.merge!(#{e0.rewrite(context)})"
|
569
|
+
else
|
570
|
+
res = "#{var}[#{e0.rewrite(context)}]=(#{e1.rewrite(context)})"
|
571
|
+
end
|
572
|
+
res += " if (#{ifexp.e3.rewrite(context)})" if defined?(ifexp.e3)
|
573
|
+
res += ";"
|
574
|
+
res += args_rest.al.rewrite(context, var) if
|
575
|
+
defined?(args_rest.al) && !args_rest.al.text_value.empty?
|
576
|
+
res
|
571
577
|
end
|
572
578
|
end
|
573
579
|
end
|
data/lib/delorean/version.rb
CHANGED
data/spec/eval_spec.rb
CHANGED
@@ -587,6 +587,21 @@ eof
|
|
587
587
|
]
|
588
588
|
end
|
589
589
|
|
590
|
+
it "handles literal hashes with conditionals" do
|
591
|
+
engine.parse defn("A:",
|
592
|
+
" a = {'a':1 if 123, 'b':'x' if nil}",
|
593
|
+
" b = {'a':a if a, 2: a if true, 'c':nil if 2*2}",
|
594
|
+
" c = 1>2",
|
595
|
+
" d = {1: {1: 2 if b}, 3: 3 if c, 2: {2: 3 if a}}",
|
596
|
+
)
|
597
|
+
|
598
|
+
engine.evaluate("A", %w{a b d}).should == [
|
599
|
+
{"a"=>1},
|
600
|
+
{"a"=>{"a"=>1}, 2=>{"a"=>1}, "c"=>nil},
|
601
|
+
{1=>{1=>2}, 2=>{2=>3}},
|
602
|
+
]
|
603
|
+
end
|
604
|
+
|
590
605
|
it "should eval hash comprehension" do
|
591
606
|
engine.parse defn("A:",
|
592
607
|
" b = {i*5 :i for i in [1,2,3]}",
|
@@ -914,12 +929,17 @@ eof
|
|
914
929
|
" x = _.a * _.b",
|
915
930
|
" y = a && _",
|
916
931
|
" z = (B() + _).x",
|
932
|
+
" w = B(**_).x",
|
933
|
+
" v = {**_, 'a': 123}",
|
917
934
|
)
|
918
935
|
|
919
936
|
engine.evaluate("A", "x", {"a"=>3, "b"=>5}).should == 15
|
920
937
|
h = {"a"=>1, "b"=>2, "c"=>3}
|
921
938
|
engine.evaluate("A", "y", {"a"=>1, "b"=>2, "c"=>3}).should == h
|
922
939
|
engine.evaluate("A", "z", {"a"=>1, "b"=>2, "c"=>3}).should == -1
|
940
|
+
engine.evaluate("A", "w", {"a"=>4, "b"=>5, "c"=>3}).should == -1
|
941
|
+
engine.evaluate("A", "v", {"a"=>4, "b"=>5, "c"=>3}).should == {
|
942
|
+
"a"=>123, "b"=>5, "c"=>3}
|
923
943
|
end
|
924
944
|
|
925
945
|
it "implements positional args in node calls" do
|
@@ -955,6 +975,40 @@ eof
|
|
955
975
|
expect(r).to eq 3
|
956
976
|
end
|
957
977
|
|
978
|
+
it "node calls with double splats" do
|
979
|
+
engine.parse defn("A:",
|
980
|
+
" a =?",
|
981
|
+
" b =?",
|
982
|
+
" c = a+b",
|
983
|
+
" h = {'a': 123}",
|
984
|
+
" k = {'b': 456}",
|
985
|
+
" x = A(**h, **k).c"
|
986
|
+
)
|
987
|
+
r = engine.evaluate("A", "x")
|
988
|
+
expect(r).to eq 579
|
989
|
+
end
|
990
|
+
|
991
|
+
it "hash literal with double splats" do
|
992
|
+
engine.parse defn("A:",
|
993
|
+
" a =?",
|
994
|
+
" b =?",
|
995
|
+
" h = {'a': 123, **a}",
|
996
|
+
" k = {'b': 456, **h, **a, **b}",
|
997
|
+
" l = {**k}",
|
998
|
+
" m = {**k, 1:1, 2:2, 3:33}",
|
999
|
+
" n = {**k if false, 1:1, 2:2, 3:33}",
|
1000
|
+
)
|
1001
|
+
r = engine.evaluate("A", ["h", "k", "l", "m", "n"],
|
1002
|
+
{"a" => {3=>3, 4=>4}, "b" => {5=>5, "a" => "aa"}})
|
1003
|
+
expect(r).to eq [
|
1004
|
+
{"a"=>123, 3=>3, 4=>4},
|
1005
|
+
{"b"=>456, "a"=>"aa", 3=>3, 4=>4, 5=>5},
|
1006
|
+
{"b"=>456, "a"=>"aa", 3=>3, 4=>4, 5=>5},
|
1007
|
+
{"b"=>456, "a"=>"aa", 3=>33, 4=>4, 5=>5, 1=>1, 2=>2},
|
1008
|
+
{1=>1, 2=>2, 3=>33},
|
1009
|
+
]
|
1010
|
+
end
|
1011
|
+
|
958
1012
|
it "understands openstructs" do
|
959
1013
|
engine.parse defn("A:",
|
960
1014
|
" os = Dummy.returns_openstruct",
|
data/spec/parse_spec.rb
CHANGED
@@ -614,6 +614,13 @@ describe "Delorean" do
|
|
614
614
|
}.should raise_error(Delorean::ParseError)
|
615
615
|
end
|
616
616
|
|
617
|
+
it "should be able to parse conditional hash literals" do
|
618
|
+
engine.parse defn("A:",
|
619
|
+
" a = {}",
|
620
|
+
" c = {'a':a if a, 'b': 2, 'c':-3 if 123}",
|
621
|
+
)
|
622
|
+
end
|
623
|
+
|
617
624
|
it "should handle trailing ',' with hashes" do
|
618
625
|
engine.parse defn("A:",
|
619
626
|
" b = {-1:1,}",
|
@@ -746,6 +753,26 @@ describe "Delorean" do
|
|
746
753
|
)
|
747
754
|
end
|
748
755
|
|
756
|
+
it "allow conditional args to node calls" do
|
757
|
+
engine.parse defn("A:",
|
758
|
+
" d = A(a=1, b=4 if true, c=4 if false)",
|
759
|
+
)
|
760
|
+
end
|
761
|
+
|
762
|
+
it "allow double splats in node calls" do
|
763
|
+
engine.parse defn("A:",
|
764
|
+
" a =?",
|
765
|
+
" d = A(**a, **(a+a), a=123, b=456)",
|
766
|
+
)
|
767
|
+
end
|
768
|
+
|
769
|
+
it "allow double splats in literal hashes" do
|
770
|
+
engine.parse defn("A:",
|
771
|
+
" a =?",
|
772
|
+
" d = {'a':1, 2:2, **a, **(a+a)}",
|
773
|
+
)
|
774
|
+
end
|
775
|
+
|
749
776
|
it "should parse instance calls" do
|
750
777
|
engine.parse defn("A:",
|
751
778
|
" a = [1,2,[4]].flatten(1)",
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delorean_lang
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arman Bostani
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: treetop
|