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
@@ -1,442 +1,221 @@
1
- #
2
- # DO NOT MODIFY!!!!
3
- # This file is automatically generated by Racc 1.4.14
4
- # from Racc grammer file "".
5
- #
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "values"
4
+ require_relative "errors"
5
+ require_relative "float_text"
6
+ require_relative "duration_text"
7
+ require_relative "lexer"
8
+ require_relative "ast"
9
+ require_relative "parser/token_text"
10
+ require_relative "parser/operators"
11
+ require_relative "parser/postfix"
12
+ require_relative "parser/primaries"
13
+ require_relative "parser/durations"
14
+ require_relative "parser/statements"
6
15
 
7
- require 'racc/parser.rb'
8
-
9
- require "predicator/lexer.rex"
10
- require "predicator/visitors"
11
- require "predicator/ast"
12
16
  module Predicator
13
- class Parser < Racc::Parser
14
-
15
- def initialize
16
- @lexer = Lexer.new
17
- end
18
-
19
- def parse string
20
- @lexer.parse string
21
- do_parse
22
- end
23
-
24
- def next_token
25
- @lexer.next_token
26
- end
27
-
28
- def on_error type, val, values
29
- super
30
- rescue Racc::ParseError
31
- trace = values.each_with_index.map{|l, i| "#{' ' * i}#{l}"}
32
- raise ParseError, "\nparse error on value #{val.inspect}\n#{trace.join("\n")}"
17
+ # The compiler's second stage: the token stream the {Lexer} answers in,
18
+ # the reference's syntax tree ({Ast}) out, or the one refusal that stopped
19
+ # it. It is a recursive descent over the reference's grammar, with its
20
+ # precedence, lowest first:
21
+ #
22
+ # program -> statement ( ";" statement )* ";"?
23
+ # statement -> if | while | location "=" expression | expression
24
+ # expression -> logical_or
25
+ # logical_or -> logical_and ( ( "OR" | "||" ) logical_and )*
26
+ # logical_and -> logical_not ( ( "AND" | "&&" ) logical_not )*
27
+ # logical_not -> ( "NOT" | "!" ) logical_not | comparison
28
+ # comparison -> addition ( comparison_op | "in" | "contains" ) addition )?
29
+ # addition -> multiplication ( ( "+" | "-" ) multiplication )*
30
+ # multiplication -> unary ( ( "*" | "/" | "%" ) unary )*
31
+ # unary -> ( "-" | "!" ) unary | postfix
32
+ # postfix -> primary ( "[" expression "]" | "." name | "::" type )*
33
+ #
34
+ # The binary operators associate to the left; a comparison takes one
35
+ # operator at most, so a second is a token after a finished expression.
36
+ # A statement ending in a closing brace needs no separator before the
37
+ # next; a program holds one statement at least.
38
+ #
39
+ # A refusal is a {Lexer::Refusal}: a {Predicator::ParseError} carrying the
40
+ # reference's message, the position and span of the token it names, and a
41
+ # reason, which is this package's token for the refusal. Nothing raises.
42
+ #
43
+ # One refusal is not the reference's, which follows a source as deep as it
44
+ # nests: a source that nests more than {DEPTH_LIMIT} levels is refused
45
+ # with the reason `nesting_depth_exceeded`, at the token the descent
46
+ # stopped on, rather than left to exhaust the stack. A level is counted
47
+ # each time the descent re-enters the whole expression (inside a
48
+ # parenthesis, a list, an object, an index, a call's arguments, a
49
+ # statement's condition or value), for each prefix operator, a relative
50
+ # date's operand, a block's statements, an `else if` and a statement's
51
+ # assignment probe; a chain of left-associative operators or of postfix
52
+ # steps is read in a loop and costs none. Where the stack a parse runs on
53
+ # is too small to reach the limit, as a Fiber's default stack is, the
54
+ # source is refused under the same reason at the token the stack ran out
55
+ # on, with a message saying so, rather than raising.
56
+ #
57
+ # The module holds no state; each call parses with its own {Descent}. It
58
+ # is private to the package.
59
+ module Parser
60
+ # How many levels a source may nest, the whole expression counting as
61
+ # the first. A source at exactly this depth parses. It is above the 300
62
+ # parentheses the reference's compile transcript compiles, and below
63
+ # the depth the costliest nesting reaches on a thread's default stack.
64
+ DEPTH_LIMIT = 400
65
+
66
+ # The seven scalar type names a cast may name, in the reference's order.
67
+ CAST_TYPE_NAMES = %w[integer float string boolean date datetime duration].freeze
68
+
69
+ # The tag a refusal is thrown with, to the catch around a parse or a
70
+ # probe.
71
+ REFUSED = :refused
72
+
73
+ # Parses one expression.
74
+ #
75
+ # @param tokens [Array<Lexer::Token>] a stream the lexer answered, which
76
+ # ends in its `:eof` token
77
+ # @return [Object, Lexer::Refusal] the expression's {Ast} node, or the
78
+ # refusal
79
+ def self.parse(tokens) = Descent.new(tokens).whole_expression
80
+
81
+ # Parses a program: one or more statements.
82
+ #
83
+ # @param tokens [Array<Lexer::Token>] a stream the lexer answered, which
84
+ # ends in its `:eof` token
85
+ # @return [Ast::Program, Lexer::Refusal] the program, or the refusal
86
+ def self.parse_program(tokens) = Descent.new(tokens).whole_program
87
+
88
+ # One parse of one token stream: the tokens, the cursor and how many
89
+ # productions are open. A refusal is thrown to the catch around the
90
+ # whole parse (or around the assignment probe that discards it) and is
91
+ # never seen outside this class.
92
+ class Descent
93
+ include TokenText
94
+ include Operators
95
+ include Postfix
96
+ include Primaries
97
+ include Durations
98
+ include Statements
99
+
100
+ def initialize(tokens)
101
+ @tokens = tokens
102
+ @index = 0
103
+ @depth = 0
104
+ end
105
+
106
+ # @return [Object, Lexer::Refusal]
107
+ def whole_expression
108
+ guarded do
109
+ node = expression
110
+ trailing = peek
111
+ refuse("trailing_token", "Unexpected token #{describe(trailing)} after expression", trailing) unless at?(:eof)
112
+ node
113
+ end
114
+ end
115
+
116
+ # @return [Ast::Program, Lexer::Refusal]
117
+ def whole_program
118
+ guarded do
119
+ start = start_of(peek)
120
+ statements = statement_sequence(:eof)
121
+ after_statement(peek) unless at?(:eof)
122
+ Ast::Program.new(statements:, position: start,
123
+ span: between(statements.first.span.start, statements.last.span.end))
124
+ end
125
+ end
126
+
127
+ private
128
+
129
+ # The parse in the block, or the refusal it threw; a stack exhausted
130
+ # short of the depth limit is refused where it ran out.
131
+ def guarded(&)
132
+ catch(REFUSED, &)
133
+ rescue SystemStackError
134
+ catch(REFUSED) do
135
+ refuse("nesting_depth_exceeded", "Expression nests deeper than the stack this parse runs on can follow, " \
136
+ "short of the depth limit of #{DEPTH_LIMIT} levels", peek)
137
+ end
138
+ end
139
+
140
+ def peek = @tokens[@index]
141
+
142
+ def previous = @tokens[@index - 1]
143
+
144
+ def at?(type) = peek.type == type
145
+
146
+ def advance
147
+ token = peek
148
+ @index += 1
149
+ token
150
+ end
151
+
152
+ # The token, consumed, when it is of the type given; the refusal
153
+ # naming it otherwise.
154
+ def expect(type, reason, message)
155
+ refuse(reason, "#{message} #{describe(peek)}", peek) unless at?(type)
156
+ advance
157
+ end
158
+
159
+ # Reads one production one level deeper, or refuses at the token the
160
+ # descent stopped on when the source is already at the limit.
161
+ def descend
162
+ enter
163
+ yield
164
+ ensure
165
+ @depth -= 1
166
+ end
167
+
168
+ # The whole expression, one level deeper. It is the production read most
169
+ # often, so it counts its own level rather than through a block.
170
+ def expression
171
+ enter
172
+ logical_or
173
+ ensure
174
+ @depth -= 1
175
+ end
176
+
177
+ # Counts one more open production; the ensure of whoever called it counts
178
+ # it back, the refusal included.
179
+ def enter
180
+ @depth += 1
181
+ return if @depth <= DEPTH_LIMIT
182
+
183
+ refuse("nesting_depth_exceeded", "Expression nests past the depth limit of #{DEPTH_LIMIT} levels, " \
184
+ "the whole expression counting as the first", peek)
185
+ end
186
+
187
+ # Throws the refusal: its position is its span's start.
188
+ def refuse(reason, message, token, span = span_of(token))
189
+ error = ParseError.new(reason:, message:, position: span.start)
190
+ throw REFUSED, Lexer::Refusal.new(error:, span:)
191
+ end
192
+
193
+ def start_of(token) = Position.new(line: token.line, column: token.column)
194
+
195
+ # One past the token's last character; a string token, which may hold
196
+ # a line feed, carries its own.
197
+ def end_of(token) = token.end_position || Position.new(line: token.line, column: token.column + token.length)
198
+
199
+ def span_of(token) = Lexer::Span.new(start: start_of(token), end: end_of(token))
200
+
201
+ # A node's own token, as its position and its span.
202
+ def leaf(token) = { position: start_of(token), span: span_of(token) }
203
+
204
+ # From an opening token to past its closing one.
205
+ def delimited(open, close) = { position: start_of(open), span: between(start_of(open), end_of(close)) }
206
+
207
+ # From a node's start to past a token.
208
+ def reaching(node, token) = between(node.span.start, end_of(token))
209
+
210
+ # From a prefix token to its operand's end.
211
+ def prefixed(token, operand) = { position: start_of(token), span: between(start_of(token), operand.span.end) }
212
+
213
+ # An infix node: at its operator, over its operands.
214
+ def infix(token, left, right) = { position: start_of(token), span: between(left.span.start, right.span.end) }
215
+
216
+ def between(start, finish) = Lexer::Span.new(start:, end: finish)
33
217
  end
34
-
35
- ##### State transition tables begin ###
36
-
37
- racc_action_table = [
38
- 6, 7, 10, 6, 7, 10, 9, 15, 31, 9,
39
- 6, 7, 10, 6, 7, 10, 9, 16, 17, 9,
40
- 16, 17, 14, 16, 17, 14, 52, 6, 7, 10,
41
- 25, 28, 14, 9, 53, 14, 20, 21, 22, 23,
42
- 24, 18, 19, 37, 57, 38, 39, 41, 38, 14,
43
- 26, 27, 16, 17, 37, 38, 56, 39, 41, 37,
44
- 38, 60, 39, 41, 37, 38, 70, 39, 41, 72,
45
- 71, 16, 17, 73, 58, 59, 37, 38, 39, 41,
46
- 61, 52, 37, 37, 38 ]
47
-
48
- racc_action_check = [
49
- 0, 0, 0, 9, 9, 9, 0, 1, 15, 9,
50
- 10, 10, 10, 16, 16, 16, 10, 29, 29, 16,
51
- 1, 1, 0, 32, 32, 9, 24, 17, 17, 17,
52
- 8, 8, 10, 17, 25, 16, 8, 8, 8, 8,
53
- 8, 8, 8, 23, 30, 26, 23, 23, 27, 17,
54
- 8, 8, 30, 30, 20, 20, 28, 20, 20, 21,
55
- 21, 48, 21, 21, 22, 22, 62, 22, 22, 64,
56
- 62, 33, 33, 64, 40, 40, 52, 52, 61, 61,
57
- 49, 53, 60, 71, 73 ]
58
-
59
- racc_action_pointer = [
60
- -2, 7, nil, nil, nil, nil, nil, nil, 21, 1,
61
- 8, nil, nil, nil, nil, 8, 11, 25, nil, nil,
62
- 32, 37, 42, 21, 20, 15, 22, 25, 32, 4,
63
- 39, nil, 10, 58, nil, nil, nil, nil, nil, nil,
64
- 47, nil, nil, nil, nil, nil, nil, nil, 48, 67,
65
- nil, nil, 54, 75, nil, nil, nil, nil, nil, nil,
66
- 60, 53, 59, nil, 62, nil, nil, nil, nil, nil,
67
- nil, 61, nil, 61, nil, nil ]
68
-
69
- racc_action_default = [
70
- -48, -48, -1, -2, -3, -4, -5, -6, -7, -48,
71
- -48, -14, -15, -16, -46, -48, -48, -48, -11, -12,
72
- -48, -48, -48, -48, -48, -48, -48, -48, -48, -8,
73
- -48, 76, -9, -10, -17, -23, -30, -40, -41, -42,
74
- -48, -45, -18, -24, -31, -19, -25, -32, -48, -48,
75
- -21, -26, -48, -48, -28, -29, -47, -13, -43, -44,
76
- -48, -48, -48, -35, -48, -38, -22, -27, -20, -33,
77
- -34, -48, -37, -48, -36, -39 ]
78
-
79
- racc_goto_table = [
80
- 35, 43, 46, 62, 50, 51, 54, 55, 34, 42,
81
- 45, 48, 1, 36, 44, 47, 49, 64, nil, nil,
82
- nil, 29, 30, nil, nil, nil, nil, nil, 32, 33,
83
- nil, nil, 65, 66, 67, nil, nil, nil, nil, nil,
84
- 63, nil, nil, nil, nil, nil, nil, nil, 68, nil,
85
- nil, nil, nil, 75, 69, nil, nil, nil, nil, 74 ]
86
-
87
- racc_goto_check = [
88
- 12, 12, 12, 15, 11, 13, 12, 12, 10, 10,
89
- 10, 10, 1, 14, 14, 14, 14, 16, nil, nil,
90
- nil, 1, 1, nil, nil, nil, nil, nil, 1, 1,
91
- nil, nil, 12, 11, 13, nil, nil, nil, nil, nil,
92
- 10, nil, nil, nil, nil, nil, nil, nil, 10, nil,
93
- nil, nil, nil, 12, 14, nil, nil, nil, nil, 10 ]
94
-
95
- racc_goto_pointer = [
96
- nil, 12, nil, nil, nil, nil, nil, nil, nil, nil,
97
- -12, -20, -20, -19, -7, -49, -35, nil ]
98
-
99
- racc_goto_default = [
100
- nil, nil, 2, 3, 4, 5, 8, 11, 12, 13,
101
- nil, nil, nil, nil, nil, nil, nil, 40 ]
102
-
103
- racc_reduce_table = [
104
- 0, 0, :racc_error,
105
- 1, 32, :_reduce_none,
106
- 1, 32, :_reduce_none,
107
- 1, 32, :_reduce_none,
108
- 1, 32, :_reduce_none,
109
- 1, 33, :_reduce_5,
110
- 1, 33, :_reduce_6,
111
- 1, 33, :_reduce_7,
112
- 2, 34, :_reduce_8,
113
- 3, 34, :_reduce_9,
114
- 3, 34, :_reduce_10,
115
- 2, 34, :_reduce_11,
116
- 2, 34, :_reduce_12,
117
- 3, 35, :_reduce_13,
118
- 1, 36, :_reduce_none,
119
- 1, 36, :_reduce_none,
120
- 1, 36, :_reduce_none,
121
- 3, 38, :_reduce_17,
122
- 3, 38, :_reduce_18,
123
- 3, 38, :_reduce_19,
124
- 5, 38, :_reduce_20,
125
- 3, 38, :_reduce_21,
126
- 4, 38, :_reduce_22,
127
- 3, 39, :_reduce_23,
128
- 3, 39, :_reduce_24,
129
- 3, 39, :_reduce_25,
130
- 3, 39, :_reduce_26,
131
- 4, 39, :_reduce_27,
132
- 3, 39, :_reduce_28,
133
- 3, 39, :_reduce_29,
134
- 3, 40, :_reduce_30,
135
- 3, 40, :_reduce_31,
136
- 3, 40, :_reduce_32,
137
- 5, 40, :_reduce_33,
138
- 3, 42, :_reduce_34,
139
- 1, 46, :_reduce_none,
140
- 3, 46, :_reduce_36,
141
- 3, 44, :_reduce_37,
142
- 1, 47, :_reduce_none,
143
- 3, 47, :_reduce_39,
144
- 1, 41, :_reduce_40,
145
- 1, 43, :_reduce_41,
146
- 1, 45, :_reduce_42,
147
- 2, 45, :_reduce_43,
148
- 2, 45, :_reduce_44,
149
- 1, 48, :_reduce_45,
150
- 1, 37, :_reduce_46,
151
- 3, 37, :_reduce_47 ]
152
-
153
- racc_reduce_n = 48
154
-
155
- racc_shift_n = 76
156
-
157
- racc_token_table = {
158
- false => 0,
159
- :error => 1,
160
- :TRUE => 2,
161
- :FALSE => 3,
162
- :LPAREN => 4,
163
- :RPAREN => 5,
164
- :LBRACKET => 6,
165
- :RBRACKET => 7,
166
- :BANG => 8,
167
- :NOT => 9,
168
- :DOT => 10,
169
- :COMMA => 11,
170
- :AT => 12,
171
- :AND => 13,
172
- :OR => 14,
173
- :EQ => 15,
174
- :GT => 16,
175
- :LT => 17,
176
- :BETWEEN => 18,
177
- :IN => 19,
178
- :PRESENT => 20,
179
- :BLANK => 21,
180
- :INTEGER => 22,
181
- :STRING => 23,
182
- :IDENTIFIER => 24,
183
- :DATE => 25,
184
- :DURATION => 26,
185
- :AGO => 27,
186
- :FROMNOW => 28,
187
- :STARTSWITH => 29,
188
- :ENDSWITH => 30 }
189
-
190
- racc_nt_base = 31
191
-
192
- racc_use_result_var = false
193
-
194
- Racc_arg = [
195
- racc_action_table,
196
- racc_action_check,
197
- racc_action_default,
198
- racc_action_pointer,
199
- racc_goto_table,
200
- racc_goto_check,
201
- racc_goto_default,
202
- racc_goto_pointer,
203
- racc_nt_base,
204
- racc_reduce_table,
205
- racc_token_table,
206
- racc_shift_n,
207
- racc_reduce_n,
208
- racc_use_result_var ]
209
-
210
- Racc_token_to_s_table = [
211
- "$end",
212
- "error",
213
- "TRUE",
214
- "FALSE",
215
- "LPAREN",
216
- "RPAREN",
217
- "LBRACKET",
218
- "RBRACKET",
219
- "BANG",
220
- "NOT",
221
- "DOT",
222
- "COMMA",
223
- "AT",
224
- "AND",
225
- "OR",
226
- "EQ",
227
- "GT",
228
- "LT",
229
- "BETWEEN",
230
- "IN",
231
- "PRESENT",
232
- "BLANK",
233
- "INTEGER",
234
- "STRING",
235
- "IDENTIFIER",
236
- "DATE",
237
- "DURATION",
238
- "AGO",
239
- "FROMNOW",
240
- "STARTSWITH",
241
- "ENDSWITH",
242
- "$start",
243
- "predicate",
244
- "boolean_predicate",
245
- "logical_predicate",
246
- "group_predicate",
247
- "comparison_predicate",
248
- "variable",
249
- "integer_comparison_predicate",
250
- "string_comparison_predicate",
251
- "date_comparison_predicate",
252
- "integer",
253
- "integer_array",
254
- "string",
255
- "string_array",
256
- "date",
257
- "integer_array_contents",
258
- "string_array_contents",
259
- "duration" ]
260
-
261
- Racc_debug_parser = false
262
-
263
- ##### State transition tables end #####
264
-
265
- # reduce 0 omitted
266
-
267
- # reduce 1 omitted
268
-
269
- # reduce 2 omitted
270
-
271
- # reduce 3 omitted
272
-
273
- # reduce 4 omitted
274
-
275
- def _reduce_5(val, _values)
276
- AST::True.new true
277
- end
278
-
279
- def _reduce_6(val, _values)
280
- AST::False.new false
281
- end
282
-
283
- def _reduce_7(val, _values)
284
- AST::BooleanVariable.new val.first
285
- end
286
-
287
- def _reduce_8(val, _values)
288
- AST::Not.new val.last
289
- end
290
-
291
- def _reduce_9(val, _values)
292
- AST::And.new val.first, val.last
293
- end
294
-
295
- def _reduce_10(val, _values)
296
- AST::Or.new val.first, val.last
297
- end
298
-
299
- def _reduce_11(val, _values)
300
- AST::Present.new val.first
301
- end
302
-
303
- def _reduce_12(val, _values)
304
- AST::Blank.new val.first
305
- end
306
-
307
- def _reduce_13(val, _values)
308
- AST::Group.new val[1]
309
- end
310
-
311
- # reduce 14 omitted
312
-
313
- # reduce 15 omitted
314
-
315
- # reduce 16 omitted
316
-
317
- def _reduce_17(val, _values)
318
- AST::IntegerEqual.new val.first, val.last
319
- end
320
-
321
- def _reduce_18(val, _values)
322
- AST::IntegerGreaterThan.new val.first, val.last
323
- end
324
-
325
- def _reduce_19(val, _values)
326
- AST::IntegerLessThan.new val.first, val.last
218
+ private_constant :Descent
219
+ end
220
+ private_constant :Parser
327
221
  end
328
-
329
- def _reduce_20(val, _values)
330
- AST::IntegerBetween.new val.first, val[2], val.last
331
- end
332
-
333
- def _reduce_21(val, _values)
334
- AST::IntegerIn.new val.first, val.last
335
- end
336
-
337
- def _reduce_22(val, _values)
338
- AST::IntegerNotIn.new val.first, val.last
339
- end
340
-
341
- def _reduce_23(val, _values)
342
- AST::StringEqual.new val.first, val.last
343
- end
344
-
345
- def _reduce_24(val, _values)
346
- AST::StringGreaterThan.new val.first, val.last
347
- end
348
-
349
- def _reduce_25(val, _values)
350
- AST::StringLessThan.new val.first, val.last
351
- end
352
-
353
- def _reduce_26(val, _values)
354
- AST::StringIn.new val.first, val.last
355
- end
356
-
357
- def _reduce_27(val, _values)
358
- AST::StringNotIn.new val.first, val.last
359
- end
360
-
361
- def _reduce_28(val, _values)
362
- AST::StringStartsWith.new val.first, val.last
363
- end
364
-
365
- def _reduce_29(val, _values)
366
- AST::StringEndsWith.new val.first, val.last
367
- end
368
-
369
- def _reduce_30(val, _values)
370
- AST::DateEqual.new val.first, val.last
371
- end
372
-
373
- def _reduce_31(val, _values)
374
- AST::DateGreaterThan.new val.first, val.last
375
- end
376
-
377
- def _reduce_32(val, _values)
378
- AST::DateLessThan.new val.first, val.last
379
- end
380
-
381
- def _reduce_33(val, _values)
382
- AST::DateBetween.new val.first, val[2], val.last
383
- end
384
-
385
- def _reduce_34(val, _values)
386
- AST::IntegerArray.new val[1]
387
- end
388
-
389
- # reduce 35 omitted
390
-
391
- def _reduce_36(val, _values)
392
- [val.first, val.last].flatten
393
- end
394
-
395
- def _reduce_37(val, _values)
396
- AST::StringArray.new val[1]
397
- end
398
-
399
- # reduce 38 omitted
400
-
401
- def _reduce_39(val, _values)
402
- [val.first, val.last].flatten
403
- end
404
-
405
- def _reduce_40(val, _values)
406
- AST::Integer.new val.first.to_i
407
- end
408
-
409
- def _reduce_41(val, _values)
410
- AST::String.new val.first
411
- end
412
-
413
- def _reduce_42(val, _values)
414
- AST::Date.new val.first
415
- end
416
-
417
- def _reduce_43(val, _values)
418
- AST::DateAgo.new val.first
419
- end
420
-
421
- def _reduce_44(val, _values)
422
- AST::DateFromNow.new val.first
423
- end
424
-
425
- def _reduce_45(val, _values)
426
- AST::Duration.new val.first
427
- end
428
-
429
- def _reduce_46(val, _values)
430
- AST::Variable.new val.first
431
- end
432
-
433
- def _reduce_47(val, _values)
434
- AST::Variable.new [val.first, val.last].flatten.join(".")
435
- end
436
-
437
- def _reduce_none(val, _values)
438
- val[0]
439
- end
440
-
441
- end # class Parser
442
- end # module Predicator
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Predicator
4
+ # What a call answers: a value, or an error value saying why there is none.
5
+ # Results are values, never raised; each entry point's raising variant
6
+ # raises {Predicator::Error} carrying the same error instead.
7
+ #
8
+ # A successful result's value may itself be nil (predicator's null), so
9
+ # {#ok?} is read from the error, not from the value.
10
+ #
11
+ # @!attribute [r] value
12
+ # @return [Object] the answer; nil when the result failed
13
+ # @!attribute [r] error
14
+ # @return [Object, nil] the error value; nil when the result succeeded
15
+ Result = Data.define(:value, :error) do
16
+ # @raise [ArgumentError] when it carries both a value and an error
17
+ def initialize(value:, error:)
18
+ raise ArgumentError, "a result carries a value or an error, not both" unless value.nil? || error.nil?
19
+
20
+ super
21
+ end
22
+
23
+ # @return [Boolean] whether the call succeeded
24
+ def ok? = error.nil?
25
+ end
26
+
27
+ # The two ways the package builds a {Result}. Private to the package: a
28
+ # host reads results and does not need to make them (Result.new and
29
+ # Result[] stay public, as Data defines them).
30
+ module Results
31
+ module_function
32
+
33
+ # A successful result.
34
+ def ok(value) = Result.new(value:, error: nil)
35
+
36
+ # A failed result.
37
+ #
38
+ # @raise [ArgumentError] when error is nil
39
+ def failure(error)
40
+ raise ArgumentError, "a failed result carries an error" if error.nil?
41
+
42
+ Result.new(value: nil, error:)
43
+ end
44
+ end
45
+ private_constant :Results
46
+ end