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.
@@ -5,7 +5,10 @@ require 'yadriggy/ruby_typecheck'
5
5
 
6
6
  module Yadriggy
7
7
 
8
- # Type checker for Ruby with type inference.
8
+ # Type checker for Ruby with type inference. The type system is slightly
9
+ # different from Ruby's one.
10
+ # For example, once an integer is assigned to a local variable,
11
+ # assigning a String value to it causes a type error.
9
12
  #
10
13
  class RubyTypeInferer < RubyTypeChecker
11
14
 
@@ -29,7 +32,7 @@ module Yadriggy
29
32
  end
30
33
  end
31
34
 
32
- # @private
35
+ # @api private
33
36
  # When the initial value of a variable is an InstanceType,
34
37
  # the type of the variable has to be a RubyCass type
35
38
  # corresponding to that instance type.
@@ -48,9 +51,47 @@ module Yadriggy
48
51
  # Note that obj.name += ... is regarded as obj.name=(obj.name + ...).
49
52
  #
50
53
  rule(Assign) do
51
- rtype = type(ast.right)
52
- left_expr = ast.left
53
- if ast.op != :'=' # if op is += etc.
54
+ if ast.left.is_a?(Array)
55
+ type_multi_assign(ast.left, ast.op, ast.right, ast.parent)
56
+ else
57
+ ast_right = ast.right
58
+ if ast_right.is_a?(Array)
59
+ ast_right.each {|e| type(e) }
60
+ type_assign(ast.left, ast.op, ast_right, DynType, ast.parent)
61
+ else
62
+ rtype = type(ast_right)
63
+ type_assign(ast.left, ast.op, ast_right, rtype, ast.parent)
64
+ end
65
+ end
66
+ end
67
+
68
+ # @api private
69
+ # Overriding and overloading
70
+ def type_multi_assign(ast_left, ast_op, ast_right, ast_parent)
71
+ if ast_right.is_a?(Array)
72
+ rtypes = ast_right.map {|e| type(e) }
73
+ ast_left.each_with_index do |v, i|
74
+ if i < rtypes.size
75
+ type_assign(v, ast_op, ast_right[i], rtypes[i], ast_parent)
76
+ else
77
+ # passing "ast_right" is wrong but it will not be used..
78
+ type_assign(v, ast_op, ast_right, RubyClass::NilClass, ast_parent)
79
+ end
80
+ end
81
+ else
82
+ type(ast_right)
83
+ ast_left.each_with_index do |v, i|
84
+ # passing "ast_right" is wrong but it will not be used..
85
+ type_assign(v, ast_op, ast_right, DynType, ast_parent)
86
+ end
87
+ end
88
+ DynType
89
+ end
90
+
91
+ # @api private
92
+ # Overriding and overloading.
93
+ def type_assign(left_expr, ast_op, right_expr, rtype, ast_parent)
94
+ if ast_op != :'=' # if op is += etc.
54
95
  ltype = type(left_expr)
55
96
  LocalVarType.role(ltype)&.definition = left_expr
56
97
  ltype
@@ -58,9 +99,11 @@ module Yadriggy
58
99
  vtype = type_env.bound_name?(left_expr)
59
100
  if vtype.nil? # if a new name is found,
60
101
  method_name = left_expr.name + '='
61
- if is_attr_accessor?(ast, type_env, method_name.to_sym) # self.name=()?
62
- call_expr = Call.make(name: method_name, args: [ ast.right ])
63
- get_call_expr_type(call_expr, type_env, method_name)
102
+ if is_attr_accessor?(type_env, method_name.to_sym) # self.name=()?
103
+ call_expr = Call.make(name: method_name, args: [ right_expr ],
104
+ parent: ast_parent)
105
+ get_call_expr_type_with_argtypes(call_expr, type_env, method_name,
106
+ [ rtype ])
64
107
  else
65
108
  bind_local_var(type_env, left_expr, rtype)
66
109
  end
@@ -72,9 +115,10 @@ module Yadriggy
72
115
  elsif left_expr.is_a?(Call) && left_expr.op == :'.' # obj.name=()?
73
116
  method_name = left_expr.name.name + '='
74
117
  call_expr = Call.make(receiver: left_expr.receiver,
75
- name: method_name, args: [ ast.right ],
76
- parent: ast.parent)
77
- get_call_expr_type(call_expr, type_env, method_name)
118
+ name: method_name, args: [ right_expr ],
119
+ parent: ast_parent)
120
+ get_call_expr_type_with_argtypes(call_expr, type_env, method_name,
121
+ [ rtype ])
78
122
  elsif left_expr.is_a?(InstanceVariable) # @var = ..., @@cvar = ..., @var += ...
79
123
  get_instance_variable_type(type_env.context, left_expr, true,
80
124
  InstanceType.role(rtype)&.supertype || rtype)
@@ -86,8 +130,8 @@ module Yadriggy
86
130
  end
87
131
  end
88
132
 
89
- # @private
90
- def is_attr_accessor?(expr, tenv, name)
133
+ # @api private
134
+ def is_attr_accessor?(tenv, name)
91
135
  self_t = type_env.context
92
136
  !self_t.nil? &&
93
137
  (self_t.method_defined?(name) ||
@@ -102,7 +146,7 @@ module Yadriggy
102
146
  type
103
147
  else
104
148
  method_name = name_ast.to_sym
105
- if is_attr_accessor?(name_ast, tenv, method_name.to_sym)
149
+ if is_attr_accessor?(tenv, method_name.to_sym)
106
150
  call_expr = Call.make(name: method_name, parent: name_ast.parent)
107
151
  get_call_expr_type(call_expr, tenv, method_name)
108
152
  else
@@ -117,15 +161,6 @@ module Yadriggy
117
161
  end
118
162
  end
119
163
 
120
- rule(Const) do
121
- v = ast.value
122
- if v == Undef
123
- DynType
124
- else
125
- InstanceType.new(v)
126
- end
127
- end
128
-
129
164
  rule(GlobalVariable) do
130
165
  v = ast.value
131
166
  if v == Undef
@@ -191,7 +226,7 @@ module Yadriggy
191
226
  binary_type(ast, right_t, left_t)
192
227
  end
193
228
 
194
- # @private
229
+ # @api private
195
230
  def binary_type(bin_expr, right_t, left_t)
196
231
  op = bin_expr.op
197
232
  case op
@@ -263,7 +298,7 @@ module Yadriggy
263
298
  end
264
299
  end
265
300
 
266
- # @private
301
+ # @api private
267
302
  # Overrides {RubyTypeChecker#get_return_type}.
268
303
  #
269
304
  def get_return_type(an_ast, mthd, new_tenv, arg_types)
@@ -3,8 +3,26 @@
3
3
  require 'ripper'
4
4
  require 'pry'
5
5
 
6
+ class Pry
7
+ class History
8
+ # @api private
9
+ # We modify Pry::History::push to record a duplicated line as well.
10
+ def push(line)
11
+ unless line.empty? || line.include?("\0")
12
+ @pusher.call(line)
13
+ @history << line
14
+ if !should_ignore?(line) && Pry.config.history.should_save
15
+ @saver.call(line)
16
+ end
17
+ end
18
+ line
19
+ end
20
+ alias << push
21
+ end
22
+ end
23
+
6
24
  module Yadriggy
7
- # @private
25
+ # @api private
8
26
  # Retrieves source code in the S-expression style.
9
27
  class SourceCode
10
28
 
@@ -24,24 +42,15 @@ module Yadriggy
24
42
 
25
43
  def self.read_pry_history
26
44
  cmds = Pry.commands
27
-
28
- # The line number seems wrong if the source code is in pry_history.
29
- # To correct the line number, a blank line is added to source.
30
- source = "\n"
31
-
32
- lineno = 0
33
- lineno1 = Pry.history.original_lines
34
- File.foreach(Pry.config.history.file) do |line|
35
- lineno += 1
36
- if lineno > lineno1
37
- if cmds.select {|k,v| v.matches?(line) }.empty?
38
- source << line
39
- else
40
- # source << "\n"
41
- end
45
+ his = Pry.history.to_a[Pry.history.original_lines ...
46
+ Pry.history.history_line_count]
47
+ his.reduce('') do |source, line|
48
+ if cmds.select {|k,v| v.matches?(line) }.empty?
49
+ source << line << "\n"
50
+ else
51
+ source # << "\n"
42
52
  end
43
53
  end
44
- source
45
54
  end
46
55
 
47
56
  def self.min(a, b)
@@ -105,6 +114,7 @@ module Yadriggy
105
114
  prog[1][2][0] == line
106
115
  end
107
116
 
117
+ # @api private
108
118
  class Cons
109
119
  include Enumerable
110
120
  attr_accessor :head, :tail
@@ -21,7 +21,9 @@ module Yadriggy
21
21
  # also defined in ast.rb
22
22
 
23
23
  # The user type (or non-terminal symbol) corresponding
24
- # to this node.
24
+ # to this node. This is effective only after checking syntax
25
+ # by {Syntax#check}.
26
+ #
25
27
  # @return [Symbol|nil] the user type.
26
28
  attr_accessor :usertype
27
29
  end
@@ -65,7 +67,7 @@ module Yadriggy
65
67
  update_hash(ast.tree.body)
66
68
  end
67
69
 
68
- # @private
70
+ # @api private
69
71
  # @param [Body] body
70
72
  # @return [void]
71
73
  def update_hash(body)
@@ -402,13 +404,25 @@ module Yadriggy
402
404
  # For example, `Binary = { op: :+ }` specifies that the `op` property
403
405
  # of `Binary` has to be `:+`. Note that the other properties such
404
406
  # as `left` and `right` are not checked. Hence, when the rules are
405
- # <pre>Binary = { op: :+ }
406
- # Unary = { op: :! }</pre>
407
+ #
408
+ # Binary = { op: :+ }
409
+ # Unary = { op: :! }
410
+ #
407
411
  # `a + -b` causes no syntax error since the unary expression `-b` is
408
412
  # the right operand of the binary expression. The rule for {Binary}
409
413
  # is passed and hence the rule for {Unary} is not applied to `-b`.
410
- # <pre>Binary = { op: :+, right: Unary }
411
- # Unary = { op: :! }</pre>
414
+ #
415
+ # Binary = { op: :+, right: Unary }
416
+ # Unary = { op: :! }
417
+ #
418
+ # If the right-hand side of = (or <=) is `nil`, then the rule always
419
+ # fails. For example,
420
+ #
421
+ # SymbolLiteral = nil
422
+ #
423
+ # This specifies that any `SymbolLiteral` node does not pass syntax
424
+ # checking. If the right-hand side is `{}`, any `SymbolLiteral` passes
425
+ # without further checking.
412
426
  #
413
427
  # An AST subtree passes syntax checking if no rule is found for that
414
428
  # subtree.
@@ -475,7 +489,8 @@ module Yadriggy
475
489
  Binary <= { left: expr, op: Symbol, right: expr }
476
490
  ArrayRef <= { array: expr, indexes: [ expr ] }
477
491
  ArrayRefField <= ArrayRef
478
- Assign <= Binary
492
+ Assign <= { left: [expr] | expr, op: Symbol,
493
+ right: [expr] | expr }
479
494
  Dots <= Binary
480
495
  Unary <= { op: Symbol, operand: expr }
481
496
  ConstPathRef <= { scope: (ConstPathRef | Const), name: Const }
@@ -499,7 +514,7 @@ module Yadriggy
499
514
  block_param: (Identifier) }
500
515
  Block <= Parameters + { body: exprs }
501
516
  Lambda <= Block
502
- Call <= { receiver: (expr), op: (Symbol), name: Identifier,
517
+ Call <= { receiver: (expr), op: (Symbol), name: (Identifier),
503
518
  args: [ expr ], block_arg: (expr), block: (Block) }
504
519
  Command <= Call
505
520
  Exprs <= { expressions: [ expr ] }
@@ -8,7 +8,7 @@ module Yadriggy
8
8
  # Don't use `is_a?` but use `has_role?` or `role`.
9
9
  #
10
10
  class Type
11
- # @private
11
+ # @api private
12
12
  def self.get_instance_method_object(recv_type, method_name)
13
13
  recv_type.get_method_object(method_name)
14
14
  end
@@ -54,7 +54,7 @@ module Yadriggy
54
54
  self == t
55
55
  end
56
56
 
57
- # @private
57
+ # @api private
58
58
  # Only {DynType}, {UnionType} and {OptionalRole} override this method.
59
59
  def is_super_of? (t)
60
60
  false
@@ -96,7 +96,7 @@ module Yadriggy
96
96
  DynType
97
97
  end
98
98
 
99
- # @private
99
+ # @api private
100
100
  # Gets a method with the given name declared in this type.
101
101
  # `nil` is returned when the method is not exactly determined.
102
102
  #
@@ -111,7 +111,7 @@ module Yadriggy
111
111
  end
112
112
  end
113
113
 
114
- # @private
114
+ # @api private
115
115
  class NonRubyType < Type
116
116
  def initialize(obj_name, type_name)
117
117
  @obj_name = obj_name
@@ -152,7 +152,7 @@ module Yadriggy
152
152
  # Dynamic type.
153
153
  DynType = NonRubyType.new('#<Yadriggy::DynType>', 'DynType')
154
154
 
155
- # @private
155
+ # @api private
156
156
  def DynType.is_super_of?(t)
157
157
  true
158
158
  end
@@ -198,17 +198,17 @@ module Yadriggy
198
198
  (normalize(self) | normalize(ut)).size <= @types.size
199
199
  end
200
200
 
201
- # @private
201
+ # @api private
202
202
  def normalize(utype)
203
203
  utype.types.map {|e| e.copy(OptionalRole) }
204
204
  end
205
205
 
206
- # @private
206
+ # @api private
207
207
  def hash
208
208
  @types.hash
209
209
  end
210
210
 
211
- # @private
211
+ # @api private
212
212
  # Check the subtype relation.
213
213
  # @param [Type] t the other type.
214
214
  # @return [Boolean] true if `self` is equivalent to `t`
@@ -244,17 +244,17 @@ module Yadriggy
244
244
  @type = t
245
245
  end
246
246
 
247
- # @private
247
+ # @api private
248
248
  def == (t)
249
249
  CommonSuperType.role(t)&.type == @type
250
250
  end
251
251
 
252
- # @private
252
+ # @api private
253
253
  def hash
254
254
  @type.hash + 1
255
255
  end
256
256
 
257
- # @private
257
+ # @api private
258
258
  # Check the subtype relation.
259
259
  # @param [Type] t the other type.
260
260
  # @return [Boolean] true if `self` is equivalent to `t`
@@ -268,7 +268,7 @@ module Yadriggy
268
268
  end
269
269
  end
270
270
 
271
- # @private
271
+ # @api private
272
272
  def get_method_object(method_name)
273
273
  nil
274
274
  end
@@ -295,17 +295,17 @@ module Yadriggy
295
295
  # by {CommonSuperType}.
296
296
  #
297
297
  class RubyClass < Type
298
- # @private
298
+ # @api private
299
299
  Table = {}
300
300
 
301
- # @private
301
+ # @api private
302
302
  def self.make(clazz)
303
303
  obj = RubyClass.new(clazz)
304
304
  Table[clazz] = obj
305
305
  obj
306
306
  end
307
307
 
308
- # @private
308
+ # @api private
309
309
  def self.set_alias(clazz, ruby_class)
310
310
  Table[clazz] = ruby_class
311
311
  end
@@ -330,12 +330,12 @@ module Yadriggy
330
330
  RubyClass.role(t)&.exact_type == @ruby_class
331
331
  end
332
332
 
333
- # @private
333
+ # @api private
334
334
  def hash
335
335
  @ruby_class.hash
336
336
  end
337
337
 
338
- # @private
338
+ # @api private
339
339
  # Check the subtype relation.
340
340
  # @param [Type] t the other type.
341
341
  # @return [Boolean] true if `self` is equivalent to `t`
@@ -353,14 +353,14 @@ module Yadriggy
353
353
  end
354
354
  end
355
355
 
356
- # @private
356
+ # @api private
357
357
  def get_method_object(method_name)
358
358
  @ruby_class.instance_method(method_name)
359
359
  rescue NameError
360
360
  Type.error_found!("no such method: #{@ruby_class}\##{method_name}")
361
361
  end
362
362
 
363
- # @private
363
+ # @api private
364
364
  def exact_type
365
365
  @ruby_class
366
366
  end
@@ -417,17 +417,17 @@ module Yadriggy
417
417
  @object = obj
418
418
  end
419
419
 
420
- # @private
420
+ # @api private
421
421
  def == (t)
422
422
  InstanceType.role(t)&.object == @object
423
423
  end
424
424
 
425
- # @private
425
+ # @api private
426
426
  def hash
427
427
  @object.hash
428
428
  end
429
429
 
430
- # @private
430
+ # @api private
431
431
  # Check the subtype relation.
432
432
  # @param [Type] t the other type.
433
433
  # @return [Boolean] true if `self` is equivalent to `t`
@@ -442,13 +442,13 @@ module Yadriggy
442
442
  end
443
443
  end
444
444
 
445
- # @private
445
+ # @api private
446
446
  # Recall that `1.class` was `Fixnum` in Ruby earlier than 2.4.
447
447
  def exact_type
448
448
  @object.is_a?(Integer) ? Integer : @object.class
449
449
  end
450
450
 
451
- # @private
451
+ # @api private
452
452
  def get_method_object(method_name)
453
453
  @object.method(method_name)
454
454
  rescue NameError
@@ -500,18 +500,18 @@ module Yadriggy
500
500
  # @return [Parameters] the method definition.
501
501
  def method_def() @method_def end
502
502
 
503
- # @private
503
+ # @api private
504
504
  def == (t)
505
505
  mt = MethodType.role(t)
506
506
  !mt.nil? && @result_type == mt.result_type && @param_types == mt.params
507
507
  end
508
508
 
509
- # @private
509
+ # @api private
510
510
  def hash
511
511
  @result_type.hash + @param_types.hash
512
512
  end
513
513
 
514
- # @private
514
+ # @api private
515
515
  def <= (t)
516
516
  if t.is_super_of?(self)
517
517
  true
@@ -584,12 +584,12 @@ module Yadriggy
584
584
  ct.args == @args
585
585
  end
586
586
 
587
- # @private
587
+ # @api private
588
588
  def hash
589
589
  @ruby_class.hash + @args.reduce(0) {|h,p| h + p.hash }
590
590
  end
591
591
 
592
- # @private
592
+ # @api private
593
593
  # Check the subtype relation.
594
594
  # @param [Type] t the other type.
595
595
  # @return [Boolean] true if `self` is equivalent to `t`
@@ -608,7 +608,7 @@ module Yadriggy
608
608
  end
609
609
  end
610
610
 
611
- # @private
611
+ # @api private
612
612
  def exact_type
613
613
  @ruby_class
614
614
  end
@@ -636,7 +636,7 @@ module Yadriggy
636
636
  @type = type
637
637
  end
638
638
 
639
- # @private
639
+ # @api private
640
640
  def copy(without_role)
641
641
  chain = @type.copy(without_role)
642
642
  if self.is_a?(without_role)
@@ -652,7 +652,7 @@ module Yadriggy
652
652
  end
653
653
  end
654
654
 
655
- # @private
655
+ # @api private
656
656
  def update_type(t)
657
657
  @type = t
658
658
  end
@@ -663,22 +663,22 @@ module Yadriggy
663
663
  @type == t
664
664
  end
665
665
 
666
- # @private
666
+ # @api private
667
667
  def hash
668
668
  @type.hash
669
669
  end
670
670
 
671
- # @private
671
+ # @api private
672
672
  def <= (t)
673
673
  @type <= t
674
674
  end
675
675
 
676
- # @private
676
+ # @api private
677
677
  def is_super_of?(t)
678
678
  @type.is_super_of?(t)
679
679
  end
680
680
 
681
- # @private
681
+ # @api private
682
682
  def has_role?(a_role)
683
683
  if self.is_a?(a_role)
684
684
  self
@@ -687,12 +687,12 @@ module Yadriggy
687
687
  end
688
688
  end
689
689
 
690
- # @private
690
+ # @api private
691
691
  def exact_type
692
692
  @type.exact_type
693
693
  end
694
694
 
695
- # @private
695
+ # @api private
696
696
  def get_method_object(method_name)
697
697
  Type.get_instance_method_object(@type, method_name)
698
698
  end