yadriggy 1.1.0 → 1.2.0

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