sparkql 1.2.5 → 1.3.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.
- checksums.yaml +5 -13
- data/.rubocop.yml +111 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +16 -0
- data/Gemfile +1 -2
- data/Rakefile +2 -3
- data/VERSION +1 -1
- data/lib/sparkql/errors.rb +68 -71
- data/lib/sparkql/evaluator.rb +13 -9
- data/lib/sparkql/expression_resolver.rb +2 -3
- data/lib/sparkql/expression_state.rb +7 -9
- data/lib/sparkql/function_resolver.rb +777 -676
- data/lib/sparkql/geo/record_circle.rb +1 -1
- data/lib/sparkql/lexer.rb +54 -56
- data/lib/sparkql/parser.rb +35 -35
- data/lib/sparkql/parser_compatibility.rb +98 -77
- data/lib/sparkql/parser_tools.rb +159 -139
- data/lib/sparkql/token.rb +25 -25
- data/lib/sparkql/version.rb +1 -1
- data/sparkql.gemspec +20 -18
- data/test/unit/errors_test.rb +4 -5
- data/test/unit/evaluator_test.rb +15 -16
- data/test/unit/expression_state_test.rb +14 -15
- data/test/unit/function_resolver_test.rb +445 -203
- data/test/unit/geo/record_circle_test.rb +2 -2
- data/test/unit/lexer_test.rb +15 -16
- data/test/unit/parser_compatability_test.rb +177 -151
- data/test/unit/parser_test.rb +133 -99
- metadata +36 -35
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
NTEyZDlkMWVjY2RjZDY2ZTJlMTg3YjNjNTc4NmE2ZDc3ODdhNTAyYQ==
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5988efc1b20cbe3cd2baec8537c3d9331564962a1d502d2ceaf63d78f520d218
|
4
|
+
data.tar.gz: e1b20183b39463bf221eeddbcbdbb32a3d06457f2108c5c2ae6da471fdfdebf3
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
MmQ3MzMwNjBiYjIzMzE3Y2JiNzk2YzQxMzgyM2EzZDM1MTc4OTVkYmM5YTQw
|
11
|
-
YjQ3NDVjZWVmZjcyZDkxNzE3ZjI5NTFhODU4NWI5ZjZhYWU1YTA=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
MmE5ODM3ODdmYTY3MGMxN2I2Mjc2MmQ2NjQwNDM0ZTNiYzU4YTkxYjVkNWE3
|
14
|
-
YThkMmNlZDA3MjI0MWU0MDdkOTVmNzI4NDhkMmMzNTA5ZGYyNTM1ZGM4NDZm
|
15
|
-
M2M2ZjdmYTBlMTEwODUxN2ZkMDIyYmQyZDFjNjNmNGU5YTU2MGQ=
|
6
|
+
metadata.gz: 707c7f59f776ff70d9c4d164c4177a4800a81da80b2a0b4c7a99fdf7f1c7e632a67e155f11ead5c836fb48cc2ceec364adb8d359ecea6d714cc86f2bea2e0c58
|
7
|
+
data.tar.gz: e74cf6657fd5e9cd81dbd23cbdb07c0506ac84280d7651d1b07e11dc8656f56105bde0b6273e7d4fdfd83a91edaa1742e77a4ce6717ca94daf60e5124c34f313
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
AllCops:
|
2
|
+
NewCops: disable
|
3
|
+
Exclude:
|
4
|
+
- 'bin/*'
|
5
|
+
- 'config/**/*'
|
6
|
+
- 'Rakefile'
|
7
|
+
- 'Capfile'
|
8
|
+
- 'Gemfile'
|
9
|
+
- 'Guardfile'
|
10
|
+
- 'test/factories/*'
|
11
|
+
- 'test/support/*'
|
12
|
+
- 'config/routes.rb'
|
13
|
+
- 'script/*'
|
14
|
+
- 'db/**/*'
|
15
|
+
- 'vendor/**/*'
|
16
|
+
|
17
|
+
Style/FrozenStringLiteralComment:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
Style/Documentation:
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
Metrics/AbcSize:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Metrics/BlockLength:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Metrics/ClassLength:
|
30
|
+
Enabled: false
|
31
|
+
|
32
|
+
Metrics/CyclomaticComplexity:
|
33
|
+
Enabled: false
|
34
|
+
|
35
|
+
Layout/LineLength:
|
36
|
+
Enabled: false
|
37
|
+
|
38
|
+
Metrics/MethodLength:
|
39
|
+
Enabled: false
|
40
|
+
|
41
|
+
Metrics/ModuleLength:
|
42
|
+
Enabled: false
|
43
|
+
|
44
|
+
Metrics/PerceivedComplexity:
|
45
|
+
Enabled: false
|
46
|
+
|
47
|
+
# "Favor `unless` over `if` for negative conditions."
|
48
|
+
Style/NegatedIf:
|
49
|
+
Enabled: false
|
50
|
+
# safe_yaml seems to break all the things.
|
51
|
+
Security/YAMLLoad:
|
52
|
+
Enabled: false
|
53
|
+
|
54
|
+
# "Use a guard clause (`return unless extra_types.any?`) instead
|
55
|
+
# of wrapping the code inside a conditional expression."
|
56
|
+
#
|
57
|
+
# Justification: guard clauses don't work very well with long lines.
|
58
|
+
# Also, when there's an if check that (say) adds an error to a model
|
59
|
+
# validation, it makes more sense to wrap the operation in an if block
|
60
|
+
# than to guard the error entry with a double negative.
|
61
|
+
Style/GuardClause:
|
62
|
+
Enabled: false
|
63
|
+
|
64
|
+
# Justification:
|
65
|
+
#
|
66
|
+
# `class MyModule::ClassName` is a lot more concise, especially for tests
|
67
|
+
# covering a class that is within a module, than having to wrap the whole
|
68
|
+
# class in a module, and indent.
|
69
|
+
#
|
70
|
+
# "Use nested module/class definitions instead of compact style."
|
71
|
+
Style/ClassAndModuleChildren:
|
72
|
+
Enabled: false
|
73
|
+
|
74
|
+
# Justification:
|
75
|
+
#
|
76
|
+
# A single-line guard clause isn't always a good thing.
|
77
|
+
Style/IfUnlessModifier:
|
78
|
+
Enabled: false
|
79
|
+
|
80
|
+
# Justification:
|
81
|
+
#
|
82
|
+
# Hundreds of existing infractions, and it's not really that confusion to
|
83
|
+
# see regex without parens around it.
|
84
|
+
Lint/AmbiguousRegexpLiteral:
|
85
|
+
Enabled: false
|
86
|
+
|
87
|
+
# Justification:
|
88
|
+
#
|
89
|
+
# Is it so wrong to have a variable named fgo_listing_1, instead
|
90
|
+
# of fgo_listing1?
|
91
|
+
Naming/VariableNumber:
|
92
|
+
Enabled: false
|
93
|
+
|
94
|
+
# Justification:
|
95
|
+
#
|
96
|
+
# Explicit else's are much clearer than
|
97
|
+
# a branch that ends with an `elsif`, and presumes
|
98
|
+
# a nil else.
|
99
|
+
Style/EmptyElse:
|
100
|
+
Enabled: false
|
101
|
+
|
102
|
+
# Justification:
|
103
|
+
#
|
104
|
+
# We've generally prefered this, and honestly, I find
|
105
|
+
# this often makes readability much clearer to include
|
106
|
+
# it.
|
107
|
+
Style/RedundantSelf:
|
108
|
+
Enabled: false
|
109
|
+
|
110
|
+
Style/StringLiterals:
|
111
|
+
Enabled: false
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.5.8
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
v1.3.0, 2022-02-01
|
2
|
+
-------------------
|
3
|
+
* [BUGFIX] Redesign FunctionResolver to better support other timezones
|
4
|
+
|
5
|
+
v1.2.8, 2021-08-11
|
6
|
+
-------------------
|
7
|
+
* [IMPROVEMENT] all() function
|
8
|
+
|
9
|
+
v1.2.7, 2021-05-06
|
10
|
+
-------------------
|
11
|
+
* [IMPROVEMENT] dayofweek(), dayofyear(), and weekdays() functions
|
12
|
+
|
13
|
+
v1.2.6, 2019-04-01
|
14
|
+
-------------------
|
15
|
+
* [IMPROVEMENT] hours(), minutes(), and seconds() functions
|
16
|
+
|
1
17
|
v1.2.5, 2018-12-19
|
2
18
|
-------------------
|
3
19
|
* [BUGFIX] Correctly handle arithmetic grouping
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -15,7 +15,7 @@ rule '.rb' => '.y' do |t|
|
|
15
15
|
end
|
16
16
|
|
17
17
|
desc "Compile the racc parser from the grammar"
|
18
|
-
task :
|
18
|
+
task compile: ["lib/sparkql/parser.rb", "grammar"]
|
19
19
|
|
20
20
|
desc "Generate grammar Documenation"
|
21
21
|
task :grammar do
|
@@ -27,5 +27,4 @@ Rake::Task[:test].prerequisites.unshift "lib/sparkql/parser.rb"
|
|
27
27
|
Rake::Task[:test].prerequisites.unshift "grammar"
|
28
28
|
|
29
29
|
desc 'Default: run unit tests.'
|
30
|
-
task :
|
31
|
-
|
30
|
+
task default: :test
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.3.0
|
data/lib/sparkql/errors.rb
CHANGED
@@ -1,87 +1,84 @@
|
|
1
1
|
module Sparkql
|
2
|
+
class ErrorsProcessor
|
3
|
+
attr_accessor :errors
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
+
def initialize(errors = [])
|
6
|
+
@errors = Array(errors)
|
7
|
+
end
|
5
8
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
+
# true if the error stack contains at least one error
|
10
|
+
def errors?
|
11
|
+
@errors.size.positive?
|
12
|
+
end
|
9
13
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
+
# true if there is at least one error of status :status in the error stack
|
15
|
+
def errors_by_status?(status)
|
16
|
+
@errors.each do |error|
|
17
|
+
return true if status == error.status
|
18
|
+
end
|
19
|
+
false
|
20
|
+
end
|
14
21
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
return true if status == error.status
|
22
|
+
# true if there is at least one :fatal error in the error stack
|
23
|
+
def fatal_errors?
|
24
|
+
errors_by_status? :fatal
|
19
25
|
end
|
20
|
-
false
|
21
|
-
end
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
+
# true if there is at least one :dropped error in the error stack
|
28
|
+
def dropped_errors?
|
29
|
+
errors_by_status? :dropped
|
30
|
+
end
|
27
31
|
|
28
|
-
|
29
|
-
|
30
|
-
|
32
|
+
# true if there is at least one :recovered error in the error stack
|
33
|
+
def recovered_errors?
|
34
|
+
errors_by_status? :recovered
|
35
|
+
end
|
31
36
|
end
|
32
37
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
38
|
+
class ParserError
|
39
|
+
attr_accessor :token, :token_index, :expression, :message, :status, :recovered_as,
|
40
|
+
:sparkql, :nested_errors
|
41
|
+
attr_writer :syntax, :constraint
|
37
42
|
|
38
|
-
|
43
|
+
def initialize(error_hash = {})
|
44
|
+
@token = error_hash[:token]
|
45
|
+
@token_index = error_hash[:token_index]
|
46
|
+
@expression = error_hash[:expression]
|
47
|
+
@message = error_hash[:message]
|
48
|
+
@status = error_hash[:status]
|
49
|
+
@recovered_as = error_hash[:recovered_as]
|
50
|
+
@recovered_as = error_hash[:recovered_as]
|
51
|
+
@sparkql = error_hash[:sparkql]
|
52
|
+
@nested_errors = error_hash[:nested_errors]
|
53
|
+
self.syntax = error_hash[:syntax] != false
|
54
|
+
self.constraint = error_hash[:constraint] == true
|
55
|
+
end
|
39
56
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
attr_writer :syntax, :constraint
|
57
|
+
def syntax?
|
58
|
+
@syntax
|
59
|
+
end
|
44
60
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
@expression = error_hash[:expression]
|
49
|
-
@message = error_hash[:message]
|
50
|
-
@status = error_hash[:status]
|
51
|
-
@recovered_as = error_hash[:recovered_as]
|
52
|
-
@recovered_as = error_hash[:recovered_as]
|
53
|
-
@sparkql = error_hash[:sparkql]
|
54
|
-
@nested_errors = error_hash[:nested_errors]
|
55
|
-
self.syntax= error_hash[:syntax] == false ? false : true
|
56
|
-
self.constraint= error_hash[:constraint] == true
|
57
|
-
end
|
58
|
-
|
59
|
-
def syntax?
|
60
|
-
@syntax
|
61
|
-
end
|
62
|
-
|
63
|
-
def constraint?
|
64
|
-
@constraint
|
65
|
-
end
|
61
|
+
def constraint?
|
62
|
+
@constraint
|
63
|
+
end
|
66
64
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
65
|
+
def to_s
|
66
|
+
str = case @status
|
67
|
+
# Do nothing. Dropping the expressions isn't special
|
68
|
+
when :dropped then "Dropped: "
|
69
|
+
# Fatal errors cannot be recovered from, and should cause anaylisis or
|
70
|
+
# compilation to stop.
|
71
|
+
when :fatal then "Fatal: "
|
72
|
+
# Recovered errors are those that are syntatically
|
73
|
+
# or symantically incorrect, but are ones that we could "guess" at the
|
74
|
+
# intention
|
75
|
+
when :recovered
|
76
|
+
"Recovered as #{@recovered_as}: "
|
77
|
+
else ""
|
78
|
+
end
|
79
|
+
str += "<#{@token}> in " unless @token.nil?
|
80
|
+
str += "<#{@expression}>: #{@message}."
|
81
|
+
str
|
82
|
+
end
|
84
83
|
end
|
85
84
|
end
|
86
|
-
|
87
|
-
end
|
data/lib/sparkql/evaluator.rb
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
# fields. Plus, it has some optimizations built in to skip the processing for
|
5
5
|
# any expressions that don't contribute to the net result of the filter.
|
6
6
|
class Sparkql::Evaluator
|
7
|
-
|
8
7
|
# The struct here mimics some of the parser information about an expression,
|
9
8
|
# but should not be confused for an expression. Nodes reduce the expressions
|
10
9
|
# to a result based on conjunction logic, and only one exists per block group.
|
@@ -16,15 +15,17 @@ class Sparkql::Evaluator
|
|
16
15
|
:match,
|
17
16
|
:good_ors,
|
18
17
|
:expressions,
|
19
|
-
:unary
|
18
|
+
:unary
|
19
|
+
)
|
20
20
|
|
21
21
|
attr_reader :processed_count
|
22
22
|
|
23
|
-
def initialize
|
23
|
+
def initialize(expression_resolver)
|
24
24
|
@resolver = expression_resolver
|
25
25
|
end
|
26
26
|
|
27
27
|
def evaluate(expressions)
|
28
|
+
@dropped_expression = nil
|
28
29
|
@processed_count = 0
|
29
30
|
@index = Node.new(0, 0, "And", 0, true, false, 0, nil)
|
30
31
|
@groups = [@index]
|
@@ -33,10 +34,11 @@ class Sparkql::Evaluator
|
|
33
34
|
adjust_expression_for_dropped_field(expression)
|
34
35
|
check_for_good_ors(expression)
|
35
36
|
next if skip?(expression)
|
37
|
+
|
36
38
|
evaluate_expression(expression)
|
37
39
|
end
|
38
40
|
cleanup
|
39
|
-
|
41
|
+
@index[:match]
|
40
42
|
end
|
41
43
|
|
42
44
|
private
|
@@ -58,9 +60,10 @@ class Sparkql::Evaluator
|
|
58
60
|
# each block_group. This logic is re-used when merging the final result of one
|
59
61
|
# block group with the previous.
|
60
62
|
def evaluate_expression(expression)
|
61
|
-
|
63
|
+
@processed_count += 1
|
62
64
|
evaluate_node(expression, @resolver.resolve(expression))
|
63
65
|
end
|
66
|
+
|
64
67
|
def evaluate_node(node, result)
|
65
68
|
if result == :drop
|
66
69
|
@dropped_expression = node
|
@@ -73,7 +76,7 @@ class Sparkql::Evaluator
|
|
73
76
|
(node[:conjunction_level] == node[:level] ||
|
74
77
|
node[:conjunction_level] == @index[:level])
|
75
78
|
@index[:match] = !result if @index[:match]
|
76
|
-
elsif node[:conjunction] == 'And' || @index[:expressions]
|
79
|
+
elsif node[:conjunction] == 'And' || (@index[:expressions]).zero?
|
77
80
|
@index[:match] = result if @index[:match]
|
78
81
|
elsif node[:conjunction] == 'Or' && result
|
79
82
|
@index[:match] = result
|
@@ -97,7 +100,7 @@ class Sparkql::Evaluator
|
|
97
100
|
end
|
98
101
|
end
|
99
102
|
end
|
100
|
-
if !good_index.nil? && good_index[:expressions]
|
103
|
+
if !good_index.nil? && (good_index[:expressions]).positive? && good_index[:match]
|
101
104
|
good_index[:good_ors] = true
|
102
105
|
end
|
103
106
|
end
|
@@ -112,8 +115,8 @@ class Sparkql::Evaluator
|
|
112
115
|
|
113
116
|
def new_group(expression)
|
114
117
|
Node.new(expression[:level], expression[:block_group],
|
115
|
-
|
116
|
-
|
118
|
+
expression[:conjunction], expression[:conjunction_level],
|
119
|
+
true, false, 0, nil)
|
117
120
|
end
|
118
121
|
|
119
122
|
# When the last expression was dropped, we need to repair the filter by
|
@@ -125,6 +128,7 @@ class Sparkql::Evaluator
|
|
125
128
|
expression[:conjunction] = @dropped_expression[:conjunction]
|
126
129
|
expression[:conjunction_level] = @dropped_expression[:conjunction_level]
|
127
130
|
end
|
131
|
+
|
128
132
|
@dropped_expression = nil
|
129
133
|
end
|
130
134
|
|
@@ -1,17 +1,16 @@
|
|
1
1
|
# Base class for handling expression resolution
|
2
2
|
class Sparkql::ExpressionResolver
|
3
|
-
|
4
3
|
# Accepted results from the resolve method:
|
5
4
|
# * true and false reflect the expression's boolean result (as all expressions
|
6
5
|
# should).
|
7
6
|
# * :drop is a special symbol indicating that the expression should be omitted
|
8
7
|
# from the filter. Special rules apply for a dropped expression, such as
|
9
8
|
# keeping the conjunction of the dropped expression.
|
10
|
-
VALID_RESULTS = [true, false, :drop]
|
9
|
+
VALID_RESULTS = [true, false, :drop].freeze
|
11
10
|
|
12
11
|
# Evaluate the result of this expression. Allows for any of the values in
|
13
12
|
# VALID_RESULTS
|
14
|
-
def resolve(
|
13
|
+
def resolve(_expression)
|
15
14
|
true
|
16
15
|
end
|
17
16
|
end
|
@@ -1,23 +1,21 @@
|
|
1
|
-
# Custom fields need to add a table join to the customfieldsearch table when AND'd together,
|
1
|
+
# Custom fields need to add a table join to the customfieldsearch table when AND'd together,
|
2
2
|
# but not when they are OR'd or nested. This class maintains the state for all custom field expressions
|
3
3
|
# lets the parser know when to do either.
|
4
4
|
class Sparkql::ExpressionState
|
5
|
-
|
6
5
|
def initialize
|
7
|
-
@expressions = {0=>[]}
|
6
|
+
@expressions = { 0 => [] }
|
8
7
|
@last_conjunction = "And" # always start with a join
|
9
8
|
@block_group = 0
|
10
9
|
end
|
11
|
-
|
10
|
+
|
12
11
|
def push(expression)
|
13
12
|
@block_group = expression[:block_group]
|
14
|
-
@expressions[@block_group] ||= []
|
13
|
+
@expressions[@block_group] ||= []
|
15
14
|
@expressions[@block_group] << expression
|
16
15
|
@last_conjunction = expression[:conjunction]
|
17
16
|
end
|
18
|
-
|
17
|
+
|
19
18
|
def needs_join?
|
20
|
-
|
19
|
+
@expressions[@block_group].size == 1 || %w[Not And].include?(@last_conjunction)
|
21
20
|
end
|
22
|
-
|
23
|
-
end
|
21
|
+
end
|