delorean_lang 0.4.2 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|