yadriggy 1.1.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 990b3b201219b7511cbcf4554f24b3dc9bd14128
4
- data.tar.gz: 6a45aaa8d3f036656f787d7979dbd7954f636d34
3
+ metadata.gz: f73ad8ceb3807a109ddbd4c11ddafee6f1854c32
4
+ data.tar.gz: 307556c77ee0760ddde8e55cece63d6975505ace
5
5
  SHA512:
6
- metadata.gz: c295ef7bb85cefc84a43ac7459e28477e082bb28f093a797a1bde44fc02ac8578d802255bbb01f984056da4de554a1c6f9451a32d8b649b7122dacf25604bf36
7
- data.tar.gz: 39b8d8ad06e9374558229724038f9db1f5edee7dc79f94fa3e470229784e3379dbf743754d6d731dcdbd9cf82f22bb1a046ad37d0cdb10aeb4d5976cacf05889
6
+ metadata.gz: a0922e73e77795a2d351887d3e9a11760b4e4ec5f8fb8535a440ef6cca8c707f0ad402805aec919a8f52f58fa1ae17c7c877549a6ee44119b0b0bf73cfce8ee0
7
+ data.tar.gz: 66d4ad0cdef21663929443687fb5d3e3f3b1f57a86c00fd35f38cd1b70f691a98f9a44515da17fb222c116072ea36152a7827f042695599c66e47e9fd2f74a5f
@@ -0,0 +1,3 @@
1
+ --hide-api private
2
+ --markup markdown
3
+ lib/**/*.rb
data/README.md CHANGED
@@ -2,22 +2,23 @@
2
2
 
3
3
  Yadriggy (mistletoe in Japanese) is a library for building a
4
4
  domain-specific language (DSL) embedded in Ruby. It was developed for
5
- a particular kind of embedded DSLs, which we call hemiparasitic DSLs.
5
+ a particular kind of embedded DSLs.
6
6
  These DSLs borrow the syntax from the host language, Ruby, and the
7
7
  code written in the DSLs is embedded in normal Ruby code. However,
8
8
  the execution of the DSL code is independent of Ruby. Its semantics
9
9
  can be totally different from Ruby and the code can be run out of the
10
- Ruby VM. Hemiparasitic DSLs look like Ruby but they are different
10
+ Ruby VM. These DSLs look like Ruby but they are different
11
11
  languages except their syntax.
12
- They parasitize Ruby by borrowing the syntax but their parasitism is
13
- hemi; their execution engines are their own.
12
+ They are embedded in Ruby by borrowing the syntax but their embedding is
13
+ outward; their execution engines are their own.
14
14
 
15
15
  For details, the documentation is available from [Wiki](https://github.com/csg-tokyo/yadriggy/wiki).
16
16
 
17
- ## Hemiparasitic DSLs
17
+ ## An example
18
18
 
19
- A typical example of hemiparasitic DSL is computation offloading from
20
- Ruby. For example, Yadriggy provides a simple DSL to offload
19
+ Computation offloading from Ruby is a typical example of the DSLs
20
+ implemented by Yadriggy.
21
+ For example, Yadriggy provides a simple DSL to offload
21
22
  from Ruby to native C language.
22
23
 
23
24
  ```ruby
@@ -30,7 +31,7 @@ def fib(n) ! Integer
30
31
  if n > 1
31
32
  return fib(n - 1) + fib(n - 2)
32
33
  else
33
- return 1
34
+ return n
34
35
  end
35
36
  end
36
37
 
@@ -59,7 +60,7 @@ The variable `n` in the Ruby code keeps the old value.
59
60
 
60
61
  Note that the definition of `fib` contains type declarations
61
62
  since this DSL is not Ruby.
62
- A hemiparasitic DSL looks like Ruby but it is a different language.
63
+ This DSL looks like Ruby but it is a different language.
63
64
  `! Integer` following `def fib(n)` specifies the return type.
64
65
  `typedecl` specifies the types of the parameters (and local variables
65
66
  if any). In this DSL, most types have to be statically given
@@ -84,6 +85,8 @@ ast = Yadriggy.reify {|a| a + 1 }
84
85
  `reify` returns the AST of the given block `{|a| a + 1 }`.
85
86
  It takes not only a block but also a `Method` or `Proc` object.
86
87
 
88
+ Yadriggy works with Pry and IRuby unless a syntax error occurs.
89
+
87
90
  The idea of `reify` was proposed in the following paper:
88
91
 
89
92
  - Shigeru Chiba, YungYu Zhuang, Maximilian Scherr, "Deeply Reifying Running Code for Constructing a Domain-Specific Language", PPPJ'16, Article No. 1, ACM, August 2016.
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
+ require "yard"
3
4
 
4
5
  Rake::TestTask.new(:test) do |t|
5
6
  t.libs << "test"
@@ -7,4 +8,8 @@ Rake::TestTask.new(:test) do |t|
7
8
  t.test_files = FileList['test/**/*_test.rb']
8
9
  end
9
10
 
11
+ YARD::Rake::YardocTask.new do |t|
12
+ t.options = ['-m markdown', '--no-private']
13
+ end
14
+
10
15
  task :default => :test
@@ -25,7 +25,7 @@ module Yadriggy
25
25
  def self.debug() @@debug end
26
26
 
27
27
  # Sets the current debug level.
28
- # @param [Integer] level.
28
+ # @param [Integer] level the debug level.
29
29
  def self.debug=(level)
30
30
  @@debug = level
31
31
  end
@@ -113,8 +113,15 @@ module Yadriggy
113
113
  binary(left, op, right)
114
114
  end
115
115
 
116
+ # @param [Object|Array] left the left value.
117
+ # @param [Symbol] op the operator.
118
+ # @param [Object|Array] right the right value.
116
119
  def assign(left, op, right)
117
- binary(left, op, right)
120
+ if left.is_a?(Array) || right.is_a?(Array)
121
+ raise NotImplementedError.new('multiple assignment')
122
+ else
123
+ binary(left, op, right)
124
+ end
118
125
  end
119
126
 
120
127
  def array_ref(array, index)
@@ -259,13 +266,24 @@ module Yadriggy
259
266
  end
260
267
  end
261
268
 
262
- # evaluator for Algebra
269
+ # Evaluator for Algebra
263
270
  #
264
271
  class EvalAlgebra < Eval
272
+ # Initializes.
273
+ #
274
+ # @param [Algebra] algebra
265
275
  def initialize(algebra)
266
276
  @algebra = algebra
267
277
  end
268
278
 
279
+ def evaluate(expr)
280
+ if expr.nil?
281
+ nil_value(nil)
282
+ else
283
+ expr.accept(self)
284
+ end
285
+ end
286
+
269
287
  def nil_value(expr)
270
288
  @algebra.nil_value
271
289
  end
@@ -355,7 +373,17 @@ module Yadriggy
355
373
  end
356
374
 
357
375
  def assign(expr)
358
- @algebra.assign(evaluate(expr.left), expr.op, evaluate(expr.right))
376
+ right = if expr.right.is_a?(Array)
377
+ expr.right.map {|e| evaluate(e) }
378
+ else
379
+ evaluate(expr.right)
380
+ end
381
+ left = if expr.left.is_a?(Array)
382
+ expr.left.map {|e| evaluate(e) }
383
+ else
384
+ evaluate(expr.left)
385
+ end
386
+ @algebra.assign(left, expr.op, right)
359
387
  end
360
388
 
361
389
  def array_ref(expr)
@@ -22,7 +22,7 @@ module Yadriggy
22
22
  end
23
23
  end
24
24
 
25
- # @private
25
+ # @api private
26
26
  def self.puts_reason(reason, evar=nil)
27
27
  puts '--- Yadriggy::Assert ---'
28
28
  print evar.cause.class.name, ': ' if evar&.cause
@@ -52,13 +52,13 @@ module Yadriggy
52
52
 
53
53
  # Reason that an assertion fails.
54
54
  class Reason
55
- # @private
55
+ # @api private
56
56
  def setup(ast, results)
57
57
  @ast = ast
58
58
  @results = results
59
59
  end
60
60
 
61
- # Gets the AST of the block given to {#assertion}.
61
+ # Gets the AST of the block given to {Yadriggy::Assert::assertion}.
62
62
  # @return [ASTnode] an abstract syntax tree.
63
63
  def ast() @ast end
64
64
 
@@ -85,7 +85,7 @@ module Yadriggy
85
85
  output.reverse!
86
86
  end
87
87
 
88
- # @private
88
+ # @api private
89
89
  # @return [String] the new header.
90
90
  def show2(ast, header, output)
91
91
  if ast.is_a?(Paren)
@@ -136,7 +136,7 @@ module Yadriggy
136
136
  end
137
137
  end
138
138
 
139
- # @private
139
+ # @api private
140
140
  # Obtains the text representation of the given value.
141
141
  def str_rep(v)
142
142
  max = 70
@@ -149,7 +149,7 @@ module Yadriggy
149
149
  end
150
150
  end
151
151
 
152
- # Exception thrown by {#assertion}.
152
+ # Exception thrown by {Yadriggy::Assert#assertion}.
153
153
  #
154
154
  class AssertFailure < StandardError
155
155
  def initialize(reason, msg=nil, cause=nil)
@@ -167,7 +167,7 @@ module Yadriggy
167
167
  def reason() @reason end
168
168
  end
169
169
 
170
- # @private
170
+ # @api private
171
171
  # Executes the given AST and records the result.
172
172
  # @param [ASTnode] ast the given AST.
173
173
  # @param [Binding] blk_binding the binding for executing the AST.
@@ -210,7 +210,7 @@ module Yadriggy
210
210
  end
211
211
  end
212
212
 
213
- # @private
213
+ # @api private
214
214
  # Eval the AST by the RubyVM
215
215
  # @return [Pair<String,Object>] an array. The first element is the source code
216
216
  # and the second element is the resulting value.
@@ -16,11 +16,13 @@ module Yadriggy
16
16
  # the recorded ASTs are shared among {ASTree} objects.
17
17
  # Every call to {Yadriggy#reify} on {Yadriggy} makes a new table. Hence,
18
18
  #
19
- # <pre>ast1 = Yadriggy.reify(proc1)
19
+ # ```
20
+ # ast1 = Yadriggy.reify(proc1)
20
21
  # ast2 = ast.reify(proc2)
21
- # a1 = Yadriggy.reify(proc1) # a1 != ast1
22
+ # a1 = Yadriggy.reify(proc1) # a1 != ast1
22
23
  # a2 = a1.reify(proc2) # a2 != ast2
23
- # b2 = a1.reify(proc2) # b2 == a2</pre>
24
+ # b2 = a1.reify(proc2) # b2 == a2
25
+ # ```
24
26
  #
25
27
  # Although `ast1` and `a1`, and `ast2` and `a2` are different copies
26
28
  # of the AST of the same proc, `a2` and `b2` refer to the same AST.
@@ -136,7 +138,7 @@ module Yadriggy
136
138
  end
137
139
  end
138
140
 
139
- # Reserved words such self, true, and false.
141
+ # Reserved words such as self, nil, true, and false.
140
142
  #
141
143
  class Reserved < Name
142
144
  def self.tag() :@kw end
@@ -324,7 +326,12 @@ module Yadriggy
324
326
 
325
327
  def initialize(sexp)
326
328
  if sexp[0] == :dyna_symbol
327
- init(has_tag?(sexp[1][0], :@tstring_content))
329
+ sexp2 = if sexp[1][0] == :string_content
330
+ sexp[1][1]
331
+ else
332
+ sexp[1][0]
333
+ end
334
+ init(has_tag?(sexp2, :@tstring_content))
328
335
  elsif sexp[0] == :symbol_literal
329
336
  init(has_tag?(sexp[1], :symbol)[1])
330
337
  else
@@ -399,7 +406,8 @@ module Yadriggy
399
406
  def self.tag() :paren end
400
407
 
401
408
  def initialize(sexp)
402
- @expression = to_node(sexp[1][0])
409
+ e = if sexp[1][0].is_a?(Array) then sexp[1][0] else sexp[1] end
410
+ @expression = to_node(e)
403
411
  add_child(@expression)
404
412
  end
405
413
 
@@ -424,6 +432,10 @@ module Yadriggy
424
432
  def initialize(sexp)
425
433
  if sexp[1].nil?
426
434
  @elements = []
435
+ elsif is_percent_literal(sexp[1])
436
+ @elements = sexp[1].map do |e|
437
+ StringInterpolation.new([:string_literal, [:string_content] + e])
438
+ end
427
439
  else
428
440
  @elements = to_nodes(sexp[1])
429
441
  end
@@ -436,6 +448,15 @@ module Yadriggy
436
448
  def accept(evaluator)
437
449
  evaluator.array(self)
438
450
  end
451
+
452
+ private
453
+
454
+ def is_percent_literal(sexp)
455
+ sexp.is_a?(Array) && sexp.size > 0 &&
456
+ sexp.all? do |e|
457
+ e.is_a?(Array) && e.all? {|ee| ee.is_a?(Array) }
458
+ end
459
+ end
439
460
  end
440
461
 
441
462
  # String interpolation.
@@ -673,25 +694,33 @@ module Yadriggy
673
694
  end
674
695
 
675
696
  # Assignment such as `=` and `+=`.
697
+ # `Assign#left` and `Assign#right` return an `ASTnode`,
698
+ # or an array of `ASTnode` if the node represents multiple
699
+ # assignment.
676
700
  #
677
701
  class Assign < Binary
678
- def self.tags() [:assign, :opassign] end
702
+ def self.tags() [:assign, :opassign, :massign] end
679
703
 
680
704
  def initialize(sexp)
681
705
  case sexp[0]
682
706
  when :assign
683
707
  @left = to_node(sexp[1])
708
+ add_child(@left)
684
709
  @op = :'='
685
- @right = to_node(sexp[2])
710
+ init_right(sexp[2])
686
711
  when :opassign
687
712
  @left = to_node(sexp[1])
713
+ add_child(@left)
688
714
  @op = has_tag?(sexp[2], :@op)[1].to_sym
689
- @right = to_node(sexp[3])
715
+ init_right(sexp[3])
716
+ when :massign
717
+ @left = to_nodes(sexp[1])
718
+ add_children(@left)
719
+ @op = :'='
720
+ init_right(sexp[2])
690
721
  else
691
722
  raise "unknown assignment " + sexp[0].to_s
692
723
  end
693
- add_child(@left)
694
- add_child(@right)
695
724
  end
696
725
 
697
726
  # A method for Visitor pattern.
@@ -700,6 +729,19 @@ module Yadriggy
700
729
  def accept(evaluator)
701
730
  evaluator.assign(self)
702
731
  end
732
+
733
+ private
734
+
735
+ # @api private
736
+ def init_right(right_operand)
737
+ if right_operand[0] == :mrhs_new_from_args
738
+ @right = to_nodes(right_operand[1]) + [to_node(right_operand[2])]
739
+ add_children(@right)
740
+ else
741
+ @right = to_node(right_operand)
742
+ add_child(@right)
743
+ end
744
+ end
703
745
  end
704
746
 
705
747
  # Hash table.
@@ -792,7 +834,7 @@ module Yadriggy
792
834
  parent, link_from_children)
793
835
  end
794
836
 
795
- # @private
837
+ # @api private
796
838
  def initialize2(recv, op, name, args, barg, blk,
797
839
  parent, link_from_children)
798
840
  @receiver = recv
@@ -826,6 +868,12 @@ module Yadriggy
826
868
  marg = sexp[1]
827
869
  if marg[0] == :method_add_arg
828
870
  initialize_method_arg(marg[1], marg[2])
871
+ elsif marg[0] == :command
872
+ initialize_call([:call, nil, nil, marg[1]])
873
+ initialize_args(marg[2]) if marg.length > 2
874
+ elsif marg[0] == :command_call
875
+ initialize_call([:call, marg[1], marg[2], marg[3]])
876
+ initialize_args(marg[4]) if marg.length > 4
829
877
  else
830
878
  initialize_method_arg(marg, [])
831
879
  end
@@ -865,13 +913,21 @@ module Yadriggy
865
913
  def initialize_call(sexp)
866
914
  @receiver = to_node(sexp[1])
867
915
  @op = sexp[2] # :"." or :"::" or nil.
868
- @name = to_node(has_tag?(sexp[3], :@ident))
916
+ @name = if sexp[3] == :call
917
+ nil
918
+ else
919
+ @name = to_node(has_tag?(sexp[3], :@ident))
920
+ end
869
921
  add_child(@receiver)
870
922
  add_child(@name)
871
923
  end
872
924
 
873
925
  def initialize_args(args_block)
874
- args = has_tag?(args_block, :args_add_block)[1]
926
+ args = if args_block[0] == :args_add_block
927
+ args_block[1]
928
+ else
929
+ args_block
930
+ end
875
931
  args2 = initialize_star_arg(args)
876
932
  @args = to_nodes(args2)
877
933
  @block_arg = if args_block[2]
@@ -1652,7 +1708,7 @@ module Yadriggy
1652
1708
  end
1653
1709
  end
1654
1710
 
1655
- # @private
1711
+ # @api private
1656
1712
  # A table of reified abstract syntax trees.
1657
1713
  # It is used for guaranteeing the uniqueness
1658
1714
  # of ASTree objects.
@@ -27,7 +27,7 @@ module Yadriggy
27
27
  end
28
28
  end
29
29
 
30
- # @private
30
+ # @api private
31
31
  class GetLocation < EvalAll
32
32
  def initialize
33
33
  @unknown = true
@@ -95,7 +95,7 @@ module Yadriggy
95
95
  mod
96
96
  end
97
97
 
98
- # @private
98
+ # @api private
99
99
  # @return [Pair<Module,Array<String>>]
100
100
  def self.compile0(obj, lib_name, dir, module_name,
101
101
  typechecker_class, gen_class)
@@ -109,7 +109,7 @@ module Yadriggy
109
109
  end
110
110
  end
111
111
 
112
- # @private
112
+ # @api private
113
113
  # @return [Pair<Module,Array<String>>]
114
114
  def self.compile1(obj, lib_name, dir, module_name,
115
115
  typechecker_class, gen_class)
@@ -141,7 +141,7 @@ module Yadriggy
141
141
  attach_funcs(pub_methods, checker, gen, module_name, lib_name, dir)
142
142
  end
143
143
 
144
- # @private
144
+ # @api private
145
145
  # @return [Array<ASTree>] the ASTs of compiled methods.
146
146
  def self.compiled_methods(checker, method_objs)
147
147
  ast = nil
@@ -165,7 +165,7 @@ module Yadriggy
165
165
  return pub_methods
166
166
  end
167
167
 
168
- # @private
168
+ # @api private
169
169
  def self.generate_funcs(ast, gen, printer)
170
170
  gen.name_global_variables
171
171
  gen.headers
@@ -185,7 +185,7 @@ module Yadriggy
185
185
  raise BuildError.new(gen.error_messages) if gen.errors?
186
186
  end
187
187
 
188
- # @private
188
+ # @api private
189
189
  # @return [Pair<Module,Array<String>>] the module where the methods
190
190
  # are attached. The second element is method names.
191
191
  def self.attach_funcs(pub_methods, checker, gen, module_name,