taipo 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/taipo.rb +0 -86
- data/lib/taipo/cache.rb +2 -1
- data/lib/taipo/check.rb +9 -7
- data/lib/taipo/parser.rb +292 -153
- data/lib/taipo/parser/stack.rb +143 -0
- data/lib/taipo/parser/syntax_state.rb +12 -11
- data/lib/taipo/parser/validater.rb +126 -58
- data/lib/taipo/refinements.rb +30 -0
- data/lib/taipo/type_element.rb +50 -68
- data/lib/taipo/type_element/children.rb +47 -0
- data/lib/taipo/type_element/constraint.rb +11 -15
- data/lib/taipo/type_element/constraints.rb +36 -0
- data/lib/taipo/type_elements.rb +33 -0
- data/lib/taipo/utilities.rb +81 -0
- data/lib/taipo/version.rb +1 -1
- metadata +8 -3
- data/lib/taipo/type_element/child_type.rb +0 -40
@@ -0,0 +1,30 @@
|
|
1
|
+
module Taipo
|
2
|
+
|
3
|
+
# Refinements on core classes used in Taipo
|
4
|
+
#
|
5
|
+
# @since 1.4.0
|
6
|
+
# @api private
|
7
|
+
module Refinements
|
8
|
+
|
9
|
+
# Refinements to String
|
10
|
+
#
|
11
|
+
# @since 1.4.0
|
12
|
+
# @api private
|
13
|
+
refine String do
|
14
|
+
|
15
|
+
# Check if the string represents a bare constraint
|
16
|
+
#
|
17
|
+
# Taipo allows certain bare constraints to be written in type
|
18
|
+
# definitions. A bare constraint can be either an instance method
|
19
|
+
# or a symbol.
|
20
|
+
#
|
21
|
+
# @return [Boolean] the result
|
22
|
+
#
|
23
|
+
# @since 1.4.0
|
24
|
+
# @api private
|
25
|
+
def bare_constraint?
|
26
|
+
(self[0] == ':' && self[1] != ':') || self[0] == '#'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/taipo/type_element.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'taipo/exceptions'
|
2
|
-
require 'taipo/
|
2
|
+
require 'taipo/type_elements'
|
3
|
+
require 'taipo/type_element/children'
|
4
|
+
require 'taipo/type_element/constraints'
|
3
5
|
require 'taipo/type_element/constraint'
|
4
6
|
|
5
7
|
module Taipo
|
@@ -16,13 +18,13 @@ module Taipo
|
|
16
18
|
# @api private
|
17
19
|
attr_accessor :name
|
18
20
|
|
19
|
-
# The
|
21
|
+
# The children for this element
|
20
22
|
#
|
21
|
-
# @since 1.
|
23
|
+
# @since 1.4.0
|
22
24
|
# @api private
|
23
|
-
attr_accessor :
|
25
|
+
attr_accessor :children
|
24
26
|
|
25
|
-
# The
|
27
|
+
# The constraints for this element
|
26
28
|
#
|
27
29
|
# @since 1.0.0
|
28
30
|
# @api private
|
@@ -31,53 +33,38 @@ module Taipo
|
|
31
33
|
# Initialize a new type element
|
32
34
|
#
|
33
35
|
# @param name [String] the name of this type
|
34
|
-
# @param
|
35
|
-
#
|
36
|
-
# @param constraints [Array<Taipo::TypeElement::Constraints>|NilClass]
|
37
|
-
#
|
36
|
+
# @param children [Taipo::TypeElement::Children|NilClass] the children for
|
37
|
+
# this type
|
38
|
+
# @param constraints [Array<Taipo::TypeElement::Constraints>|NilClass] the
|
39
|
+
# constraints for this type
|
38
40
|
#
|
39
|
-
# @raise [::TypeError] if +name+, +
|
41
|
+
# @raise [::TypeError] if +name+, +children+ or +constraints+ was of the
|
40
42
|
# wrong type
|
41
|
-
# @raise [::ArgumentError] if +name+, +
|
42
|
-
# blank
|
43
|
-
# is a duck type (ie. a method the type responds to) or a symbol
|
43
|
+
# @raise [::ArgumentError] if +name+, +children+ or +constraints+ was
|
44
|
+
# blank/empty
|
44
45
|
#
|
45
46
|
# @since 1.0.0
|
46
47
|
# @api private
|
47
|
-
def initialize(name:,
|
48
|
+
def initialize(name:, children: nil, constraints: nil)
|
48
49
|
msg = 'Argument name was not a String.'
|
49
50
|
raise ::TypeError, msg unless name.is_a? String
|
50
51
|
msg = 'Argument name was an empty string.'
|
51
|
-
raise ::ArgumentError
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
msg = 'Argument constraints was not
|
60
|
-
raise ::TypeError
|
52
|
+
raise ::ArgumentError if name.empty?
|
53
|
+
|
54
|
+
msg = 'Argument children was not a Taipo::TypeElement::Children.'
|
55
|
+
raise ::TypeError unless children.nil? ||
|
56
|
+
children.is_a?(Taipo::TypeElement::Children)
|
57
|
+
msg = 'Argument children was empty.'
|
58
|
+
raise ::ArgumentError if !children.nil? && children.empty?
|
59
|
+
|
60
|
+
msg = 'Argument constraints was not a Taipo::TypeElement::Constraints.'
|
61
|
+
raise ::TypeError unless constraints.nil? ||
|
62
|
+
constraints.is_a?(Taipo::TypeElement::Constraints)
|
61
63
|
msg = 'Argument constraints was empty.'
|
62
|
-
raise ::ArgumentError
|
63
|
-
|
64
|
-
if Taipo.instance_method?(name) || Taipo.symbol?(name)
|
65
|
-
msg = 'Argument child_type should have been nil.'
|
66
|
-
raise ::ArgumentError, msg unless child_type.nil?
|
67
|
-
msg = 'Argument constraints should have been nil.'
|
68
|
-
raise ::ArgumentError, msg unless constraints.nil?
|
69
|
-
|
70
|
-
constraints = if Taipo.instance_method? name
|
71
|
-
[Taipo::TypeElement::Constraint.new(name: nil,
|
72
|
-
value: name[1..-1])]
|
73
|
-
elsif Taipo.symbol? name
|
74
|
-
[Taipo::TypeElement::Constraint.new(name: 'val',
|
75
|
-
value: name)]
|
76
|
-
end
|
77
|
-
name = 'Object'
|
78
|
-
end
|
64
|
+
raise ::ArgumentError if !constraints.nil? && constraints.empty?
|
65
|
+
|
79
66
|
@name = name
|
80
|
-
@
|
67
|
+
@children = children
|
81
68
|
@constraints = constraints
|
82
69
|
end
|
83
70
|
|
@@ -95,21 +82,21 @@ module Taipo
|
|
95
82
|
msg = 'Object to be compared must be of type Taipo::TypeElement.'
|
96
83
|
raise ::TypeError, msg unless comp.is_a? Taipo::TypeElement
|
97
84
|
|
98
|
-
@name == comp.name && @
|
85
|
+
@name == comp.name && @children == comp.children
|
99
86
|
end
|
100
87
|
|
101
88
|
# Set the element's constraints to +csts+
|
102
89
|
#
|
103
|
-
# @param csts [
|
90
|
+
# @param csts [Taipo::TypeElement::Constraints] the constraints
|
104
91
|
#
|
105
|
-
# @raise [::TypeError] if +csts+ was not
|
92
|
+
# @raise [::TypeError] if +csts+ was not a Taipo::TypeElement::Constraints
|
106
93
|
# @raise [Taipo::SyntaxError] if there are constraints with the same name
|
107
94
|
#
|
108
95
|
# @since 1.0.0
|
109
96
|
# @api private
|
110
97
|
def constraints=(csts)
|
111
|
-
msg = 'Argument csts was not
|
112
|
-
raise ::TypeError, msg unless csts.is_a?
|
98
|
+
msg = 'Argument csts was not a Taipo::TypeElement::Constraints.'
|
99
|
+
raise ::TypeError, msg unless csts.is_a? Taipo::TypeElement::Constraints
|
113
100
|
|
114
101
|
names = Hash.new
|
115
102
|
csts.each do |c|
|
@@ -137,7 +124,8 @@ module Taipo
|
|
137
124
|
# @api private
|
138
125
|
def match?(arg)
|
139
126
|
return true if optional? && arg.nil?
|
140
|
-
|
127
|
+
|
128
|
+
match_class?(arg) && match_constraints?(arg) && match_children?(arg)
|
141
129
|
end
|
142
130
|
|
143
131
|
# Check if the class of the argument itself matches this element
|
@@ -161,16 +149,16 @@ module Taipo
|
|
161
149
|
end
|
162
150
|
end
|
163
151
|
|
164
|
-
# Check if the class of the argument's
|
152
|
+
# Check if the class of the argument's children match
|
165
153
|
#
|
166
154
|
# @param arg [Object] the argument to compare
|
167
155
|
#
|
168
156
|
# @return [Boolean] the result
|
169
157
|
#
|
170
|
-
# @since 1.
|
158
|
+
# @since 1.4.0
|
171
159
|
# @api private
|
172
|
-
def
|
173
|
-
self_childless = @
|
160
|
+
def match_children?(arg)
|
161
|
+
self_childless = @children.nil?
|
174
162
|
arg_childless = !arg.is_a?(Enumerable) || arg.count == 0
|
175
163
|
return true if self_childless
|
176
164
|
return false if !self_childless && arg_childless
|
@@ -178,11 +166,11 @@ module Taipo
|
|
178
166
|
arg.all? do |a|
|
179
167
|
if !arg.is_a?(Array) && a.is_a?(Array)
|
180
168
|
a.each.with_index.reduce(nil) do |memo,(component,index)|
|
181
|
-
result = @
|
169
|
+
result = @children[index].any? { |c| c.match? component }
|
182
170
|
(memo.nil?) ? result : memo && result
|
183
171
|
end
|
184
172
|
else # The elements of this collection have no components
|
185
|
-
@
|
173
|
+
@children.first.any? { |c| c.match? a }
|
186
174
|
end
|
187
175
|
end
|
188
176
|
end
|
@@ -198,9 +186,7 @@ module Taipo
|
|
198
186
|
def match_constraints?(arg)
|
199
187
|
return true if @constraints.nil?
|
200
188
|
|
201
|
-
@constraints.all?
|
202
|
-
c.constrain?(arg)
|
203
|
-
end
|
189
|
+
@constraints.all? { |c| c.constrain?(arg) }
|
204
190
|
end
|
205
191
|
|
206
192
|
# Check whether this element is an optional
|
@@ -209,7 +195,8 @@ module Taipo
|
|
209
195
|
# Taipo borrows the syntax used in some other languages of denoting
|
210
196
|
# optional types by appending a question mark to the end of the class name.
|
211
197
|
#
|
212
|
-
# @note This merely checks whether
|
198
|
+
# @note This merely checks whether {Taipo::TypeElement#name} ends in a
|
199
|
+
# question mark.
|
213
200
|
#
|
214
201
|
# @return [Boolean] the result
|
215
202
|
#
|
@@ -221,20 +208,15 @@ module Taipo
|
|
221
208
|
|
222
209
|
# Return the String representation of this TypeElement
|
223
210
|
#
|
211
|
+
# @return [String] the representation as a String
|
212
|
+
#
|
224
213
|
# @since 1.1.0
|
225
214
|
# @api private
|
226
215
|
def to_s
|
227
216
|
name_str = @name
|
228
|
-
|
229
|
-
constraints_str =
|
230
|
-
|
231
|
-
else
|
232
|
-
inner = @constraints.reduce('') do |memo,c|
|
233
|
-
(memo == '') ? c.to_s : memo + ',' + c.to_s
|
234
|
-
end
|
235
|
-
'(' + inner + ')'
|
236
|
-
end
|
237
|
-
name_str + child_type_str + constraints_str
|
217
|
+
children_str = (@children.nil?) ? '' : @children.to_s
|
218
|
+
constraints_str = (@constraints.nil?) ? '' : @constraints.to_s
|
219
|
+
name_str + children_str + constraints_str
|
238
220
|
end
|
239
221
|
end
|
240
222
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'taipo/type_elements'
|
2
|
+
|
3
|
+
module Taipo
|
4
|
+
class TypeElement
|
5
|
+
|
6
|
+
# A set of {Taipo::TypeElements} representing the types of the children of
|
7
|
+
# a collection type
|
8
|
+
#
|
9
|
+
# @since 1.4.0
|
10
|
+
# @api private
|
11
|
+
class Children < Array
|
12
|
+
|
13
|
+
# Initialize a new set of children
|
14
|
+
#
|
15
|
+
# @note The +children+ argument is an array of {Taipo::TypeElements}
|
16
|
+
# because the element returned by an enumerator for a collection can
|
17
|
+
# consist of multiple components (eg. in a Hash, where it consists of
|
18
|
+
# two elements).
|
19
|
+
#
|
20
|
+
# @param children [Array<Taipo::TypeElements>] the components that make
|
21
|
+
# up the children of the collection
|
22
|
+
#
|
23
|
+
# @since 1.4.0
|
24
|
+
# @api private
|
25
|
+
def initialize(children = nil)
|
26
|
+
children&.each { |c| self.push c }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Return the String representation of this object
|
30
|
+
#
|
31
|
+
# @return [String] the representation as a String
|
32
|
+
#
|
33
|
+
# @since 1.4.0
|
34
|
+
# @api private
|
35
|
+
def to_s
|
36
|
+
inner = self.reduce(nil) do |memo_e,component|
|
37
|
+
el = component.reduce(nil) do |memo_c,c|
|
38
|
+
(memo_c.nil?) ? c.to_s : memo_c + '|' + c.to_s
|
39
|
+
end
|
40
|
+
(memo_e.nil?) ? el : memo_e + ',' + el
|
41
|
+
end
|
42
|
+
'<' + inner + '>'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
@@ -6,7 +6,7 @@ module Taipo
|
|
6
6
|
# @since 1.0.0
|
7
7
|
# @api private
|
8
8
|
class Constraint
|
9
|
-
|
9
|
+
|
10
10
|
# The identifier for an instance method
|
11
11
|
#
|
12
12
|
# @since 1.0.0
|
@@ -27,30 +27,24 @@ module Taipo
|
|
27
27
|
|
28
28
|
# Initialize a new constraint
|
29
29
|
#
|
30
|
-
# @param name [String|NilClass] the name of the constraint (if nil, this
|
30
|
+
# @param name [String|NilClass] the name of the constraint (if nil, this
|
31
31
|
# is an instance method)
|
32
32
|
# @param value [String|NilClass] the value of the constraint (sometimes a
|
33
33
|
# Constraint is initialized before the value is known)
|
34
34
|
#
|
35
|
-
# @raise [::TypeError] if +name+
|
36
|
-
# +value+ was not of the correct type for the type of constraint
|
35
|
+
# @raise [::TypeError] if +name+ was the wrong type, or
|
36
|
+
# +value+ was not of the correct type for the type of constraint
|
37
37
|
# @raise [::ArgumentError] if +name+ was blank
|
38
38
|
#
|
39
39
|
# @since 1.0.0
|
40
|
-
# @api private
|
40
|
+
# @api private
|
41
41
|
def initialize(name: nil, value: nil)
|
42
42
|
msg = 'Argument name was not nil or a String.'
|
43
43
|
raise ::TypeError, msg unless name.nil? || name.is_a?(String)
|
44
44
|
msg = 'Argument name was an empty string.'
|
45
|
-
raise ::ArgumentError, msg if name
|
45
|
+
raise ::ArgumentError, msg if !name.nil? && name.empty?
|
46
46
|
|
47
|
-
@name =
|
48
|
-
Constraint::METHOD
|
49
|
-
elsif name == ':'
|
50
|
-
'val'
|
51
|
-
else
|
52
|
-
name
|
53
|
-
end
|
47
|
+
@name = name
|
54
48
|
@value = self.parse_value value
|
55
49
|
end
|
56
50
|
|
@@ -97,6 +91,8 @@ module Taipo
|
|
97
91
|
#
|
98
92
|
# @param v [Object] the value
|
99
93
|
#
|
94
|
+
# @return [Object] the parsed value
|
95
|
+
#
|
100
96
|
# @raise [::TypeError] if the value is not appropriate for this type of
|
101
97
|
# constraint
|
102
98
|
#
|
@@ -125,7 +121,7 @@ module Taipo
|
|
125
121
|
|
126
122
|
# Return the String representation of this constraint
|
127
123
|
#
|
128
|
-
# @return [String] the String
|
124
|
+
# @return [String] the representation as a String
|
129
125
|
#
|
130
126
|
# @since 1.0.0
|
131
127
|
# @api private
|
@@ -144,7 +140,7 @@ module Taipo
|
|
144
140
|
|
145
141
|
# Set +v+ to be the value for this constraint
|
146
142
|
#
|
147
|
-
# @param v [Object] the value to set (this will be parsed using
|
143
|
+
# @param v [Object] the value to set (this will be parsed using
|
148
144
|
# {#parse_value})
|
149
145
|
#
|
150
146
|
# @raise [::TypeError] if the value is not appropriate for this type of
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'taipo/type_element/constraint'
|
2
|
+
|
3
|
+
module Taipo
|
4
|
+
class TypeElement
|
5
|
+
|
6
|
+
# A set of {Taipo::TypeElement::Constraint} objects
|
7
|
+
#
|
8
|
+
# @since 1.4.0
|
9
|
+
# @api private
|
10
|
+
class Constraints < Array
|
11
|
+
|
12
|
+
# Initialize a new set of {Taipo::TypeElement::Constraint}
|
13
|
+
#
|
14
|
+
# @param els [Array<Taipo::TypeElement::Constraint>] the constraints
|
15
|
+
#
|
16
|
+
# @since 1.4.0
|
17
|
+
# @api private
|
18
|
+
def initialize(constraints = nil)
|
19
|
+
constraints&.each { |c| self.push c }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return the String representation of this object
|
23
|
+
#
|
24
|
+
# @return [String] the representation as a String
|
25
|
+
#
|
26
|
+
# @since 1.4.0
|
27
|
+
# @api private
|
28
|
+
def to_s
|
29
|
+
inner = self.reduce('') do |memo,c|
|
30
|
+
(memo == '') ? c.to_s : memo + ',' + c.to_s
|
31
|
+
end
|
32
|
+
'(' + inner + ')'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'taipo/type_element'
|
2
|
+
|
3
|
+
module Taipo
|
4
|
+
|
5
|
+
# A set of {Taipo::TypeElement} objects
|
6
|
+
#
|
7
|
+
# @since 1.4.0
|
8
|
+
# @api private
|
9
|
+
class TypeElements < Array
|
10
|
+
|
11
|
+
# Initialize a new set of {Taipo::TypeElement}
|
12
|
+
#
|
13
|
+
# @param els [Array<Taipo::TypeElement>] the elements
|
14
|
+
#
|
15
|
+
# @since 1.4.0
|
16
|
+
# @api private
|
17
|
+
def initialize(els = nil)
|
18
|
+
els&.each { |el| self.push el }
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return the String representation of this object
|
22
|
+
#
|
23
|
+
# @return [String] the representation as a String
|
24
|
+
#
|
25
|
+
# @since 1.4.0
|
26
|
+
# @api private
|
27
|
+
def to_s
|
28
|
+
self.reduce('') do |memo,el|
|
29
|
+
(memo == '') ? el.to_s : memo + '|' + el.to_s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Taipo
|
2
|
+
|
3
|
+
# Utility methods for Taipo
|
4
|
+
#
|
5
|
+
# @since 1.4.0
|
6
|
+
# @api private
|
7
|
+
module Utilities
|
8
|
+
|
9
|
+
# Check if a string is the name of an instance method
|
10
|
+
#
|
11
|
+
# @note All this does is check whether the given string begins with a hash
|
12
|
+
# symbol.
|
13
|
+
#
|
14
|
+
# @param str [String] the string to check
|
15
|
+
#
|
16
|
+
# @return [Boolean] the result
|
17
|
+
#
|
18
|
+
# @since 1.4.0
|
19
|
+
# @api private
|
20
|
+
def self.instance_method?(str)
|
21
|
+
str[0] == '#'
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return the type definition for an object
|
25
|
+
#
|
26
|
+
# @note This assume that each element returned by Enumerator#each has the same
|
27
|
+
# number of components.
|
28
|
+
#
|
29
|
+
# @param obj [Object] the object
|
30
|
+
#
|
31
|
+
# @return [String] a type definition of the object
|
32
|
+
#
|
33
|
+
# @since 1.4.0
|
34
|
+
# @api private
|
35
|
+
def self.object_to_type_def(obj)
|
36
|
+
return obj.class.name unless obj.is_a? Enumerable
|
37
|
+
|
38
|
+
if obj.is_a? Array
|
39
|
+
element_types = Hash.new
|
40
|
+
obj.each { |o| element_types[self.object_to_type_def(o)] = true }
|
41
|
+
if element_types.empty?
|
42
|
+
obj.class.name
|
43
|
+
else
|
44
|
+
obj.class.name + '<' + element_types.keys.join('|') + '>'
|
45
|
+
end
|
46
|
+
else
|
47
|
+
element_types = Array.new
|
48
|
+
obj.each.with_index do |element,index_e|
|
49
|
+
element.each.with_index do |component,index_c|
|
50
|
+
element_types[index_c] = Hash.new if index_e == 0
|
51
|
+
c_type = self.object_to_type_def(component)
|
52
|
+
element_types[index_c][c_type] = true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
inner = element_types.reduce('') do |memo,e|
|
56
|
+
e_type = e.keys.join('|')
|
57
|
+
(memo == '') ? e_type : memo + ',' + e_type
|
58
|
+
end
|
59
|
+
if element_types.empty?
|
60
|
+
obj.class.name
|
61
|
+
else
|
62
|
+
obj.class.name + '<' + inner + '>'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Check if a string is the name of a symbol
|
68
|
+
#
|
69
|
+
# @note All this does is check whether the given string begins with a colon.
|
70
|
+
#
|
71
|
+
# @param str [String] the string to check
|
72
|
+
#
|
73
|
+
# @return [Boolean] the result
|
74
|
+
#
|
75
|
+
# @since 1.4.0
|
76
|
+
# @api private
|
77
|
+
def self.symbol?(str)
|
78
|
+
str[0] == ':'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|