speculation 0.4.0 → 0.4.2

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.
@@ -3,5 +3,5 @@ gem 'sinatra'
3
3
  gem 'sequel'
4
4
  gem 'bcrypt'
5
5
  gem 'sqlite3'
6
- gem 'speculation', :git => "https://github.com/english/speculation.git", :ref => "ae3ee09"
6
+ gem 'speculation', :path => File.expand_path('../../..', __FILE__)
7
7
  gem 'pry'
@@ -1,9 +1,7 @@
1
- GIT
2
- remote: https://github.com/english/speculation.git
3
- revision: ae3ee095765816a8d5bfa5b29c808439f04633b0
4
- ref: ae3ee09
1
+ PATH
2
+ remote: /Users/jamie/Projects/speculation
5
3
  specs:
6
- speculation (0.2.0)
4
+ speculation (0.4.1)
7
5
  concurrent-ruby (~> 1.0)
8
6
  rantly (~> 1.0)
9
7
 
@@ -64,7 +64,7 @@ module User
64
64
 
65
65
  def self.serialize_validation_errors(user)
66
66
  data = S.explain_data(ns(:user), user)
67
- data[ns(S, :problems)].map { |problem| Validation.serialize_problem(problem) }
67
+ data[:problems].map { |problem| Validation.serialize_problem(problem) }
68
68
  end
69
69
 
70
70
  def self.fake
@@ -121,7 +121,7 @@ module User
121
121
 
122
122
  USER_ERROR_MESSAGE_MAP = {
123
123
  [] => {
124
- S::Utils.method(:key?) => ->(args) {
124
+ S::Predicates.method(:key?) => ->(args) {
125
125
  key = args.first
126
126
  if key == S.or_keys(:email, :username)
127
127
  "email or username is required"
@@ -146,8 +146,8 @@ module User
146
146
  }
147
147
  end
148
148
 
149
- S.def ns(:email), S.with_gen(S.and(String, Validation::EMAIL_REGEX), Generators.method(:email))
150
- S.def ns(:username), S.with_gen(S.and(String, Validation.method(:validate_username_length)), Generators.method(:username))
151
- S.def ns(:password), S.with_gen(S.and(String, Validation.method(:validate_password_length), Validation.method(:validate_password_complexity)), Generators.method(:password))
149
+ S.def ns(:email), S.with_gen(S.and(String, Validation::EMAIL_REGEX)) { Generators.method(:email) }
150
+ S.def ns(:username), S.with_gen(S.and(String, Validation.method(:validate_username_length))) { Generators.method(:username) }
151
+ S.def ns(:password), S.with_gen(S.and(String, Validation.method(:validate_password_length), Validation.method(:validate_password_complexity))) { Generators.method(:password) }
152
152
  S.def ns(:user), S.keys(:req_un => [S.or_keys(ns(:email), ns(:username)), ns(:password)])
153
153
  end
@@ -155,7 +155,7 @@ S.explain ns(:name_or_id), :foo
155
155
  # as a string or explain_data to receive the errors as data.
156
156
 
157
157
  S.explain_data ns(:name_or_id), :foo
158
- # => {:"Speculation/problems"=>
158
+ # => {:problems=>
159
159
  # [{:path=>[:name],
160
160
  # :val=>:foo,
161
161
  # :via=>[:"Object/name_or_id"],
@@ -214,8 +214,8 @@ S.valid? ns(:person), ns(:first_name) => "Elon", ns(:last_name) => "Musk", ns(:e
214
214
 
215
215
  # Fails required key check
216
216
  S.explain ns(:person), ns(:first_name) => "Elon"
217
- # >> val: {:"Object/first_name"=>"Elon"} fails spec: :"Object/person" predicate: [#<Method: Speculation::Utils.key?>, [:"Object/last_name"]]
218
- # >> val: {:"Object/first_name"=>"Elon"} fails spec: :"Object/person" predicate: [#<Method: Speculation::Utils.key?>, [:"Object/email"]]
217
+ # >> val: {:"Object/first_name"=>"Elon"} fails spec: :"Object/person" predicate: [#<Method: Speculation::Predicates.key?>, [:"Object/last_name"]]
218
+ # >> val: {:"Object/first_name"=>"Elon"} fails spec: :"Object/person" predicate: [#<Method: Speculation::Predicates.key?>, [:"Object/email"]]
219
219
 
220
220
  # Fails attribute conformance
221
221
  S.explain ns(:person), ns(:first_name) => "Elon", ns(:last_name) => "Musk", ns(:email) => "n/a"
@@ -248,8 +248,8 @@ S.explain :"unq/person", :first_name => "Elon", :last_name => "Musk", :email =>
248
248
  # >> In: [:email] val: "n/a" fails spec: :"Object/email_type" at: [:email] predicate: [/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$/, ["n/a"]]
249
249
 
250
250
  S.explain :"unq/person", :first_name => "Elon"
251
- # >> val: {:first_name=>"Elon"} fails spec: :"unq/person" predicate: [#<Method: Speculation::Utils.key?>, [:"Object/last_name"]]
252
- # >> val: {:first_name=>"Elon"} fails spec: :"unq/person" predicate: [#<Method: Speculation::Utils.key?>, [:"Object/email"]]
251
+ # >> val: {:first_name=>"Elon"} fails spec: :"unq/person" predicate: [#<Method: Speculation::Predicates.key?>, [:"Object/last_name"]]
252
+ # >> val: {:first_name=>"Elon"} fails spec: :"unq/person" predicate: [#<Method: Speculation::Predicates.key?>, [:"Object/email"]]
253
253
 
254
254
  # Unqualified keys can also be used to validate record attributes - don't support
255
255
  # Keyword args keys* - don't support
@@ -301,7 +301,7 @@ S.conform ns(:vnum3), [1, 2, 3] # => #<Set: {1, 2, 3}>
301
301
  S.explain ns(:vnum3), Set[1, 2, 3] # not an array
302
302
  # >> val: #<Set: {1, 2, 3}> fails spec: :"Object/vnum3" predicate: [Array, [#<Set: {1, 2, 3}>]]
303
303
  S.explain ns(:vnum3), [1, 1, 1] # not distinct
304
- # >> val: [1, 1, 1] fails spec: :"Object/vnum3" predicate: [#<Method: Speculation::Utils.distinct?>, [[1, 1, 1]]]
304
+ # >> val: [1, 1, 1] fails spec: :"Object/vnum3" predicate: [#<Method: Speculation::Predicates.distinct?>, [[1, 1, 1]]]
305
305
  S.explain ns(:vnum3), [1, 2, :a] # not a number
306
306
  # >> In: [2] val: :a fails spec: :"Object/vnum3" predicate: [Numeric, [:a]]
307
307
 
@@ -499,15 +499,15 @@ end
499
499
  S.check_asserts = true
500
500
  person_name 100 rescue $!
501
501
  # => #<Speculation::Error: Spec assertion failed
502
- # val: 100 fails predicate: [#<Method: Speculation::Utils.hash?>, [100]]
502
+ # val: 100 fails predicate: [#<Method: Speculation::Predicates.hash?>, [100]]
503
503
  # Speculation/failure :assertion_failed
504
- # {:"Speculation/problems"=>
504
+ # {:problems=>
505
505
  # [{:path=>[],
506
- # :pred=>[#<Method: Speculation::Utils.hash?>, [100]],
506
+ # :pred=>[#<Method: Speculation::Predicates.hash?>, [100]],
507
507
  # :val=>100,
508
508
  # :via=>[],
509
509
  # :in=>[]}],
510
- # :"Speculation/failure"=>:assertion_failed}
510
+ # :failure=>:assertion_failed}
511
511
  # >
512
512
 
513
513
  # A deeper level of integration is to call conform and use the return value to
@@ -925,7 +925,7 @@ S.exercise_fn(method(:ranged_rand))
925
925
  # predicate implicitly presumes values of a particular type but the spec does
926
926
  # not specify them:
927
927
 
928
- Gen.generate S.gen(:even?.to_proc) rescue $! # => #<Speculation::Error: unable to construct gen at: [] for: Speculation::Spec(#<Proc:0x007fb69b1908f8(&:even?)>) {:"Speculation/failure"=>:no_gen, :"Speculation/path"=>[]}\n>
928
+ Gen.generate S.gen(:even?.to_proc) rescue $! # => #<Speculation::Error: unable to construct gen at: [] for: Speculation::Spec(#<Proc:0x007fb69b1908f8(&:even?)>) {:failure=>:no_gen, :"Speculation/path"=>[]}\n>
929
929
 
930
930
  # In this case spec was not able to find a generator for the even? predicate.
931
931
  # Most of the primitive generators in spec are mapped to the common type
@@ -1013,8 +1013,10 @@ Gen.sample sym_gen, 5
1013
1013
  # To redefine our spec using this custom generator, use with_gen which takes a
1014
1014
  # spec and a replacement generator as a block:
1015
1015
 
1016
- gen = S.gen(Set[:"my.domain/name", :"my.domain/occupation", :"my.domain/id"])
1017
- S.def(ns(:syms), S.with_gen(S.and(Symbol, ->(s) { S::NamespacedSymbols.namespace(s) == "my.domain" }), gen))
1016
+ syms_spec = S.with_gen(S.and(Symbol, ->(s) { S::NamespacedSymbols.namespace(s) == "my.domain" })) do
1017
+ S.gen(Set[:"my.domain/name", :"my.domain/occupation", :"my.domain/id"])
1018
+ end
1019
+ S.def ns(:syms), syms_spec
1018
1020
 
1019
1021
  S.valid? ns(:syms), :"my.domain/name"
1020
1022
  Gen.sample S.gen(ns(:syms)), 5
@@ -1025,7 +1027,7 @@ Gen.sample S.gen(ns(:syms)), 5
1025
1027
  # :"my.domain/id"]
1026
1028
 
1027
1029
  # Note that with_gen (and other places that take a custom generator) take a
1028
- # one-arg function that returns the generator, allowing it to be lazily
1030
+ # no-arg block that returns the generator, allowing it to be lazily
1029
1031
  # realized.
1030
1032
 
1031
1033
  # One downside to this approach is we are missing what property testing is
@@ -1048,11 +1050,14 @@ Gen.sample sym_gen_2, 5 # => [:"my.domain/hLZnEpj", :"my.domain/kvy", :"my.domai
1048
1050
  # Returning to our "hello" example, we now have the tools to make that
1049
1051
  # generator:
1050
1052
 
1051
- S.def ns(:hello), S.with_gen(->(s) { s.include?("hello") }, ->(rantly) {
1052
- s1 = rantly.sized(rantly.range(0, 10)) { rantly.string(:alpha) }
1053
- s2 = rantly.sized(rantly.range(0, 10)) { rantly.string(:alpha) }
1054
- "#{s1}hello#{s2}"
1055
- })
1053
+ hello_spec = S.with_gen(->(s) { s.include?("hello") }) do
1054
+ ->(rantly) {
1055
+ s1 = rantly.sized(rantly.range(0, 10)) { rantly.string(:alpha) }
1056
+ s2 = rantly.sized(rantly.range(0, 10)) { rantly.string(:alpha) }
1057
+ "#{s1}hello#{s2}"
1058
+ }
1059
+ end
1060
+ S.def ns(:hello), hello_spec
1056
1061
 
1057
1062
  Gen.sample S.gen(ns(:hello))
1058
1063
  # => ["XRLhtLshelloaY",
@@ -1136,7 +1141,7 @@ ranged_rand 8, 5 rescue $!
1136
1141
  # Speculation/args [8, 5]
1137
1142
  # Speculation/failure :instrument
1138
1143
  # Speculation::Test/caller "/var/folders/4l/j2mycv0j4rx7z47sp01r93vc3kfxzs/T/seeing_is_believing_temp_dir20170304-91770-1jtmc1y/program.rb:901:in `<main>'"
1139
- # {:"Speculation/problems"=>
1144
+ # {:problems=>
1140
1145
  # [{:path=>[:args],
1141
1146
  # :val=>{:start=>8, :end=>5},
1142
1147
  # :via=>[],
@@ -1144,9 +1149,9 @@ ranged_rand 8, 5 rescue $!
1144
1149
  # :pred=>
1145
1150
  # [#<Proc:0x007fb69a8a0fb8@/var/folders/4l/j2mycv0j4rx7z47sp01r93vc3kfxzs/T/seeing_is_believing_temp_dir20170304-91770-1jtmc1y/program.rb:548 (lambda)>,
1146
1151
  # [{:start=>8, :end=>5}]]}],
1147
- # :"Speculation/args"=>[8, 5],
1148
- # :"Speculation/failure"=>:instrument,
1149
- # :"Speculation::Test/caller"=>
1152
+ # :args=>[8, 5],
1153
+ # :failure=>:instrument,
1154
+ # :caller=>
1150
1155
  # "/var/folders/4l/j2mycv0j4rx7z47sp01r93vc3kfxzs/T/seeing_is_believing_temp_dir20170304-91770-1jtmc1y/program.rb:901:in `<main>'"}
1151
1156
  # >
1152
1157
 
@@ -1172,7 +1177,7 @@ ranged_rand 8, 5 rescue $!
1172
1177
 
1173
1178
  STest.check method(:ranged_rand)
1174
1179
  # => [{:spec=>Speculation::FSpec(main.ranged_rand),
1175
- # :"Speculation::Test/ret"=>{:num_tests=>1000, :result=>true},
1180
+ # :ret=>{:num_tests=>1000, :result=>true},
1176
1181
  # :method=>#<Method: main.ranged_rand>}]
1177
1182
 
1178
1183
  # check also takes a number of options ~~that can be passed to test.check to
@@ -1193,7 +1198,7 @@ STest.abbrev_result STest.check(method(:ranged_rand)).first
1193
1198
  # >> {:spec=>"Speculation::FSpec(main.ranged_rand)",
1194
1199
  # >> :method=>#<Method: main.ranged_rand>,
1195
1200
  # >> :failure=>
1196
- # >> {:"Speculation/problems"=>
1201
+ # >> {:problems=>
1197
1202
  # >> [{:path=>[:fn],
1198
1203
  # >> :val=>{:args=>{:start=>-1, :end=>0}, :block=>nil, :ret=>0},
1199
1204
  # >> :via=>[],
@@ -1201,10 +1206,10 @@ STest.abbrev_result STest.check(method(:ranged_rand)).first
1201
1206
  # >> :pred=>
1202
1207
  # >> [#<Proc:0x007fb69a8a0c98@/var/folders/4l/j2mycv0j4rx7z47sp01r93vc3kfxzs/T/seeing_is_believing_temp_dir20170304-91770-1jtmc1y/program.rb:551 (lambda)>,
1203
1208
  # >> [{:args=>{:start=>-1, :end=>0}, :block=>nil, :ret=>0}]]}],
1204
- # >> :"Speculation::Test/args"=>[-1, 0],
1205
- # >> :"Speculation::Test/val"=>
1209
+ # >> :args=>[-1, 0],
1210
+ # >> :val=>
1206
1211
  # >> {:args=>{:start=>-1, :end=>0}, :block=>nil, :ret=>0},
1207
- # >> :"Speculation/failure"=>:check_failed}}
1212
+ # >> :failure=>:check_failed}}
1208
1213
 
1209
1214
  # check has reported an error in the :fn spec. We can see the arguments passed
1210
1215
  # were -1 and 0 and the return value was -0, which is out of the expected
@@ -12,6 +12,7 @@ require "speculation/version"
12
12
  require "speculation/namespaced_symbols"
13
13
  require "speculation/method_identifier"
14
14
  require "speculation/utils"
15
+ require "speculation/predicates"
15
16
  require "speculation/spec"
16
17
  require "speculation/error"
17
18
 
@@ -22,9 +23,8 @@ module Speculation
22
23
  # Enables or disables spec asserts. Defaults to false.
23
24
  attr_accessor :check_asserts
24
25
 
25
- # A soft limit on how many times a branching spec (or/alt/zero_or_more) can
26
- # be recursed through during generation. After this a non-recursive branch
27
- # will be chosen.
26
+ # A soft limit on how many times a branching spec (or/alt/zero_or_more/opt keys) can be recursed
27
+ # through during generation. After this a non-recursive branch will be chosen.
28
28
  attr_accessor :recursion_limit
29
29
 
30
30
  # The number of times an anonymous fn specified by fspec will be
@@ -47,29 +47,6 @@ module Speculation
47
47
 
48
48
  @registry_ref = Concurrent::Atom.new({})
49
49
 
50
- INVALID = ns(:invalid)
51
-
52
- # @private
53
- OP = ns(:op)
54
- # @private
55
- ALT = ns(:alt)
56
- # @private
57
- AMP = ns(:amp)
58
- # @private
59
- PCAT = ns(:pcat)
60
- # @private
61
- REP = ns(:rep)
62
- # @private
63
- ACCEPT = ns(:accept)
64
- # @private
65
- NIL = ns(:nil)
66
- # @private
67
- RECURSION_LIMIT = ns(:recursion_limit)
68
- # @private
69
- GEN = ns(:gen)
70
- # @private
71
- NAME = ns(:name)
72
-
73
50
  # Can be enabled or disabled at runtime:
74
51
  # - enabled/disabled by setting `check_asserts`.
75
52
  # - enabled by setting environment variable SPECULATION_CHECK_ASSERTS to the
@@ -83,7 +60,7 @@ module Speculation
83
60
  return x unless check_asserts
84
61
  return x if valid?(spec, x)
85
62
 
86
- ed = _explain_data(spec, [], [], [], x).merge(ns(:failure) => :assertion_failed)
63
+ ed = _explain_data(spec, [], [], [], x).merge(:failure => :assertion_failed)
87
64
  out = StringIO.new
88
65
  explain_out(ed, out)
89
66
 
@@ -110,28 +87,28 @@ module Speculation
110
87
  gens << [1, ->(r) { r.choose(Float::INFINITY, -Float::INFINITY) }] if infinite
111
88
  gens << [1, ->(_) { Float::NAN }] if nan
112
89
 
113
- spec(self.and(*preds), :gen => ->(rantly) { rantly.freq(*gens) })
90
+ spec(self.and(*preds), :gen => ->() { ->(rantly) { rantly.freq(*gens) } })
114
91
  end
115
92
 
116
93
  # @param range [Range<Integer>]
117
94
  # @return Spec that validates ints in the given range
118
95
  def self.int_in(range)
119
96
  spec(self.and(Integer, ->(x) { range.include?(x) }),
120
- :gen => ->(_) { rand(range) })
97
+ :gen => ->() { ->(_) { rand(range) } })
121
98
  end
122
99
 
123
100
  # @param time_range [Range<Time>]
124
101
  # @return Spec that validates times in the given range
125
102
  def self.time_in(time_range)
126
103
  spec(self.and(Time, ->(x) { time_range.cover?(x) }),
127
- :gen => ->(_) { rand(time_range) })
104
+ :gen => ->() { ->(_) { rand(time_range) } })
128
105
  end
129
106
 
130
107
  # @param date_range [Range<Date>]
131
108
  # @return Spec that validates dates in the given range
132
109
  def self.date_in(date_range)
133
110
  spec(self.and(Date, ->(x) { date_range.cover?(x) }),
134
- :gen => ->(_) { rand(date_range) })
111
+ :gen => ->() { ->(_) { rand(date_range) } })
135
112
  end
136
113
 
137
114
  # @param x [Spec, Object]
@@ -143,30 +120,43 @@ module Speculation
143
120
  # @param x [Hash, Object]
144
121
  # @return [Hash, false] x if x is a (Speculation) regex op, else logical false
145
122
  def self.regex?(x)
146
- Utils.hash?(x) && x[OP] && x
123
+ x.is_a?(Hash) && x[:op] && x
147
124
  end
148
125
 
149
126
  # @param value return value of a `conform` call
150
127
  # @return [Boolean] true if value is the result of an unsuccessful conform
151
128
  def self.invalid?(value)
152
- value.equal?(INVALID)
129
+ value.equal?(:"Speculation/invalid")
153
130
  end
154
131
 
155
132
  # @param spec [Spec]
156
133
  # @param value value to conform
157
- # @return [Symbol, Object] :Speculation/invalid if value does not match spec, else the (possibly destructured) value
134
+ # @return [Symbol, Object] :Speculation/invalid if value does not match spec, else the (possibly
135
+ # destructured) value
158
136
  def self.conform(spec, value)
159
137
  spec = MethodIdentifier(spec)
160
138
  specize(spec).conform(value)
161
139
  end
162
140
 
163
- # Takes a spec and a one-arg generator function and returns a version of the spec that uses that generator
164
141
  # @param spec [Spec]
165
- # @param gen [Proc] generator proc that receives a Rantly instance
142
+ # @value value [Object] value created by `conform` call and given `spec`
143
+ # @return value with conform destructuring undone
144
+ def self.unform(spec, value)
145
+ specize(spec).unform(value)
146
+ end
147
+
148
+ # Takes a spec and a no-arg generator returning block and returns a version of the spec that uses
149
+ # that generator
150
+ # @param spec [Spec]
151
+ # @yieldreturn Rantly generator
166
152
  # @return [Spec]
167
- def self.with_gen(spec, gen)
153
+ def self.with_gen(spec, &gen)
154
+ if gen && !gen.arity.zero?
155
+ raise ArgumentError, "gen must be a no-arg block that returns a generator"
156
+ end
157
+
168
158
  if regex?(spec)
169
- spec.merge(ns(:gfn) => gen)
159
+ spec.merge(:gfn => gen)
170
160
  else
171
161
  specize(spec).with_gen(gen)
172
162
  end
@@ -177,19 +167,21 @@ module Speculation
177
167
  probs = specize(spec).explain(path, via, inn, value)
178
168
 
179
169
  if probs && probs.any?
180
- { ns(:problems) => probs }
170
+ { :problems => probs,
171
+ :spec => spec,
172
+ :value => value }
181
173
  end
182
174
  end
183
175
 
184
176
  # Given a spec and a value x which ought to conform, returns nil if x
185
- # conforms, else a hash with at least the key :"Speculation/problems" whose
177
+ # conforms, else a hash with at least the key :problems whose
186
178
  # value is a collection of problem-hashes, where problem-hash has at least
187
179
  # :path :pred and :val keys describing the predicate and the value that failed
188
180
  # at that path.
189
181
  # @param spec [Spec]
190
182
  # @param x value which ought to conform
191
183
  # @return [nil, Hash] nil if x conforms, else a hash with at least the key
192
- # :Speculation/problems whose value is a collection of problem-hashes,
184
+ # :problems whose value is a collection of problem-hashes,
193
185
  # where problem-hash has at least :path :pred and :val keys describing the
194
186
  # predicate and the value that failed at that path.
195
187
  def self.explain_data(spec, x)
@@ -203,7 +195,9 @@ module Speculation
203
195
  def self.explain_out(ed, out = STDOUT)
204
196
  return out.puts("Success!") unless ed
205
197
 
206
- ed.fetch(ns(:problems)).each do |prob|
198
+ problems = Utils.sort_descending(ed.fetch(:problems)) { |prob| prob[:path] }
199
+
200
+ problems.each do |prob|
207
201
  path, pred, val, reason, via, inn = prob.values_at(:path, :pred, :val, :reason, :via, :in)
208
202
 
209
203
  out.print("In: ", inn.to_a.inspect, " ") unless inn.empty?
@@ -223,7 +217,7 @@ module Speculation
223
217
  end
224
218
 
225
219
  ed.each do |k, v|
226
- out.puts("#{k} #{PP.pp(v, String.new)}") unless k == ns(:problems)
220
+ out.puts("#{k.inspect} #{PP.pp(v, String.new)}") unless k == :problems
227
221
  end
228
222
 
229
223
  nil
@@ -251,13 +245,14 @@ module Speculation
251
245
 
252
246
  spec = specize(spec)
253
247
  gfn = overrides[spec_name(spec) || spec] || overrides[path]
248
+ gfn = gfn.call if gfn
254
249
  g = gfn || spec.gen(overrides, path, rmap)
255
250
 
256
251
  if g
257
252
  Gen.such_that(g) { |x| valid?(spec, x) }
258
253
  else
259
254
  raise Speculation::Error.new("unable to construct gen at: #{path.inspect} for: #{spec.inspect}",
260
- ns(:failure) => :no_gen, ns(:path) => path)
255
+ :failure => :no_gen, :path => path)
261
256
  end
262
257
  end
263
258
 
@@ -277,7 +272,7 @@ module Speculation
277
272
  # @return [Proc]
278
273
  def self.gen(spec, overrides = nil)
279
274
  spec = MethodIdentifier(spec)
280
- gensub(spec, overrides, [], RECURSION_LIMIT => recursion_limit)
275
+ gensub(spec, overrides, [], :recursion_limit => recursion_limit)
281
276
  end
282
277
 
283
278
  # @private
@@ -329,8 +324,8 @@ module Speculation
329
324
  # NOTE: it is not generally necessary to wrap predicates in spec when using
330
325
  # `S.def` etc., only to attach a unique generator.
331
326
  #
332
- # Optionally takes :gen generator function, which must be a proc of one arg
333
- # (Rantly instance) that generates a valid value.
327
+ # Optionally takes :gen generator function, which must be a no-arg proc that returns a generator
328
+ # (proc that receives a Rantly instance) that generates a valid value.
334
329
  #
335
330
  # @param pred [Proc, Method, Set, Class, Regexp, Hash] Takes a single predicate. A
336
331
  # predicate can be one of:
@@ -346,8 +341,8 @@ module Speculation
346
341
  # zero_or_more, one_or_more, zero_or_one, in which case it will return a
347
342
  # regex-conforming spec, useful when nesting an independent regex.
348
343
  #
349
- # @param gen [Proc] generator function, which must be a proc of one
350
- # arg (Rantly instance) that generates a valid value.
344
+ # @param gen [Proc] generator returning function, which must be a zero arg proc that returns a
345
+ # proc of one arg (Rantly instance) that generates a valid value.
351
346
  # @return [Spec]
352
347
  def self.spec(pred, gen: nil)
353
348
  spec_impl(pred, gen, false) if pred
@@ -381,20 +376,20 @@ module Speculation
381
376
  # @param opt [Array<Symbol>]
382
377
  # @param req_un [Array<Symbol>]
383
378
  # @param opt_un [Array<Symbol>]
384
- # @param gen [Proc] generator function, which must be a proc of one arg
385
- # (Rantly instance) that generates a valid value
379
+ # @param gen [Proc] generator returning function, which must be a zero arg proc that
380
+ # returns a proc of one arg (Rantly instance) that generates a valid value.
386
381
  def self.keys(req: [], opt: [], req_un: [], opt_un: [], gen: nil)
387
382
  HashSpec.new(req, opt, req_un, opt_un, gen)
388
383
  end
389
384
 
390
385
  # @see keys
391
386
  def self.or_keys(*ks)
392
- [ns(:or), *ks]
387
+ [:"Speculation/or", *ks]
393
388
  end
394
389
 
395
390
  # @see keys
396
391
  def self.and_keys(*ks)
397
- [ns(:and), *ks]
392
+ [:"Speculation/and", *ks]
398
393
  end
399
394
 
400
395
  # @param key_preds [Hash] Takes key+pred hash
@@ -440,8 +435,8 @@ module Speculation
440
435
  # @option opts :into [Array, Hash, Set] (Array) one of [], {}, Set[], the
441
436
  # default collection to generate into (default: empty coll as generated by
442
437
  # :kind pred if supplied, else [])
443
- # @option opts :gen [Proc] generator proc, which must be a proc of one arg
444
- # (Rantly instance) that generates a valid value.
438
+ # @option opts :gen [Proc] generator returning function, which must be a zero arg proc that
439
+ # returns a proc of one arg (Rantly instance) that generates a valid value.
445
440
  # @see coll_of
446
441
  # @see every_kv
447
442
  # @return [Spec] spec that validates collection elements against pred
@@ -461,9 +456,9 @@ module Speculation
461
456
  # @param vpred val pred
462
457
  # @param options [Hash]
463
458
  # @return [Spec] spec that validates associative collections
464
- def self.every_kv(kpred, vpred, options)
465
- every(tuple(kpred, vpred), ns(:kfn) => ->(_i, v) { v.first },
466
- :into => {},
459
+ def self.every_kv(kpred, vpred, options = {})
460
+ every(tuple(kpred, vpred), :kfn => ->(_i, v) { v.first },
461
+ :into => {},
467
462
  **options)
468
463
  end
469
464
 
@@ -479,13 +474,13 @@ module Speculation
479
474
  # @param opts [Hash]
480
475
  # @return [Spec]
481
476
  def self.coll_of(pred, opts = {})
482
- every(pred, ns(:conform_all) => true, **opts)
477
+ every(pred, :conform_all => true, **opts)
483
478
  end
484
479
 
485
480
  # Returns a spec for a hash whose keys satisfy kpred and vals satisfy vpred.
486
481
  # Unlike 'every_kv', hash_of will exhaustively conform every value.
487
482
  #
488
- # Same options as 'every', :kind defaults to `Speculation::Utils.hash?`, with
483
+ # Same options as 'every', :kind defaults to `Speculation::Predicates.hash?`, with
489
484
  # the addition of:
490
485
  #
491
486
  # :conform_keys - conform keys as well as values (default false)
@@ -496,8 +491,8 @@ module Speculation
496
491
  # @param options [Hash]
497
492
  # @return [Spec]
498
493
  def self.hash_of(kpred, vpred, options = {})
499
- every_kv(kpred, vpred, :kind => Utils.method(:hash?),
500
- ns(:conform_all) => true,
494
+ every_kv(kpred, vpred, :kind => Predicates.method(:hash?),
495
+ :conform_all => true,
501
496
  **options)
502
497
  end
503
498
 
@@ -519,7 +514,7 @@ module Speculation
519
514
  # @return [Hash] regex op that matches zero or one value matching pred. Produces a
520
515
  # single value (not a collection) if matched.
521
516
  def self.zero_or_one(pred)
522
- _alt([pred, accept(NIL)], nil)
517
+ _alt([pred, accept(:nil)], nil)
523
518
  end
524
519
 
525
520
  # @param kv_specs [Hash] key+pred pairs
@@ -550,14 +545,15 @@ module Speculation
550
545
  # resulting value to the conjunction of the predicates, and any conforming
551
546
  # they might perform.
552
547
  def self.constrained(re, *preds)
553
- { OP => AMP, :p1 => re, :predicates => preds }
548
+ { :op => :amp, :p1 => re, :predicates => preds }
554
549
  end
555
550
 
556
- # @param f predicate function with the semantics of conform i.e. it should
551
+ # @param f [#call] function with the semantics of conform i.e. it should
557
552
  # return either a (possibly converted) value or :"Speculation/invalid"
553
+ # @param unformer [#call] function that does the unform of the result of `f`
558
554
  # @return [Spec] a spec that uses pred as a predicate/conformer.
559
- def self.conformer(f)
560
- spec_impl(f, nil, true)
555
+ def self.conformer(f, unformer = nil)
556
+ spec_impl(f, nil, true, unformer)
561
557
  end
562
558
 
563
559
  # Takes :args :ret and (optional) :block and :fn kwargs whose values are preds and returns a spec
@@ -571,8 +567,8 @@ module Speculation
571
567
  # @param ret predicate
572
568
  # @param fn predicate
573
569
  # @param block predicate
574
- # @param gen [Proc] generator proc, which must be a proc of one arg (Rantly
575
- # instance) that generates a valid value.
570
+ # @param gen [Proc] generator returning function, which must be a zero arg proc that
571
+ # returns a proc of one arg (Rantly instance) that generates a valid value.
576
572
  # @return [Spec]
577
573
  # @see fdef See 'fdef' for a single operation that creates an fspec and registers it, as well as a
578
574
  # full description of :args, :block, :ret and :fn
@@ -662,7 +658,7 @@ module Speculation
662
658
  # @return [Array] an array of triples of [args, block, ret].
663
659
  def self.exercise_fn(method, n = 10, fspec = nil)
664
660
  fspec ||= get_spec(method)
665
- raise ArgumentError, "No fspec found for #{method}" unless fspec
661
+ raise ArgumentError, "No :args spec found for #{method}" unless fspec && fspec.args
666
662
 
667
663
  block_gen = fspec.block ? gen(fspec.block) : Utils.constantly(nil)
668
664
  gen = Gen.tuple(gen(fspec.args), block_gen)
@@ -674,7 +670,7 @@ module Speculation
674
670
 
675
671
  # @private
676
672
  def self.recur_limit?(rmap, id, path, k)
677
- rmap[id] > rmap[RECURSION_LIMIT] &&
673
+ rmap[id] > rmap[:recursion_limit] &&
678
674
  path.include?(k)
679
675
  end
680
676
 
@@ -692,11 +688,11 @@ module Speculation
692
688
  if spec
693
689
  conform(spec, x)
694
690
  elsif pred.is_a?(Module) || pred.is_a?(::Regexp)
695
- pred === x ? x : INVALID
691
+ pred === x ? x : :"Speculation/invalid"
696
692
  elsif pred.is_a?(Set)
697
- pred.include?(x) ? x : INVALID
693
+ pred.include?(x) ? x : :"Speculation/invalid"
698
694
  elsif pred.respond_to?(:call)
699
- pred.call(x) ? x : INVALID
695
+ pred.call(x) ? x : :"Speculation/invalid"
700
696
  else
701
697
  raise "#{pred} is not a class, proc, set or regexp"
702
698
  end
@@ -723,16 +719,16 @@ module Speculation
723
719
  end
724
720
 
725
721
  # @private
726
- def self.spec_impl(pred, gen, should_conform)
722
+ def self.spec_impl(pred, gen, should_conform, unconformer = nil)
727
723
  if spec?(pred)
728
- with_gen(pred, gen)
724
+ with_gen(pred, &gen)
729
725
  elsif regex?(pred)
730
726
  RegexSpec.new(pred, gen)
731
727
  elsif Utils.ident?(pred)
732
728
  spec = the_spec(pred)
733
- gen ? with_gen(spec, gen) : spec
729
+ gen ? with_gen(spec, &gen) : spec
734
730
  else
735
- PredicateSpec.new(pred, should_conform, gen)
731
+ PredicateSpec.new(pred, should_conform, gen, unconformer)
736
732
  end
737
733
  end
738
734
 
@@ -761,7 +757,7 @@ module Speculation
761
757
  p = reg_resolve!(p)
762
758
 
763
759
  id, op, ps, ks, p1, p2, ret, id, gen = p.values_at(
764
- :id, OP, :predicates, :keys, :p1, :p2, :return_value, :id, GEN
760
+ :id, :op, :predicates, :keys, :p1, :p2, :return_value, :id, :gfn
765
761
  ) if regex?(p)
766
762
 
767
763
  id = p.id if spec?(p)
@@ -786,19 +782,21 @@ module Speculation
786
782
  overrides[path]
787
783
 
788
784
  if ogen
785
+ gen = ogen.call
786
+
789
787
  if [:accept, nil].include?(op)
790
- return ->(rantly) { [*ogen.call(rantly)] }
788
+ return Gen.fmap(gen) { |x| [x] }
791
789
  else
792
- return ->(rantly) { ogen.call(rantly) }
790
+ return gen
793
791
  end
794
792
  end
795
793
 
796
- return gen if gen
794
+ return gen.call if gen
797
795
 
798
796
  if p
799
797
  case op
800
- when ACCEPT
801
- if ret == NIL
798
+ when :accept
799
+ if ret == :nil
802
800
  ->(_rantly) { [] }
803
801
  else
804
802
  ->(_rantly) { [ret] }
@@ -806,10 +804,10 @@ module Speculation
806
804
  when nil
807
805
  g = gensub(p, overrides, path, rmap)
808
806
 
809
- ->(rantly) { [g.call(rantly)] }
810
- when AMP
807
+ Gen.fmap(g) { |x| [x] }
808
+ when :amp
811
809
  re_gen(p1, overrides, path, rmap)
812
- when PCAT
810
+ when :pcat
813
811
  gens = ggens.call(ps, ks)
814
812
 
815
813
  if gens.all?
@@ -817,11 +815,11 @@ module Speculation
817
815
  gens.flat_map { |gg| gg.call(rantly) }
818
816
  end
819
817
  end
820
- when ALT
818
+ when :alt
821
819
  gens = ggens.call(ps, ks).compact
822
820
 
823
821
  ->(rantly) { rantly.branch(*gens) } unless gens.empty?
824
- when REP
822
+ when :rep
825
823
  if recur_limit?(rmap, id, [id], id)
826
824
  ->(_rantly) { [] }
827
825
  else
@@ -841,15 +839,45 @@ module Speculation
841
839
  def self.re_conform(regex, data)
842
840
  data.each do |x|
843
841
  regex = deriv(regex, x)
844
- return INVALID unless regex
842
+ return :"Speculation/invalid" unless regex
845
843
  end
846
844
 
847
845
  if accept_nil?(regex)
848
846
  return_value = preturn(regex)
849
847
 
850
- return_value == NIL ? nil : return_value
848
+ return_value == :nil ? nil : return_value
851
849
  else
852
- INVALID
850
+ :"Speculation/invalid"
851
+ end
852
+ end
853
+
854
+ # @private
855
+ def self.op_unform(regex, value)
856
+ return unform(regex, value) unless regex?(regex)
857
+
858
+ case regex[:op]
859
+ when :accept
860
+ [regex[:return_value]]
861
+ when :amp
862
+ px = regex[:predicates].reverse.reduce(value) { |val, pred| op_unform(pred, val) }
863
+ op_unform(regex[:p1], px)
864
+ when :rep
865
+ value.flat_map { |val| op_unform(regex[:p1], val) }
866
+ when :pcat
867
+ if regex[:keys] # it's a `cat`
868
+ kps = Hash[regex[:keys].zip(regex[:predicates])]
869
+ regex[:keys].flat_map { |key| value.include?(key) ? op_unform(kps[key], value[key]) : [] }
870
+ else # it's a `one_or_more`
871
+ value.flat_map { |val| op_unform(regex[:predicates].first, val) }
872
+ end
873
+ when :alt
874
+ if regex[:keys] # it's an `alt`
875
+ kps = Hash[regex[:keys].zip(regex[:predicates])]
876
+ k, v = value
877
+ op_unform(kps[k], v)
878
+ else # it's a `zero_or_one`
879
+ [unform(regex[:predicates].first, value)]
880
+ end
853
881
  end
854
882
  end
855
883
 
@@ -866,7 +894,7 @@ module Speculation
866
894
  end
867
895
 
868
896
  if accept?(p)
869
- if p[OP] == PCAT
897
+ if p[:op] == :pcat
870
898
  return op_explain(p, path, via, Utils.conj(inn, index), input[index..-1])
871
899
  else
872
900
  return [{ :path => path,
@@ -929,7 +957,7 @@ module Speculation
929
957
  if Utils.ident?(spec)
930
958
  spec
931
959
  elsif regex?(spec)
932
- spec.merge(NAME => name)
960
+ spec.merge(:name => name)
933
961
  else
934
962
  spec.tap { |s| s.name = name }
935
963
  end
@@ -939,7 +967,7 @@ module Speculation
939
967
  if Utils.ident?(spec)
940
968
  spec
941
969
  elsif regex?(spec)
942
- spec[NAME]
970
+ spec[:name]
943
971
  elsif spec.respond_to?(:name)
944
972
  spec.name
945
973
  end
@@ -973,7 +1001,7 @@ module Speculation
973
1001
  def and_preds(x, preds)
974
1002
  preds.each do |pred|
975
1003
  x = dt(pred, x)
976
- return INVALID if invalid?(x)
1004
+ return :"Speculation/invalid" if invalid?(x)
977
1005
  end
978
1006
 
979
1007
  x
@@ -986,6 +1014,8 @@ module Speculation
986
1014
  case spec
987
1015
  when Symbol, MethodIdentifier
988
1016
  specize(reg_resolve!(spec))
1017
+ when nil
1018
+ raise ArgumentError, "#{spec.inspect} can not be a spec"
989
1019
  else
990
1020
  spec_impl(spec, nil, false)
991
1021
  end
@@ -995,12 +1025,12 @@ module Speculation
995
1025
  ### regex ###
996
1026
 
997
1027
  def accept(x)
998
- { OP => ACCEPT, :return_value => x }
1028
+ { :op => :accept, :return_value => x }
999
1029
  end
1000
1030
 
1001
1031
  def accept?(hash)
1002
1032
  if hash.is_a?(Hash)
1003
- hash[OP] == ACCEPT
1033
+ hash[:op] == :accept
1004
1034
  end
1005
1035
  end
1006
1036
 
@@ -1013,7 +1043,7 @@ module Speculation
1013
1043
  return unless regex[:predicates].all?
1014
1044
 
1015
1045
  unless accept?(predicate)
1016
- return { OP => PCAT,
1046
+ return { :op => :pcat,
1017
1047
  :predicates => regex[:predicates],
1018
1048
  :keys => keys,
1019
1049
  :return_value => regex[:return_value] }
@@ -1034,7 +1064,7 @@ module Speculation
1034
1064
  def rep(p1, p2, return_value, splice)
1035
1065
  return unless p1
1036
1066
 
1037
- regex = { OP => REP, :p2 => p2, :splice => splice, :id => SecureRandom.uuid }
1067
+ regex = { :op => :rep, :p2 => p2, :splice => splice, :id => SecureRandom.uuid }
1038
1068
 
1039
1069
  if accept?(p1)
1040
1070
  regex.merge(:p1 => p2, :return_value => Utils.conj(return_value, p1[:return_value]))
@@ -1059,7 +1089,7 @@ module Speculation
1059
1089
  predicate, *rest_predicates = predicates
1060
1090
  key, *_rest_keys = keys
1061
1091
 
1062
- return_value = { OP => ALT, :predicates => predicates, :keys => keys }
1092
+ return_value = { :op => :alt, :predicates => predicates, :keys => keys }
1063
1093
  return return_value unless rest_predicates.empty?
1064
1094
 
1065
1095
  return predicate unless key
@@ -1077,24 +1107,24 @@ module Speculation
1077
1107
  end
1078
1108
 
1079
1109
  def no_ret?(p1, pret)
1080
- return true if pret == NIL
1110
+ return true if pret == :nil
1081
1111
 
1082
1112
  regex = reg_resolve!(p1)
1083
- op = regex[OP]
1113
+ op = regex[:op]
1084
1114
 
1085
- [REP, PCAT].include?(op) && pret.empty? || nil
1115
+ [:rep, :pcat].include?(op) && pret.empty? || nil
1086
1116
  end
1087
1117
 
1088
1118
  def accept_nil?(regex)
1089
1119
  regex = reg_resolve!(regex)
1090
1120
  return unless regex?(regex)
1091
1121
 
1092
- case regex[OP]
1093
- when ACCEPT then true
1094
- when PCAT then regex[:predicates].all? { |p| accept_nil?(p) }
1095
- when ALT then regex[:predicates].any? { |p| accept_nil?(p) }
1096
- when REP then (regex[:p1] == regex[:p2]) || accept_nil?(regex[:p1])
1097
- when AMP
1122
+ case regex[:op]
1123
+ when :accept then true
1124
+ when :pcat then regex[:predicates].all? { |p| accept_nil?(p) }
1125
+ when :alt then regex[:predicates].any? { |p| accept_nil?(p) }
1126
+ when :rep then (regex[:p1] == regex[:p2]) || accept_nil?(regex[:p1])
1127
+ when :amp
1098
1128
  p1 = regex[:p1]
1099
1129
 
1100
1130
  return false unless accept_nil?(p1)
@@ -1102,7 +1132,7 @@ module Speculation
1102
1132
  no_ret?(p1, preturn(p1)) ||
1103
1133
  !invalid?(and_preds(preturn(p1), regex[:predicates]))
1104
1134
  else
1105
- raise "Unexpected #{OP} #{regex[OP]}"
1135
+ raise "Unexpected op #{regex[:op]}"
1106
1136
  end
1107
1137
  end
1108
1138
 
@@ -1113,30 +1143,30 @@ module Speculation
1113
1143
  p0, *_pr = regex[:predicates]
1114
1144
  k, *_ks = regex[:keys]
1115
1145
 
1116
- case regex[OP]
1117
- when ACCEPT then regex[:return_value]
1118
- when PCAT then add_ret(p0, regex[:return_value], k)
1119
- when REP then add_ret(regex[:p1], regex[:return_value], k)
1120
- when AMP
1146
+ case regex[:op]
1147
+ when :accept then regex[:return_value]
1148
+ when :pcat then add_ret(p0, regex[:return_value], k)
1149
+ when :rep then add_ret(regex[:p1], regex[:return_value], k)
1150
+ when :amp
1121
1151
  pret = preturn(regex[:p1])
1122
1152
 
1123
1153
  if no_ret?(regex[:p1], pret)
1124
- NIL
1154
+ :nil
1125
1155
  else
1126
1156
  and_preds(pret, regex[:predicates])
1127
1157
  end
1128
- when ALT
1158
+ when :alt
1129
1159
  pred, key = regex[:predicates].zip(Array(regex[:keys])).find { |(p, _k)| accept_nil?(p) }
1130
1160
 
1131
1161
  r = if pred.nil?
1132
- NIL
1162
+ :nil
1133
1163
  else
1134
1164
  preturn(pred)
1135
1165
  end
1136
1166
 
1137
1167
  key ? [key, r] : r
1138
1168
  else
1139
- raise "Unexpected #{OP} #{regex[OP]}"
1169
+ raise "Unexpected op #{regex[:op]}"
1140
1170
  end
1141
1171
  end
1142
1172
 
@@ -1144,16 +1174,16 @@ module Speculation
1144
1174
  regex = reg_resolve!(regex)
1145
1175
  return r unless regex?(regex)
1146
1176
 
1147
- case regex[OP]
1148
- when ACCEPT, ALT, AMP
1177
+ case regex[:op]
1178
+ when :accept, :alt, :amp
1149
1179
  return_value = preturn(regex)
1150
1180
 
1151
- if return_value == NIL
1181
+ if return_value == :nil
1152
1182
  r
1153
1183
  else
1154
1184
  Utils.conj(r, key ? { key => return_value } : return_value)
1155
1185
  end
1156
- when PCAT, REP
1186
+ when :pcat, :rep
1157
1187
  return_value = preturn(regex)
1158
1188
 
1159
1189
  if return_value.empty?
@@ -1164,7 +1194,7 @@ module Speculation
1164
1194
  regex[:splice] ? Utils.into(r, val) : Utils.conj(r, val)
1165
1195
  end
1166
1196
  else
1167
- raise "Unexpected #{OP} #{regex[OP]}"
1197
+ raise "Unexpected op #{regex[:op]}"
1168
1198
  end
1169
1199
  end
1170
1200
 
@@ -1187,9 +1217,9 @@ module Speculation
1187
1217
  pred, *rest_preds = predicates
1188
1218
  key, *rest_keys = keys
1189
1219
 
1190
- case regex[OP]
1191
- when ACCEPT then nil
1192
- when PCAT
1220
+ case regex[:op]
1221
+ when :accept then nil
1222
+ when :pcat
1193
1223
  regex1 = pcat(:predicates => [deriv(pred, value), *rest_preds], :keys => keys, :return_value => return_value)
1194
1224
  regex2 = nil
1195
1225
 
@@ -1201,9 +1231,9 @@ module Speculation
1201
1231
  end
1202
1232
 
1203
1233
  alt2(regex1, regex2)
1204
- when ALT
1234
+ when :alt
1205
1235
  _alt(predicates.map { |p| deriv(p, value) }, keys)
1206
- when REP
1236
+ when :rep
1207
1237
  regex1 = rep(deriv(p1, value), p2, return_value, splice)
1208
1238
  regex2 = nil
1209
1239
 
@@ -1212,18 +1242,18 @@ module Speculation
1212
1242
  end
1213
1243
 
1214
1244
  alt2(regex1, regex2)
1215
- when AMP
1245
+ when :amp
1216
1246
  p1 = deriv(p1, value)
1217
1247
  return unless p1
1218
1248
 
1219
- if p1[OP] == ACCEPT
1249
+ if p1[:op] == :accept
1220
1250
  ret = and_preds(preturn(p1), predicates)
1221
1251
  accept(ret) unless invalid?(ret)
1222
1252
  else
1223
1253
  constrained(p1, *predicates)
1224
1254
  end
1225
1255
  else
1226
- raise "Unexpected #{OP} #{regex[OP]}"
1256
+ raise "Unexpected op #{regex[:op]}"
1227
1257
  end
1228
1258
  end
1229
1259
 
@@ -1251,9 +1281,9 @@ module Speculation
1251
1281
  end
1252
1282
  end
1253
1283
 
1254
- case p[OP]
1255
- when ACCEPT then nil
1256
- when AMP
1284
+ case p[:op]
1285
+ when :accept then nil
1286
+ when :amp
1257
1287
  if input.empty?
1258
1288
  if accept_nil?(p[:p1])
1259
1289
  explain_pred_list(p[:predicates], path, via, inn, preturn(p[:p1]))
@@ -1269,7 +1299,7 @@ module Speculation
1269
1299
  op_explain(p[:p1], path, via, inn, input)
1270
1300
  end
1271
1301
  end
1272
- when PCAT
1302
+ when :pcat
1273
1303
  pks = p[:predicates].zip(Array(p[:keys]))
1274
1304
  pred, k = if pks.count == 1
1275
1305
  pks.first
@@ -1284,7 +1314,7 @@ module Speculation
1284
1314
  else
1285
1315
  op_explain(pred, path, via, inn, input)
1286
1316
  end
1287
- when ALT
1317
+ when :alt
1288
1318
  return insufficient(p, path, via, inn) if input.empty?
1289
1319
 
1290
1320
  probs = p[:predicates].zip(Array(p[:keys])).flat_map { |(predicate, key)|
@@ -1292,21 +1322,21 @@ module Speculation
1292
1322
  }
1293
1323
 
1294
1324
  probs.compact
1295
- when REP
1325
+ when :rep
1296
1326
  op_explain(p[:p1], path, via, inn, input)
1297
1327
  else
1298
- raise "Unexpected #{OP} #{p[OP]}"
1328
+ raise "Unexpected :op #{p[:op]}"
1299
1329
  end
1300
1330
  end
1301
1331
  end
1302
1332
 
1303
1333
  @registry_ref.reset(
1304
- ns(:any) => with_gen(Utils.constantly(true), ->(r) { r.branch(*Gen::GEN_BUILTINS.values) }),
1334
+ ns(:any) => with_gen(Utils.constantly(true)) { ->(r) { r.branch(*Gen::GEN_BUILTINS.values) } },
1305
1335
  ns(:boolean) => Set[true, false],
1306
- ns(:positive_integer) => with_gen(self.and(Integer, ->(x) { x > 0 }), ->(r) { r.range(1) }),
1336
+ ns(:positive_integer) => with_gen(self.and(Integer, ->(x) { x > 0 })) { ->(r) { r.range(1) } },
1307
1337
  # Rantly#positive_integer is actually a natural integer
1308
- ns(:natural_integer) => with_gen(self.and(Integer, ->(x) { x >= 0 }), :positive_integer.to_proc),
1309
- ns(:negative_integer) => with_gen(self.and(Integer, ->(x) { x < 0 }), ->(r) { r.range(nil, -1) }),
1310
- ns(:empty) => with_gen(Utils.method(:empty?), Utils.constantly([]))
1338
+ ns(:natural_integer) => with_gen(self.and(Integer, ->(x) { x >= 0 })) { :positive_integer.to_proc },
1339
+ ns(:negative_integer) => with_gen(self.and(Integer, ->(x) { x < 0 })) { ->(r) { r.range(nil, -1) } },
1340
+ ns(:empty) => with_gen(Predicates.method(:empty?)) { Utils.constantly([]) }
1311
1341
  )
1312
1342
  end