taipo 1.3.0 → 1.4.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 +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
|