ruby-rego 0.1.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 +7 -0
- data/.reek.yml +80 -0
- data/.vscode/extensions.json +19 -0
- data/.vscode/launch.json +35 -0
- data/.vscode/settings.json +25 -0
- data/.vscode/tasks.json +117 -0
- data/.yardopts +12 -0
- data/ARCHITECTURE.md +39 -0
- data/CHANGELOG.md +25 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +183 -0
- data/RELEASING.md +37 -0
- data/Rakefile +38 -0
- data/SECURITY.md +26 -0
- data/Steepfile +10 -0
- data/TODO.md +35 -0
- data/benchmark/builtin_calls.rb +29 -0
- data/benchmark/complex_policy.rb +19 -0
- data/benchmark/comprehensions.rb +19 -0
- data/benchmark/simple_rules.rb +20 -0
- data/examples/README.md +27 -0
- data/examples/sample_config.yaml +2 -0
- data/examples/simple_policy.rego +7 -0
- data/examples/validation_policy.rego +11 -0
- data/exe/rego-validate +6 -0
- data/lib/ruby/rego/ast/base.rb +95 -0
- data/lib/ruby/rego/ast/binary_op.rb +64 -0
- data/lib/ruby/rego/ast/call.rb +27 -0
- data/lib/ruby/rego/ast/composite.rb +48 -0
- data/lib/ruby/rego/ast/comprehension.rb +63 -0
- data/lib/ruby/rego/ast/every.rb +37 -0
- data/lib/ruby/rego/ast/import.rb +32 -0
- data/lib/ruby/rego/ast/literal.rb +70 -0
- data/lib/ruby/rego/ast/module.rb +32 -0
- data/lib/ruby/rego/ast/package.rb +22 -0
- data/lib/ruby/rego/ast/query.rb +63 -0
- data/lib/ruby/rego/ast/reference.rb +58 -0
- data/lib/ruby/rego/ast/rule.rb +114 -0
- data/lib/ruby/rego/ast/unary_op.rb +42 -0
- data/lib/ruby/rego/ast/variable.rb +22 -0
- data/lib/ruby/rego/ast.rb +17 -0
- data/lib/ruby/rego/builtins/aggregates.rb +124 -0
- data/lib/ruby/rego/builtins/base.rb +95 -0
- data/lib/ruby/rego/builtins/collections/array_ops.rb +103 -0
- data/lib/ruby/rego/builtins/collections/object_ops.rb +120 -0
- data/lib/ruby/rego/builtins/collections/set_ops.rb +51 -0
- data/lib/ruby/rego/builtins/collections.rb +137 -0
- data/lib/ruby/rego/builtins/comparisons/casts.rb +139 -0
- data/lib/ruby/rego/builtins/comparisons.rb +84 -0
- data/lib/ruby/rego/builtins/numeric_helpers.rb +56 -0
- data/lib/ruby/rego/builtins/registry.rb +199 -0
- data/lib/ruby/rego/builtins/registry_helpers.rb +27 -0
- data/lib/ruby/rego/builtins/strings/case_ops.rb +22 -0
- data/lib/ruby/rego/builtins/strings/concat.rb +19 -0
- data/lib/ruby/rego/builtins/strings/formatting.rb +35 -0
- data/lib/ruby/rego/builtins/strings/helpers.rb +62 -0
- data/lib/ruby/rego/builtins/strings/number_helpers.rb +48 -0
- data/lib/ruby/rego/builtins/strings/search.rb +63 -0
- data/lib/ruby/rego/builtins/strings/split.rb +19 -0
- data/lib/ruby/rego/builtins/strings/substring.rb +22 -0
- data/lib/ruby/rego/builtins/strings/trim.rb +42 -0
- data/lib/ruby/rego/builtins/strings/trim_helpers.rb +62 -0
- data/lib/ruby/rego/builtins/strings.rb +58 -0
- data/lib/ruby/rego/builtins/types.rb +89 -0
- data/lib/ruby/rego/call_name.rb +55 -0
- data/lib/ruby/rego/cli.rb +1122 -0
- data/lib/ruby/rego/compiled_module.rb +114 -0
- data/lib/ruby/rego/compiler.rb +1097 -0
- data/lib/ruby/rego/environment/overrides.rb +33 -0
- data/lib/ruby/rego/environment/reference_resolution.rb +86 -0
- data/lib/ruby/rego/environment.rb +230 -0
- data/lib/ruby/rego/environment_pool.rb +71 -0
- data/lib/ruby/rego/error_handling.rb +58 -0
- data/lib/ruby/rego/error_payload.rb +34 -0
- data/lib/ruby/rego/errors.rb +196 -0
- data/lib/ruby/rego/evaluator/assignment_support.rb +126 -0
- data/lib/ruby/rego/evaluator/binding_helpers.rb +60 -0
- data/lib/ruby/rego/evaluator/comprehension_evaluator.rb +182 -0
- data/lib/ruby/rego/evaluator/expression_dispatch.rb +45 -0
- data/lib/ruby/rego/evaluator/expression_evaluator.rb +492 -0
- data/lib/ruby/rego/evaluator/object_literal_evaluator.rb +52 -0
- data/lib/ruby/rego/evaluator/operator_evaluator.rb +163 -0
- data/lib/ruby/rego/evaluator/query_node_builder.rb +38 -0
- data/lib/ruby/rego/evaluator/reference_key_resolver.rb +50 -0
- data/lib/ruby/rego/evaluator/reference_resolver.rb +352 -0
- data/lib/ruby/rego/evaluator/rule_evaluator/bindings.rb +70 -0
- data/lib/ruby/rego/evaluator/rule_evaluator.rb +550 -0
- data/lib/ruby/rego/evaluator/rule_value_provider.rb +56 -0
- data/lib/ruby/rego/evaluator/variable_collector.rb +221 -0
- data/lib/ruby/rego/evaluator.rb +174 -0
- data/lib/ruby/rego/lexer/number_reader.rb +68 -0
- data/lib/ruby/rego/lexer/stream.rb +137 -0
- data/lib/ruby/rego/lexer/string_reader.rb +90 -0
- data/lib/ruby/rego/lexer/template_string_reader.rb +62 -0
- data/lib/ruby/rego/lexer.rb +206 -0
- data/lib/ruby/rego/location.rb +73 -0
- data/lib/ruby/rego/memoization.rb +67 -0
- data/lib/ruby/rego/parser/collections.rb +173 -0
- data/lib/ruby/rego/parser/expressions.rb +216 -0
- data/lib/ruby/rego/parser/precedence.rb +42 -0
- data/lib/ruby/rego/parser/query.rb +139 -0
- data/lib/ruby/rego/parser/references.rb +115 -0
- data/lib/ruby/rego/parser/rules.rb +310 -0
- data/lib/ruby/rego/parser.rb +210 -0
- data/lib/ruby/rego/policy.rb +50 -0
- data/lib/ruby/rego/result.rb +91 -0
- data/lib/ruby/rego/token.rb +206 -0
- data/lib/ruby/rego/unifier.rb +451 -0
- data/lib/ruby/rego/value.rb +379 -0
- data/lib/ruby/rego/version.rb +7 -0
- data/lib/ruby/rego/with_modifiers/with_modifier.rb +37 -0
- data/lib/ruby/rego/with_modifiers/with_modifier_applier.rb +48 -0
- data/lib/ruby/rego/with_modifiers/with_modifier_builtin_override.rb +128 -0
- data/lib/ruby/rego/with_modifiers/with_modifier_context.rb +120 -0
- data/lib/ruby/rego/with_modifiers/with_modifier_path_key_resolver.rb +42 -0
- data/lib/ruby/rego/with_modifiers/with_modifier_path_override.rb +99 -0
- data/lib/ruby/rego/with_modifiers/with_modifier_root_scope.rb +58 -0
- data/lib/ruby/rego.rb +72 -0
- data/sig/objspace.rbs +4 -0
- data/sig/psych.rbs +7 -0
- data/sig/rego_validate.rbs +382 -0
- data/sig/ruby/rego.rbs +2150 -0
- metadata +172 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ruby
|
|
4
|
+
module Rego
|
|
5
|
+
class Evaluator
|
|
6
|
+
# Shared helpers for assignment/unification logic.
|
|
7
|
+
module AssignmentSupport
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# :reek:TooManyStatements
|
|
11
|
+
def evaluate_assignment(node)
|
|
12
|
+
pattern = node.left
|
|
13
|
+
value = evaluate(node.right)
|
|
14
|
+
binding_sets = unifier.unify(pattern, value, environment)
|
|
15
|
+
return UndefinedValue.new unless binding_sets.size == 1
|
|
16
|
+
|
|
17
|
+
apply_bindings(binding_sets.first)
|
|
18
|
+
value
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# :reek:TooManyStatements
|
|
22
|
+
def evaluate_unification(node)
|
|
23
|
+
binding_sets, resolved_value = unification_result(node, environment)
|
|
24
|
+
return UndefinedValue.new unless binding_sets.size == 1
|
|
25
|
+
|
|
26
|
+
apply_bindings(binding_sets.first)
|
|
27
|
+
resolved_value
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# :reek:TooManyStatements
|
|
31
|
+
def unification_binding_sets(node, env)
|
|
32
|
+
binding_sets, = unification_result(node, env)
|
|
33
|
+
binding_sets
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# :reek:TooManyStatements
|
|
37
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
38
|
+
def unification_result(node, env)
|
|
39
|
+
left_node = node.left
|
|
40
|
+
right_node = node.right
|
|
41
|
+
right_value = evaluate(right_node)
|
|
42
|
+
right_undefined = right_value.is_a?(UndefinedValue)
|
|
43
|
+
if right_undefined && left_node.is_a?(AST::Reference) && right_node.is_a?(AST::Variable)
|
|
44
|
+
bindings = bind_reference_variable(right_node, reference_bindings_for(left_node, env))
|
|
45
|
+
return [bindings, UndefinedValue.new]
|
|
46
|
+
end
|
|
47
|
+
# @type var binding_sets: Array[Hash[String, Value]]
|
|
48
|
+
binding_sets = []
|
|
49
|
+
binding_sets = unifier.unify(left_node, right_value, env) unless right_undefined
|
|
50
|
+
return [binding_sets, right_value] unless binding_sets.empty?
|
|
51
|
+
|
|
52
|
+
left_value = evaluate(left_node)
|
|
53
|
+
return [binding_sets, left_value] if left_value.is_a?(UndefinedValue)
|
|
54
|
+
|
|
55
|
+
[unifier.unify(right_node, left_value, env), left_value]
|
|
56
|
+
end
|
|
57
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
58
|
+
|
|
59
|
+
# :reek:TooManyStatements
|
|
60
|
+
# :reek:UtilityFunction
|
|
61
|
+
def reference_bindings_for(reference, env)
|
|
62
|
+
base_value = reference_base_override(reference)
|
|
63
|
+
unifier.reference_bindings(
|
|
64
|
+
reference,
|
|
65
|
+
env,
|
|
66
|
+
{},
|
|
67
|
+
base_value: base_value,
|
|
68
|
+
variable_resolver: method(:resolve_reference_variable_key)
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def reference_base_override(reference)
|
|
73
|
+
name = reference_base_name(reference)
|
|
74
|
+
return nil unless name
|
|
75
|
+
|
|
76
|
+
resolve_reference_base(name)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def reference_base_name(reference)
|
|
80
|
+
case reference.base
|
|
81
|
+
in AST::Variable[name:]
|
|
82
|
+
return nil unless unresolved_reference_base?(name)
|
|
83
|
+
|
|
84
|
+
name
|
|
85
|
+
else
|
|
86
|
+
nil
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def unresolved_reference_base?(name)
|
|
91
|
+
!environment.local_bound?(name) && environment.lookup(name).is_a?(UndefinedValue)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def resolve_reference_base(name)
|
|
95
|
+
reference_resolver.resolve_import_variable(name) ||
|
|
96
|
+
reference_resolver.resolve_rule_variable(name)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# :reek:TooManyStatements
|
|
100
|
+
# :reek:UtilityFunction
|
|
101
|
+
def bind_reference_variable(variable, reference_bindings)
|
|
102
|
+
reference_bindings.filter_map do |(bindings, value)|
|
|
103
|
+
next if value.is_a?(UndefinedValue)
|
|
104
|
+
|
|
105
|
+
name = variable.name
|
|
106
|
+
next bindings if name == "_"
|
|
107
|
+
|
|
108
|
+
existing = bindings[name]
|
|
109
|
+
next if existing && existing != value
|
|
110
|
+
|
|
111
|
+
additions = {} # @type var additions: Hash[String, Value]
|
|
112
|
+
additions[name] = value
|
|
113
|
+
bindings.merge(additions)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def apply_bindings(bindings)
|
|
118
|
+
bindings.each do |name, binding_value|
|
|
119
|
+
environment.bind(name, binding_value)
|
|
120
|
+
end
|
|
121
|
+
bindings
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ruby
|
|
4
|
+
module Rego
|
|
5
|
+
class Evaluator
|
|
6
|
+
# Shared helpers for collection binding iteration.
|
|
7
|
+
# :reek:DataClump
|
|
8
|
+
module BindingHelpers
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# :reek:NestedIterators
|
|
12
|
+
# :reek:TooManyStatements
|
|
13
|
+
def each_array_binding(variables, collection_value)
|
|
14
|
+
Enumerator.new do |yielder|
|
|
15
|
+
values = collection_value.to_ruby
|
|
16
|
+
case variables.length
|
|
17
|
+
when 1 then values.each { |value| yielder << bindings_for(variables[0], value) }
|
|
18
|
+
when 2 then values.each_with_index { |value, index| yielder << bindings_for_pair(variables, index, value) }
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# :reek:NestedIterators
|
|
24
|
+
def each_set_binding(variables, collection_value)
|
|
25
|
+
Enumerator.new do |yielder|
|
|
26
|
+
collection_value.to_ruby.each do |value|
|
|
27
|
+
yielder << bindings_for(variables[0], value)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# :reek:NestedIterators
|
|
33
|
+
# :reek:TooManyStatements
|
|
34
|
+
def each_object_binding(variables, collection_value)
|
|
35
|
+
Enumerator.new do |yielder|
|
|
36
|
+
pairs = collection_value.to_ruby
|
|
37
|
+
case variables.length
|
|
38
|
+
when 1 then pairs.each_key { |key| yielder << bindings_for(variables[0], key) }
|
|
39
|
+
when 2 then pairs.each { |key, value| yielder << bindings_for_pair(variables, key, value) }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def bindings_for_pair(variables, first_value, second_value)
|
|
45
|
+
bindings = bindings_for(variables[0], first_value)
|
|
46
|
+
bindings.merge!(bindings_for(variables[1], second_value))
|
|
47
|
+
bindings
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# :reek:UtilityFunction
|
|
51
|
+
def bindings_for(variable, value)
|
|
52
|
+
name = variable.name
|
|
53
|
+
return {} if name == "_"
|
|
54
|
+
|
|
55
|
+
{ name => Value.from_ruby(value) }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ruby
|
|
4
|
+
module Rego
|
|
5
|
+
class Evaluator
|
|
6
|
+
# Evaluates comprehensions in isolated scopes.
|
|
7
|
+
# :reek:TooManyMethods
|
|
8
|
+
# rubocop:disable Metrics/ClassLength
|
|
9
|
+
class ComprehensionEvaluator
|
|
10
|
+
# Tracks object keys for conflict detection.
|
|
11
|
+
class ObjectAccumulator
|
|
12
|
+
def initialize
|
|
13
|
+
@key_sources = {} # @type var @key_sources: Hash[Object, Object]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# :reek:TooManyStatements
|
|
17
|
+
def add(values, key, value)
|
|
18
|
+
normalized_key = key.is_a?(Symbol) ? key.to_s : key
|
|
19
|
+
if key_sources.key?(normalized_key)
|
|
20
|
+
existing = key_sources[normalized_key]
|
|
21
|
+
raise ObjectKeyConflictError, "Conflicting object keys: #{existing.inspect} and #{key.inspect}"
|
|
22
|
+
end
|
|
23
|
+
key_sources[normalized_key] = key
|
|
24
|
+
values[normalized_key] = value
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
attr_reader :key_sources
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private_constant :ObjectAccumulator
|
|
33
|
+
|
|
34
|
+
# Bundles object comprehension accumulation state.
|
|
35
|
+
ObjectPairContext = Struct.new(:result_values, :accumulator)
|
|
36
|
+
private_constant :ObjectPairContext
|
|
37
|
+
|
|
38
|
+
# @param expression_evaluator [ExpressionEvaluator]
|
|
39
|
+
# @param environment [Environment]
|
|
40
|
+
def initialize(expression_evaluator:, environment:)
|
|
41
|
+
@expression_evaluator = expression_evaluator
|
|
42
|
+
@environment = environment
|
|
43
|
+
@query_evaluator = nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @param query_evaluator [RuleEvaluator]
|
|
47
|
+
# @return [void]
|
|
48
|
+
def attach_query_evaluator(query_evaluator)
|
|
49
|
+
@query_evaluator = query_evaluator
|
|
50
|
+
nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param node [AST::ArrayComprehension]
|
|
54
|
+
# @return [Value]
|
|
55
|
+
def eval_array(node)
|
|
56
|
+
ArrayValue.new(collect_values(node))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @param node [AST::ObjectComprehension]
|
|
60
|
+
# @return [Value]
|
|
61
|
+
def eval_object(node)
|
|
62
|
+
ObjectValue.new(object_pairs(node))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @param node [AST::SetComprehension]
|
|
66
|
+
# @return [Value]
|
|
67
|
+
def eval_set(node)
|
|
68
|
+
SetValue.new(collect_values(node))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
attr_reader :expression_evaluator, :environment, :query_evaluator
|
|
74
|
+
|
|
75
|
+
def object_pairs(node)
|
|
76
|
+
values = {} # @type var values: Hash[Object, Value]
|
|
77
|
+
context = ObjectPairContext.new(values, ObjectAccumulator.new)
|
|
78
|
+
each_comprehension_binding(node.body) do |bindings|
|
|
79
|
+
apply_object_binding(context, node.term, bindings)
|
|
80
|
+
end
|
|
81
|
+
values
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def apply_object_binding(context, term, bindings)
|
|
85
|
+
environment.with_bindings(bindings) do
|
|
86
|
+
pair = resolve_pair(term)
|
|
87
|
+
return unless pair
|
|
88
|
+
|
|
89
|
+
key, value = pair
|
|
90
|
+
context.accumulator.add(context.result_values, key, value)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def resolve_pair(term)
|
|
95
|
+
key = evaluate_defined_key(term[0])
|
|
96
|
+
return nil if key.is_a?(Value) && key.undefined?
|
|
97
|
+
|
|
98
|
+
value = evaluate_defined_value(term[1])
|
|
99
|
+
return nil unless value
|
|
100
|
+
|
|
101
|
+
[key, value]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def each_comprehension_binding(body, &)
|
|
105
|
+
with_comprehension_scope(body) { comprehension_solutions(body).each(&) }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def collect_values(node)
|
|
109
|
+
values = [] # @type var values: Array[Value]
|
|
110
|
+
each_comprehension_binding(node.body) do |bindings|
|
|
111
|
+
append_value(values, node.term, bindings)
|
|
112
|
+
end
|
|
113
|
+
values
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def append_value(values, term, bindings)
|
|
117
|
+
environment.with_bindings(bindings) do
|
|
118
|
+
value = evaluate_defined_value(term)
|
|
119
|
+
values << value if value
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# :reek:FeatureEnvy
|
|
124
|
+
def evaluate_defined_key(node)
|
|
125
|
+
value = expression_evaluator.evaluate(node)
|
|
126
|
+
return UndefinedValue.new if value.is_a?(Value) && value.undefined?
|
|
127
|
+
|
|
128
|
+
value.object_key
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# :reek:FeatureEnvy
|
|
132
|
+
def evaluate_defined_value(node)
|
|
133
|
+
value = expression_evaluator.evaluate(node)
|
|
134
|
+
return nil if value.is_a?(Value) && value.undefined?
|
|
135
|
+
|
|
136
|
+
value
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def comprehension_solutions(body)
|
|
140
|
+
return query_evaluator.query_solutions(body, environment) if query_evaluator
|
|
141
|
+
|
|
142
|
+
raise EvaluationError.new("Query evaluator not configured", rule: nil, location: nil)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def with_comprehension_scope(body)
|
|
146
|
+
environment.push_scope
|
|
147
|
+
shadow_comprehension_locals(body)
|
|
148
|
+
yield
|
|
149
|
+
ensure
|
|
150
|
+
environment.pop_scope
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def shadow_comprehension_locals(body)
|
|
154
|
+
details = BoundVariableCollector.new.collect_details(body)
|
|
155
|
+
explicit = details[:explicit]
|
|
156
|
+
shadow_explicit_locals(explicit)
|
|
157
|
+
shadow_unification_locals(details[:unification], explicit)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def shadow_explicit_locals(names)
|
|
161
|
+
names.each { |name| bind_undefined(name) }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def shadow_unification_locals(names, explicit_names)
|
|
165
|
+
names.each do |name|
|
|
166
|
+
next if explicit_names.include?(name)
|
|
167
|
+
next unless environment.lookup(name).is_a?(UndefinedValue)
|
|
168
|
+
|
|
169
|
+
bind_undefined(name)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def bind_undefined(name)
|
|
174
|
+
return if Environment::RESERVED_NAMES.include?(name) || name == "_"
|
|
175
|
+
|
|
176
|
+
environment.bind(name, UndefinedValue.new)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
# rubocop:enable Metrics/ClassLength
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ruby
|
|
4
|
+
module Rego
|
|
5
|
+
class Evaluator
|
|
6
|
+
# Dispatches expression evaluation for primitive and AST nodes.
|
|
7
|
+
class ExpressionDispatch
|
|
8
|
+
# @param primitive_types [Array<Class>]
|
|
9
|
+
# @param node_evaluators [Array<Array<Class, Proc>>]
|
|
10
|
+
def initialize(primitive_types:, node_evaluators:)
|
|
11
|
+
@primitive_types = primitive_types
|
|
12
|
+
@node_evaluators = node_evaluators
|
|
13
|
+
@handler_cache = {} # @type var handler_cache: Hash[Class, Proc?]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @param node [Object]
|
|
17
|
+
# @return [Value, nil]
|
|
18
|
+
def primitive_value(node)
|
|
19
|
+
return Value.from_ruby(node) if primitive_types.any? { |klass| node.is_a?(klass) }
|
|
20
|
+
|
|
21
|
+
nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @param node [Object]
|
|
25
|
+
# @param evaluator [ExpressionEvaluator]
|
|
26
|
+
# @return [Value, nil]
|
|
27
|
+
def dispatch_node(node, evaluator)
|
|
28
|
+
handler = handler_for(node)
|
|
29
|
+
handler&.call(node, evaluator)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
attr_reader :primitive_types, :node_evaluators, :handler_cache
|
|
35
|
+
|
|
36
|
+
def handler_for(node)
|
|
37
|
+
node_class = node.class
|
|
38
|
+
return handler_cache[node_class] if handler_cache.key?(node_class)
|
|
39
|
+
|
|
40
|
+
handler_cache[node_class] = node_evaluators.find { |klass, _| node.is_a?(klass) }&.last
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|