speculation 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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