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.
@@ -50,7 +50,7 @@ grammar Delorean
50
50
  /
51
51
  '(' sp? al:kw_args? sp? ')' <NodeCall>
52
52
  /
53
- '.' sp? i:identifier '(' sp? al:kw_args? sp? ')' <Call>
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? ':' sp? e1:expression
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
- k:(i:identifier sp? '=' sp?)? arg0:expression
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
 
@@ -27,6 +27,13 @@ module Delorean
27
27
  @param_set = Set.new
28
28
 
29
29
  @imports = {}
30
+
31
+ @hcount = 0
32
+ end
33
+
34
+ # used in counting literal hashes
35
+ def hcount
36
+ @hcount += 1
30
37
  end
31
38
 
32
39
  def curr_line
@@ -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 + ".freeze"
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 + ".freeze"
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) unless al.text_value.empty?
318
- []
317
+ al.text_value.empty? ? [] : al.check(context)
319
318
  end
320
319
 
321
320
  def rewrite(context, vcode)
322
- args, kw = al.text_value.empty? ? [[], {}] : al.rewrite(context)
323
-
324
- raise "Keyword arguments not supported" unless
325
- kw.empty?
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, args.count, class_name)
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) unless al.text_value.empty?
344
- []
340
+ al.text_value.empty? ? [] : al.check(context)
345
341
  end
346
342
 
347
343
  def rewrite(context, node_name)
348
- args, kw = al.text_value.empty? ? [[], {}] : al.rewrite(context)
349
-
350
- kw_str =
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) : "") + "].freeze"
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)}) }.freeze"
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}.freeze"
460
+ "Set#{super}"
469
461
  end
470
462
  end
471
463
 
472
464
  class SetComprehension < ListComprehension
473
465
  def rewrite(context)
474
- "Set[*#{super}].freeze"
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)})}.freeze"
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
- "{#{args.rewrite(context) if defined?(args)}}.freeze"
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
- defined?(args_rest.al) && !args_rest.al.empty? ?
538
- args_rest.al.check(context) : [])
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?(args_rest.al) && !args_rest.al.text_value.empty?
545
- args, kw = args_rest.al.rewrite(context)
540
+ if defined?(splat)
541
+ res = "#{var}.merge!(#{arg0_rw})"
546
542
  else
547
- args, kw = [], {}
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?(k.i)
551
- kw[k.i.text_value] = arg0_rw
552
- else
553
- args << arg0_rw
554
- end
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) + e1.check(context) +
563
- (defined?(args_rest.al) && !args_rest.al.empty? ?
564
- args_rest.al.check(context) : [])
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
- e0.rewrite(context) + " => " + e1.rewrite(context) +
569
- (defined?(args_rest.al) && !args_rest.al.text_value.empty? ?
570
- ", " + args_rest.al.rewrite(context) : "")
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
@@ -1,3 +1,3 @@
1
1
  module Delorean
2
- VERSION = "0.4.2"
2
+ VERSION = "0.4.4"
3
3
  end
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.2
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-09-28 00:00:00.000000000 Z
11
+ date: 2018-10-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: treetop