factbase 0.19.11 → 0.19.12
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.
- checksums.yaml +4 -4
- data/Gemfile +5 -4
- data/Gemfile.lock +16 -12
- data/README.md +49 -18
- data/Rakefile +2 -7
- data/factbase.gemspec +11 -11
- data/lib/factbase/accum.rb +1 -1
- data/lib/factbase/cached/cached_fact.rb +1 -2
- data/lib/factbase/cached/cached_factbase.rb +3 -3
- data/lib/factbase/cached/cached_query.rb +4 -6
- data/lib/factbase/cached/cached_term.rb +1 -2
- data/lib/factbase/churn.rb +4 -8
- data/lib/factbase/fact.rb +12 -9
- data/lib/factbase/flatten.rb +2 -2
- data/lib/factbase/impatient.rb +14 -13
- data/lib/factbase/indexed/indexed_and.rb +14 -20
- data/lib/factbase/indexed/indexed_eq.rb +5 -1
- data/lib/factbase/indexed/indexed_fact.rb +1 -4
- data/lib/factbase/indexed/indexed_factbase.rb +4 -4
- data/lib/factbase/indexed/indexed_gt.rb +3 -1
- data/lib/factbase/indexed/indexed_gte.rb +51 -0
- data/lib/factbase/indexed/indexed_lt.rb +3 -1
- data/lib/factbase/indexed/indexed_lte.rb +51 -0
- data/lib/factbase/indexed/indexed_not.rb +1 -1
- data/lib/factbase/indexed/indexed_or.rb +2 -2
- data/lib/factbase/indexed/indexed_query.rb +6 -7
- data/lib/factbase/indexed/indexed_term.rb +10 -6
- data/lib/factbase/indexed/indexed_unique.rb +4 -2
- data/lib/factbase/inv.rb +3 -3
- data/lib/factbase/lazy_taped.rb +10 -13
- data/lib/factbase/lazy_taped_hash.rb +2 -1
- data/lib/factbase/light.rb +1 -1
- data/lib/factbase/logged.rb +37 -34
- data/lib/factbase/pre.rb +3 -3
- data/lib/factbase/query.rb +4 -5
- data/lib/factbase/rules.rb +8 -8
- data/lib/factbase/sync/sync_factbase.rb +2 -2
- data/lib/factbase/syntax.rb +18 -19
- data/lib/factbase/tallied.rb +6 -7
- data/lib/factbase/taped.rb +5 -11
- data/lib/factbase/tee.rb +2 -2
- data/lib/factbase/term.rb +53 -60
- data/lib/factbase/terms/agg.rb +3 -4
- data/lib/factbase/terms/arithmetic.rb +7 -7
- data/lib/factbase/terms/as.rb +2 -2
- data/lib/factbase/terms/assert.rb +5 -13
- data/lib/factbase/terms/base.rb +6 -7
- data/lib/factbase/terms/best.rb +1 -1
- data/lib/factbase/terms/boolean.rb +1 -1
- data/lib/factbase/terms/compare.rb +2 -1
- data/lib/factbase/terms/defn.rb +8 -6
- data/lib/factbase/terms/empty.rb +1 -1
- data/lib/factbase/terms/first.rb +2 -2
- data/lib/factbase/terms/head.rb +3 -3
- data/lib/factbase/terms/inverted.rb +2 -2
- data/lib/factbase/terms/join.rb +8 -7
- data/lib/factbase/terms/matches.rb +4 -4
- data/lib/factbase/terms/max.rb +1 -1
- data/lib/factbase/terms/min.rb +1 -1
- data/lib/factbase/terms/nth.rb +3 -3
- data/lib/factbase/terms/plus.rb +1 -1
- data/lib/factbase/terms/prev.rb +3 -6
- data/lib/factbase/terms/sorted.rb +2 -2
- data/lib/factbase/terms/sprintf.rb +5 -4
- data/lib/factbase/terms/sum.rb +1 -1
- data/lib/factbase/terms/to_float.rb +2 -2
- data/lib/factbase/terms/to_integer.rb +2 -2
- data/lib/factbase/terms/to_string.rb +1 -1
- data/lib/factbase/terms/to_time.rb +2 -2
- data/lib/factbase/terms/traced.rb +2 -2
- data/lib/factbase/terms/undef.rb +2 -2
- data/lib/factbase/terms/unique.rb +3 -7
- data/lib/factbase/to_json.rb +1 -1
- data/lib/factbase/to_xml.rb +5 -9
- data/lib/factbase/to_yaml.rb +1 -1
- data/lib/factbase/version.rb +1 -2
- data/lib/factbase.rb +27 -10
- data/lib/fuzz.rb +3 -3
- metadata +3 -1
data/lib/factbase/syntax.rb
CHANGED
|
@@ -43,7 +43,7 @@ class Factbase::Syntax
|
|
|
43
43
|
rescue StandardError => e
|
|
44
44
|
err = "#{e.message} (#{Backtrace.new(e)}) in \"#{@query}\""
|
|
45
45
|
err = "#{err}, tokens: #{@tokens}" unless @tokens.nil?
|
|
46
|
-
raise
|
|
46
|
+
raise(Broken, err)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
private
|
|
@@ -52,12 +52,12 @@ class Factbase::Syntax
|
|
|
52
52
|
# @return [Term] The term detected
|
|
53
53
|
def build
|
|
54
54
|
@tokens ||= to_tokens
|
|
55
|
-
raise 'No tokens' if @tokens.empty?
|
|
55
|
+
raise(StandardError, 'No tokens') if @tokens.empty?
|
|
56
56
|
@ast ||= to_ast(@tokens, 0)
|
|
57
|
-
raise "Too many terms (#{@ast[1]} != #{@tokens.size})" if @ast[1] != @tokens.size
|
|
57
|
+
raise(ArgumentError, "Too many terms (#{@ast[1]} != #{@tokens.size})") if @ast[1] != @tokens.size
|
|
58
58
|
t = @ast[0]
|
|
59
|
-
raise 'No terms found in the AST' if t.nil?
|
|
60
|
-
raise "#{t.class.name} is not an instance of Term" unless t.is_a?(Factbase::Term)
|
|
59
|
+
raise(StandardError, 'No terms found in the AST') if t.nil?
|
|
60
|
+
raise(ArgumentError, "#{t.class.name} is not an instance of Term") unless t.is_a?(Factbase::Term)
|
|
61
61
|
t
|
|
62
62
|
end
|
|
63
63
|
|
|
@@ -73,25 +73,24 @@ class Factbase::Syntax
|
|
|
73
73
|
# @param [Integer] at Position to start parsing from
|
|
74
74
|
# @return [Array<Factbase::Term,Integer>] The term detected and ending position
|
|
75
75
|
def to_ast(tokens, at)
|
|
76
|
-
raise "Closing too soon at ##{at}" if tokens[at] == :close
|
|
76
|
+
raise(StandardError, "Closing too soon at ##{at}") if tokens[at] == :close
|
|
77
77
|
return [tokens[at], at + 1] unless tokens[at] == :open
|
|
78
78
|
at += 1
|
|
79
79
|
op = tokens[at]
|
|
80
|
-
raise 'No token found' if op == :close
|
|
80
|
+
raise(StandardError, 'No token found') if op == :close
|
|
81
81
|
operands = []
|
|
82
82
|
at += 1
|
|
83
83
|
loop do
|
|
84
|
-
raise "End of token stream at ##{at}" if tokens[at].nil?
|
|
84
|
+
raise(StandardError, "End of token stream at ##{at}") if tokens[at].nil?
|
|
85
85
|
break if tokens[at] == :close
|
|
86
86
|
(operand, at1) = to_ast(tokens, at)
|
|
87
|
-
raise "Stuck at position ##{at}" if at == at1
|
|
88
|
-
raise "Jump back at position ##{at}" if at1 < at
|
|
87
|
+
raise(StandardError, "Stuck at position ##{at}") if at == at1
|
|
88
|
+
raise(StandardError, "Jump back at position ##{at}") if at1 < at
|
|
89
89
|
at = at1
|
|
90
90
|
operands << operand
|
|
91
91
|
break if tokens[at] == :close
|
|
92
92
|
end
|
|
93
|
-
|
|
94
|
-
[t, at + 1]
|
|
93
|
+
[Factbase::Term.new(op, operands), at + 1]
|
|
95
94
|
end
|
|
96
95
|
|
|
97
96
|
# Turns a query into an array of tokens.
|
|
@@ -100,7 +99,7 @@ class Factbase::Syntax
|
|
|
100
99
|
list = []
|
|
101
100
|
acc = ''
|
|
102
101
|
quotes = ['\'', '"']
|
|
103
|
-
spaces = [' ', ')']
|
|
102
|
+
spaces = [' ', ')', "\n", "\t", "\r"]
|
|
104
103
|
string = false
|
|
105
104
|
comment = false
|
|
106
105
|
@query.to_s.chars.each do |c|
|
|
@@ -128,26 +127,26 @@ class Factbase::Syntax
|
|
|
128
127
|
when ')'
|
|
129
128
|
list << :close
|
|
130
129
|
when ' ', "\n", "\t", "\r"
|
|
131
|
-
|
|
130
|
+
next
|
|
132
131
|
else
|
|
133
132
|
acc += c
|
|
134
133
|
end
|
|
135
134
|
end
|
|
136
|
-
raise 'String not closed' if string
|
|
135
|
+
raise(StandardError, 'String not closed') if string
|
|
137
136
|
list.map do |t|
|
|
138
137
|
if t.is_a?(Symbol)
|
|
139
138
|
t
|
|
140
139
|
elsif t.start_with?('\'', '"')
|
|
141
|
-
raise 'String literal can\'t be empty' if t.length <= 2
|
|
140
|
+
raise(ArgumentError, 'String literal can\'t be empty') if t.length <= 2
|
|
142
141
|
t[1..-2]
|
|
143
142
|
elsif t.match?(/^(\+|-)?[0-9]+$/)
|
|
144
|
-
t
|
|
143
|
+
Integer(t, 10)
|
|
145
144
|
elsif t.match?(/^(\+|-)?[0-9]+\.[0-9]+(e(\+|-)[0-9]+)?$/)
|
|
146
|
-
t
|
|
145
|
+
Float(t)
|
|
147
146
|
elsif t.match?(/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/)
|
|
148
147
|
Time.parse(t)
|
|
149
148
|
else
|
|
150
|
-
raise "Wrong symbol format (#{t})" unless t.match?(/^([_a-z][a-zA-Z0-9_]*|\$[_a-z]+)$/)
|
|
149
|
+
raise(ArgumentError, "Wrong symbol format (#{t})") unless t.match?(/^([_a-z][a-zA-Z0-9_]*|\$[_a-z]+)$/)
|
|
151
150
|
t.to_sym
|
|
152
151
|
end
|
|
153
152
|
end
|
data/lib/factbase/tallied.rb
CHANGED
|
@@ -18,7 +18,7 @@ class Factbase::Tallied
|
|
|
18
18
|
attr_reader :churn
|
|
19
19
|
|
|
20
20
|
def initialize(fb, churn = Factbase::Churn.new)
|
|
21
|
-
raise 'The "fb" is nil' if fb.nil?
|
|
21
|
+
raise(ArgumentError, 'The "fb" is nil') if fb.nil?
|
|
22
22
|
@fb = fb
|
|
23
23
|
@churn = churn
|
|
24
24
|
end
|
|
@@ -26,9 +26,8 @@ class Factbase::Tallied
|
|
|
26
26
|
decoor(:fb)
|
|
27
27
|
|
|
28
28
|
def insert
|
|
29
|
-
f = Fact.new(@fb.insert, @churn)
|
|
30
29
|
@churn.append(1, 0, 0)
|
|
31
|
-
|
|
30
|
+
Fact.new(@fb.insert, @churn)
|
|
32
31
|
end
|
|
33
32
|
|
|
34
33
|
def query(query, maps = nil)
|
|
@@ -39,13 +38,13 @@ class Factbase::Tallied
|
|
|
39
38
|
before = @churn.dup
|
|
40
39
|
commit = false
|
|
41
40
|
@fb.txn do |fbt|
|
|
42
|
-
catch
|
|
43
|
-
yield
|
|
41
|
+
catch(:rollback) do
|
|
42
|
+
yield(Factbase::Tallied.new(fbt, @churn))
|
|
44
43
|
commit = true
|
|
45
44
|
end
|
|
46
45
|
rescue Factbase::Rollback => e
|
|
47
46
|
@churn = before
|
|
48
|
-
raise
|
|
47
|
+
raise(e)
|
|
49
48
|
ensure
|
|
50
49
|
@churn = before unless commit
|
|
51
50
|
end
|
|
@@ -96,7 +95,7 @@ class Factbase::Tallied
|
|
|
96
95
|
def each(fb = @fb, params = {}, &)
|
|
97
96
|
return to_enum(__method__, fb, params) unless block_given?
|
|
98
97
|
@query.each(fb, params) do |f|
|
|
99
|
-
yield
|
|
98
|
+
yield(Fact.new(f, @churn))
|
|
100
99
|
end
|
|
101
100
|
end
|
|
102
101
|
|
data/lib/factbase/taped.rb
CHANGED
|
@@ -52,13 +52,13 @@ class Factbase::Taped
|
|
|
52
52
|
def each
|
|
53
53
|
return to_enum(__method__) unless block_given?
|
|
54
54
|
@origin.each do |m|
|
|
55
|
-
yield
|
|
55
|
+
yield(TapedHash.new(m, @added))
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def delete_if
|
|
60
60
|
@origin.delete_if do |m|
|
|
61
|
-
r = yield
|
|
61
|
+
r = yield(m)
|
|
62
62
|
@deleted.append(m.object_id) if r
|
|
63
63
|
r
|
|
64
64
|
end
|
|
@@ -153,14 +153,8 @@ class Factbase::Taped
|
|
|
153
153
|
private
|
|
154
154
|
|
|
155
155
|
def join(other)
|
|
156
|
-
|
|
157
|
-
raise '
|
|
158
|
-
|
|
159
|
-
Factbase::Taped.new(
|
|
160
|
-
n,
|
|
161
|
-
inserted: @inserted,
|
|
162
|
-
deleted: @deleted,
|
|
163
|
-
added: @added
|
|
164
|
-
)
|
|
156
|
+
raise(ArgumentError, 'Cannot join with another Taped') if other.respond_to?(:inserted)
|
|
157
|
+
raise(ArgumentError, 'Can only join with array') unless other.is_a?(Array)
|
|
158
|
+
Factbase::Taped.new(yield(@origin.to_a, other.to_a), inserted: @inserted, deleted: @deleted, added: @added)
|
|
165
159
|
end
|
|
166
160
|
end
|
data/lib/factbase/tee.rb
CHANGED
|
@@ -16,9 +16,9 @@ class Factbase::Tee
|
|
|
16
16
|
# @param [Factbase::Fact] fact Primary fact to use for reading
|
|
17
17
|
# @param [Factbase::Fact] upper Fact to access with a "$" prefix
|
|
18
18
|
def initialize(fact, upper)
|
|
19
|
-
raise 'Fact is nil' if fact.nil?
|
|
19
|
+
raise(ArgumentError, 'Fact is nil') if fact.nil?
|
|
20
20
|
@fact = fact
|
|
21
|
-
raise 'Upper is nil' if upper.nil?
|
|
21
|
+
raise(ArgumentError, 'Upper is nil') if upper.nil?
|
|
22
22
|
@upper = upper
|
|
23
23
|
end
|
|
24
24
|
|
data/lib/factbase/term.rb
CHANGED
|
@@ -7,60 +7,60 @@ require 'backtrace'
|
|
|
7
7
|
require_relative '../factbase'
|
|
8
8
|
require_relative 'fact'
|
|
9
9
|
require_relative 'tee'
|
|
10
|
-
require_relative 'terms/
|
|
11
|
-
require_relative 'terms/
|
|
10
|
+
require_relative 'terms/absent'
|
|
11
|
+
require_relative 'terms/agg'
|
|
12
|
+
require_relative 'terms/always'
|
|
13
|
+
require_relative 'terms/and'
|
|
14
|
+
require_relative 'terms/as'
|
|
15
|
+
require_relative 'terms/assert'
|
|
12
16
|
require_relative 'terms/concat'
|
|
13
|
-
require_relative 'terms/sprintf'
|
|
14
|
-
require_relative 'terms/matches'
|
|
15
17
|
require_relative 'terms/contains'
|
|
16
|
-
require_relative 'terms/
|
|
18
|
+
require_relative 'terms/count'
|
|
19
|
+
require_relative 'terms/defn'
|
|
20
|
+
require_relative 'terms/div'
|
|
21
|
+
require_relative 'terms/either'
|
|
22
|
+
require_relative 'terms/empty'
|
|
17
23
|
require_relative 'terms/ends_with'
|
|
18
|
-
require_relative 'terms/traced'
|
|
19
|
-
require_relative 'terms/assert'
|
|
20
24
|
require_relative 'terms/env'
|
|
21
|
-
require_relative 'terms/
|
|
22
|
-
require_relative 'terms/undef'
|
|
23
|
-
require_relative 'terms/as'
|
|
24
|
-
require_relative 'terms/join'
|
|
25
|
+
require_relative 'terms/eq'
|
|
25
26
|
require_relative 'terms/exists'
|
|
26
|
-
require_relative 'terms/
|
|
27
|
-
require_relative 'terms/
|
|
28
|
-
require_relative 'terms/
|
|
29
|
-
require_relative 'terms/nil'
|
|
30
|
-
require_relative 'terms/many'
|
|
31
|
-
require_relative 'terms/one'
|
|
32
|
-
require_relative 'terms/to_string'
|
|
33
|
-
require_relative 'terms/to_integer'
|
|
34
|
-
require_relative 'terms/to_float'
|
|
35
|
-
require_relative 'terms/to_time'
|
|
36
|
-
require_relative 'terms/sorted'
|
|
37
|
-
require_relative 'terms/inverted'
|
|
27
|
+
require_relative 'terms/first'
|
|
28
|
+
require_relative 'terms/gt'
|
|
29
|
+
require_relative 'terms/gte'
|
|
38
30
|
require_relative 'terms/head'
|
|
39
|
-
require_relative 'terms/
|
|
40
|
-
require_relative 'terms/
|
|
41
|
-
require_relative 'terms/times'
|
|
42
|
-
require_relative 'terms/div'
|
|
43
|
-
require_relative 'terms/zero'
|
|
44
|
-
require_relative 'terms/eq'
|
|
31
|
+
require_relative 'terms/inverted'
|
|
32
|
+
require_relative 'terms/join'
|
|
45
33
|
require_relative 'terms/lt'
|
|
46
34
|
require_relative 'terms/lte'
|
|
47
|
-
require_relative 'terms/
|
|
48
|
-
require_relative 'terms/
|
|
49
|
-
require_relative 'terms/
|
|
35
|
+
require_relative 'terms/many'
|
|
36
|
+
require_relative 'terms/matches'
|
|
37
|
+
require_relative 'terms/max'
|
|
38
|
+
require_relative 'terms/min'
|
|
39
|
+
require_relative 'terms/minus'
|
|
50
40
|
require_relative 'terms/never'
|
|
41
|
+
require_relative 'terms/nil'
|
|
51
42
|
require_relative 'terms/not'
|
|
52
|
-
require_relative 'terms/or'
|
|
53
|
-
require_relative 'terms/and'
|
|
54
|
-
require_relative 'terms/when'
|
|
55
|
-
require_relative 'terms/either'
|
|
56
|
-
require_relative 'terms/count'
|
|
57
|
-
require_relative 'terms/first'
|
|
58
43
|
require_relative 'terms/nth'
|
|
44
|
+
require_relative 'terms/one'
|
|
45
|
+
require_relative 'terms/or'
|
|
46
|
+
require_relative 'terms/plus'
|
|
47
|
+
require_relative 'terms/prev'
|
|
48
|
+
require_relative 'terms/size'
|
|
49
|
+
require_relative 'terms/sorted'
|
|
50
|
+
require_relative 'terms/sprintf'
|
|
51
|
+
require_relative 'terms/starts_with'
|
|
59
52
|
require_relative 'terms/sum'
|
|
60
|
-
require_relative 'terms/
|
|
61
|
-
require_relative 'terms/
|
|
62
|
-
require_relative 'terms/
|
|
63
|
-
require_relative 'terms/
|
|
53
|
+
require_relative 'terms/times'
|
|
54
|
+
require_relative 'terms/to_float'
|
|
55
|
+
require_relative 'terms/to_integer'
|
|
56
|
+
require_relative 'terms/to_string'
|
|
57
|
+
require_relative 'terms/to_time'
|
|
58
|
+
require_relative 'terms/traced'
|
|
59
|
+
require_relative 'terms/type'
|
|
60
|
+
require_relative 'terms/undef'
|
|
61
|
+
require_relative 'terms/unique'
|
|
62
|
+
require_relative 'terms/when'
|
|
63
|
+
require_relative 'terms/zero'
|
|
64
64
|
|
|
65
65
|
# Term.
|
|
66
66
|
#
|
|
@@ -90,13 +90,7 @@ require_relative 'terms/max'
|
|
|
90
90
|
# Copyright:: Copyright (c) 2024-2026 Yegor Bugayenko
|
|
91
91
|
# License:: MIT
|
|
92
92
|
class Factbase::Term < Factbase::TermBase
|
|
93
|
-
|
|
94
|
-
# @return [Symbol] The operator
|
|
95
|
-
attr_reader :op
|
|
96
|
-
|
|
97
|
-
# The operands of this term
|
|
98
|
-
# @return [Array] The operands
|
|
99
|
-
attr_reader :operands
|
|
93
|
+
attr_reader :op, :operands
|
|
100
94
|
|
|
101
95
|
# Ctor.
|
|
102
96
|
# @param [Symbol] operator Operator
|
|
@@ -167,9 +161,8 @@ class Factbase::Term < Factbase::TermBase
|
|
|
167
161
|
# @param [Module] type The type to extend with
|
|
168
162
|
# @param [Hash] args Attributes to set
|
|
169
163
|
def redress!(type, **args)
|
|
170
|
-
extend
|
|
171
|
-
|
|
172
|
-
args.each { |k, v| send(:instance_variable_set, :"@#{k}", v) }
|
|
164
|
+
extend(type)
|
|
165
|
+
args.each { |k, v| __send__(:instance_variable_set, :"@#{k}", v) }
|
|
173
166
|
@operands.map do |op|
|
|
174
167
|
if op.is_a?(Factbase::Term)
|
|
175
168
|
op.redress!(type, **args)
|
|
@@ -195,7 +188,7 @@ class Factbase::Term < Factbase::TermBase
|
|
|
195
188
|
maps
|
|
196
189
|
end
|
|
197
190
|
elsif respond_to?(m)
|
|
198
|
-
|
|
191
|
+
__send__(m, maps, fb, params)
|
|
199
192
|
else
|
|
200
193
|
maps
|
|
201
194
|
end
|
|
@@ -210,12 +203,12 @@ class Factbase::Term < Factbase::TermBase
|
|
|
210
203
|
if @terms.key?(@op)
|
|
211
204
|
@terms[@op].evaluate(fact, maps, fb)
|
|
212
205
|
else
|
|
213
|
-
|
|
206
|
+
__send__(@op, fact, maps, fb)
|
|
214
207
|
end
|
|
215
208
|
rescue NoMethodError => e
|
|
216
|
-
raise "Probably the term '#{@op}' is not defined at #{self}: #{e.message}"
|
|
209
|
+
raise(RuntimeError, "Probably the term '#{@op}' is not defined at #{self}: #{e.message}")
|
|
217
210
|
rescue StandardError => e
|
|
218
|
-
raise "#{e.message.inspect} at #{self} at #{e.backtrace[0]}"
|
|
211
|
+
raise(RuntimeError, "#{e.message.inspect} at #{self} at #{e.backtrace[0]}")
|
|
219
212
|
end
|
|
220
213
|
|
|
221
214
|
# Simplify it if possible.
|
|
@@ -226,7 +219,7 @@ class Factbase::Term < Factbase::TermBase
|
|
|
226
219
|
else
|
|
227
220
|
m = "#{@op}_simplify"
|
|
228
221
|
if respond_to?(m, true)
|
|
229
|
-
|
|
222
|
+
__send__(m)
|
|
230
223
|
else
|
|
231
224
|
self
|
|
232
225
|
end
|
|
@@ -262,11 +255,11 @@ class Factbase::Term < Factbase::TermBase
|
|
|
262
255
|
def at(fact, maps, fb)
|
|
263
256
|
assert_args(2)
|
|
264
257
|
i = _values(0, fact, maps, fb)
|
|
265
|
-
raise "Too many values (#{i.size}) at first position, one expected" unless i.size == 1
|
|
258
|
+
raise(RuntimeError, "Too many values (#{i.size}) at first position, one expected") unless i.size == 1
|
|
266
259
|
i = i[0]
|
|
267
|
-
return
|
|
260
|
+
return if i.nil?
|
|
268
261
|
v = _values(1, fact, maps, fb)
|
|
269
|
-
return
|
|
262
|
+
return if v.nil?
|
|
270
263
|
v[i]
|
|
271
264
|
end
|
|
272
265
|
end
|
data/lib/factbase/terms/agg.rb
CHANGED
|
@@ -23,13 +23,12 @@ class Factbase::Agg < Factbase::TermBase
|
|
|
23
23
|
assert_args(2)
|
|
24
24
|
selector = @operands[0]
|
|
25
25
|
unless selector.is_a?(Factbase::Term) || selector.is_a?(Factbase::TermBase)
|
|
26
|
-
raise "A term is expected, but '#{selector}' provided"
|
|
26
|
+
raise(ArgumentError, "A term is expected, but '#{selector}' provided")
|
|
27
27
|
end
|
|
28
28
|
term = @operands[1]
|
|
29
29
|
unless term.is_a?(Factbase::Term) || term.is_a?(Factbase::TermBase)
|
|
30
|
-
raise "A term is expected, but '#{term}' provided"
|
|
30
|
+
raise(ArgumentError, "A term is expected, but '#{term}' provided")
|
|
31
31
|
end
|
|
32
|
-
|
|
33
|
-
term.evaluate(nil, subset, fb)
|
|
32
|
+
term.evaluate(nil, fb.query(selector, maps).each(fb, fact).to_a, fb)
|
|
34
33
|
end
|
|
35
34
|
end
|
|
@@ -24,16 +24,16 @@ class Factbase::Arithmetic < Factbase::TermBase
|
|
|
24
24
|
def evaluate(fact, maps, fb)
|
|
25
25
|
assert_args(2)
|
|
26
26
|
lefts = _values(0, fact, maps, fb)
|
|
27
|
-
return
|
|
28
|
-
raise 'Too many values at first position, one expected' unless lefts.size == 1
|
|
27
|
+
return if lefts.nil?
|
|
28
|
+
raise(ArgumentError, 'Too many values at first position, one expected') unless lefts.size == 1
|
|
29
29
|
rights = _values(1, fact, maps, fb)
|
|
30
|
-
return
|
|
31
|
-
raise 'Too many values at second position, one expected' unless rights.size == 1
|
|
30
|
+
return if rights.nil?
|
|
31
|
+
raise(ArgumentError, 'Too many values at second position, one expected') unless rights.size == 1
|
|
32
32
|
v = lefts[0]
|
|
33
33
|
r = rights[0]
|
|
34
34
|
if v.is_a?(Time) && r.is_a?(String)
|
|
35
35
|
(num, units) = r.split
|
|
36
|
-
num = num
|
|
36
|
+
num = Integer(num, 10)
|
|
37
37
|
r =
|
|
38
38
|
case units
|
|
39
39
|
when 'seconds', 'second'
|
|
@@ -47,9 +47,9 @@ class Factbase::Arithmetic < Factbase::TermBase
|
|
|
47
47
|
when 'weeks', 'week'
|
|
48
48
|
num * 60 * 60 * 24 * 7
|
|
49
49
|
else
|
|
50
|
-
raise "Unknown time unit '#{units}' in '#{r}"
|
|
50
|
+
raise(ArgumentError, "Unknown time unit '#{units}' in '#{r}")
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
|
-
v.
|
|
53
|
+
v.__send__(@op, r)
|
|
54
54
|
end
|
|
55
55
|
end
|
data/lib/factbase/terms/as.rb
CHANGED
|
@@ -23,9 +23,9 @@ class Factbase::As < Factbase::TermBase
|
|
|
23
23
|
def evaluate(fact, maps, fb)
|
|
24
24
|
assert_args(2)
|
|
25
25
|
a = @operands[0]
|
|
26
|
-
raise "A symbol is expected as first argument of 'as'" unless a.is_a?(Symbol)
|
|
26
|
+
raise(ArgumentError, "A symbol is expected as first argument of 'as'") unless a.is_a?(Symbol)
|
|
27
27
|
vv = _values(1, fact, maps, fb)
|
|
28
|
-
vv&.each { |v| fact.
|
|
28
|
+
vv&.each { |v| fact.__send__(:"#{a}=", v) }
|
|
29
29
|
true
|
|
30
30
|
end
|
|
31
31
|
end
|
|
@@ -26,24 +26,16 @@ class Factbase::Assert < Factbase::TermBase
|
|
|
26
26
|
assert_args(2)
|
|
27
27
|
message = @operands[0]
|
|
28
28
|
unless message.is_a?(String)
|
|
29
|
-
raise
|
|
30
|
-
"A string is expected as first argument of 'assert', but '#{message}' provided"
|
|
29
|
+
raise(ArgumentError, "A string is expected as first argument of 'assert', but '#{message}' provided")
|
|
31
30
|
end
|
|
32
31
|
t = @operands[1]
|
|
33
32
|
unless t.is_a?(Factbase::Term)
|
|
34
|
-
raise
|
|
35
|
-
"A term is expected as second argument of 'assert', but '#{t}' provided"
|
|
33
|
+
raise(ArgumentError, "A term is expected as second argument of 'assert', but '#{t}' provided")
|
|
36
34
|
end
|
|
37
35
|
result = t.evaluate(fact, maps, fb)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if result.is_a?(Array)
|
|
42
|
-
result.any? { |v| v && v != 0 }
|
|
43
|
-
else
|
|
44
|
-
result && result != 0
|
|
45
|
-
end
|
|
46
|
-
raise message unless truthy
|
|
36
|
+
unless result.is_a?(Array) ? result.any? { |v| v && v != 0 } : (result && result != 0)
|
|
37
|
+
raise(StandardError, message)
|
|
38
|
+
end
|
|
47
39
|
true
|
|
48
40
|
end
|
|
49
41
|
end
|
data/lib/factbase/terms/base.rb
CHANGED
|
@@ -33,15 +33,14 @@ class Factbase::TermBase
|
|
|
33
33
|
|
|
34
34
|
def assert_args(num)
|
|
35
35
|
c = @operands.size
|
|
36
|
-
raise "Too many (#{c}) operands for '#{@op}' (#{num} expected)" if c > num
|
|
37
|
-
raise "Too few (#{c}) operands for '#{@op}' (#{num} expected)" if c < num
|
|
36
|
+
raise(ArgumentError, "Too many (#{c}) operands for '#{@op}' (#{num} expected)") if c > num
|
|
37
|
+
raise(ArgumentError, "Too few (#{c}) operands for '#{@op}' (#{num} expected)") if c < num
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def _by_symbol(pos, fact)
|
|
41
41
|
o = @operands[pos]
|
|
42
|
-
raise "A symbol expected at ##{pos}, but '#{o}' (#{o.class}) provided" unless o.is_a?(Symbol)
|
|
43
|
-
|
|
44
|
-
fact[k]
|
|
42
|
+
raise(ArgumentError, "A symbol expected at ##{pos}, but '#{o}' (#{o.class}) provided") unless o.is_a?(Symbol)
|
|
43
|
+
fact[o.to_s]
|
|
45
44
|
end
|
|
46
45
|
|
|
47
46
|
# @return [Array|nil] Either array of values or NIL
|
|
@@ -59,9 +58,9 @@ class Factbase::TermBase
|
|
|
59
58
|
[v]
|
|
60
59
|
end
|
|
61
60
|
end
|
|
62
|
-
raise 'Why not array?' unless v.is_a?(Array)
|
|
61
|
+
raise(ArgumentError, 'Why not array?') unless v.is_a?(Array)
|
|
63
62
|
unless v.all? { |i| [Float, Integer, String, Time, TrueClass, FalseClass].any? { |t| i.is_a?(t) } }
|
|
64
|
-
raise 'Wrong type inside'
|
|
63
|
+
raise(ArgumentError, 'Wrong type inside')
|
|
65
64
|
end
|
|
66
65
|
v
|
|
67
66
|
end
|
data/lib/factbase/terms/best.rb
CHANGED
|
@@ -10,7 +10,7 @@ class Factbase::Best
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def evaluate(key, maps)
|
|
13
|
-
raise "A symbol is expected, but #{key} provided" unless key.is_a?(Symbol)
|
|
13
|
+
raise(ArgumentError, "A symbol is expected, but #{key} provided") unless key.is_a?(Symbol)
|
|
14
14
|
best = nil
|
|
15
15
|
maps.each do |m|
|
|
16
16
|
vv = m[key.to_s]
|
|
@@ -23,6 +23,6 @@ class Factbase::Boolean
|
|
|
23
23
|
val = val[0] if val.respond_to?(:each)
|
|
24
24
|
return false if val.nil?
|
|
25
25
|
return val if val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
|
26
|
-
raise "Boolean is expected, while #{val.class} received from #{@from}"
|
|
26
|
+
raise(ArgumentError, "Boolean is expected, while #{val.class} received from #{@from}")
|
|
27
27
|
end
|
|
28
28
|
end
|
|
@@ -43,9 +43,10 @@ class Factbase::Compare < Factbase::TermBase
|
|
|
43
43
|
# @param [Object] right Right value
|
|
44
44
|
# @return [Boolean] The result of the comparison
|
|
45
45
|
def _compare(left, right)
|
|
46
|
-
left.
|
|
46
|
+
left.__send__(@op, right)
|
|
47
47
|
rescue ArgumentError => e
|
|
48
48
|
raise(
|
|
49
|
+
RuntimeError,
|
|
49
50
|
"Cannot compare #{left.inspect} (#{left.class}) " \
|
|
50
51
|
"with #{right.inspect} (#{right.class}) using (compare #{@op}): #{e.message}"
|
|
51
52
|
)
|
data/lib/factbase/terms/defn.rb
CHANGED
|
@@ -25,13 +25,15 @@ class Factbase::Defn < Factbase::TermBase
|
|
|
25
25
|
def evaluate(_fact, _maps, _fb)
|
|
26
26
|
assert_args(2)
|
|
27
27
|
fn = @operands[0]
|
|
28
|
-
raise "A symbol expected as first argument of 'defn'" unless fn.is_a?(Symbol)
|
|
29
|
-
raise "Can't use '#{fn}' name as a term" if Factbase::Term.method_defined?(fn)
|
|
30
|
-
raise "Term '#{fn}' is already defined" if Factbase::Term.private_method_defined?(fn, false)
|
|
31
|
-
raise "The '#{fn}' is a bad name for a term" unless fn.match?(/^[a-z_]+$/)
|
|
32
|
-
e = "class Factbase::Term\nprivate\ndef #{fn}(fact, maps, fb)\n#{@operands[1]}\nend\nend"
|
|
28
|
+
raise(ArgumentError, "A symbol expected as first argument of 'defn'") unless fn.is_a?(Symbol)
|
|
29
|
+
raise(ArgumentError, "Can't use '#{fn}' name as a term") if Factbase::Term.method_defined?(fn)
|
|
30
|
+
raise(ArgumentError, "Term '#{fn}' is already defined") if Factbase::Term.private_method_defined?(fn, false)
|
|
31
|
+
raise(ArgumentError, "The '#{fn}' is a bad name for a term") unless fn.match?(/^[a-z_]+$/)
|
|
33
32
|
# rubocop:disable Security/Eval
|
|
34
|
-
eval(
|
|
33
|
+
eval(
|
|
34
|
+
"class Factbase::Term\nprivate\ndef #{fn}(fact, maps, fb)\n#{@operands[1]}\nend\nend",
|
|
35
|
+
binding, __FILE__, __LINE__ - 1
|
|
36
|
+
)
|
|
35
37
|
# rubocop:enable Security/Eval
|
|
36
38
|
true
|
|
37
39
|
end
|
data/lib/factbase/terms/empty.rb
CHANGED
|
@@ -24,7 +24,7 @@ class Factbase::Empty < Factbase::TermBase
|
|
|
24
24
|
assert_args(1)
|
|
25
25
|
term = @operands[0]
|
|
26
26
|
unless term.is_a?(Factbase::Term) || term.is_a?(Factbase::TermBase)
|
|
27
|
-
raise "A term is expected, but '#{term}' provided"
|
|
27
|
+
raise(ArgumentError, "A term is expected, but '#{term}' provided")
|
|
28
28
|
end
|
|
29
29
|
# rubocop:disable Lint/UnreachableLoop
|
|
30
30
|
fb.query(term, maps).each(fb, fact) do
|
data/lib/factbase/terms/first.rb
CHANGED
|
@@ -22,9 +22,9 @@ class Factbase::First < Factbase::TermBase
|
|
|
22
22
|
def evaluate(_fact, maps, _fb)
|
|
23
23
|
assert_args(1)
|
|
24
24
|
k = @operands[0]
|
|
25
|
-
raise "A symbol is expected, but #{k} provided" unless k.is_a?(Symbol)
|
|
25
|
+
raise(ArgumentError, "A symbol is expected, but #{k} provided") unless k.is_a?(Symbol)
|
|
26
26
|
first = maps[0]
|
|
27
|
-
return
|
|
27
|
+
return if first.nil?
|
|
28
28
|
first[k.to_s]
|
|
29
29
|
end
|
|
30
30
|
end
|
data/lib/factbase/terms/head.rb
CHANGED
|
@@ -27,11 +27,11 @@ class Factbase::Head < Factbase::TermBase
|
|
|
27
27
|
def predict(maps, fb, params)
|
|
28
28
|
assert_args(2)
|
|
29
29
|
max = @operands[0]
|
|
30
|
-
raise "An integer is expected as first argument of '#{@op}'" unless max.is_a?(Integer)
|
|
30
|
+
raise(ArgumentError, "An integer is expected as first argument of '#{@op}'") unless max.is_a?(Integer)
|
|
31
31
|
term = @operands[1]
|
|
32
|
-
raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
|
|
32
|
+
raise(ArgumentError, "A term is expected, but '#{term}' provided") unless term.is_a?(Factbase::Term)
|
|
33
33
|
fb.query(term, maps).each(fb, params).to_a
|
|
34
34
|
.take(max)
|
|
35
|
-
.map { |m| m.all_properties.to_h { |k| [k, m[k]] } }
|
|
35
|
+
.map! { |m| m.all_properties.to_h { |k| [k, m[k]] } }
|
|
36
36
|
end
|
|
37
37
|
end
|
|
@@ -26,9 +26,9 @@ class Factbase::Inverted < Factbase::TermBase
|
|
|
26
26
|
def predict(maps, fb, params)
|
|
27
27
|
assert_args(1)
|
|
28
28
|
term = @operands[0]
|
|
29
|
-
raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
|
|
29
|
+
raise(ArgumentError, "A term is expected, but '#{term}' provided") unless term.is_a?(Factbase::Term)
|
|
30
30
|
fb.query(term, maps).each(fb, params).to_a
|
|
31
31
|
.reverse
|
|
32
|
-
.map { |m| m.all_properties.to_h { |k| [k, m[k]] } }
|
|
32
|
+
.map! { |m| m.all_properties.to_h { |k| [k, m[k]] } }
|
|
33
33
|
end
|
|
34
34
|
end
|
data/lib/factbase/terms/join.rb
CHANGED
|
@@ -23,18 +23,19 @@ class Factbase::Join < Factbase::TermBase
|
|
|
23
23
|
def evaluate(fact, maps, fb)
|
|
24
24
|
assert_args(2)
|
|
25
25
|
jumps = @operands[0]
|
|
26
|
-
raise "A string is expected as first argument of 'join'" unless jumps.is_a?(String)
|
|
27
|
-
jumps =
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
raise(ArgumentError, "A string is expected as first argument of 'join'") unless jumps.is_a?(String)
|
|
27
|
+
jumps =
|
|
28
|
+
jumps.split(',')
|
|
29
|
+
.map(&:strip)
|
|
30
|
+
.map! { |j| j.split('<=').map(&:strip) }
|
|
31
|
+
.map! { |j| j.size == 1 ? [j[0], j[0]] : j }
|
|
31
32
|
term = @operands[1]
|
|
32
|
-
raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
|
|
33
|
+
raise(ArgumentError, "A term is expected, but '#{term}' provided") unless term.is_a?(Factbase::Term)
|
|
33
34
|
subset = fb.query(term, maps).each(fb, fact).to_a
|
|
34
35
|
subset.each do |s|
|
|
35
36
|
jumps.each do |to, from|
|
|
36
37
|
s[from]&.each do |v|
|
|
37
|
-
fact.
|
|
38
|
+
fact.__send__(:"#{to}=", v)
|
|
38
39
|
end
|
|
39
40
|
end
|
|
40
41
|
end
|
|
@@ -26,10 +26,10 @@ class Factbase::Matches < Factbase::TermBase
|
|
|
26
26
|
assert_args(2)
|
|
27
27
|
str = _values(0, fact, maps, fb)
|
|
28
28
|
return false if str.nil?
|
|
29
|
-
raise 'Exactly one string is expected' unless str.size == 1
|
|
29
|
+
raise(RuntimeError, 'Exactly one string is expected') unless str.size == 1
|
|
30
30
|
re = _values(1, fact, maps, fb)
|
|
31
|
-
raise 'Regexp is nil' if re.nil?
|
|
32
|
-
raise 'Exactly one regexp is expected' unless re.size == 1
|
|
31
|
+
raise(RuntimeError, 'Regexp is nil') if re.nil?
|
|
32
|
+
raise(RuntimeError, 'Exactly one regexp is expected') unless re.size == 1
|
|
33
33
|
str[0].to_s.match?(regexp(re[0]))
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -39,6 +39,6 @@ class Factbase::Matches < Factbase::TermBase
|
|
|
39
39
|
key = pattern.to_s
|
|
40
40
|
@regexps[key] ||= Regexp.new(key)
|
|
41
41
|
rescue RegexpError => e
|
|
42
|
-
raise "Invalid regexp '#{key}': #{e.message}"
|
|
42
|
+
raise(RuntimeError, "Invalid regexp '#{key}': #{e.message}")
|
|
43
43
|
end
|
|
44
44
|
end
|