speculation 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/speculation.rb CHANGED
@@ -5,15 +5,13 @@ require "securerandom"
5
5
 
6
6
  require "speculation/version"
7
7
  require "speculation/namespaced_symbols"
8
- require "speculation/conj"
9
8
  require "speculation/identifier"
10
9
  require "speculation/utils"
11
10
  require "speculation/spec_impl"
12
11
  require "speculation/error"
13
12
 
14
13
  module Speculation
15
- using NamespacedSymbols.refine(self)
16
- using Conj
14
+ extend NamespacedSymbols
17
15
 
18
16
  class << self
19
17
  # Enables or disables spec asserts. Defaults to false.
@@ -36,7 +34,7 @@ module Speculation
36
34
  attr_accessor :coll_error_limit
37
35
  end
38
36
 
39
- @check_asserts = false
37
+ @check_asserts = ENV["SPECULATION_CHECK_ASSERTS"] == "true"
40
38
  @recursion_limit = 4
41
39
  @fspec_iterations = 21
42
40
  @coll_check_limit = 101
@@ -44,20 +42,24 @@ module Speculation
44
42
 
45
43
  @registry_ref = Concurrent::Atom.new({})
46
44
 
47
- # Can be enabled/disabled by setting check_asserts.
45
+ # Can be enabled or disabled at runtime:
46
+ # - enabled/disabled by setting `check_asserts`.
47
+ # - enabled by setting environment variable SPECULATION_CHECK_ASSERTS to the
48
+ # string "true"
49
+ # Defaults to false if not set.
48
50
  # @param spec [Spec]
49
51
  # @param x value to validate
50
52
  # @return x if x is valid? according to spec
51
53
  # @raise [Error] with explain_data plus :Speculation/failure of :assertion_failed
52
54
  def self.assert(spec, x)
53
55
  return x unless check_asserts
54
- return x unless valid?(spec, x)
56
+ return x if valid?(spec, x)
55
57
 
56
- ed = S._explain_data(spec, [], [], [], x)
58
+ ed = _explain_data(spec, [], [], [], x).merge(ns(:failure) => :assertion_failed)
57
59
  out = StringIO.new
58
- S.explain_out(ed, out)
60
+ explain_out(ed, out)
59
61
 
60
- raise Speculation::Error.new("Spec assertion failed\n#{out.string}", :failure.ns => :assertion_failed)
62
+ raise Speculation::Error.new("Spec assertion failed\n#{out.string}", ed)
61
63
  end
62
64
 
63
65
  # @param infinite [Boolean] whether +/- infinity allowed (default true)
@@ -76,30 +78,30 @@ module Speculation
76
78
  max ||= Float::MAX
77
79
 
78
80
  gens = [[20, ->(_) { rand(min.to_f..max.to_f) }]]
79
- gens << [1, ->(_) { Float::INFINITY }] if infinite
81
+ gens << [1, ->(r) { r.choose(Float::INFINITY, -Float::INFINITY) }] if infinite
80
82
  gens << [1, ->(_) { Float::NAN }] if nan
81
83
 
82
- spec(S.and(*preds), :gen => ->(rantly) { rantly.freq(*gens) })
84
+ spec(self.and(*preds), :gen => ->(rantly) { rantly.freq(*gens) })
83
85
  end
84
86
 
85
87
  # @param range [Range<Integer>]
86
88
  # @return Spec that validates ints in the given range
87
89
  def self.int_in(range)
88
- spec(S.and(Integer, ->(x) { range.include?(x) }),
90
+ spec(self.and(Integer, ->(x) { range.include?(x) }),
89
91
  :gen => ->(_) { rand(range) })
90
92
  end
91
93
 
92
94
  # @param time_range [Range<Time>]
93
95
  # @return Spec that validates times in the given range
94
96
  def self.time_in(time_range)
95
- spec(S.and(Time, ->(x) { time_range.cover?(x) }),
97
+ spec(self.and(Time, ->(x) { time_range.cover?(x) }),
96
98
  :gen => ->(_) { rand(time_range) })
97
99
  end
98
100
 
99
101
  # @param date_range [Range<Date>]
100
102
  # @return Spec that validates dates in the given range
101
103
  def self.date_in(date_range)
102
- spec(S.and(Date, ->(x) { date_range.cover?(x) }),
104
+ spec(self.and(Date, ->(x) { date_range.cover?(x) }),
103
105
  :gen => ->(_) { rand(date_range) })
104
106
  end
105
107
 
@@ -112,13 +114,13 @@ module Speculation
112
114
  # @param x [Hash, Object]
113
115
  # @return [Hash, false] x if x is a (Speculation) regex op, else logical false
114
116
  def self.regex?(x)
115
- Utils.hash?(x) && x[:op.ns] && x
117
+ Utils.hash?(x) && x[ns(:op)] && x
116
118
  end
117
119
 
118
120
  # @param value return value of a `conform` call
119
121
  # @return [Boolean] true if value is the result of an unsuccessful conform
120
122
  def self.invalid?(value)
121
- value.equal?(:invalid.ns)
123
+ value.equal?(ns(:invalid))
122
124
  end
123
125
 
124
126
  # @param spec [Spec]
@@ -129,13 +131,13 @@ module Speculation
129
131
  specize(spec).conform(value)
130
132
  end
131
133
 
132
- # Takes a spec and a one-arg generator block and returns a version of the spec that uses that generator
134
+ # Takes a spec and a one-arg generator function and returns a version of the spec that uses that generator
133
135
  # @param spec [Spec]
134
- # @yield [Rantly] generator block
136
+ # @param gen [Proc] generator proc that receives a Rantly instance
135
137
  # @return [Spec]
136
- def self.with_gen(spec, &gen)
138
+ def self.with_gen(spec, gen)
137
139
  if regex?(spec)
138
- spec.merge(:gfn.ns => gen)
140
+ spec.merge(ns(:gfn) => gen)
139
141
  else
140
142
  specize(spec).tap { |s| s.gen = gen }
141
143
  end
@@ -146,7 +148,7 @@ module Speculation
146
148
  probs = specize(spec).explain(path, via, inn, value)
147
149
 
148
150
  if probs && probs.any?
149
- { :problems.ns => probs }
151
+ { ns(:problems) => probs }
150
152
  end
151
153
  end
152
154
 
@@ -172,7 +174,7 @@ module Speculation
172
174
  def self.explain_out(ed, out = STDOUT)
173
175
  return out.puts("Success!") unless ed
174
176
 
175
- ed.fetch(:problems.ns).each do |prob|
177
+ ed.fetch(ns(:problems)).each do |prob|
176
178
  path, pred, val, reason, via, inn = prob.values_at(:path, :pred, :val, :reason, :via, :in)
177
179
 
178
180
  out.print("In: ", inn.to_a.inspect, " ") unless inn.empty?
@@ -184,7 +186,7 @@ module Speculation
184
186
 
185
187
  prob.each do |k, v|
186
188
  unless [:path, :pred, :val, :reason, :via, :in].include?(k)
187
- out.print("\n\t ", k.inspect, v.inspect)
189
+ out.print("\n\t ", k.inspect, PP.pp(v, String.new))
188
190
  end
189
191
  end
190
192
 
@@ -192,7 +194,7 @@ module Speculation
192
194
  end
193
195
 
194
196
  ed.each do |k, v|
195
- out.puts(k, v) unless k == :problems.ns
197
+ out.puts("#{k} #{PP.pp(v, String.new)}") unless k == ns(:problems)
196
198
  end
197
199
 
198
200
  nil
@@ -226,7 +228,7 @@ module Speculation
226
228
  Gen.such_that(->(x) { valid?(spec, x) }, g)
227
229
  else
228
230
  raise Speculation::Error.new("unable to construct gen at: #{path.inspect} for: #{spec.inspect}",
229
- :failure.ns => :no_gen, :path.ns => path)
231
+ ns(:failure) => :no_gen, ns(:path) => path)
230
232
  end
231
233
  end
232
234
 
@@ -246,10 +248,9 @@ module Speculation
246
248
  # @return [Proc]
247
249
  def self.gen(spec, overrides = nil)
248
250
  spec = Identifier(spec)
249
- gensub(spec, overrides, [], :recursion_limit.ns => recursion_limit)
251
+ gensub(spec, overrides, [], ns(:recursion_limit) => recursion_limit)
250
252
  end
251
253
 
252
- # rubocop:disable Style/MethodName
253
254
  # @private
254
255
  def self.Identifier(x)
255
256
  case x
@@ -258,19 +259,17 @@ module Speculation
258
259
  else x
259
260
  end
260
261
  end
261
- # rubocop:enable Style/MethodName
262
262
 
263
263
  # Given a namespace-qualified symbol key, and a spec, spec name, predicate or
264
264
  # regex-op makes an entry in the registry mapping key to the spec
265
265
  # @param key [Symbol] namespace-qualified symbol
266
266
  # @param spec [Spec, Symbol, Proc, Hash] a spec, spec name, predicate or regex-op
267
- # @return [Symbol, Identifier]
267
+ # @return [Symbol, Method]
268
268
  def self.def(key, spec)
269
269
  key = Identifier(key)
270
270
 
271
- unless Utils.ident?(key) && key.namespace
272
- raise ArgumentError,
273
- "key must be a namespaced Symbol, e.g. #{:my_spec.ns}, given #{key}, or a Speculation::Identifier"
271
+ unless Utils.ident?(key) && (!key.is_a?(Symbol) || NamespacedSymbols.namespace(key))
272
+ raise ArgumentError, "key must be a namespaced Symbol, e.g. #{ns(:my_spec)}, or a Method"
274
273
  end
275
274
 
276
275
  spec = if spec?(spec) || regex?(spec) || registry[spec]
@@ -280,10 +279,10 @@ module Speculation
280
279
  end
281
280
 
282
281
  @registry_ref.swap do |reg|
283
- reg.merge(key => with_name(spec, key))
282
+ reg.merge(key => with_name(spec, key)).freeze
284
283
  end
285
284
 
286
- key
285
+ key.is_a?(Identifier) ? key.get_method : key
287
286
  end
288
287
 
289
288
  # @return [Hash] the registry hash
@@ -336,8 +335,8 @@ module Speculation
336
335
  #
337
336
  # The :req key array supports 'and_keys' and 'or_keys' for key groups:
338
337
  #
339
- # S.keys(req: [:x.ns, :y.ns, S.or_keys(:secret.ns, S.and_keys(:user.ns, :pwd.ns))],
340
- # opt: [:z.ns])
338
+ # S.keys(req: [ns(:x), ns(:y), S.or_keys(ns(:secret), S.and_keys(ns(:user), ns(:pwd)))],
339
+ # opt: [ns(:z)])
341
340
  #
342
341
  # There are also _un versions of :req and :opt. These allow you to connect
343
342
  # unqualified keys to specs. In each case, fully qualfied keywords are passed,
@@ -367,12 +366,12 @@ module Speculation
367
366
 
368
367
  # @see keys
369
368
  def self.or_keys(*ks)
370
- [:or.ns, *ks]
369
+ [ns(:or), *ks]
371
370
  end
372
371
 
373
372
  # @see keys
374
373
  def self.and_keys(*ks)
375
- [:and.ns, *ks]
374
+ [ns(:and), *ks]
376
375
  end
377
376
 
378
377
  # @param key_preds [Hash] Takes key+pred hash
@@ -442,8 +441,8 @@ module Speculation
442
441
  # @param options [Hash]
443
442
  # @return [Spec] spec that validates associative collections
444
443
  def self.every_kv(kpred, vpred, options)
445
- every(tuple(kpred, vpred), :kfn.ns => ->(_i, v) { v.first },
446
- :into => {},
444
+ every(tuple(kpred, vpred), ns(:kfn) => ->(_i, v) { v.first },
445
+ :into => {},
447
446
  **options)
448
447
  end
449
448
 
@@ -459,7 +458,7 @@ module Speculation
459
458
  # @param opts [Hash]
460
459
  # @return [Spec]
461
460
  def self.coll_of(pred, opts = {})
462
- every(pred, :conform_all.ns => true, **opts)
461
+ every(pred, ns(:conform_all) => true, **opts)
463
462
  end
464
463
 
465
464
  # Returns a spec for a hash whose keys satisfy kpred and vals satisfy vpred.
@@ -476,8 +475,8 @@ module Speculation
476
475
  # @param options [Hash]
477
476
  # @return [Spec]
478
477
  def self.hash_of(kpred, vpred, options = {})
479
- every_kv(kpred, vpred, :kind => Utils.method(:hash?).to_proc,
480
- :conform_all.ns => true,
478
+ every_kv(kpred, vpred, :kind => Utils.method(:hash?),
479
+ ns(:conform_all) => true,
481
480
  **options)
482
481
  end
483
482
 
@@ -499,7 +498,7 @@ module Speculation
499
498
  # @return [Hash] regex op that matches zero or one value matching pred. Produces a
500
499
  # single value (not a collection) if matched.
501
500
  def self.zero_or_one(pred)
502
- _alt([pred, accept(:nil.ns)], nil)
501
+ _alt([pred, accept(ns(:nil))], nil)
503
502
  end
504
503
 
505
504
  # @param kv_specs [Hash] key+pred pairs
@@ -530,14 +529,14 @@ module Speculation
530
529
  # resulting value to the conjunction of the predicates, and any conforming
531
530
  # they might perform.
532
531
  def self.constrained(re, *preds)
533
- { :op.ns => :amp.ns, :p1 => re, :predicates => preds }
532
+ { ns(:op) => ns(:amp), :p1 => re, :predicates => preds }
534
533
  end
535
534
 
536
- # @param f predicate function with the semantics of conform i.e. it should
535
+ # @yield [value] predicate function with the semantics of conform i.e. it should
537
536
  # return either a (possibly converted) value or :"Speculation/invalid"
538
- # @return [Spec] a spec that uses pred as a predicate/conformer.
539
- def self.conformer(f)
540
- spec_impl(f, true)
537
+ # @return [Spec] a spec that uses block as a predicate/conformer.
538
+ def self.conformer(&pred)
539
+ spec_impl(pred, true)
541
540
  end
542
541
 
543
542
  # Takes :args :ret and (optional) :block and :fn kwargs whose values are preds and returns a spec
@@ -556,7 +555,7 @@ module Speculation
556
555
  # @return [Spec]
557
556
  # @see fdef See 'fdef' for a single operation that creates an fspec and registers it, as well as a full description of :args, :block, :ret and :fn
558
557
  def self.fspec(args: nil, ret: nil, fn: nil, block: nil, gen: nil)
559
- FSpec.new(:argspec => spec(args), :retspec => spec(ret), :fnspec => spec(fn), :blockspec => spec(block)).tap do |spec|
558
+ FSpec.new(:args => spec(args), :ret => spec(ret), :fn => spec(fn), :block => spec(block)).tap do |spec|
560
559
  spec.gen = gen
561
560
  end
562
561
  end
@@ -575,8 +574,8 @@ module Speculation
575
574
  # S.fdef(Hash.method(:[]),
576
575
  # args: S.alt(
577
576
  # hash: Hash,
578
- # array_of_pairs: S.coll_of(S.tuple(:any.ns(S), :any.ns(S)), kind: Array),
579
- # kvs: S.constrained(S.one_or_more(:any.ns(S)), -> (kvs) { kvs.count.even? })
577
+ # array_of_pairs: S.coll_of(S.tuple(ns(S, :any), ns(S, :any)), kind: Array),
578
+ # kvs: S.constrained(S.one_or_more(ns(S, :any)), -> (kvs) { kvs.count.even? })
580
579
  # ),
581
580
  # ret: Hash
582
581
  # )
@@ -589,13 +588,12 @@ module Speculation
589
588
  # @option spec :fn a spec of the relationship between args and ret - the value passed is
590
589
  # { args: conformed_args, block: given_block, ret: conformed_ret } and is expected to contain
591
590
  # predicates that relate those values
592
- # @return [Identifier] the Speculation::Identifier object representing the method which is used as the spec's
593
- # key in the spec registry.
591
+ # @return [Method] the method spec'ed
594
592
  # @note Note that :fn specs require the presence of :args and :ret specs to conform values, and so :fn
595
593
  # specs will be ignored if :args or :ret are missing.
596
594
  def self.fdef(method, spec)
597
- ident = Identifier(method)
598
- self.def(ident, fspec(spec))
595
+ self.def(Identifier(method), fspec(spec))
596
+ method
599
597
  end
600
598
 
601
599
  # @param spec
@@ -638,14 +636,14 @@ module Speculation
638
636
  fspec ||= get_spec(method)
639
637
  raise ArgumentError, "No fspec found for #{method}" unless fspec
640
638
 
641
- Gen.sample(gen(fspec.argspec), n).map { |args| [args, method.call(*args)] }
639
+ Gen.sample(gen(fspec.args), n).map { |args| [args, method.call(*args)] }
642
640
  end
643
641
 
644
642
  ### impl ###
645
643
 
646
644
  # @private
647
645
  def self.recur_limit?(rmap, id, path, k)
648
- rmap[id] > rmap[:recursion_limit.ns] &&
646
+ rmap[id] > rmap[ns(:recursion_limit)] &&
649
647
  path.include?(k)
650
648
  end
651
649
 
@@ -663,11 +661,11 @@ module Speculation
663
661
  if spec
664
662
  conform(spec, x)
665
663
  elsif pred.is_a?(Module) || pred.is_a?(::Regexp)
666
- pred === x ? x : :invalid.ns
664
+ pred === x ? x : ns(:invalid)
667
665
  elsif pred.is_a?(Set)
668
- pred.include?(x) ? x : :invalid.ns
666
+ pred.include?(x) ? x : ns(:invalid)
669
667
  elsif pred.respond_to?(:call)
670
- pred.call(x) ? x : :invalid.ns
668
+ pred.call(x) ? x : ns(:invalid)
671
669
  else
672
670
  raise "#{pred} is not a class, proc, set or regexp"
673
671
  end
@@ -685,11 +683,11 @@ module Speculation
685
683
 
686
684
  if spec?(spec)
687
685
  name = spec_name(spec)
688
- via = via.conj(name) if name
686
+ via = Utils.conj(via, name) if name
689
687
 
690
688
  spec.explain(path, via, inn, value)
691
689
  else
692
- [{ :path => path, :val => value, :via => via, :in => inn, :pred => pred }]
690
+ [{ :path => path, :val => value, :via => via, :in => inn, :pred => [pred, [value]] }]
693
691
  end
694
692
  end
695
693
 
@@ -731,7 +729,7 @@ module Speculation
731
729
  p = reg_resolve!(p)
732
730
 
733
731
  id, op, ps, ks, p1, p2, ret, id, gen = p.values_at(
734
- :id, :op.ns, :predicates, :keys, :p1, :p2, :return_value, :id, :gen.ns
732
+ :id, ns(:op), :predicates, :keys, :p1, :p2, :return_value, :id, ns(:gen)
735
733
  ) if regex?(p)
736
734
 
737
735
  id = p.id if spec?(p)
@@ -743,9 +741,9 @@ module Speculation
743
741
  preds.zip(keys).map do |pred, k|
744
742
  unless rmap && id && k && recur_limit?(rmap, id, path, k)
745
743
  if id
746
- Gen.delay { Speculation.re_gen(pred, overrides, k ? path.conj(k) : path, rmap) }
744
+ Gen.delay { Speculation.re_gen(pred, overrides, k ? Utils.conj(path, k) : path, rmap) }
747
745
  else
748
- re_gen(pred, overrides, k ? path.conj(k) : path, rmap)
746
+ re_gen(pred, overrides, k ? Utils.conj(path, k) : path, rmap)
749
747
  end
750
748
  end
751
749
  end
@@ -767,8 +765,8 @@ module Speculation
767
765
 
768
766
  if p
769
767
  case op
770
- when :accept.ns
771
- if ret == :nil.ns
768
+ when ns(:accept)
769
+ if ret == ns(:nil)
772
770
  ->(_rantly) { [] }
773
771
  else
774
772
  ->(_rantly) { [ret] }
@@ -777,9 +775,9 @@ module Speculation
777
775
  g = gensub(p, overrides, path, rmap)
778
776
 
779
777
  ->(rantly) { [g.call(rantly)] }
780
- when :amp.ns
778
+ when ns(:amp)
781
779
  re_gen(p1, overrides, path, rmap)
782
- when :pcat.ns
780
+ when ns(:pcat)
783
781
  gens = ggens.call(ps, ks)
784
782
 
785
783
  if gens.all?
@@ -787,11 +785,11 @@ module Speculation
787
785
  gens.flat_map { |gg| gg.call(rantly) }
788
786
  end
789
787
  end
790
- when :alt.ns
788
+ when ns(:alt)
791
789
  gens = ggens.call(ps, ks).compact
792
790
 
793
791
  ->(rantly) { rantly.branch(*gens) } unless gens.empty?
794
- when :rep.ns
792
+ when ns(:rep)
795
793
  if recur_limit?(rmap, id, [id], id)
796
794
  ->(_rantly) { [] }
797
795
  else
@@ -812,11 +810,11 @@ module Speculation
812
810
  x, *xs = data
813
811
 
814
812
  if data.empty?
815
- return :invalid.ns unless accept_nil?(regex)
813
+ return ns(:invalid) unless accept_nil?(regex)
816
814
 
817
815
  return_value = preturn(regex)
818
816
 
819
- if return_value == :nil.ns
817
+ if return_value == ns(:nil)
820
818
  nil
821
819
  else
822
820
  return_value
@@ -827,7 +825,7 @@ module Speculation
827
825
  if dp
828
826
  re_conform(dp, xs)
829
827
  else
830
- :invalid.ns
828
+ ns(:invalid)
831
829
  end
832
830
  end
833
831
  end
@@ -845,22 +843,22 @@ module Speculation
845
843
  end
846
844
 
847
845
  if accept?(p)
848
- if p[:op.ns] == :pcat.ns
849
- return op_explain(p, path, via, inn.conj(index), input[index..-1])
846
+ if p[ns(:op)] == ns(:pcat)
847
+ return op_explain(p, path, via, Utils.conj(inn, index), input[index..-1])
850
848
  else
851
849
  return [{ :path => path,
852
850
  :reason => "Extra input",
853
851
  :val => input,
854
852
  :via => via,
855
- :in => inn.conj(index) }]
853
+ :in => Utils.conj(inn, index) }]
856
854
  end
857
855
  else
858
- return op_explain(p, path, via, inn.conj(index), input[index..-1]) ||
856
+ return op_explain(p, path, via, Utils.conj(inn, index), input[index..-1]) ||
859
857
  [{ :path => path,
860
858
  :reason => "Extra input",
861
859
  :val => input,
862
860
  :via => via,
863
- :in => inn.conj(index) }]
861
+ :in => Utils.conj(inn, index) }]
864
862
  end
865
863
  end
866
864
 
@@ -908,7 +906,7 @@ module Speculation
908
906
  if Utils.ident?(spec)
909
907
  spec
910
908
  elsif regex?(spec)
911
- spec.merge(:name.ns => name)
909
+ spec.merge(ns(:name) => name)
912
910
  else
913
911
  spec.tap { |s| s.name = name }
914
912
  end
@@ -918,7 +916,7 @@ module Speculation
918
916
  if Utils.ident?(spec)
919
917
  spec
920
918
  elsif regex?(spec)
921
- spec[:name.ns]
919
+ spec[ns(:name)]
922
920
  elsif spec.respond_to?(:name)
923
921
  spec.name
924
922
  end
@@ -955,7 +953,7 @@ module Speculation
955
953
  x = dt(pred, x)
956
954
 
957
955
  if invalid?(x)
958
- :invalid.ns
956
+ ns(:invalid)
959
957
  elsif preds.empty?
960
958
  x
961
959
  else
@@ -979,12 +977,12 @@ module Speculation
979
977
  ### regex ###
980
978
 
981
979
  def accept(x)
982
- { :op.ns => :accept.ns, :return_value => x }
980
+ { ns(:op) => ns(:accept), :return_value => x }
983
981
  end
984
982
 
985
983
  def accept?(hash)
986
984
  if hash.is_a?(Hash)
987
- hash[:op.ns] == :accept.ns
985
+ hash[ns(:op)] == ns(:accept)
988
986
  end
989
987
  end
990
988
 
@@ -997,14 +995,14 @@ module Speculation
997
995
  return unless regex[:predicates].all?
998
996
 
999
997
  unless accept?(predicate)
1000
- return { :op.ns => :pcat.ns,
998
+ return { ns(:op) => ns(:pcat),
1001
999
  :predicates => regex[:predicates],
1002
1000
  :keys => keys,
1003
1001
  :return_value => regex[:return_value] }
1004
1002
  end
1005
1003
 
1006
1004
  val = keys ? { key => predicate[:return_value] } : predicate[:return_value]
1007
- return_value = regex[:return_value].conj(val)
1005
+ return_value = Utils.conj(regex[:return_value], val)
1008
1006
 
1009
1007
  if rest_predicates
1010
1008
  pcat(:predicates => rest_predicates,
@@ -1018,10 +1016,10 @@ module Speculation
1018
1016
  def rep(p1, p2, return_value, splice)
1019
1017
  return unless p1
1020
1018
 
1021
- regex = { :op.ns => :rep.ns, :p2 => p2, :splice => splice, :id => SecureRandom.uuid }
1019
+ regex = { ns(:op) => ns(:rep), :p2 => p2, :splice => splice, :id => SecureRandom.uuid }
1022
1020
 
1023
1021
  if accept?(p1)
1024
- regex.merge(:p1 => p2, :return_value => return_value.conj(p1[:return_value]))
1022
+ regex.merge(:p1 => p2, :return_value => Utils.conj(return_value, p1[:return_value]))
1025
1023
  else
1026
1024
  regex.merge(:p1 => p1, :return_value => return_value)
1027
1025
  end
@@ -1037,13 +1035,13 @@ module Speculation
1037
1035
  end
1038
1036
 
1039
1037
  def _alt(predicates, keys)
1040
- predicates, keys = filter_alt(predicates, keys, &:itself)
1038
+ predicates, keys = filter_alt(predicates, keys, &Utils.method(:itself))
1041
1039
  return unless predicates
1042
1040
 
1043
1041
  predicate, *rest_predicates = predicates
1044
1042
  key, *_rest_keys = keys
1045
1043
 
1046
- return_value = { :op.ns => :alt.ns, :predicates => predicates, :keys => keys }
1044
+ return_value = { ns(:op) => ns(:alt), :predicates => predicates, :keys => keys }
1047
1045
  return return_value unless rest_predicates.empty?
1048
1046
 
1049
1047
  return predicate unless key
@@ -1061,24 +1059,24 @@ module Speculation
1061
1059
  end
1062
1060
 
1063
1061
  def no_ret?(p1, pret)
1064
- return true if pret == :nil.ns
1062
+ return true if pret == ns(:nil)
1065
1063
 
1066
1064
  regex = reg_resolve!(p1)
1067
- op = regex[:op.ns]
1065
+ op = regex[ns(:op)]
1068
1066
 
1069
- [:rep.ns, :pcat.ns].include?(op) && pret.empty? || nil
1067
+ [ns(:rep), ns(:pcat)].include?(op) && pret.empty? || nil
1070
1068
  end
1071
1069
 
1072
1070
  def accept_nil?(regex)
1073
1071
  regex = reg_resolve!(regex)
1074
1072
  return unless regex?(regex)
1075
1073
 
1076
- case regex[:op.ns]
1077
- when :accept.ns then true
1078
- when :pcat.ns then regex[:predicates].all?(&method(:accept_nil?))
1079
- when :alt.ns then regex[:predicates].any?(&method(:accept_nil?))
1080
- when :rep.ns then (regex[:p1] == regex[:p2]) || accept_nil?(regex[:p1])
1081
- when :amp.ns
1074
+ case regex[ns(:op)]
1075
+ when ns(:accept) then true
1076
+ when ns(:pcat) then regex[:predicates].all?(&method(:accept_nil?))
1077
+ when ns(:alt) then regex[:predicates].any?(&method(:accept_nil?))
1078
+ when ns(:rep) then (regex[:p1] == regex[:p2]) || accept_nil?(regex[:p1])
1079
+ when ns(:amp)
1082
1080
  p1 = regex[:p1]
1083
1081
 
1084
1082
  return false unless accept_nil?(p1)
@@ -1086,7 +1084,7 @@ module Speculation
1086
1084
  no_ret?(p1, preturn(p1)) ||
1087
1085
  !invalid?(and_preds(preturn(p1), regex[:predicates]))
1088
1086
  else
1089
- raise "Unexpected #{:op.ns} #{regex[:op.ns]}"
1087
+ raise "Unexpected #{ns(:op)} #{regex[ns(:op)]}"
1090
1088
  end
1091
1089
  end
1092
1090
 
@@ -1097,23 +1095,23 @@ module Speculation
1097
1095
  p0, *_pr = regex[:predicates]
1098
1096
  k, *ks = regex[:keys]
1099
1097
 
1100
- case regex[:op.ns]
1101
- when :accept.ns then regex[:return_value]
1102
- when :pcat.ns then add_ret(p0, regex[:return_value], k)
1103
- when :rep.ns then add_ret(regex[:p1], regex[:return_value], k)
1104
- when :amp.ns
1098
+ case regex[ns(:op)]
1099
+ when ns(:accept) then regex[:return_value]
1100
+ when ns(:pcat) then add_ret(p0, regex[:return_value], k)
1101
+ when ns(:rep) then add_ret(regex[:p1], regex[:return_value], k)
1102
+ when ns(:amp)
1105
1103
  pret = preturn(regex[:p1])
1106
1104
 
1107
1105
  if no_ret?(regex[:p1], pret)
1108
- :nil.ns
1106
+ ns(:nil)
1109
1107
  else
1110
1108
  and_preds(pret, regex[:predicates])
1111
1109
  end
1112
- when :alt.ns
1110
+ when ns(:alt)
1113
1111
  ps, ks = filter_alt(regex[:predicates], regex[:keys], &method(:accept_nil?))
1114
1112
 
1115
1113
  r = if ps.first.nil?
1116
- :nil.ns
1114
+ ns(:nil)
1117
1115
  else
1118
1116
  preturn(ps.first)
1119
1117
  end
@@ -1124,7 +1122,7 @@ module Speculation
1124
1122
  r
1125
1123
  end
1126
1124
  else
1127
- raise "Unexpected #{:op.ns} #{regex[:op.ns]}"
1125
+ raise "Unexpected #{ns(:op)} #{regex[ns(:op)]}"
1128
1126
  end
1129
1127
  end
1130
1128
 
@@ -1140,22 +1138,22 @@ module Speculation
1140
1138
  else
1141
1139
  val = key ? { key => return_value } : return_value
1142
1140
 
1143
- regex[:splice] ? Utils.into(r, val) : r.conj(val)
1141
+ regex[:splice] ? Utils.into(r, val) : Utils.conj(r, val)
1144
1142
  end
1145
1143
  end
1146
1144
 
1147
- case regex[:op.ns]
1148
- when :accept.ns, :alt.ns, :amp.ns
1145
+ case regex[ns(:op)]
1146
+ when ns(:accept), ns(:alt), ns(:amp)
1149
1147
  return_value = preturn(regex)
1150
1148
 
1151
- if return_value == :nil.ns
1149
+ if return_value == ns(:nil)
1152
1150
  r
1153
1151
  else
1154
- r.conj(key ? { key => return_value } : return_value)
1152
+ Utils.conj(r, key ? { key => return_value } : return_value)
1155
1153
  end
1156
- when :pcat.ns, :rep.ns then prop.call
1154
+ when ns(:pcat), ns(:rep) then prop.call
1157
1155
  else
1158
- raise "Unexpected #{:op.ns} #{regex[:op.ns]}"
1156
+ raise "Unexpected #{ns(:op)} #{regex[ns(:op)]}"
1159
1157
  end
1160
1158
  end
1161
1159
 
@@ -1178,9 +1176,9 @@ module Speculation
1178
1176
  pred, *rest_preds = predicates
1179
1177
  key, *rest_keys = keys
1180
1178
 
1181
- case regex[:op.ns]
1182
- when :accept.ns then nil
1183
- when :pcat.ns
1179
+ case regex[ns(:op)]
1180
+ when ns(:accept) then nil
1181
+ when ns(:pcat)
1184
1182
  regex1 = pcat(:predicates => [deriv(pred, value), *rest_preds], :keys => keys, :return_value => return_value)
1185
1183
  regex2 = nil
1186
1184
 
@@ -1192,9 +1190,9 @@ module Speculation
1192
1190
  end
1193
1191
 
1194
1192
  alt2(regex1, regex2)
1195
- when :alt.ns
1193
+ when ns(:alt)
1196
1194
  _alt(predicates.map { |p| deriv(p, value) }, keys)
1197
- when :rep.ns
1195
+ when ns(:rep)
1198
1196
  regex1 = rep(deriv(p1, value), p2, return_value, splice)
1199
1197
  regex2 = nil
1200
1198
 
@@ -1203,24 +1201,25 @@ module Speculation
1203
1201
  end
1204
1202
 
1205
1203
  alt2(regex1, regex2)
1206
- when :amp.ns
1204
+ when ns(:amp)
1207
1205
  p1 = deriv(p1, value)
1208
1206
  return unless p1
1209
1207
 
1210
- if p1[:op.ns] == :accept.ns
1208
+ if p1[ns(:op)] == ns(:accept)
1211
1209
  ret = and_preds(preturn(p1), predicates)
1212
1210
  accept(ret) unless invalid?(ret)
1213
1211
  else
1214
1212
  constrained(p1, *predicates)
1215
1213
  end
1216
1214
  else
1217
- raise "Unexpected #{:op.ns} #{regex[:op.ns]}"
1215
+ raise "Unexpected #{ns(:op)} #{regex[ns(:op)]}"
1218
1216
  end
1219
1217
  end
1220
1218
 
1221
- def insufficient(path, via, inn)
1219
+ def insufficient(pred, path, via, inn)
1222
1220
  [{ :path => path,
1223
1221
  :reason => "Insufficient input",
1222
+ :pred => [pred, []],
1224
1223
  :val => [],
1225
1224
  :via => via,
1226
1225
  :in => inn }]
@@ -1235,20 +1234,20 @@ module Speculation
1235
1234
 
1236
1235
  unless regex?(p)
1237
1236
  if input.empty?
1238
- return insufficient(path, via, inn)
1237
+ return insufficient(p, path, via, inn)
1239
1238
  else
1240
1239
  return explain1(p, path, via, inn, x)
1241
1240
  end
1242
1241
  end
1243
1242
 
1244
- case p[:op.ns]
1245
- when :accept.ns then nil
1246
- when :amp.ns
1243
+ case p[ns(:op)]
1244
+ when ns(:accept) then nil
1245
+ when ns(:amp)
1247
1246
  if input.empty?
1248
1247
  if accept_nil?(p[:p1])
1249
1248
  explain_pred_list(p[:predicates], path, via, inn, preturn(p[:p1]))
1250
1249
  else
1251
- insufficient(path, via, inn)
1250
+ insufficient(p, path, via, inn)
1252
1251
  end
1253
1252
  else
1254
1253
  p1 = deriv(p[:p1], x)
@@ -1259,46 +1258,46 @@ module Speculation
1259
1258
  op_explain(p[:p1], path, via, inn, input)
1260
1259
  end
1261
1260
  end
1262
- when :pcat.ns
1261
+ when ns(:pcat)
1263
1262
  pks = p[:predicates].zip(p[:keys] || [])
1264
1263
  pred, k = if pks.count == 1
1265
1264
  pks.first
1266
1265
  else
1267
1266
  pks.lazy.reject { |(predicate, _)| accept_nil?(predicate) }.first
1268
1267
  end
1269
- path = path.conj(k) if k
1268
+ path = Utils.conj(path, k) if k
1270
1269
 
1271
1270
  if input.empty? && !pred
1272
- insufficient(path, via, inn)
1271
+ insufficient(pred, path, via, inn)
1273
1272
  else
1274
1273
  op_explain(pred, path, via, inn, input)
1275
1274
  end
1276
- when :alt.ns
1277
- return insufficient(path, via, inn) if input.empty?
1275
+ when ns(:alt)
1276
+ return insufficient(p, path, via, inn) if input.empty?
1278
1277
 
1279
1278
  probs = p[:predicates].zip(p[:keys]).flat_map { |(predicate, key)|
1280
- op_explain(predicate, key ? path.conj(key) : path, via, inn, input)
1279
+ op_explain(predicate, key ? Utils.conj(path, key) : path, via, inn, input)
1281
1280
  }
1282
1281
 
1283
1282
  probs.compact
1284
- when :rep.ns
1283
+ when ns(:rep)
1285
1284
  op_explain(p[:p1], path, via, inn, input)
1286
1285
  else
1287
- raise "Unexpected #{:op.ns} #{p[:op.ns]}"
1286
+ raise "Unexpected #{ns(:op)} #{p[ns(:op)]}"
1288
1287
  end
1289
1288
  end
1290
1289
 
1291
1290
  # Resets the spec registry to only builtin specs
1292
1291
  def reset_registry!
1293
1292
  builtins = {
1294
- :any.ns => with_gen(Utils.constantly(true)) { |r| r.branch(*Gen::GEN_BUILTINS.values) },
1295
- :boolean.ns => Set[true, false],
1296
- :positive_integer.ns => with_gen(self.and(Integer, ->(x) { x > 0 })) { |r| r.range(1) },
1293
+ ns(:any) => with_gen(Utils.constantly(true), ->(r) { r.branch(*Gen::GEN_BUILTINS.values) }),
1294
+ ns(:boolean) => Set[true, false],
1295
+ ns(:positive_integer) => with_gen(self.and(Integer, ->(x) { x > 0 }), ->(r) { r.range(1) }),
1297
1296
  # Rantly#positive_integer is actually a natural integer
1298
- :natural_integer.ns => with_gen(self.and(Integer, ->(x) { x >= 0 }), &:positive_integer),
1299
- :negative_integer.ns => with_gen(self.and(Integer, ->(x) { x < 0 })) { |r| r.range(nil, -1) },
1300
- :empty.ns => with_gen(:empty?.to_proc) { |_| [] }
1301
- }
1297
+ ns(:natural_integer) => with_gen(self.and(Integer, ->(x) { x >= 0 }), :positive_integer.to_proc),
1298
+ ns(:negative_integer) => with_gen(self.and(Integer, ->(x) { x < 0 }), ->(r) { r.range(nil, -1) }),
1299
+ ns(:empty) => with_gen(Utils.method(:empty?), Utils.constantly([]))
1300
+ }.freeze
1302
1301
 
1303
1302
  @registry_ref.reset(builtins)
1304
1303
  end