predicator 1.2.1 → 2.0.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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -0
  3. data/{LICENSE.txt → LICENSE} +1 -1
  4. data/README.md +322 -46
  5. data/lib/predicator/ast.rb +74 -187
  6. data/lib/predicator/boundary.rb +237 -0
  7. data/lib/predicator/civil.rb +112 -0
  8. data/lib/predicator/duration_text.rb +92 -0
  9. data/lib/predicator/emitter/chains.rb +62 -0
  10. data/lib/predicator/emitter/statements.rb +86 -0
  11. data/lib/predicator/emitter.rb +187 -0
  12. data/lib/predicator/errors.rb +140 -0
  13. data/lib/predicator/evaluator/access.rb +108 -0
  14. data/lib/predicator/evaluator/arithmetic.rb +132 -0
  15. data/lib/predicator/evaluator/calls.rb +129 -0
  16. data/lib/predicator/evaluator/casts.rb +158 -0
  17. data/lib/predicator/evaluator/chronology.rb +88 -0
  18. data/lib/predicator/evaluator/compare.rb +158 -0
  19. data/lib/predicator/evaluator/json_functions.rb +167 -0
  20. data/lib/predicator/evaluator/json_scan.rb +303 -0
  21. data/lib/predicator/evaluator/library.rb +127 -0
  22. data/lib/predicator/evaluator/math_functions.rb +72 -0
  23. data/lib/predicator/evaluator/order.rb +120 -0
  24. data/lib/predicator/evaluator/rich_types.rb +146 -0
  25. data/lib/predicator/evaluator/statements.rb +151 -0
  26. data/lib/predicator/evaluator.rb +391 -117
  27. data/lib/predicator/float_text.rb +94 -0
  28. data/lib/predicator/iso_text.rb +99 -0
  29. data/lib/predicator/lexer/literals.rb +115 -0
  30. data/lib/predicator/lexer/numbers.rb +91 -0
  31. data/lib/predicator/lexer.rb +194 -0
  32. data/lib/predicator/parser/durations.rb +108 -0
  33. data/lib/predicator/parser/operators.rb +86 -0
  34. data/lib/predicator/parser/postfix.rb +57 -0
  35. data/lib/predicator/parser/primaries.rb +122 -0
  36. data/lib/predicator/parser/statements.rb +114 -0
  37. data/lib/predicator/parser/token_text.rb +52 -0
  38. data/lib/predicator/parser.rb +217 -438
  39. data/lib/predicator/result.rb +46 -0
  40. data/lib/predicator/tagged/reader.rb +139 -0
  41. data/lib/predicator/tagged/writer.rb +106 -0
  42. data/lib/predicator/tagged.rb +134 -0
  43. data/lib/predicator/values.rb +147 -0
  44. data/lib/predicator/version.rb +4 -1
  45. data/lib/predicator.rb +384 -14
  46. metadata +53 -147
  47. data/.gitignore +0 -11
  48. data/.rubocop.yml +0 -46
  49. data/.travis.yml +0 -5
  50. data/CODE_OF_CONDUCT.md +0 -49
  51. data/Gemfile +0 -4
  52. data/HISTORY.md +0 -51
  53. data/Rakefile +0 -24
  54. data/lib/predicator/context.rb +0 -18
  55. data/lib/predicator/lexer.rex +0 -69
  56. data/lib/predicator/lexer.rex.rb +0 -187
  57. data/lib/predicator/parser.y +0 -120
  58. data/lib/predicator/visitors/dot.rb +0 -113
  59. data/lib/predicator/visitors/each.rb +0 -16
  60. data/lib/predicator/visitors/instructions.rb +0 -183
  61. data/lib/predicator/visitors/string.rb +0 -76
  62. data/lib/predicator/visitors/visitor.rb +0 -100
  63. data/lib/predicator/visitors.rb +0 -5
  64. data/predicator.gemspec +0 -34
data/lib/predicator.rb CHANGED
@@ -1,24 +1,394 @@
1
- require "predicator/context"
2
- require "predicator/evaluator"
3
- require "predicator/parser"
1
+ # frozen_string_literal: true
4
2
 
3
+ require_relative "predicator/version"
4
+ require_relative "predicator/values"
5
+ require_relative "predicator/errors"
6
+ require_relative "predicator/result"
7
+ require_relative "predicator/evaluator"
8
+ require_relative "predicator/lexer"
9
+ require_relative "predicator/parser"
10
+ require_relative "predicator/emitter"
11
+
12
+ # A safe, non-evaluating condition language for end users.
13
+ #
14
+ # Every entry point answers a {Predicator::Result} (`ok?`, `value`, `error`)
15
+ # and never raises for an outcome of the source or the program: a source
16
+ # that does not compile, a run that stops, a value outside the domain all
17
+ # answer a failed result carrying an error value. Each has a bang variant
18
+ # (`compile!`, `evaluate!`, ...) that answers the value itself or raises
19
+ # {Predicator::Error} carrying the same error value. Equality, ordering and
20
+ # membership walk nested lists and maps without recursing, so a value a
21
+ # program nests to any depth is compared, never overflowing the stack.
22
+ #
23
+ # Two things still raise. A caller's own bug in the call itself (an argument
24
+ # of the wrong kind, an unknown or malformed option, a clock that does not
25
+ # respond to `call`) raises ArgumentError at the entry point. And an
26
+ # exception that is not a StandardError (an Interrupt, a SystemExit, a
27
+ # NoMemoryError), raised by host code a run calls (a host function or the
28
+ # clock), propagates unchanged: it is the host failing, not an outcome of
29
+ # the program.
30
+ #
31
+ # The value domain is Ruby's own values where they fit - nil (null), true and
32
+ # false, Integer and Float (kept apart), String, Array (a list) and Hash (a
33
+ # map, keyed by a String, an Integer, true, false, nil or
34
+ # {Predicator::UNDEFINED}) - plus {Predicator::UNDEFINED},
35
+ # {Predicator::Date}, {Predicator::DateTime} (built with
36
+ # `Predicator::DateTime.new(epoch_seconds:, microsecond:)`) and
37
+ # {Predicator::Duration}. The codec for the
38
+ # conformance corpus's tagged encoding is loaded on its own, with
39
+ # `require "predicator/tagged"`.
40
+ #
41
+ # The options of an evaluation are keywords of the call that runs it, and
42
+ # nothing is configured globally:
43
+ #
44
+ # - `functions:` a Hash of String name to a callable (anything that responds
45
+ # to `call`), the host's functions for this run. A function is called with
46
+ # the call's arguments only, first argument first, each a copy, so that
47
+ # nothing it does to one changes the context; the counts of arguments it
48
+ # takes are read from its parameters; it refuses by raising a
49
+ # StandardError, which fails the run with an EvaluationError whose reason
50
+ # and message are `Function NAME() raised: ` and the exception's inspect;
51
+ # an answer
52
+ # outside the value domain fails the run too. A name here shadows the
53
+ # library function of the same name, for this run only. `{}` by default.
54
+ # - `clock:` a callable answering the current time as a
55
+ # {Predicator::DateTime}, read at most once per run and only when the
56
+ # program reads the time; the system's real-time clock by default. A
57
+ # clock that answers anything else, or raises a StandardError, fails the
58
+ # run with an EvaluationError whose reason is `clock_failed`, whether a
59
+ # relative date or `Date.now()` read it.
60
+ # - `on_unbound:` what a load of a variable the context does not bind does:
61
+ # `:undefined` (the default) pushes {Predicator::UNDEFINED}, and the run
62
+ # fails with an UndefinedVariableError only if undefined is its answer;
63
+ # `:error` fails the run at the load.
64
+ # - `loop_budget:` how many back edges (`jump_backward`) one run may take,
65
+ # an Integer of at least 0; 10_000 by default. A run that finds it spent
66
+ # fails with the reason `loop_budget_exceeded`.
67
+ #
68
+ # A context, and any `lit` operand of an instruction list the host built, is
69
+ # checked against the value domain before anything runs: a root key of the
70
+ # context that is not a String, a map key or a value the domain has no member
71
+ # for (a Symbol, a Time, any other object), a Float that is not finite, a
72
+ # list or map that contains itself, and lists and maps nested past 100
73
+ # levels along any path each fail the run with an EvaluationError (reasons
74
+ # `unsupported_host_value`, `non_finite_number`, `cyclic_value`,
75
+ # `depth_limit_exceeded`).
5
76
  module Predicator
6
- def self.parse source
7
- Predicator::Parser.new.parse source
77
+ # The instruction-set version this build emits and runs.
78
+ #
79
+ # @example
80
+ # Predicator.isa_version # => 6
81
+ #
82
+ # @return [Integer]
83
+ def self.isa_version = Evaluator::ISA_VERSION
84
+
85
+ # Compiles an expression's source to the instruction list the evaluator
86
+ # runs. The operands are predicator values: a date literal is a
87
+ # {Predicator::Date}, a decimal a Float, `undefined`
88
+ # {Predicator::UNDEFINED}.
89
+ #
90
+ # A source that does not compile answers a failed result carrying a
91
+ # {Predicator::ParseError}: the first refusal the scan, the parse or the
92
+ # emit met, with its reason, the reference's message where the reference
93
+ # has one, and its position. A statement (an assignment, an `if`, a
94
+ # `while`) is not an expression and is refused; {compile_program} compiles
95
+ # one.
96
+ #
97
+ # @example
98
+ # Predicator.compile("score > 85").value
99
+ # # => [["load", "score"], ["lit", 85], ["compare", "GT"]]
100
+ #
101
+ # @param source [String] the text, in UTF-8 or an encoding that converts
102
+ # to it
103
+ # @return [Result] the instruction list, or the {ParseError}
104
+ # @raise [ArgumentError] when source is not a String, or is not text in
105
+ # its encoding
106
+ def self.compile(source) = compiled(source) { |tokens| Parser.parse(tokens) }
107
+
108
+ # As {compile}, answering the instruction list itself.
109
+ #
110
+ # @param source [String]
111
+ # @return [Array] the instruction list
112
+ # @raise [Predicator::Error] carrying the {ParseError}
113
+ # @raise [ArgumentError] as {compile} raises it
114
+ def self.compile!(source) = unwrap(compile(source))
115
+
116
+ # Compiles a program's source, one or more statements separated by `;`, to
117
+ # the instruction list the evaluator runs in statement mode. A statement is
118
+ # an assignment to a location (an identifier, a property access or a
119
+ # bracket access), an `if` with an optional `else` or `else if`, a
120
+ # `while`, or an expression whose value is discarded. Every statement
121
+ # leaves the stack as it found it: an assignment ends in a `store`, an
122
+ # expression in a `pop`.
123
+ #
124
+ # A source that does not compile answers a failed result carrying a
125
+ # {Predicator::ParseError}, as {compile} does.
126
+ #
127
+ # @example
128
+ # Predicator.compile_program("x = 1; x + 1").value
129
+ # # => [["lit", "x"], ["lit", 1], ["store", 1], ["load", "x"], ["lit", 1], ["add"], ["pop"]]
130
+ #
131
+ # @param source [String] the text, in UTF-8 or an encoding that converts
132
+ # to it
133
+ # @return [Result] the instruction list, or the {ParseError}
134
+ # @raise [ArgumentError] when source is not a String, or is not text in
135
+ # its encoding
136
+ def self.compile_program(source) = compiled(source, program: true) { |tokens| Parser.parse_program(tokens) }
137
+
138
+ # As {compile_program}, answering the instruction list itself.
139
+ #
140
+ # @param source [String]
141
+ # @return [Array] the instruction list
142
+ # @raise [Predicator::Error] carrying the {ParseError}
143
+ # @raise [ArgumentError] as {compile_program} raises it
144
+ def self.compile_program!(source) = unwrap(compile_program(source))
145
+
146
+ # Runs an expression against a context and answers its value: the top of
147
+ # the stack at halt. The program is an instruction list, or an
148
+ # expression's source, which is compiled as {compile} compiles it (a
149
+ # source that does not compile answers its {ParseError}).
150
+ #
151
+ # A run that stops answers the error value that stopped it: an
152
+ # {EvaluationError}, a {TypeMismatchError} or an
153
+ # {UndefinedVariableError}. An empty stack at halt is an error, and so is
154
+ # undefined at halt when a load did not find its variable.
155
+ #
156
+ # @example
157
+ # Predicator.evaluate("score > 85", { "score" => 90 }).value # => true
158
+ # Predicator.evaluate([["load", "x"], ["lit", 1], ["add"]], { "x" => 2 }).value # => 3
159
+ #
160
+ # @param program [Array, String] an instruction list, or an expression's
161
+ # source
162
+ # @param context [Hash{String => Object}] the variables a load reads
163
+ # @param options [Hash] `functions:`, `clock:`, `on_unbound:` and
164
+ # `loop_budget:`, as {Predicator} describes them
165
+ # @return [Result] the value, or the error value
166
+ # @raise [ArgumentError] when the program or the context is not of its
167
+ # kind, or an option is unknown or malformed (a clock that does not
168
+ # respond to `call` among them)
169
+ def self.evaluate(program, context = {}, **)
170
+ instructions = instructions_of(program) { |source| compile(source) }
171
+ return instructions if instructions.is_a?(Result)
172
+
173
+ refused = refusal_of(instructions, context)
174
+ refused ? Results.failure(refused) : Evaluator.evaluate(instructions, context, **)
8
175
  end
9
176
 
10
- def self.compile source
11
- ast = parse source
12
- ast.to_instructions
177
+ # As {evaluate}, answering the value itself.
178
+ #
179
+ # @param program [Array, String]
180
+ # @param context [Hash{String => Object}]
181
+ # @param options [Hash] as {evaluate} takes them
182
+ # @return [Object] the value
183
+ # @raise [Predicator::Error] carrying the error value
184
+ # @raise [ArgumentError] as {evaluate} raises it
185
+ def self.evaluate!(program, context = {}, **) = unwrap(evaluate(program, context, **))
186
+
187
+ # Runs a statement program against a context and answers the context at
188
+ # halt, a Hash holding every write the program made. The program is an
189
+ # instruction list, or a program's source, which is compiled as
190
+ # {compile_program} compiles it. The context passed is never changed: a
191
+ # write builds a new one, and when nothing is written the context at halt
192
+ # is the one passed. An empty stack at halt is a normal halt.
193
+ #
194
+ # A run that stops answers the error value that stopped it, and no
195
+ # context.
196
+ #
197
+ # @example
198
+ # Predicator.execute("x = 1; y = x + 2").value # => {"x" => 1, "y" => 3}
199
+ #
200
+ # @param program [Array, String] an instruction list, or a program's
201
+ # source
202
+ # @param context [Hash{String => Object}] the variables a load reads
203
+ # @param options [Hash] as {evaluate} takes them
204
+ # @return [Result] the context at halt, or the error value
205
+ # @raise [ArgumentError] as {evaluate} raises it
206
+ def self.execute(program, context = {}, **)
207
+ ran = statements(program, context, **)
208
+ return ran if ran.is_a?(Result)
209
+
210
+ ran.ok? ? Results.ok(ran.context) : Results.failure(ran.error)
13
211
  end
14
212
 
15
- def self.evaluate source, context={}
16
- instructions = compile source
17
- evaluate_instructions instructions, context
213
+ # As {execute}, answering the context itself.
214
+ #
215
+ # @param program [Array, String]
216
+ # @param context [Hash{String => Object}]
217
+ # @param options [Hash] as {evaluate} takes them
218
+ # @return [Hash] the context at halt
219
+ # @raise [Predicator::Error] carrying the error value
220
+ # @raise [ArgumentError] as {evaluate} raises it
221
+ def self.execute!(program, context = {}, **) = unwrap(execute(program, context, **))
222
+
223
+ # Runs a statement program as {execute} does and answers a pair: the value
224
+ # of its last expression statement (the value the last `pop` discarded,
225
+ # {Predicator::UNDEFINED} when no expression statement ran) and the context
226
+ # at halt.
227
+ #
228
+ # A run that stops answers the error value that stopped it, and neither a
229
+ # value nor a context.
230
+ #
231
+ # @example
232
+ # value, context = Predicator.execute_value!("x = 2; x * 10")
233
+ # value # => 20
234
+ # context # => {"x" => 2}
235
+ #
236
+ # @param program [Array, String] an instruction list, or a program's
237
+ # source
238
+ # @param context [Hash{String => Object}] the variables a load reads
239
+ # @param options [Hash] as {evaluate} takes them
240
+ # @return [Result] the pair `[value, context]`, or the error value
241
+ # @raise [ArgumentError] as {evaluate} raises it
242
+ def self.execute_value(program, context = {}, **)
243
+ ran = statements(program, context, **)
244
+ return ran if ran.is_a?(Result)
245
+
246
+ ran.ok? ? Results.ok([ran.value, ran.context]) : Results.failure(ran.error)
18
247
  end
19
248
 
20
- def self.evaluate_instructions instructions, context={}
21
- evaluator = Evaluator.new instructions, context
22
- evaluator.result
249
+ # As {execute_value}, answering the pair itself.
250
+ #
251
+ # @param program [Array, String]
252
+ # @param context [Hash{String => Object}]
253
+ # @param options [Hash] as {evaluate} takes them
254
+ # @return [Array] the pair `[value, context]`
255
+ # @raise [Predicator::Error] carrying the error value
256
+ # @raise [ArgumentError] as {evaluate} raises it
257
+ def self.execute_value!(program, context = {}, **) = unwrap(execute_value(program, context, **))
258
+
259
+ # Parses an expression's source to its syntax tree, without compiling it:
260
+ # the first two of the three stages {compile} runs, so a source this
261
+ # refuses, {compile} refuses with the same {ParseError}. The converse does
262
+ # not hold: the third stage, the emitter, refuses a decimal literal too
263
+ # large for a finite Float (`number_out_of_range`), and a tree whose walk
264
+ # runs out of stack (`nesting_depth_exceeded`), and such a source parses
265
+ # here.
266
+ #
267
+ # The tree is the reference's: each node an immutable value of the node's
268
+ # kind, carrying its fields, its position and its span. The node classes
269
+ # are private to the package, and the tree's shape is not a compatibility
270
+ # promise: it may change in a minor version.
271
+ #
272
+ # @param source [String] the text, in UTF-8 or an encoding that converts
273
+ # to it
274
+ # @return [Result] the root node of the tree, or the {ParseError}
275
+ # @raise [ArgumentError] as {compile} raises it
276
+ def self.parse(source)
277
+ tokens = Lexer.tokenize(text_of(source))
278
+ tree = refused?(tokens) ? tokens : Parser.parse(tokens)
279
+ refused?(tree) ? failed(tree) : Results.ok(tree)
23
280
  end
281
+
282
+ # As {parse}, answering the tree itself.
283
+ #
284
+ # @param source [String]
285
+ # @return [Object] the root node of the tree
286
+ # @raise [Predicator::Error] carrying the {ParseError}
287
+ # @raise [ArgumentError] as {compile} raises it
288
+ def self.parse!(source) = unwrap(parse(source))
289
+
290
+ # Reads a duration from its literal spelling, such as `"3d8h30m"` or
291
+ # `"1.5s"`: the spelling the `::duration` cast reads and the `::string`
292
+ # cast writes. It is one or more components, each a whole number with an
293
+ # optional decimal fraction followed by one of the units `y`, `mo`, `w`,
294
+ # `d`, `h`, `m`, `s` and `ms`, with no whitespace, no sign and no empty
295
+ # text. A repeated unit adds to what came before; a fraction expands into
296
+ # whole smaller units (a month 30 days, a year 365), and one that is not a
297
+ # whole number of milliseconds is refused.
298
+ #
299
+ # A text that does not spell a duration answers a failed result carrying
300
+ # an {EvaluationError} with the reason `invalid_duration_format`.
301
+ #
302
+ # @example
303
+ # Predicator.parse_duration("1h30m").value.minutes # => 30
304
+ #
305
+ # @param text [String]
306
+ # @return [Result] the {Predicator::Duration}, or the error value
307
+ # @raise [ArgumentError] when text is not a String, or is not text in its
308
+ # encoding
309
+ def self.parse_duration(text)
310
+ duration = DurationText.read(text_of(text))
311
+ return Results.ok(duration) if duration
312
+
313
+ Results.failure(EvaluationError.new(reason: "invalid_duration_format", message: "Invalid duration format: #{text}"))
314
+ end
315
+
316
+ # As {parse_duration}, answering the duration itself.
317
+ #
318
+ # @param text [String]
319
+ # @return [Duration]
320
+ # @raise [Predicator::Error] carrying the error value
321
+ # @raise [ArgumentError] as {parse_duration} raises it
322
+ def self.parse_duration!(text) = unwrap(parse_duration(text))
323
+
324
+ # Scans the source, parses its tokens with the block and emits the tree,
325
+ # answering the first refusal of the three.
326
+ def self.compiled(source, program: false)
327
+ tokens = Lexer.tokenize(text_of(source))
328
+ tree = refused?(tokens) ? tokens : yield(tokens)
329
+ entries = if refused?(tree) then tree
330
+ elsif program then Emitter.emit_program(tree)
331
+ else Emitter.emit(tree)
332
+ end
333
+ return failed(entries) if refused?(entries)
334
+
335
+ Results.ok(entries.map(&:instruction))
336
+ end
337
+
338
+ def self.refused?(stage) = stage.is_a?(Lexer::Refusal)
339
+ def self.failed(refusal) = Results.failure(refusal.error)
340
+
341
+ # The source as valid UTF-8: bytes with no encoding of their own (binary
342
+ # or US-ASCII) are read as UTF-8, and text in any other encoding is
343
+ # converted to it.
344
+ def self.text_of(source)
345
+ raise ArgumentError, "a source is a String, not #{source.class}" unless source.is_a?(String)
346
+
347
+ text = if [Encoding::BINARY, Encoding::US_ASCII].include?(source.encoding)
348
+ source.dup.force_encoding(Encoding::UTF_8)
349
+ else
350
+ source.encode(Encoding::UTF_8)
351
+ end
352
+ return text if text.valid_encoding?
353
+
354
+ raise ArgumentError, "a source is text, and this one is not valid #{text.encoding}"
355
+ rescue EncodingError => e
356
+ raise ArgumentError, "a source is text that converts to UTF-8: #{e.message}"
357
+ end
358
+
359
+ # The instruction list a program names: the list itself, or the source
360
+ # compiled by the block, or the failed result of a source that does not
361
+ # compile.
362
+ def self.instructions_of(program)
363
+ return program unless program.is_a?(String)
364
+
365
+ compiled = yield(program)
366
+ compiled.ok? ? compiled.value : compiled
367
+ end
368
+
369
+ # A statement-mode run of a program, or the failed result of a source
370
+ # that does not compile.
371
+ def self.statements(program, context, **)
372
+ instructions = instructions_of(program) { |source| compile_program(source) }
373
+ return instructions if instructions.is_a?(Result)
374
+
375
+ refused = refusal_of(instructions, context)
376
+ refused ? Results.failure(refused) : Evaluator.execute(instructions, context, **)
377
+ end
378
+
379
+ # The refusal of a context or a literal operand outside the value domain,
380
+ # or nil. Arguments of the wrong kind are left for the evaluator to raise
381
+ # on.
382
+ def self.refusal_of(instructions, context)
383
+ Boundary.input_refusal(instructions, context) if instructions.is_a?(Array) && context.is_a?(Hash)
384
+ end
385
+
386
+ # The value of a result, or the raise of its error.
387
+ def self.unwrap(result)
388
+ raise Error, result.error unless result.ok?
389
+
390
+ result.value
391
+ end
392
+
393
+ private_class_method :compiled, :refused?, :failed, :text_of, :instructions_of, :statements, :refusal_of, :unwrap
24
394
  end
metadata CHANGED
@@ -1,168 +1,75 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: predicator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - JohnnyT
8
- autorequire:
9
- bindir: exe
8
+ bindir: bin
10
9
  cert_chain: []
11
- date: 2020-08-04 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: coveralls
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: minitest
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - '='
46
- - !ruby/object:Gem::Version
47
- version: 5.4.2
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - '='
53
- - !ruby/object:Gem::Version
54
- version: 5.4.2
55
- - !ruby/object:Gem::Dependency
56
- name: minitest-focus
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: oedipus_lex
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: pry-nav
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: racc
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
- - !ruby/object:Gem::Dependency
112
- name: rake
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
- description:
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Compiles a condition written as text into stack-machine instructions
13
+ and evaluates it against a context, without evaluating code. Wire-compatible with
14
+ the Elixir reference implementation. No runtime dependencies.
126
15
  email:
127
16
  - ubergeek3141@gmail.com
128
17
  executables: []
129
18
  extensions: []
130
19
  extra_rdoc_files: []
131
20
  files:
132
- - ".gitignore"
133
- - ".rubocop.yml"
134
- - ".travis.yml"
135
- - CODE_OF_CONDUCT.md
136
- - Gemfile
137
- - HISTORY.md
138
- - LICENSE.txt
21
+ - CHANGELOG.md
22
+ - LICENSE
139
23
  - README.md
140
- - Rakefile
141
24
  - lib/predicator.rb
142
25
  - lib/predicator/ast.rb
143
- - lib/predicator/context.rb
26
+ - lib/predicator/boundary.rb
27
+ - lib/predicator/civil.rb
28
+ - lib/predicator/duration_text.rb
29
+ - lib/predicator/emitter.rb
30
+ - lib/predicator/emitter/chains.rb
31
+ - lib/predicator/emitter/statements.rb
32
+ - lib/predicator/errors.rb
144
33
  - lib/predicator/evaluator.rb
145
- - lib/predicator/lexer.rex
146
- - lib/predicator/lexer.rex.rb
34
+ - lib/predicator/evaluator/access.rb
35
+ - lib/predicator/evaluator/arithmetic.rb
36
+ - lib/predicator/evaluator/calls.rb
37
+ - lib/predicator/evaluator/casts.rb
38
+ - lib/predicator/evaluator/chronology.rb
39
+ - lib/predicator/evaluator/compare.rb
40
+ - lib/predicator/evaluator/json_functions.rb
41
+ - lib/predicator/evaluator/json_scan.rb
42
+ - lib/predicator/evaluator/library.rb
43
+ - lib/predicator/evaluator/math_functions.rb
44
+ - lib/predicator/evaluator/order.rb
45
+ - lib/predicator/evaluator/rich_types.rb
46
+ - lib/predicator/evaluator/statements.rb
47
+ - lib/predicator/float_text.rb
48
+ - lib/predicator/iso_text.rb
49
+ - lib/predicator/lexer.rb
50
+ - lib/predicator/lexer/literals.rb
51
+ - lib/predicator/lexer/numbers.rb
147
52
  - lib/predicator/parser.rb
148
- - lib/predicator/parser.y
53
+ - lib/predicator/parser/durations.rb
54
+ - lib/predicator/parser/operators.rb
55
+ - lib/predicator/parser/postfix.rb
56
+ - lib/predicator/parser/primaries.rb
57
+ - lib/predicator/parser/statements.rb
58
+ - lib/predicator/parser/token_text.rb
59
+ - lib/predicator/result.rb
60
+ - lib/predicator/tagged.rb
61
+ - lib/predicator/tagged/reader.rb
62
+ - lib/predicator/tagged/writer.rb
63
+ - lib/predicator/values.rb
149
64
  - lib/predicator/version.rb
150
- - lib/predicator/visitors.rb
151
- - lib/predicator/visitors/dot.rb
152
- - lib/predicator/visitors/each.rb
153
- - lib/predicator/visitors/instructions.rb
154
- - lib/predicator/visitors/string.rb
155
- - lib/predicator/visitors/visitor.rb
156
- - predicator.gemspec
157
- homepage: https://github.com/riddler/predicator/tree/master/impl/rb
65
+ homepage: https://github.com/riddler/predicator-rb
158
66
  licenses:
159
67
  - MIT
160
68
  metadata:
161
- allowed_push_host: https://rubygems.org
162
- changelog_uri: https://github.com/riddler/predicator/blob/master/impl/rb/HISTORY.md
163
- source_code_uri: https://github.com/riddler/predicator/tree/master/impl/rb
164
- bug_tracker_uri: https://github.com/riddler/predicator/issues
165
- post_install_message:
69
+ homepage_uri: https://github.com/riddler/predicator-rb
70
+ source_code_uri: https://github.com/riddler/predicator-rb
71
+ changelog_uri: https://github.com/riddler/predicator-rb/blob/main/CHANGELOG.md
72
+ rubygems_mfa_required: 'true'
166
73
  rdoc_options: []
167
74
  require_paths:
168
75
  - lib
@@ -170,15 +77,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
170
77
  requirements:
171
78
  - - ">="
172
79
  - !ruby/object:Gem::Version
173
- version: '0'
80
+ version: '3.3'
174
81
  required_rubygems_version: !ruby/object:Gem::Requirement
175
82
  requirements:
176
83
  - - ">="
177
84
  - !ruby/object:Gem::Version
178
85
  version: '0'
179
86
  requirements: []
180
- rubygems_version: 3.0.6
181
- signing_key:
87
+ rubygems_version: 4.0.20
182
88
  specification_version: 4
183
- summary: A secure, non-evaling condition (boolean predicate) engine for end users
89
+ summary: A safe, non-evaluating condition language for end users.
184
90
  test_files: []
data/.gitignore DELETED
@@ -1,11 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /test/reports/
9
- /tmp/
10
- .ruby-gemset
11
- .ruby-version