steep 0.1.0.pre2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +1 -1
  3. data/README.md +146 -33
  4. data/bin/smoke_runner.rb +43 -10
  5. data/lib/steep/ast/annotation/collection.rb +93 -0
  6. data/lib/steep/ast/annotation.rb +131 -0
  7. data/lib/steep/ast/buffer.rb +47 -0
  8. data/lib/steep/ast/location.rb +82 -0
  9. data/lib/steep/ast/method_type.rb +116 -0
  10. data/lib/steep/ast/signature/class.rb +33 -0
  11. data/lib/steep/ast/signature/const.rb +17 -0
  12. data/lib/steep/ast/signature/env.rb +123 -0
  13. data/lib/steep/ast/signature/extension.rb +21 -0
  14. data/lib/steep/ast/signature/gvar.rb +17 -0
  15. data/lib/steep/ast/signature/interface.rb +31 -0
  16. data/lib/steep/ast/signature/members.rb +71 -0
  17. data/lib/steep/ast/signature/module.rb +21 -0
  18. data/lib/steep/ast/type_params.rb +13 -0
  19. data/lib/steep/ast/types/any.rb +39 -0
  20. data/lib/steep/ast/types/bot.rb +39 -0
  21. data/lib/steep/ast/types/class.rb +35 -0
  22. data/lib/steep/ast/types/helper.rb +21 -0
  23. data/lib/steep/ast/types/instance.rb +39 -0
  24. data/lib/steep/ast/types/intersection.rb +74 -0
  25. data/lib/steep/ast/types/name.rb +124 -0
  26. data/lib/steep/ast/types/self.rb +39 -0
  27. data/lib/steep/ast/types/top.rb +39 -0
  28. data/lib/steep/ast/types/union.rb +74 -0
  29. data/lib/steep/ast/types/var.rb +57 -0
  30. data/lib/steep/ast/types/void.rb +35 -0
  31. data/lib/steep/cli.rb +28 -1
  32. data/lib/steep/drivers/annotations.rb +32 -0
  33. data/lib/steep/drivers/check.rb +53 -77
  34. data/lib/steep/drivers/scaffold.rb +303 -0
  35. data/lib/steep/drivers/utils/each_signature.rb +66 -0
  36. data/lib/steep/drivers/utils/validator.rb +115 -0
  37. data/lib/steep/drivers/validate.rb +39 -0
  38. data/lib/steep/errors.rb +291 -19
  39. data/lib/steep/interface/abstract.rb +44 -0
  40. data/lib/steep/interface/builder.rb +470 -0
  41. data/lib/steep/interface/instantiated.rb +126 -0
  42. data/lib/steep/interface/ivar_chain.rb +26 -0
  43. data/lib/steep/interface/method.rb +60 -0
  44. data/lib/steep/{interface.rb → interface/method_type.rb} +111 -100
  45. data/lib/steep/interface/substitution.rb +65 -0
  46. data/lib/steep/module_name.rb +116 -0
  47. data/lib/steep/parser.rb +1314 -814
  48. data/lib/steep/parser.y +536 -175
  49. data/lib/steep/source.rb +220 -25
  50. data/lib/steep/subtyping/check.rb +673 -0
  51. data/lib/steep/subtyping/constraints.rb +275 -0
  52. data/lib/steep/subtyping/relation.rb +41 -0
  53. data/lib/steep/subtyping/result.rb +126 -0
  54. data/lib/steep/subtyping/trace.rb +48 -0
  55. data/lib/steep/subtyping/variable_occurrence.rb +49 -0
  56. data/lib/steep/subtyping/variable_variance.rb +69 -0
  57. data/lib/steep/type_construction.rb +1630 -524
  58. data/lib/steep/type_inference/block_params.rb +100 -0
  59. data/lib/steep/type_inference/constant_env.rb +55 -0
  60. data/lib/steep/type_inference/send_args.rb +222 -0
  61. data/lib/steep/type_inference/type_env.rb +226 -0
  62. data/lib/steep/type_name.rb +27 -7
  63. data/lib/steep/typing.rb +4 -0
  64. data/lib/steep/version.rb +1 -1
  65. data/lib/steep.rb +71 -16
  66. data/smoke/and/a.rb +4 -2
  67. data/smoke/array/a.rb +4 -5
  68. data/smoke/array/b.rb +4 -4
  69. data/smoke/block/a.rb +2 -2
  70. data/smoke/block/a.rbi +2 -0
  71. data/smoke/block/b.rb +15 -0
  72. data/smoke/case/a.rb +3 -3
  73. data/smoke/class/a.rb +3 -3
  74. data/smoke/class/b.rb +0 -2
  75. data/smoke/class/d.rb +2 -2
  76. data/smoke/class/e.rb +1 -1
  77. data/smoke/class/f.rb +2 -2
  78. data/smoke/class/g.rb +8 -0
  79. data/smoke/const/a.rb +3 -3
  80. data/smoke/dstr/a.rb +1 -1
  81. data/smoke/ensure/a.rb +22 -0
  82. data/smoke/enumerator/a.rb +6 -6
  83. data/smoke/enumerator/b.rb +22 -0
  84. data/smoke/extension/a.rb +2 -2
  85. data/smoke/extension/b.rb +3 -3
  86. data/smoke/extension/c.rb +1 -1
  87. data/smoke/hello/hello.rb +2 -2
  88. data/smoke/if/a.rb +4 -2
  89. data/smoke/kwbegin/a.rb +8 -0
  90. data/smoke/literal/a.rb +5 -5
  91. data/smoke/method/a.rb +5 -5
  92. data/smoke/method/a.rbi +4 -0
  93. data/smoke/method/b.rb +29 -0
  94. data/smoke/module/a.rb +3 -3
  95. data/smoke/module/a.rbi +9 -0
  96. data/smoke/module/b.rb +2 -2
  97. data/smoke/module/c.rb +1 -1
  98. data/smoke/module/d.rb +5 -0
  99. data/smoke/module/e.rb +13 -0
  100. data/smoke/module/f.rb +13 -0
  101. data/smoke/rescue/a.rb +62 -0
  102. data/smoke/super/a.rb +2 -2
  103. data/smoke/type_case/a.rb +35 -0
  104. data/smoke/yield/a.rb +2 -2
  105. data/stdlib/builtin.rbi +463 -24
  106. data/steep.gemspec +3 -2
  107. metadata +91 -29
  108. data/lib/steep/annotation.rb +0 -223
  109. data/lib/steep/signature/class.rb +0 -450
  110. data/lib/steep/signature/extension.rb +0 -51
  111. data/lib/steep/signature/interface.rb +0 -49
  112. data/lib/steep/types/any.rb +0 -31
  113. data/lib/steep/types/class.rb +0 -27
  114. data/lib/steep/types/instance.rb +0 -27
  115. data/lib/steep/types/merge.rb +0 -32
  116. data/lib/steep/types/name.rb +0 -57
  117. data/lib/steep/types/union.rb +0 -42
  118. data/lib/steep/types/var.rb +0 -38
  119. data/lib/steep/types.rb +0 -4
@@ -0,0 +1,82 @@
1
+ module Steep
2
+ module AST
3
+ class Location
4
+ attr_reader :buffer
5
+ attr_reader :start_pos
6
+ attr_reader :end_pos
7
+
8
+ def initialize(buffer:, start_pos:, end_pos:)
9
+ @buffer = buffer
10
+ @start_pos = start_pos
11
+ @end_pos = end_pos
12
+ end
13
+
14
+ def inspect
15
+ "#<#{self.class}:#{self.__id__} @buffer=..., @start_pos=#{start_pos}, @end_pos=#{end_pos}, source='#{source.lines.first}', start_line=#{start_line}, start_column=#{start_column}>"
16
+ end
17
+
18
+ def name
19
+ buffer.name
20
+ end
21
+
22
+ def start_line
23
+ start_loc[0]
24
+ end
25
+
26
+ def start_column
27
+ start_loc[1]
28
+ end
29
+
30
+ def end_line
31
+ end_loc[0]
32
+ end
33
+
34
+ def end_column
35
+ end_loc[1]
36
+ end
37
+
38
+ def start_loc
39
+ @start_loc ||= buffer.pos_to_loc(start_pos)
40
+ end
41
+
42
+ def end_loc
43
+ @end_loc ||= buffer.pos_to_loc(end_pos)
44
+ end
45
+
46
+ def source
47
+ @source ||= buffer.source(start_pos...end_pos)
48
+ end
49
+
50
+ def to_s
51
+ "#{start_line}:#{start_column}...#{end_line}:#{end_column}"
52
+ end
53
+
54
+ def ==(other)
55
+ other.is_a?(Location) &&
56
+ other.buffer == buffer &&
57
+ other.start_pos == start_pos &&
58
+ other.end_pos == end_pos
59
+ end
60
+
61
+ def +(other)
62
+ raise "Invalid concat: buffer=#{buffer.name}, other.buffer=#{other.buffer.name}" unless other.buffer == buffer
63
+
64
+ self.class.new(buffer: buffer,
65
+ start_pos: start_pos,
66
+ end_pos: other.end_pos)
67
+ end
68
+
69
+ def self.concat(*locations)
70
+ locations.inject {|l1, l2|
71
+ l1 + l2
72
+ }
73
+ end
74
+
75
+ def pred?(loc)
76
+ loc.is_a?(Location) &&
77
+ loc.name == name &&
78
+ loc.start_pos == end_pos
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,116 @@
1
+ module Steep
2
+ module AST
3
+ class MethodType
4
+ module Params
5
+ class Base
6
+ attr_reader :location
7
+
8
+ def initialize(location:)
9
+ @location = location
10
+ end
11
+
12
+ def update_location(location)
13
+ dup.instance_eval do
14
+ @location = location
15
+ self
16
+ end
17
+ end
18
+ end
19
+
20
+ class Required < Base
21
+ attr_reader :type
22
+ attr_reader :next_params
23
+
24
+ def initialize(location:, type:, next_params: nil)
25
+ super(location: location)
26
+ @type = type
27
+ @next_params = next_params
28
+ end
29
+ end
30
+
31
+ class Optional < Base
32
+ attr_reader :type
33
+ attr_reader :next_params
34
+
35
+ def initialize(location:, type:, next_params: nil)
36
+ super(location: location)
37
+ @type = type
38
+ @next_params = next_params
39
+ end
40
+ end
41
+
42
+ class Rest < Base
43
+ attr_reader :type
44
+ attr_reader :next_params
45
+
46
+ def initialize(location:, type:, next_params: nil)
47
+ super(location: location)
48
+ @type = type
49
+ @next_params = next_params
50
+ end
51
+ end
52
+
53
+ class RequiredKeyword < Base
54
+ attr_reader :name
55
+ attr_reader :type
56
+ attr_reader :next_params
57
+
58
+ def initialize(location:, name:, type:, next_params: nil)
59
+ super(location: location)
60
+ @name = name
61
+ @type = type
62
+ @next_params = next_params
63
+ end
64
+ end
65
+
66
+ class OptionalKeyword < Base
67
+ attr_reader :name
68
+ attr_reader :type
69
+ attr_reader :next_params
70
+
71
+ def initialize(location:, name:, type:, next_params: nil)
72
+ super(location: location)
73
+ @name = name
74
+ @type = type
75
+ @next_params = next_params
76
+ end
77
+ end
78
+
79
+ class RestKeyword < Base
80
+ attr_reader :type
81
+
82
+ def initialize(location:, type:)
83
+ super(location: location)
84
+ @type = type
85
+ end
86
+ end
87
+ end
88
+
89
+ class Block
90
+ attr_reader :location
91
+ attr_reader :params
92
+ attr_reader :return_type
93
+
94
+ def initialize(location:, params:, return_type:)
95
+ @location = location
96
+ @params = params
97
+ @return_type = return_type
98
+ end
99
+ end
100
+
101
+ attr_reader :location
102
+ attr_reader :type_params
103
+ attr_reader :params
104
+ attr_reader :block
105
+ attr_reader :return_type
106
+
107
+ def initialize(location:, type_params:, params:, block:, return_type:)
108
+ @location = location
109
+ @type_params = type_params
110
+ @params = params
111
+ @block = block
112
+ @return_type = return_type
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,33 @@
1
+ module Steep
2
+ module AST
3
+ module Signature
4
+ class SuperClass
5
+ attr_reader :location
6
+ attr_reader :name
7
+ attr_reader :args
8
+
9
+ def initialize(name:, args:, location:)
10
+ @name = name
11
+ @args = args
12
+ @location = location
13
+ end
14
+ end
15
+
16
+ class Class
17
+ attr_reader :location
18
+ attr_reader :name
19
+ attr_reader :params
20
+ attr_reader :super_class
21
+ attr_reader :members
22
+
23
+ def initialize(name:, params:, super_class:, location:, members:)
24
+ @name = name
25
+ @params = params
26
+ @super_class = super_class
27
+ @location = location
28
+ @members = members
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ module Steep
2
+ module AST
3
+ module Signature
4
+ class Const
5
+ attr_reader :location
6
+ attr_reader :name
7
+ attr_reader :type
8
+
9
+ def initialize(location:, name:, type:)
10
+ @location = location
11
+ @name = name
12
+ @type = type
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,123 @@
1
+ module Steep
2
+ module AST
3
+ module Signature
4
+ class Env
5
+ attr_reader :modules
6
+ attr_reader :classes
7
+ attr_reader :extensions
8
+ attr_reader :interfaces
9
+ attr_reader :constants
10
+ attr_reader :globals
11
+
12
+ def initialize()
13
+ @modules = {}
14
+ @classes = {}
15
+ @extensions = {}
16
+ @interfaces = {}
17
+ @constants = {}
18
+ @globals = {}
19
+ end
20
+
21
+ def add(sig)
22
+ case sig
23
+ when Signature::Class
24
+ raise "Duplicated class: #{sig.name}" if classes.key?(sig.name.absolute!) || modules.key?(sig.name.absolute!)
25
+ classes[sig.name.absolute!] = sig
26
+ when Signature::Module
27
+ raise "Duplicated module: #{sig.name}" if classes.key?(sig.name.absolute!) || modules.key?(sig.name.absolute!)
28
+ modules[sig.name.absolute!] = sig
29
+ when Signature::Interface
30
+ raise "Duplicated interface: #{sig.name}" if interfaces.key?(sig.name)
31
+ interfaces[sig.name] = sig
32
+ when Signature::Extension
33
+ extensions[sig.module_name.absolute!] ||= []
34
+ if extensions[sig.module_name.absolute!].any? {|ext| ext.name == sig.name }
35
+ raise "Duplicated extension: #{sig.module_name.absolute!} (#{sig.name})"
36
+ end
37
+ extensions[sig.module_name.absolute!] << sig
38
+ when Signature::Const
39
+ constants[sig.name.absolute!] = sig
40
+ when Signature::Gvar
41
+ raise "Duplicated global: #{sig.name}" if globals.key?(sig.name)
42
+ globals[sig.name] = sig
43
+ else
44
+ raise "Unknown signature:: #{sig}"
45
+ end
46
+ end
47
+
48
+ def find_module(name, current_module: nil)
49
+ find_name(modules, name, current_module: current_module) or raise "Unknown module: #{name}"
50
+ end
51
+
52
+ def find_class(name, current_module: nil)
53
+ find_name(classes, name, current_module: current_module) or raise "Unknown class: #{name}"
54
+ end
55
+
56
+ def find_class_or_module(name, current_module: nil)
57
+ sig =
58
+ find_name(modules, name, current_module: current_module) ||
59
+ find_name(classes, name, current_module: current_module)
60
+
61
+ sig or raise "Unknown class/module: #{name}}"
62
+ end
63
+
64
+ def find_extensions(name, current_module: nil)
65
+ find_name(extensions, name, current_module: current_module) || []
66
+ end
67
+
68
+ def find_const(name, current_module: nil)
69
+ find_name(constants, name, current_module: current_module)
70
+ end
71
+
72
+ def find_gvar(name)
73
+ globals[name]
74
+ end
75
+
76
+ def find_name(hash, name, current_module:)
77
+ if current_module
78
+ hash[current_module + name] || find_name(hash, name, current_module: current_module.parent)
79
+ else
80
+ hash[name.absolute!]
81
+ end
82
+ end
83
+
84
+ def find_interface(name)
85
+ interfaces[name] or raise "Unknown interface: #{name}"
86
+ end
87
+
88
+ def module?(type_name, current_module: nil)
89
+ name = type_name.map_module_name {|m| current_module ? current_module + m : m.absolute! }.name
90
+ modules.key?(name)
91
+ end
92
+
93
+ def class?(type_name, current_module: nil)
94
+ name = type_name.map_module_name {|m| current_module ? current_name + m : m.absolute! }.name
95
+ classes.key?(name)
96
+ end
97
+
98
+ def class_name?(name, current_module: nil)
99
+ classes.key?(current_module ? current_module + name : name.absolute!)
100
+ end
101
+
102
+ def module_name?(name, current_module: nil)
103
+ modules.key?(current_module ? current_module + name : name.absolute!)
104
+ end
105
+
106
+ def const_name?(name, current_module: nil)
107
+ constants.key?(current_module ? current_module + name : name.absolute!)
108
+ end
109
+
110
+ def each(&block)
111
+ if block_given?
112
+ classes.each_value(&block)
113
+ modules.each_value(&block)
114
+ interfaces.each_value(&block)
115
+ constants.each_value(&block)
116
+ else
117
+ enum_for :each
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,21 @@
1
+ module Steep
2
+ module AST
3
+ module Signature
4
+ class Extension
5
+ attr_reader :location
6
+ attr_reader :module_name
7
+ attr_reader :name
8
+ attr_reader :members
9
+ attr_reader :params
10
+
11
+ def initialize(location:, module_name:, params:, name:, members:)
12
+ @location = location
13
+ @module_name = module_name
14
+ @params = params
15
+ @name = name
16
+ @members = members
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module Steep
2
+ module AST
3
+ module Signature
4
+ class Gvar
5
+ attr_reader :location
6
+ attr_reader :name
7
+ attr_reader :type
8
+
9
+ def initialize(location:, name:, type:)
10
+ @location = location
11
+ @name = name
12
+ @type = type
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ module Steep
2
+ module AST
3
+ module Signature
4
+ class Interface
5
+ class Method
6
+ attr_reader :location
7
+ attr_reader :name
8
+ attr_reader :types
9
+
10
+ def initialize(location:, name:, types:)
11
+ @location = location
12
+ @name = name
13
+ @types = types
14
+ end
15
+ end
16
+
17
+ attr_reader :location
18
+ attr_reader :name
19
+ attr_reader :params
20
+ attr_reader :methods
21
+
22
+ def initialize(location:, name:, params:, methods:)
23
+ @location = location
24
+ @name = name
25
+ @params = params
26
+ @methods = methods
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,71 @@
1
+ module Steep
2
+ module AST
3
+ module Signature
4
+ module Members
5
+ class Include
6
+ attr_reader :location
7
+ attr_reader :name
8
+ attr_reader :args
9
+
10
+ def initialize(location:, name:, args:)
11
+ @location = location
12
+ @name = name
13
+ @args = args
14
+ end
15
+ end
16
+
17
+ class Extend
18
+ attr_reader :location
19
+ attr_reader :name
20
+ attr_reader :args
21
+
22
+ def initialize(location:, name:, args:)
23
+ @location = location
24
+ @name = name
25
+ @args = args
26
+ end
27
+ end
28
+
29
+ class Method
30
+ attr_reader :location
31
+ attr_reader :name
32
+ attr_reader :kind
33
+ attr_reader :types
34
+ attr_reader :attributes
35
+
36
+ def initialize(location:, name:, kind:, types:, attributes:)
37
+ @location = location
38
+ @name = name
39
+ @kind = kind
40
+ @types = types
41
+ @attributes = attributes
42
+ end
43
+
44
+ def module_method?
45
+ kind == :module || kind == :module_instance
46
+ end
47
+
48
+ def instance_method?
49
+ kind == :instance || kind == :module_instance
50
+ end
51
+
52
+ def constructor?
53
+ attributes.include?(:constructor)
54
+ end
55
+ end
56
+
57
+ class Ivar
58
+ attr_reader :location
59
+ attr_reader :name
60
+ attr_reader :type
61
+
62
+ def initialize(location:, name:, type:)
63
+ @location = location
64
+ @name = name
65
+ @type = type
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,21 @@
1
+ module Steep
2
+ module AST
3
+ module Signature
4
+ class Module
5
+ attr_reader :location
6
+ attr_reader :name
7
+ attr_reader :params
8
+ attr_reader :self_type
9
+ attr_reader :members
10
+
11
+ def initialize(name:, location:, params:, self_type:, members:)
12
+ @name = name
13
+ @location = location
14
+ @params = params
15
+ @self_type = self_type
16
+ @members = members
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ module Steep
2
+ module AST
3
+ class TypeParams
4
+ attr_reader :location
5
+ attr_reader :variables
6
+
7
+ def initialize(location: nil, variables:)
8
+ @location = location
9
+ @variables = variables
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,39 @@
1
+ module Steep
2
+ module AST
3
+ module Types
4
+ class Any
5
+ attr_reader :location
6
+
7
+ def initialize(location: nil)
8
+ @location = location
9
+ end
10
+
11
+ def ==(other)
12
+ other.is_a?(Any)
13
+ end
14
+
15
+ def hash
16
+ self.class.hash
17
+ end
18
+
19
+ alias eql? ==
20
+
21
+ def subst(s)
22
+ self
23
+ end
24
+
25
+ def to_s
26
+ "any"
27
+ end
28
+
29
+ def free_variables
30
+ Set.new
31
+ end
32
+
33
+ def level
34
+ [1]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ module Steep
2
+ module AST
3
+ module Types
4
+ class Bot
5
+ attr_reader :location
6
+
7
+ def initialize(location: nil)
8
+ @location = location
9
+ end
10
+
11
+ def ==(other)
12
+ other.is_a?(Bot)
13
+ end
14
+
15
+ def hash
16
+ self.class.hash
17
+ end
18
+
19
+ alias eql? ==
20
+
21
+ def subst(s)
22
+ self
23
+ end
24
+
25
+ def to_s
26
+ "⟘"
27
+ end
28
+
29
+ def free_variables
30
+ Set.new
31
+ end
32
+
33
+ def level
34
+ [2]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ module Steep
2
+ module AST
3
+ module Types
4
+ class Class
5
+ attr_reader :location
6
+
7
+ def initialize(location: nil)
8
+ @location = location
9
+ end
10
+
11
+ def ==(other)
12
+ other.is_a?(Class)
13
+ end
14
+
15
+ def hash
16
+ self.class.hash
17
+ end
18
+
19
+ alias eql? ==
20
+
21
+ def subst(s)
22
+ s.module_type or raise "Unexpected substitution: #{inspect}"
23
+ end
24
+
25
+ def free_variables
26
+ Set.new
27
+ end
28
+
29
+ def level
30
+ [0]
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ module Steep
2
+ module AST
3
+ module Types
4
+ module Helper
5
+ module ChildrenLevel
6
+ def level_of_children(children)
7
+ children.map(&:level).sort {|a, b| b.size <=> a.size }.inject() do |a, b|
8
+ a.zip(b).map do |(x, y)|
9
+ if x && y
10
+ x + y
11
+ else
12
+ x || y
13
+ end
14
+ end
15
+ end || []
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end