Module: Speculation
- Extended by:
- NamespacedSymbols
- Defined in:
- lib/speculation.rb,
lib/speculation/gen.rb,
lib/speculation/pmap.rb,
lib/speculation/spec.rb,
lib/speculation/test.rb,
lib/speculation/error.rb,
lib/speculation/utils.rb,
lib/speculation/version.rb,
lib/speculation/identifier.rb,
lib/speculation/spec/f_spec.rb,
lib/speculation/spec/or_spec.rb,
lib/speculation/spec/and_spec.rb,
lib/speculation/spec/hash_spec.rb,
lib/speculation/spec/every_spec.rb,
lib/speculation/spec/merge_spec.rb,
lib/speculation/spec/regex_spec.rb,
lib/speculation/spec/tuple_spec.rb,
lib/speculation/spec/nilable_spec.rb,
lib/speculation/namespaced_symbols.rb,
lib/speculation/spec/predicate_spec.rb
Defined Under Namespace
Modules: Gen, NamespacedSymbols, Test Classes: Error
Constant Summary collapse
- INVALID =
ns(:invalid)
- VERSION =
"0.3.0"
Class Attribute Summary collapse
-
.check_asserts ⇒ Object
Enables or disables spec asserts.
-
.coll_check_limit ⇒ Object
The number of elements validated in a collection spec’ed with ‘every’.
-
.coll_error_limit ⇒ Object
The number of errors reported by explain in a collection spec’ed with ‘every’.
-
.fspec_iterations ⇒ Object
The number of times an anonymous fn specified by fspec will be (generatively) tested during conform.
-
.recursion_limit ⇒ Object
A soft limit on how many times a branching spec (or/alt/zero_or_more) can be recursed through during generation.
Class Method Summary collapse
-
.alt(kv_specs) ⇒ Hash
Regex op that returns a two item array containing the key of the first matching pred and the corresponding value.
-
.and(*preds) ⇒ Spec
A spec that returns the conformed value.
- .and_keys(*ks) ⇒ Object
-
.assert(spec, x) ⇒ Object
Can be enabled or disabled at runtime: - enabled/disabled by setting ‘check_asserts`.
-
.cat(named_specs) ⇒ Hash
Regex op that matches (all) values in sequence, returning a map containing the keys of each pred and the corresponding value.
-
.coll_of(pred, opts = {}) ⇒ Spec
Returns a spec for a collection of items satisfying pred.
-
.conform(spec, value) ⇒ Symbol, Object
:Speculation/invalid if value does not match spec, else the (possibly destructured) value.
-
.conformer {|value| ... } ⇒ Spec
A spec that uses block as a predicate/conformer.
-
.constrained(re, *preds) ⇒ Hash
Regex-op that consumes input as per re but subjects the resulting value to the conjunction of the predicates, and any conforming they might perform.
-
.date_in(date_range) ⇒ Object
Spec that validates dates in the given range.
-
.def(key, spec) ⇒ Symbol, Method
Given a namespace-qualified symbol key, and a spec, spec name, predicate or regex-op makes an entry in the registry mapping key to the spec.
-
.every(pred, opts = {}) ⇒ Spec
Spec that validates collection elements against pred.
-
.every_kv(kpred, vpred, options) ⇒ Spec
Like ‘every’ but takes separate key and val preds and works on associative collections.
-
.exercise(spec, n: 10, overrides: {}) ⇒ Array
Generates a number (default 10) of values compatible with spec and maps conform over them, returning a sequence of [val conformed-val] tuples.
-
.exercise_fn(method, n: 10, fspec: nil) ⇒ Array
Exercises the method by applying it to n (default 10) generated samples of its args spec.
-
.explain(spec, x) ⇒ Object
Given a spec and a value that fails to conform, prints an explaination to STDOUT.
-
.explain_data(spec, x) ⇒ nil, Hash
Given a spec and a value x which ought to conform, returns nil if x conforms, else a hash with at least the key :“Speculation/problems” whose value is a collection of problem-hashes, where problem-hash has at least :path :pred and :val keys describing the predicate and the value that failed at that path.
- .explain_out(ed, out = STDOUT) ⇒ Object
-
.explain_str(spec, x) ⇒ String
A human readable explaination.
-
.fdef(method, spec) ⇒ Method
Once registered, specs are checked by instrument and tested by the runner Speculation::Test.check.
-
.float_in(min: nil, max: nil, infinite: true, nan: true) ⇒ Spec
That validates floats.
-
.fspec(args: nil, ret: nil, fn: nil, block: nil, gen: nil) ⇒ Spec
Takes :args :ret and (optional) :block and :fn kwargs whose values are preds and returns a spec whose conform/explain take a method/proc and validates it using generative testing.
-
.gen(spec, overrides = nil) ⇒ Proc
Given a spec, returns the generator for it, or raises if none can be constructed.
-
.get_spec(key) ⇒ Spec?
Spec registered for key, or nil.
-
.hash_of(kpred, vpred, options = {}) ⇒ Spec
Returns a spec for a hash whose keys satisfy kpred and vals satisfy vpred.
-
.int_in(range) ⇒ Object
Spec that validates ints in the given range.
-
.invalid?(value) ⇒ Boolean
True if value is the result of an unsuccessful conform.
-
.keys(req: [], opt: [], req_un: [], opt_un: [], gen: nil) ⇒ Object
Creates and returns a hash validating spec.
-
.merge(*preds) ⇒ Spec
A spec that returns a conformed hash satisfying all of the specs.
-
.nilable(pred) ⇒ Spec
A spec that accepts nil and values satisfying pred.
-
.one_or_more(pred) ⇒ Hash
an array of matches.
-
.or(key_preds) ⇒ Spec
A destructuring spec that returns a two element array containing the key of the first matching pred and the corresponding value.
- .or_keys(*ks) ⇒ Object
-
.regex?(x) ⇒ Hash, false
X if x is a (Speculation) regex op, else logical false.
-
.registry ⇒ Hash
The registry hash.
-
.spec(pred, gen: nil) ⇒ Spec
NOTE: it is not generally necessary to wrap predicates in spec when using ‘S.def` etc., only to attach a unique generator.
-
.spec?(x) ⇒ Spec, false
X if x is a spec, else false.
-
.time_in(time_range) ⇒ Object
Spec that validates times in the given range.
-
.tuple(*preds) ⇒ Spec
A spec for a tuple, an array where each element conforms to the corresponding pred.
-
.valid?(spec, x) ⇒ Boolean
True when x is valid for spec.
-
.with_gen(spec, gen) ⇒ Spec
Takes a spec and a one-arg generator function and returns a version of the spec that uses that generator.
-
.zero_or_more(pred) ⇒ Hash
Regex op that matches zero or more values matching pred.
-
.zero_or_one(pred) ⇒ Hash
single value (not a collection) if matched.
Methods included from NamespacedSymbols
Class Attribute Details
.check_asserts ⇒ Object
Enables or disables spec asserts. Defaults to false.
18 19 20 |
# File 'lib/speculation.rb', line 18 def check_asserts @check_asserts end |
.coll_check_limit ⇒ Object
The number of elements validated in a collection spec’ed with ‘every’.
30 31 32 |
# File 'lib/speculation.rb', line 30 def coll_check_limit @coll_check_limit end |
.coll_error_limit ⇒ Object
The number of errors reported by explain in a collection spec’ed with ‘every’
34 35 36 |
# File 'lib/speculation.rb', line 34 def coll_error_limit @coll_error_limit end |
.fspec_iterations ⇒ Object
The number of times an anonymous fn specified by fspec will be (generatively) tested during conform.
27 28 29 |
# File 'lib/speculation.rb', line 27 def fspec_iterations @fspec_iterations end |
.recursion_limit ⇒ Object
A soft limit on how many times a branching spec (or/alt/zero_or_more) can be recursed through during generation. After this a non-recursive branch will be chosen.
23 24 25 |
# File 'lib/speculation.rb', line 23 def recursion_limit @recursion_limit end |
Class Method Details
.alt(kv_specs) ⇒ Hash
Returns regex op that returns a two item array containing the key of the first matching pred and the corresponding value. Thus can be destructured to refer generically to the components of the return.
512 513 514 |
# File 'lib/speculation.rb', line 512 def self.alt(kv_specs) _alt(kv_specs.values, kv_specs.keys).merge(:id => SecureRandom.uuid) end |
.and(*preds) ⇒ Spec
Returns a spec that returns the conformed value. Successive conformed values propagate through rest of predicates.
394 395 396 |
# File 'lib/speculation.rb', line 394 def self.and(*preds) AndSpec.new(preds) end |
.and_keys(*ks) ⇒ Object
375 376 377 |
# File 'lib/speculation.rb', line 375 def self.and_keys(*ks) [ns(:and), *ks] end |
.assert(spec, x) ⇒ Object
Can be enabled or disabled at runtime:
-
enabled/disabled by setting ‘check_asserts`.
-
enabled by setting environment variable SPECULATION_CHECK_ASSERTS to the string “true”
Defaults to false if not set.
56 57 58 59 60 61 62 63 64 65 |
# File 'lib/speculation.rb', line 56 def self.assert(spec, x) return x unless check_asserts return x if valid?(spec, x) ed = _explain_data(spec, [], [], [], x).merge(ns(:failure) => :assertion_failed) out = StringIO.new explain_out(ed, out) raise Speculation::Error.new("Spec assertion failed\n#{out.string}", ed) end |
.cat(named_specs) ⇒ Hash
Returns regex op that matches (all) values in sequence, returning a map containing the keys of each pred and the corresponding value.
521 522 523 524 525 526 |
# File 'lib/speculation.rb', line 521 def self.cat(named_specs) keys = named_specs.keys predicates = named_specs.values pcat(:keys => keys, :predicates => predicates, :return_value => {}) end |
.coll_of(pred, opts = {}) ⇒ Spec
Returns a spec for a collection of items satisfying pred. Unlike ‘every’, coll_of will exhaustively conform every value.
Same options as ‘every’. conform will produce a collection corresponding to :into if supplied, else will match the input collection, avoiding rebuilding when possible.
462 463 464 |
# File 'lib/speculation.rb', line 462 def self.coll_of(pred, opts = {}) every(pred, ns(:conform_all) => true, **opts) end |
.conform(spec, value) ⇒ Symbol, Object
Returns :Speculation/invalid if value does not match spec, else the (possibly destructured) value.
131 132 133 134 |
# File 'lib/speculation.rb', line 131 def self.conform(spec, value) spec = Identifier(spec) specize(spec).conform(value) end |
.conformer {|value| ... } ⇒ Spec
Returns a spec that uses block as a predicate/conformer.
540 541 542 |
# File 'lib/speculation.rb', line 540 def self.conformer(&pred) spec_impl(pred, true) end |
.constrained(re, *preds) ⇒ Hash
Returns regex-op that consumes input as per re but subjects the resulting value to the conjunction of the predicates, and any conforming they might perform.
533 534 535 |
# File 'lib/speculation.rb', line 533 def self.constrained(re, *preds) { ns(:op) => ns(:amp), :p1 => re, :predicates => preds } end |
.date_in(date_range) ⇒ Object
Returns Spec that validates dates in the given range.
105 106 107 108 |
# File 'lib/speculation.rb', line 105 def self.date_in(date_range) spec(self.and(Date, ->(x) { date_range.cover?(x) }), :gen => ->(_) { rand(date_range) }) end |
.def(key, spec) ⇒ Symbol, Method
Given a namespace-qualified symbol key, and a spec, spec name, predicate or regex-op makes an entry in the registry mapping key to the spec
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/speculation.rb', line 270 def(key, spec) key = Identifier(key) unless Utils.ident?(key) && (!key.is_a?(Symbol) || NamespacedSymbols.namespace(key)) raise ArgumentError, "key must be a namespaced Symbol, e.g. #{ns(:my_spec)}, or a Method" end spec = if spec?(spec) || regex?(spec) || registry[spec] spec else spec_impl(spec, false) end @registry_ref.swap do |reg| reg.merge(key => with_name(spec, key)).freeze end key.is_a?(Identifier) ? key.get_method : key end |
.every(pred, opts = {}) ⇒ Spec
that ‘every’ does not do exhaustive checking, rather it samples ‘coll_check_limit` elements. Nor (as a result) does it do any conforming of elements. ’explain’ will report at most coll_error_limit problems. Thus ‘every’ should be suitable for potentially large collections.
Returns spec that validates collection elements against pred.
427 428 429 430 431 432 433 |
# File 'lib/speculation.rb', line 427 def self.every(pred, opts = {}) gen = opts.delete(:gen) EverySpec.new(pred, opts).tap do |spec| spec.gen = gen end end |
.every_kv(kpred, vpred, options) ⇒ Spec
Like ‘every’ but takes separate key and val preds and works on associative collections.
Same options as ‘every’, :into defaults to {}
445 446 447 448 449 |
# File 'lib/speculation.rb', line 445 def self.every_kv(kpred, vpred, ) every(tuple(kpred, vpred), ns(:kfn) => ->(_i, v) { v.first }, :into => {}, **) end |
.exercise(spec, n: 10, overrides: {}) ⇒ Array
Generates a number (default 10) of values compatible with spec and maps conform over them, returning a sequence of [val conformed-val] tuples.
624 625 626 627 628 |
# File 'lib/speculation.rb', line 624 def self.exercise(spec, n: 10, overrides: {}) Gen.sample(gen(spec, overrides), n).map { |value| [value, conform(spec, value)] } end |
.exercise_fn(method, n: 10, fspec: nil) ⇒ Array
Exercises the method by applying it to n (default 10) generated samples of its args spec. When fspec is supplied its arg spec is used, and method can be a proc.
637 638 639 640 641 642 |
# File 'lib/speculation.rb', line 637 def self.exercise_fn(method, n: 10, fspec: nil) fspec ||= get_spec(method) raise ArgumentError, "No fspec found for #{method}" unless fspec Gen.sample(gen(fspec.args), n).map { |args| [args, method.call(*args)] } end |
.explain(spec, x) ⇒ Object
Given a spec and a value that fails to conform, prints an explaination to STDOUT
208 209 210 |
# File 'lib/speculation.rb', line 208 def self.explain(spec, x) explain_out(explain_data(spec, x)) end |
.explain_data(spec, x) ⇒ nil, Hash
Given a spec and a value x which ought to conform, returns nil if x conforms, else a hash with at least the key :“Speculation/problems” whose value is a collection of problem-hashes, where problem-hash has at least :path :pred and :val keys describing the predicate and the value that failed at that path.
168 169 170 171 172 |
# File 'lib/speculation.rb', line 168 def self.explain_data(spec, x) spec = Identifier(spec) name = spec_name(spec) _explain_data(spec, [], Array(name), [], x) end |
.explain_out(ed, out = STDOUT) ⇒ Object
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/speculation.rb', line 176 def self.explain_out(ed, out = STDOUT) return out.puts("Success!") unless ed ed.fetch(ns(:problems)).each do |prob| path, pred, val, reason, via, inn = prob.values_at(:path, :pred, :val, :reason, :via, :in) out.print("In: ", inn.to_a.inspect, " ") unless inn.empty? out.print("val: ", val.inspect, " fails") out.print(" spec: ", via.last.inspect) unless via.empty? out.print(" at: ", path.to_a.inspect) unless path.empty? out.print(" predicate: ", pred.inspect) out.print(", ", reason.inspect) if reason prob.each do |k, v| unless [:path, :pred, :val, :reason, :via, :in].include?(k) out.print("\n\t ", k.inspect, PP.pp(v, String.new)) end end out.puts end ed.each do |k, v| out.puts("#{k} #{PP.pp(v, String.new)}") unless k == ns(:problems) end nil end |
.explain_str(spec, x) ⇒ String
Returns a human readable explaination.
215 216 217 218 219 |
# File 'lib/speculation.rb', line 215 def self.explain_str(spec, x) out = StringIO.new explain_out(explain_data(spec, x), out) out.string end |
.fdef(method, spec) ⇒ Method
Note that :fn specs require the presence of :args and :ret specs to conform values, and so :fn specs will be ignored if :args or :ret are missing.
Once registered, specs are checked by instrument and tested by the runner Speculation::Test.check
596 597 598 599 |
# File 'lib/speculation.rb', line 596 def self.fdef(method, spec) self.def(Identifier(method), fspec(spec)) method end |
.float_in(min: nil, max: nil, infinite: true, nan: true) ⇒ Spec
Returns that validates floats.
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/speculation.rb', line 72 def self.float_in(min: nil, max: nil, infinite: true, nan: true) preds = [Float] preds << ->(x) { !x.nan? } unless nan preds << ->(x) { !x.infinite? } unless infinite preds << ->(x) { x <= max } if max preds << ->(x) { x >= min } if min min ||= Float::MIN max ||= Float::MAX gens = [[20, ->(_) { rand(min.to_f..max.to_f) }]] gens << [1, ->(r) { r.choose(Float::INFINITY, -Float::INFINITY) }] if infinite gens << [1, ->(_) { Float::NAN }] if nan spec(self.and(*preds), :gen => ->(rantly) { rantly.freq(*gens) }) end |
.fspec(args: nil, ret: nil, fn: nil, block: nil, gen: nil) ⇒ Spec
Takes :args :ret and (optional) :block and :fn kwargs whose values are preds and returns a spec whose conform/explain take a method/proc and validates it using generative testing. The conformed value is always the method itself.
fspecs can generate procs that validate the arguments and fabricate a return value compliant with the :ret spec, ignoring the :fn spec if present.
559 560 561 562 563 |
# File 'lib/speculation.rb', line 559 def self.fspec(args: nil, ret: nil, fn: nil, block: nil, gen: nil) FSpec.new(:args => spec(args), :ret => spec(ret), :fn => spec(fn), :block => spec(block)).tap do |spec| spec.gen = gen end end |
.gen(spec, overrides = nil) ⇒ Proc
Given a spec, returns the generator for it, or raises if none can be constructed.
Optionally an overrides hash can be provided which should map spec names or paths (array of symbols) to no-arg generator Procs. These will be used instead of the generators at those names/paths. Note that parent generator (in the spec or overrides map) will supersede those of any subtrees. A generator for a regex op must always return a sequential collection (i.e. a generator for Speculation.zero_or_more should return either an empty array or an array with one item in it)
251 252 253 254 |
# File 'lib/speculation.rb', line 251 def self.gen(spec, overrides = nil) spec = Identifier(spec) gensub(spec, overrides, [], ns(:recursion_limit) => recursion_limit) end |
.get_spec(key) ⇒ Spec?
Returns spec registered for key, or nil.
298 299 300 |
# File 'lib/speculation.rb', line 298 def self.get_spec(key) registry[Identifier(key)] end |
.hash_of(kpred, vpred, options = {}) ⇒ Spec
Returns a spec for a hash whose keys satisfy kpred and vals satisfy vpred. Unlike ‘every_kv’, hash_of will exhaustively conform every value.
Same options as ‘every’, :kind defaults to ‘Speculation::Utils.hash?`, with the addition of:
:conform_keys - conform keys as well as values (default false)
479 480 481 482 483 |
# File 'lib/speculation.rb', line 479 def self.hash_of(kpred, vpred, = {}) every_kv(kpred, vpred, :kind => Utils.method(:hash?), ns(:conform_all) => true, **) end |
.int_in(range) ⇒ Object
Returns Spec that validates ints in the given range.
91 92 93 94 |
# File 'lib/speculation.rb', line 91 def self.int_in(range) spec(self.and(Integer, ->(x) { range.include?(x) }), :gen => ->(_) { rand(range) }) end |
.invalid?(value) ⇒ Boolean
Returns true if value is the result of an unsuccessful conform.
124 125 126 |
# File 'lib/speculation.rb', line 124 def self.invalid?(value) value.equal?(INVALID) end |
.keys(req: [], opt: [], req_un: [], opt_un: [], gen: nil) ⇒ Object
Creates and returns a hash validating spec. :req and :opt are both arrays of namespaced-qualified keywords (e.g. “:MyApp/foo”). The validator will ensure the :req keys are present. The :opt keys serve as documentation and may be used by the generator.
The :req key array supports ‘and_keys’ and ‘or_keys’ for key groups:
S.keys(req: [ns(:x), ns(:y), S.or_keys(ns(:secret), S.and_keys(ns(:user), ns(:pwd)))],
opt: [ns(:z)])
There are also _un versions of :req and :opt. These allow you to connect unqualified keys to specs. In each case, fully qualfied keywords are passed, which name the specs, but unqualified keys (with the same name component) are expected and checked at conform-time, and generated during gen:
S.keys(req_un: [:"MyApp/x", :"MyApp/y"])
The above says keys :x and :y are required, and will be validated and generated by specs (if they exist) named :“MyApp/x” :“MyApp/y” respectively.
In addition, the values of all namespace-qualified keys will be validated (and possibly destructured) by any registered specs. Note: there is no support for inline value specification, by design.
363 364 365 366 367 |
# File 'lib/speculation.rb', line 363 def self.keys(req: [], opt: [], req_un: [], opt_un: [], gen: nil) HashSpec.new(req, opt, req_un, opt_un).tap do |spec| spec.gen = gen end end |
.merge(*preds) ⇒ Spec
Unlike ‘and’, merge can generate maps satisfying the union of the predicates.
Returns a spec that returns a conformed hash satisfying all of the specs.
401 402 403 |
# File 'lib/speculation.rb', line 401 def self.merge(*preds) MergeSpec.new(preds) end |
.nilable(pred) ⇒ Spec
Returns a spec that accepts nil and values satisfying pred.
613 614 615 |
# File 'lib/speculation.rb', line 613 def self.nilable(pred) NilableSpec.new(pred) end |
.one_or_more(pred) ⇒ Hash
an array of matches
495 496 497 |
# File 'lib/speculation.rb', line 495 def self.one_or_more(pred) pcat(:predicates => [pred, rep(pred, pred, [], true)], :return_value => []) end |
.or(key_preds) ⇒ Spec
Returns a destructuring spec that returns a two element array containing the key of the first matching pred and the corresponding value. Thus the ‘key’ and ‘val’ functions can be used to refer generically to the components of the tagged return.
385 386 387 |
# File 'lib/speculation.rb', line 385 def self.or(key_preds) OrSpec.new(key_preds) end |
.or_keys(*ks) ⇒ Object
370 371 372 |
# File 'lib/speculation.rb', line 370 def self.or_keys(*ks) [ns(:or), *ks] end |
.regex?(x) ⇒ Hash, false
Returns x if x is a (Speculation) regex op, else logical false.
118 119 120 |
# File 'lib/speculation.rb', line 118 def self.regex?(x) Utils.hash?(x) && x[ns(:op)] && x end |
.registry ⇒ Hash
Returns the registry hash.
292 293 294 |
# File 'lib/speculation.rb', line 292 def self.registry @registry_ref.value end |
.spec(pred, gen: nil) ⇒ Spec
NOTE: it is not generally necessary to wrap predicates in spec when using ‘S.def` etc., only to attach a unique generator.
Optionally takes :gen generator function, which must be a proc of one arg (Rantly instance) that generates a valid value.
325 326 327 328 329 330 331 |
# File 'lib/speculation.rb', line 325 def self.spec(pred, gen: nil) if pred spec_impl(pred, false).tap do |spec| spec.gen = gen if gen end end end |
.spec?(x) ⇒ Spec, false
Returns x if x is a spec, else false.
112 113 114 |
# File 'lib/speculation.rb', line 112 def self.spec?(x) x if x.is_a?(Spec) end |
.time_in(time_range) ⇒ Object
Returns Spec that validates times in the given range.
98 99 100 101 |
# File 'lib/speculation.rb', line 98 def self.time_in(time_range) spec(self.and(Time, ->(x) { time_range.cover?(x) }), :gen => ->(_) { rand(time_range) }) end |
.tuple(*preds) ⇒ Spec
Returns a spec for a tuple, an array where each element conforms to the corresponding pred. Each element will be referred to in paths using its ordinal.
569 570 571 |
# File 'lib/speculation.rb', line 569 def self.tuple(*preds) TupleSpec.new(preds) end |
.valid?(spec, x) ⇒ Boolean
Returns true when x is valid for spec.
604 605 606 607 608 609 |
# File 'lib/speculation.rb', line 604 def self.valid?(spec, x) spec = Identifier(spec) spec = specize(spec) !invalid?(spec.conform(x)) end |
.with_gen(spec, gen) ⇒ Spec
Takes a spec and a one-arg generator function and returns a version of the spec that uses that generator
140 141 142 143 144 145 146 |
# File 'lib/speculation.rb', line 140 def self.with_gen(spec, gen) if regex?(spec) spec.merge(ns(:gfn) => gen) else specize(spec).tap { |s| s.gen = gen } end end |
.zero_or_more(pred) ⇒ Hash
Returns regex op that matches zero or more values matching pred. Produces an array of matches iff there is at least one match.
488 489 490 |
# File 'lib/speculation.rb', line 488 def self.zero_or_more(pred) rep(pred, pred, [], false) end |
.zero_or_one(pred) ⇒ Hash
single value (not a collection) if matched.
502 503 504 |
# File 'lib/speculation.rb', line 502 def self.zero_or_one(pred) _alt([pred, accept(ns(:nil))], nil) end |