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.1".freeze
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.
19 20 21 |
# File 'lib/speculation.rb', line 19 def check_asserts @check_asserts end |
.coll_check_limit ⇒ Object
The number of elements validated in a collection spec’ed with ‘every’.
31 32 33 |
# File 'lib/speculation.rb', line 31 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’
35 36 37 |
# File 'lib/speculation.rb', line 35 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.
28 29 30 |
# File 'lib/speculation.rb', line 28 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.
24 25 26 |
# File 'lib/speculation.rb', line 24 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.
535 536 537 |
# File 'lib/speculation.rb', line 535 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.
417 418 419 |
# File 'lib/speculation.rb', line 417 def self.and(*preds) AndSpec.new(preds) end |
.and_keys(*ks) ⇒ Object
398 399 400 |
# File 'lib/speculation.rb', line 398 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.
78 79 80 81 82 83 84 85 86 87 |
# File 'lib/speculation.rb', line 78 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.
544 545 546 547 548 549 |
# File 'lib/speculation.rb', line 544 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.
485 486 487 |
# File 'lib/speculation.rb', line 485 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.
154 155 156 157 |
# File 'lib/speculation.rb', line 154 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.
563 564 565 |
# File 'lib/speculation.rb', line 563 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.
556 557 558 |
# File 'lib/speculation.rb', line 556 def self.constrained(re, *preds) { OP => AMP, :p1 => re, :predicates => preds } end |
.date_in(date_range) ⇒ Object
Returns Spec that validates dates in the given range.
128 129 130 131 |
# File 'lib/speculation.rb', line 128 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
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/speculation.rb', line 293 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.
450 451 452 453 454 455 456 |
# File 'lib/speculation.rb', line 450 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 {}
468 469 470 471 472 |
# File 'lib/speculation.rb', line 468 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.
648 649 650 651 652 |
# File 'lib/speculation.rb', line 648 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.
661 662 663 664 665 666 |
# File 'lib/speculation.rb', line 661 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
231 232 233 |
# File 'lib/speculation.rb', line 231 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.
191 192 193 194 195 |
# File 'lib/speculation.rb', line 191 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
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/speculation.rb', line 199 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.
238 239 240 241 242 |
# File 'lib/speculation.rb', line 238 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
620 621 622 623 |
# File 'lib/speculation.rb', line 620 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.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/speculation.rb', line 94 def self.float_in(min: nil, max: nil, infinite: true, nan: true) preds = [Float] preds.push(->(x) { !x.nan? }) unless nan preds.push(->(x) { !x.infinite? }) unless infinite preds.push(->(x) { x <= max }) if max preds.push(->(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.
583 584 585 586 587 |
# File 'lib/speculation.rb', line 583 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)
274 275 276 277 |
# File 'lib/speculation.rb', line 274 def self.gen(spec, overrides = nil) spec = Identifier(spec) gensub(spec, overrides, [], RECURSION_LIMIT => recursion_limit) end |
.get_spec(key) ⇒ Spec?
Returns spec registered for key, or nil.
321 322 323 |
# File 'lib/speculation.rb', line 321 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)
502 503 504 505 506 |
# File 'lib/speculation.rb', line 502 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.
114 115 116 117 |
# File 'lib/speculation.rb', line 114 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.
147 148 149 |
# File 'lib/speculation.rb', line 147 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.
386 387 388 389 390 |
# File 'lib/speculation.rb', line 386 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.
424 425 426 |
# File 'lib/speculation.rb', line 424 def self.merge(*preds) MergeSpec.new(preds) end |
.nilable(pred) ⇒ Spec
Returns a spec that accepts nil and values satisfying pred.
637 638 639 |
# File 'lib/speculation.rb', line 637 def self.nilable(pred) NilableSpec.new(pred) end |
.one_or_more(pred) ⇒ Hash
an array of matches
518 519 520 |
# File 'lib/speculation.rb', line 518 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.
408 409 410 |
# File 'lib/speculation.rb', line 408 def self.or(key_preds) OrSpec.new(key_preds) end |
.or_keys(*ks) ⇒ Object
393 394 395 |
# File 'lib/speculation.rb', line 393 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.
141 142 143 |
# File 'lib/speculation.rb', line 141 def self.regex?(x) Utils.hash?(x) && x[OP] && x end |
.registry ⇒ Hash
Returns the registry hash.
315 316 317 |
# File 'lib/speculation.rb', line 315 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.
348 349 350 351 352 353 354 |
# File 'lib/speculation.rb', line 348 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.
135 136 137 |
# File 'lib/speculation.rb', line 135 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.
121 122 123 124 |
# File 'lib/speculation.rb', line 121 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.
593 594 595 |
# File 'lib/speculation.rb', line 593 def self.tuple(*preds) TupleSpec.new(preds) end |
.valid?(spec, x) ⇒ Boolean
Returns true when x is valid for spec.
628 629 630 631 632 633 |
# File 'lib/speculation.rb', line 628 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
163 164 165 166 167 168 169 |
# File 'lib/speculation.rb', line 163 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.
511 512 513 |
# File 'lib/speculation.rb', line 511 def self.zero_or_more(pred) rep(pred, pred, [], false) end |
.zero_or_one(pred) ⇒ Hash
single value (not a collection) if matched.
525 526 527 |
# File 'lib/speculation.rb', line 525 def self.zero_or_one(pred) _alt([pred, accept(NIL)], nil) end |