rox-rollout 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +45 -0
- data/.gitignore +8 -0
- data/Gemfile +6 -0
- data/LICENSE +50 -0
- data/README_DEVELOP.md +19 -0
- data/Rakefile +16 -0
- data/_archive/.document +5 -0
- data/_archive/.rspec +1 -0
- data/_archive/Gemfile +15 -0
- data/_archive/Gemfile.lock +87 -0
- data/_archive/README.md +32 -0
- data/_archive/README.rdoc +19 -0
- data/_archive/Rakefile +50 -0
- data/_archive/lib/expr_function_definition.rb +52 -0
- data/_archive/lib/function_definition.rb +48 -0
- data/_archive/lib/function_token.rb +12 -0
- data/_archive/lib/object_extends.rb +12 -0
- data/_archive/lib/ruby_interpreter.rb +292 -0
- data/_archive/lib/stack.rb +48 -0
- data/_archive/lib/string_extends.rb +14 -0
- data/_archive/spec/ruby_interpreter_spec.rb +203 -0
- data/_archive/spec/spec_helper.rb +30 -0
- data/_archive/spec/stack_spec.rb +77 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/e2e/container.rb +38 -0
- data/e2e/custom_props.rb +55 -0
- data/e2e/rox_e2e_test.rb +159 -0
- data/e2e/test_vars.rb +24 -0
- data/lib/rox.rb +5 -0
- data/lib/rox/core/client/buid.rb +82 -0
- data/lib/rox/core/client/device_properties.rb +45 -0
- data/lib/rox/core/client/internal_flags.rb +20 -0
- data/lib/rox/core/client/sdk_settings.rb +5 -0
- data/lib/rox/core/configuration/configuration.rb +5 -0
- data/lib/rox/core/configuration/configuration_fetched_args.rb +23 -0
- data/lib/rox/core/configuration/configuration_fetched_invoker.rb +37 -0
- data/lib/rox/core/configuration/configuration_parser.rb +85 -0
- data/lib/rox/core/configuration/fetcher_error.rb +13 -0
- data/lib/rox/core/configuration/fetcher_status.rb +10 -0
- data/lib/rox/core/configuration/models/experiment_model.rb +5 -0
- data/lib/rox/core/configuration/models/target_group_model.rb +5 -0
- data/lib/rox/core/consts/build.rb +8 -0
- data/lib/rox/core/consts/environment.rb +42 -0
- data/lib/rox/core/consts/property_type.rb +29 -0
- data/lib/rox/core/context/merged_context.rb +16 -0
- data/lib/rox/core/core.rb +131 -0
- data/lib/rox/core/entities/flag.rb +26 -0
- data/lib/rox/core/entities/flag_setter.rb +39 -0
- data/lib/rox/core/entities/variant.rb +56 -0
- data/lib/rox/core/impression/impression_args.rb +5 -0
- data/lib/rox/core/impression/impression_invoker.rb +41 -0
- data/lib/rox/core/impression/models/experiment.rb +14 -0
- data/lib/rox/core/impression/models/reporting_value.rb +5 -0
- data/lib/rox/core/logging/logging.rb +17 -0
- data/lib/rox/core/logging/no_op_logger.rb +11 -0
- data/lib/rox/core/network/configuration_fetch_result.rb +5 -0
- data/lib/rox/core/network/configuration_fetcher.rb +38 -0
- data/lib/rox/core/network/configuration_fetcher_base.rb +25 -0
- data/lib/rox/core/network/configuration_fetcher_roxy.rb +29 -0
- data/lib/rox/core/network/configuration_source.rb +9 -0
- data/lib/rox/core/network/request.rb +46 -0
- data/lib/rox/core/network/request_configuration_builder.rb +48 -0
- data/lib/rox/core/network/request_data.rb +5 -0
- data/lib/rox/core/network/response.rb +16 -0
- data/lib/rox/core/properties/custom_property.rb +18 -0
- data/lib/rox/core/properties/custom_property_type.rb +18 -0
- data/lib/rox/core/properties/device_property.rb +11 -0
- data/lib/rox/core/register/registerer.rb +35 -0
- data/lib/rox/core/reporting/error_reporter.rb +152 -0
- data/lib/rox/core/repositories/custom_property_repository.rb +61 -0
- data/lib/rox/core/repositories/experiment_repository.rb +21 -0
- data/lib/rox/core/repositories/flag_repository.rb +49 -0
- data/lib/rox/core/repositories/roxx/experiments_extensions.rb +82 -0
- data/lib/rox/core/repositories/roxx/properties_extensions.rb +26 -0
- data/lib/rox/core/repositories/target_group_repository.rb +17 -0
- data/lib/rox/core/roxx/core_stack.rb +22 -0
- data/lib/rox/core/roxx/evaluation_result.rb +28 -0
- data/lib/rox/core/roxx/node.rb +11 -0
- data/lib/rox/core/roxx/parser.rb +143 -0
- data/lib/rox/core/roxx/regular_expression_extensions.rb +33 -0
- data/lib/rox/core/roxx/string_tokenizer.rb +68 -0
- data/lib/rox/core/roxx/symbols.rb +14 -0
- data/lib/rox/core/roxx/token_type.rb +30 -0
- data/lib/rox/core/roxx/tokenized_expression.rb +119 -0
- data/lib/rox/core/roxx/value_compare_extensions.rb +137 -0
- data/lib/rox/core/security/signature_verifier.rb +18 -0
- data/lib/rox/core/utils/periodic_task.rb +12 -0
- data/lib/rox/core/utils/type_utils.rb +9 -0
- data/lib/rox/server/client/sdk_settings.rb +5 -0
- data/lib/rox/server/client/server_properties.rb +20 -0
- data/lib/rox/server/flags/rox_flag.rb +19 -0
- data/lib/rox/server/flags/rox_variant.rb +8 -0
- data/lib/rox/server/logging/server_logger.rb +35 -0
- data/lib/rox/server/rox_options.rb +27 -0
- data/lib/rox/server/rox_server.rb +83 -0
- data/lib/rox/version.rb +3 -0
- data/rox.gemspec +29 -0
- metadata +184 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rox/core/roxx/token_type'
|
2
|
+
|
3
|
+
module Rox
|
4
|
+
module Core
|
5
|
+
class PropertiesExtensions
|
6
|
+
def initialize(parser, properties_repository)
|
7
|
+
@parser = parser
|
8
|
+
@properties_repository = properties_repository
|
9
|
+
end
|
10
|
+
|
11
|
+
def extend
|
12
|
+
@parser.add_operator('property') do |parser, stack, context|
|
13
|
+
prop_name = stack.pop.to_s
|
14
|
+
property = @properties_repository.custom_property(prop_name)
|
15
|
+
|
16
|
+
if property.nil?
|
17
|
+
stack.push(TokenType::UNDEFINED)
|
18
|
+
else
|
19
|
+
value = property.value(context)
|
20
|
+
stack.push(value.nil? ? TokenType::UNDEFINED : value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Rox
|
2
|
+
module Core
|
3
|
+
class TargetGroupRepository
|
4
|
+
def initialize
|
5
|
+
@target_groups = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def target_groups=(target_groups)
|
9
|
+
@target_groups = target_groups
|
10
|
+
end
|
11
|
+
|
12
|
+
def target_group(id)
|
13
|
+
@target_groups.detect { |g| g.id == id }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rox/core/entities/flag'
|
2
|
+
require 'rox/core/utils/type_utils'
|
3
|
+
|
4
|
+
module Rox
|
5
|
+
module Core
|
6
|
+
class EvaluationResult
|
7
|
+
attr_reader :value
|
8
|
+
|
9
|
+
def initialize(value)
|
10
|
+
@value = value
|
11
|
+
end
|
12
|
+
|
13
|
+
def bool_value
|
14
|
+
return false if value.nil?
|
15
|
+
return value if Utils.boolean?(value)
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def string_value
|
20
|
+
return value if value.is_a? String
|
21
|
+
return nil if value.nil?
|
22
|
+
return Flag::FLAG_TRUE_VALUE if value
|
23
|
+
return Flag::FLAG_FALSE_VALUE unless value
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'rox/core/roxx/core_stack'
|
3
|
+
require 'rox/core/roxx/evaluation_result'
|
4
|
+
require 'rox/core/roxx/value_compare_extensions'
|
5
|
+
require 'rox/core/roxx/regular_expression_extensions'
|
6
|
+
require 'rox/core/roxx/tokenized_expression'
|
7
|
+
require 'rox/core/utils/type_utils'
|
8
|
+
require 'rox/core/logging/logging'
|
9
|
+
|
10
|
+
module Rox
|
11
|
+
module Core
|
12
|
+
class Parser
|
13
|
+
def initialize
|
14
|
+
@operators_map = {}
|
15
|
+
set_basic_operators
|
16
|
+
ValueCompareExtensions.new(self).extend
|
17
|
+
RegularExpressionExtensions.new(self).extend
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_operator(oper, &block)
|
21
|
+
@operators_map[oper] = block
|
22
|
+
end
|
23
|
+
|
24
|
+
def evaluate_expression(expression, context = nil)
|
25
|
+
stack = CoreStack.new
|
26
|
+
tokens = TokenizedExpression.new(expression, @operators_map.keys).tokens
|
27
|
+
reverse_tokens = tokens.reverse
|
28
|
+
begin
|
29
|
+
reverse_tokens.each do |token|
|
30
|
+
node = token
|
31
|
+
if node.type == NodeTypes::RAND
|
32
|
+
stack.push(node.value)
|
33
|
+
elsif node.type == NodeTypes::RATOR
|
34
|
+
handler = @operators_map[node.value]
|
35
|
+
handler.call(self, stack, context) unless handler.nil?
|
36
|
+
else
|
37
|
+
return EvaluationResult.new(nil)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
result = stack.pop
|
42
|
+
result = nil if result == TokenType::UNDEFINED
|
43
|
+
|
44
|
+
EvaluationResult.new(result)
|
45
|
+
rescue StandardError => ex
|
46
|
+
Logging.logger.warn("Roxx Exception: Failed evaluate expression: #{ex}")
|
47
|
+
EvaluationResult.new(nil)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_basic_operators
|
52
|
+
add_operator('isUndefined') do |parser, stack, context|
|
53
|
+
op1 = stack.pop
|
54
|
+
if op1.is_a?(TokenType)
|
55
|
+
stack.push(op1 == TokenType::UNDEFINED)
|
56
|
+
else
|
57
|
+
stack.push(false)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
add_operator('now') do |parser, stack, context|
|
62
|
+
stack.push((Time.now.to_f * 1000).to_i)
|
63
|
+
end
|
64
|
+
|
65
|
+
add_operator('and') do |parser, stack, context|
|
66
|
+
op1 = stack.pop
|
67
|
+
op2 = stack.pop
|
68
|
+
op1 = false if op1 == TokenType::UNDEFINED
|
69
|
+
op2 = false if op2 == TokenType::UNDEFINED
|
70
|
+
raise ArgumentError, 'should be boolean' unless Utils.boolean?(op1) && Utils.boolean?(op2)
|
71
|
+
stack.push(op1 && op2)
|
72
|
+
end
|
73
|
+
|
74
|
+
add_operator('or') do |parser, stack, context|
|
75
|
+
op1 = stack.pop
|
76
|
+
op2 = stack.pop
|
77
|
+
op1 = false if op1 == TokenType::UNDEFINED
|
78
|
+
op2 = false if op2 == TokenType::UNDEFINED
|
79
|
+
raise ArgumentError, 'should be boolean' unless Utils.boolean?(op1) && Utils.boolean?(op2)
|
80
|
+
stack.push(op1 || op2)
|
81
|
+
end
|
82
|
+
|
83
|
+
add_operator('ne') do |parser, stack, context|
|
84
|
+
op1 = stack.pop
|
85
|
+
op2 = stack.pop
|
86
|
+
op1 = false if op1 == TokenType::UNDEFINED
|
87
|
+
op2 = false if op2 == TokenType::UNDEFINED
|
88
|
+
stack.push(op1 != op2)
|
89
|
+
end
|
90
|
+
|
91
|
+
add_operator('eq') do |parser, stack, context|
|
92
|
+
op1 = stack.pop
|
93
|
+
op2 = stack.pop
|
94
|
+
op1 = false if op1 == TokenType::UNDEFINED
|
95
|
+
op2 = false if op2 == TokenType::UNDEFINED
|
96
|
+
stack.push(op1 == op2)
|
97
|
+
end
|
98
|
+
|
99
|
+
add_operator('not') do |parser, stack, context|
|
100
|
+
op1 = stack.pop
|
101
|
+
op1 = false if op1 == TokenType::UNDEFINED
|
102
|
+
raise ArgumentError, 'should be boolean' unless Utils.boolean?(op1)
|
103
|
+
stack.push(!op1)
|
104
|
+
end
|
105
|
+
|
106
|
+
add_operator('ifThen') do |parser, stack, context|
|
107
|
+
condition_expression = stack.pop
|
108
|
+
true_expression = stack.pop
|
109
|
+
false_expression = stack.pop
|
110
|
+
raise ArgumentError, 'should be boolean' unless Utils.boolean?(condition_expression)
|
111
|
+
if condition_expression
|
112
|
+
stack.push(true_expression)
|
113
|
+
else
|
114
|
+
stack.push(false_expression)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
add_operator('inArray') do |parser, stack, context|
|
119
|
+
op1 = stack.pop
|
120
|
+
op2 = stack.pop
|
121
|
+
if op2.is_a?(Array)
|
122
|
+
stack.push(op2.include?(op1))
|
123
|
+
else
|
124
|
+
stack.push(false)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
add_operator('md5') do |parser, stack, context|
|
129
|
+
op1 = stack.pop
|
130
|
+
raise ArgumentError, 'should be string' unless op1.is_a?(String)
|
131
|
+
stack.push(Digest::MD5.hexdigest(op1))
|
132
|
+
end
|
133
|
+
|
134
|
+
add_operator('concat') do |parser, stack, context|
|
135
|
+
op1 = stack.pop
|
136
|
+
op2 = stack.pop
|
137
|
+
raise ArgumentError, 'should be string' unless op1.is_a?(String) && op2.is_a?(String)
|
138
|
+
stack.push("#{op1}#{op2}")
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Rox
|
2
|
+
module Core
|
3
|
+
class RegularExpressionExtensions
|
4
|
+
def initialize(parser)
|
5
|
+
@parser = parser
|
6
|
+
end
|
7
|
+
|
8
|
+
def extend
|
9
|
+
@parser.add_operator('match') do |parser, stack, context|
|
10
|
+
text = stack.pop
|
11
|
+
pattern = stack.pop
|
12
|
+
flags = stack.pop
|
13
|
+
raise ArgumentError, 'should be string' unless text.is_a?(String) && pattern.is_a?(String) && flags.is_a?(String)
|
14
|
+
|
15
|
+
options = 0
|
16
|
+
flags.each_char do |flag|
|
17
|
+
case flag
|
18
|
+
when 'i'
|
19
|
+
options |= Regexp::IGNORECASE
|
20
|
+
when 'x'
|
21
|
+
options |= Regexp::EXTENDED
|
22
|
+
when 'm'
|
23
|
+
options |= Regexp::MULTILINE
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
matched = !Regexp.new(pattern, options).match(text).nil?
|
28
|
+
stack.push(matched)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Rox
|
2
|
+
module Core
|
3
|
+
class StringTokenizer
|
4
|
+
def initialize(text, delim, return_delims)
|
5
|
+
@current_position = 0
|
6
|
+
@new_position = -1
|
7
|
+
@text = text
|
8
|
+
@max_position = text.length
|
9
|
+
@delimiters = delim
|
10
|
+
@ret_delims = return_delims
|
11
|
+
end
|
12
|
+
|
13
|
+
def skip_delimiters(start_pos)
|
14
|
+
raise ArgumentError, 'delimiters is nil' if @delimiters.nil?
|
15
|
+
|
16
|
+
position = start_pos
|
17
|
+
while !@ret_delims && position < @max_position
|
18
|
+
c = @text[position]
|
19
|
+
break unless delimiter?(c)
|
20
|
+
position += 1
|
21
|
+
end
|
22
|
+
position
|
23
|
+
end
|
24
|
+
|
25
|
+
def scan_token(start_pos)
|
26
|
+
position = start_pos
|
27
|
+
while position < @max_position
|
28
|
+
c = @text[position]
|
29
|
+
break if delimiter?(c)
|
30
|
+
position += 1
|
31
|
+
end
|
32
|
+
|
33
|
+
if @ret_delims && start_pos == position
|
34
|
+
c = @text[position]
|
35
|
+
position += 1 if delimiter?(c)
|
36
|
+
end
|
37
|
+
|
38
|
+
position
|
39
|
+
end
|
40
|
+
|
41
|
+
def delimiter?(char)
|
42
|
+
@delimiters.include?(char)
|
43
|
+
end
|
44
|
+
|
45
|
+
def more_tokens?
|
46
|
+
@new_position = skip_delimiters(@current_position)
|
47
|
+
@new_position < @max_position
|
48
|
+
end
|
49
|
+
|
50
|
+
def next_token(delim = nil)
|
51
|
+
delims_changed = false
|
52
|
+
unless delim.nil?
|
53
|
+
@delimiters = delim
|
54
|
+
delims_changed = true
|
55
|
+
end
|
56
|
+
|
57
|
+
@current_position = @new_position >= 0 && !delims_changed ? @new_position : skip_delimiters(@current_position)
|
58
|
+
@new_position = -1
|
59
|
+
|
60
|
+
raise ArgumentError, 'invalid operation' if @current_position >= @max_position
|
61
|
+
|
62
|
+
start = @current_position
|
63
|
+
@current_position = scan_token(@current_position)
|
64
|
+
@text[start...@current_position]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Rox
|
2
|
+
module Core
|
3
|
+
module Symbols
|
4
|
+
ROXX_UNDEFINED = 'undefined'.freeze
|
5
|
+
ROXX_TRUE = 'true'.freeze
|
6
|
+
ROXX_FALSE = 'false'.freeze
|
7
|
+
ROXX_EMPTY_STRING = '""'.freeze
|
8
|
+
ROXX_STRING_TYPE = 'StringType'.freeze
|
9
|
+
ROXX_BOOL_TYPE = 'BooleanType'.freeze
|
10
|
+
ROXX_NUMBER_TYPE = 'NumberType'.freeze
|
11
|
+
ROXX_UNDEFINED_TYPE = 'UndefinedType'.freeze
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rox/core/roxx/symbols'
|
2
|
+
|
3
|
+
module Rox
|
4
|
+
module Core
|
5
|
+
class TokenType
|
6
|
+
attr_accessor :text, :pattern
|
7
|
+
|
8
|
+
def self.from_token(token)
|
9
|
+
unless token.nil?
|
10
|
+
tested_token = token.downcase
|
11
|
+
[TokenType::STRING, TokenType::NUMBER, TokenType::BOOLEAN, TokenType::UNDEFINED].each do |token_type|
|
12
|
+
return token_type unless token_type.pattern.match(tested_token).nil?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
TokenType::NOT_A_TYPE
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(text, pattern)
|
19
|
+
@text = text
|
20
|
+
@pattern = Regexp.new(pattern)
|
21
|
+
end
|
22
|
+
|
23
|
+
NOT_A_TYPE = TokenType.new('NOT_A_TYPE', '')
|
24
|
+
STRING = TokenType.new(Symbols::ROXX_STRING_TYPE, '"((\\\\.)|[^\\\\"])*"')
|
25
|
+
NUMBER = TokenType.new(Symbols::ROXX_NUMBER_TYPE, '[\\-]{0,1}\\d+[\\.]\\d+|[\\-]{0,1}\\d+')
|
26
|
+
BOOLEAN = TokenType.new(Symbols::ROXX_BOOL_TYPE, "#{Symbols::ROXX_TRUE}|#{Symbols::ROXX_FALSE}")
|
27
|
+
UNDEFINED = TokenType.new(Symbols::ROXX_UNDEFINED_TYPE, Symbols::ROXX_UNDEFINED)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'rox/core/roxx/string_tokenizer'
|
2
|
+
require 'rox/core/roxx/node'
|
3
|
+
require 'rox/core/roxx/symbols'
|
4
|
+
require 'rox/core/roxx/token_type'
|
5
|
+
|
6
|
+
module Rox
|
7
|
+
module Core
|
8
|
+
class TokenizedExpression
|
9
|
+
DICT_START_DELIMITER = '{'.freeze
|
10
|
+
DICT_END_DELIMITER = '}'.freeze
|
11
|
+
ARRAY_START_DELIMITER = '['.freeze
|
12
|
+
ARRAY_END_DELIMITER = ']'.freeze
|
13
|
+
TOKEN_DELIMITERS = "{}[]():, \t\r\n\"".freeze
|
14
|
+
PRE_POST_STRING_CHAR = ''.freeze
|
15
|
+
STRING_DELIMITER = '"'.freeze
|
16
|
+
ESCAPED_QUOTE = '\\"'.freeze
|
17
|
+
ESCAPED_QUOTE_PLACEHOLDER = '\\RO_Q'.freeze
|
18
|
+
|
19
|
+
def initialize(expression, operators)
|
20
|
+
@expression = expression
|
21
|
+
@operators = operators
|
22
|
+
@result_list = nil
|
23
|
+
@array_accumulator = nil
|
24
|
+
@dict_accumulator = nil
|
25
|
+
@dict_key = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def tokens
|
29
|
+
tokenize(@expression)
|
30
|
+
end
|
31
|
+
|
32
|
+
def push_node(node)
|
33
|
+
if !@dict_accumulator.nil? && @dict_key.nil?
|
34
|
+
@dict_key = node.value.to_s
|
35
|
+
elsif !@dict_accumulator.nil? && !@dict_key.nil?
|
36
|
+
@dict_accumulator[@dict_key] = node.value
|
37
|
+
@dict_key = nil
|
38
|
+
elsif !@array_accumulator.nil?
|
39
|
+
@array_accumulator << node.value
|
40
|
+
else
|
41
|
+
@result_list << node
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def tokenize(expression)
|
46
|
+
@result_list = []
|
47
|
+
@array_accumulator = nil
|
48
|
+
@dict_accumulator = nil
|
49
|
+
@dict_key = nil
|
50
|
+
|
51
|
+
delimiters_to_use = TokenizedExpression::TOKEN_DELIMITERS
|
52
|
+
normalized_expression = expression.gsub(TokenizedExpression::ESCAPED_QUOTE, TokenizedExpression::ESCAPED_QUOTE_PLACEHOLDER)
|
53
|
+
tokenizer = StringTokenizer.new(normalized_expression, delimiters_to_use, true)
|
54
|
+
|
55
|
+
token = nil
|
56
|
+
while tokenizer.more_tokens?
|
57
|
+
prev_token = token
|
58
|
+
token = tokenizer.next_token(delimiters_to_use)
|
59
|
+
in_string = delimiters_to_use == TokenizedExpression::STRING_DELIMITER
|
60
|
+
|
61
|
+
if !in_string && token == TokenizedExpression::DICT_START_DELIMITER
|
62
|
+
@dict_accumulator = {}
|
63
|
+
elsif !in_string && token == TokenizedExpression::DICT_END_DELIMITER
|
64
|
+
dict_result = @dict_accumulator
|
65
|
+
@dict_accumulator = nil
|
66
|
+
push_node(node_from_dict(dict_result))
|
67
|
+
elsif !in_string && token == TokenizedExpression::ARRAY_START_DELIMITER
|
68
|
+
@array_accumulator = []
|
69
|
+
elsif !in_string && token == TokenizedExpression::ARRAY_END_DELIMITER
|
70
|
+
array_result = @array_accumulator
|
71
|
+
@array_accumulator = nil
|
72
|
+
push_node(node_from_array(array_result))
|
73
|
+
elsif token == TokenizedExpression::STRING_DELIMITER
|
74
|
+
push_node(node_from_token(Symbols::ROXX_EMPTY_STRING)) if prev_token == TokenizedExpression::STRING_DELIMITER
|
75
|
+
delimiters_to_use = in_string ? TokenizedExpression::TOKEN_DELIMITERS : TokenizedExpression::STRING_DELIMITER
|
76
|
+
else
|
77
|
+
if delimiters_to_use == TokenizedExpression::STRING_DELIMITER
|
78
|
+
push_node(Node.new(NodeTypes::RAND, token.gsub(TokenizedExpression::ESCAPED_QUOTE_PLACEHOLDER, TokenizedExpression::ESCAPED_QUOTE)))
|
79
|
+
elsif !TokenizedExpression::TOKEN_DELIMITERS.include?(token) && token != TokenizedExpression::PRE_POST_STRING_CHAR
|
80
|
+
push_node(node_from_token(token))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
@result_list
|
86
|
+
end
|
87
|
+
|
88
|
+
def node_from_array(items)
|
89
|
+
Node.new(NodeTypes::RAND, items)
|
90
|
+
end
|
91
|
+
|
92
|
+
def node_from_dict(items)
|
93
|
+
Node.new(NodeTypes::RAND, items)
|
94
|
+
end
|
95
|
+
|
96
|
+
def node_from_token(token)
|
97
|
+
return Node.new(NodeTypes::RATOR, token) if @operators.include?(token)
|
98
|
+
return Node.new(NodeTypes::RAND, true) if token == Symbols::ROXX_TRUE
|
99
|
+
return Node.new(NodeTypes::RAND, false) if token == Symbols::ROXX_FALSE
|
100
|
+
return Node.new(NodeTypes::RAND, TokenType::UNDEFINED) if token == Symbols::ROXX_UNDEFINED
|
101
|
+
|
102
|
+
token_type = TokenType.from_token(token)
|
103
|
+
return Node.new(NodeTypes::RAND, token[1...-1]) if token_type == TokenType::STRING
|
104
|
+
if token_type == TokenType::NUMBER
|
105
|
+
begin
|
106
|
+
return Node.new(NodeTypes::RAND, Integer(token))
|
107
|
+
rescue ArgumentError
|
108
|
+
begin
|
109
|
+
return Node.new(NodeTypes::RAND, Float(token))
|
110
|
+
rescue ArgumentError => ex
|
111
|
+
raise ArgumentError, "Excepted Number, got '#{token}' (#{token_type}): #{ex}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
Node.new(NodeTypes::UNKNOWN, nil)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|