jaina 0.3.0 → 0.4.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 +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +51 -11
- data/lib/jaina/parser/ast/tree_builder.rb +10 -10
- data/lib/jaina/parser/code_converter/to_postfix_form.rb +18 -16
- data/lib/jaina/parser/code_converter/to_prefix_form.rb +10 -10
- data/lib/jaina/parser/expression/operator/abstract.rb +10 -2
- data/lib/jaina/parser/expression.rb +4 -4
- data/lib/jaina/parser/tokenizer/token.rb +52 -0
- data/lib/jaina/parser/tokenizer/token_builder/argument_type_caster.rb +78 -0
- data/lib/jaina/parser/tokenizer/token_builder.rb +128 -0
- data/lib/jaina/parser/tokenizer.rb +19 -5
- data/lib/jaina/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6a90305fd0da0eae831f491b174247e4e057eec761c841615c81b61eb2950ad2
|
4
|
+
data.tar.gz: 9ffe0dd0616419c9dab9d626f5047955d23d9aaa9db3cdd4054d63d0d5acaa10
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c8d4c1c643e519fee75dc68698e6a74e33ef79476a78e5b831cd80c1bc77d8465eddfdc61f451257b04d3eb851f0e40607d2d1d6fff66bf237478ad4a2c9034
|
7
|
+
data.tar.gz: c47ebf4c452f44729936a3210614999debe1e1859fa9fe0f5a775650d217d71ce189f1692332579079c99ff0b412593fd735905084322183ece79d0c4bb69ce2
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
+
## [0.4.0] - 2019-06-24
|
5
|
+
### Added
|
6
|
+
- Support for operand attributes and operator attributes;
|
7
|
+
|
4
8
|
## [0.3.0] - 2019-06-21
|
9
|
+
### Added
|
5
10
|
- Support for initial context: `Jaina.evaluate(program, **initial_context)`
|
6
11
|
|
7
12
|
## [0.2.0] - 2019-06-21
|
data/README.md
CHANGED
@@ -104,8 +104,13 @@ context.get(:b) # => Jaina::Parser::AST::Contex::UndefinedContextKeyError
|
|
104
104
|
### Parse your code (build AST)
|
105
105
|
|
106
106
|
```ruby
|
107
|
+
# NOTE: without arguments
|
107
108
|
Jaina.parse('A AND B AND (C OR D) OR A AND (C OR E)')
|
108
109
|
# => #<Jaina::Parser::AST:0x00007fd6f424a2e8>
|
110
|
+
|
111
|
+
# NOTE: with arguments
|
112
|
+
Jaina.parse('A[1,2] AND B[3,4]')
|
113
|
+
# => #<Jaina::Parser::AST:0x00007fd6f424a2e9>
|
109
114
|
```
|
110
115
|
|
111
116
|
---
|
@@ -113,14 +118,43 @@ Jaina.parse('A AND B AND (C OR D) OR A AND (C OR E)')
|
|
113
118
|
### Evaluate your code
|
114
119
|
|
115
120
|
```ruby
|
116
|
-
ast = Jaina.parse('A AND B AND (C OR D) OR A AND (C OR E)')
|
121
|
+
ast = Jaina.parse('A AND B[5,test] AND (C OR D) OR A AND (C OR E)')
|
117
122
|
ast.evaluate
|
118
123
|
|
119
124
|
# --- or ---
|
120
|
-
Jaina.evaluate('A AND B AND (C OR D) OR A AND (C OR E)')
|
125
|
+
Jaina.evaluate('A AND B[5,test] AND (C OR D) OR A AND (C OR E)')
|
121
126
|
|
122
127
|
# --- you can set initial context of your program ---
|
123
|
-
Jaina.evaluate('A AND B', login: 'admin', logged_in: true)
|
128
|
+
Jaina.evaluate('A AND B[5,test]', login: 'admin', logged_in: true)
|
129
|
+
```
|
130
|
+
|
131
|
+
---
|
132
|
+
|
133
|
+
### Custom operator/operand arguments
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
# NOTE: use []
|
137
|
+
Jaina.parse('A[1,true] AND B[false,"false"]')
|
138
|
+
|
139
|
+
# NOTE:
|
140
|
+
# all your arguments will be typecasted to
|
141
|
+
# the concrete type inferred from the argument literal
|
142
|
+
|
143
|
+
Jaina.parse('A[1,true,false,"false"]') # 1, true, false "false"
|
144
|
+
|
145
|
+
# NOTE: access to the argument list
|
146
|
+
class A < Jaina::TerminalExpr
|
147
|
+
token 'A'
|
148
|
+
|
149
|
+
def evaluate(context)
|
150
|
+
# A[1,true,false,"false"]
|
151
|
+
|
152
|
+
arguments[0] # => 1
|
153
|
+
arguments[1] # => true
|
154
|
+
arguments[2] # => false
|
155
|
+
arguments[3] # => "false"
|
156
|
+
end
|
157
|
+
end
|
124
158
|
```
|
125
159
|
|
126
160
|
---
|
@@ -151,7 +185,7 @@ Jaina.fetch_expression("KEK")
|
|
151
185
|
### Full example
|
152
186
|
|
153
187
|
```ruby
|
154
|
-
# step 1:
|
188
|
+
# step 1: create new operand
|
155
189
|
class AddNumber < Jaina::TerminalExpr
|
156
190
|
token 'ADD'
|
157
191
|
|
@@ -160,7 +194,7 @@ class AddNumber < Jaina::TerminalExpr
|
|
160
194
|
end
|
161
195
|
end
|
162
196
|
|
163
|
-
# step 2:
|
197
|
+
# step 2: create another new operand
|
164
198
|
class CheckNumber < Jaina::TerminalExpr
|
165
199
|
token 'CHECK'
|
166
200
|
|
@@ -169,28 +203,34 @@ class CheckNumber < Jaina::TerminalExpr
|
|
169
203
|
end
|
170
204
|
end
|
171
205
|
|
206
|
+
# step 4: and another new :)
|
172
207
|
class InitState < Jaina::TerminalExpr
|
173
208
|
token 'INIT'
|
174
209
|
|
175
210
|
def evaluate(context)
|
176
|
-
|
211
|
+
initial_value = arguments[0] || 0
|
212
|
+
|
213
|
+
context.set(:current_value, initial_value)
|
177
214
|
end
|
178
215
|
end
|
179
216
|
|
180
|
-
# step
|
217
|
+
# step 5: register new oeprands
|
181
218
|
Jaina.register_expression(AddNumber)
|
182
219
|
Jaina.register_expression(CheckNumber)
|
183
220
|
Jaina.register_expression(InitState)
|
184
221
|
|
185
|
-
# step
|
222
|
+
# step 6: run your program
|
186
223
|
|
187
224
|
# NOTE: with initial context
|
188
|
-
Jaina.evaluate('CHECK AND ADD', current_value: -1) # =>
|
189
|
-
Jaina.evaluate('CHECK AND ADD', current_value: 2) # =>
|
225
|
+
Jaina.evaluate('CHECK AND ADD', current_value: -1) # => 9
|
226
|
+
Jaina.evaluate('CHECK AND ADD', current_value: 2) # => false
|
190
227
|
|
191
228
|
# NOTE: without initial context
|
192
229
|
Jaina.evaluate('INIT AND ADD') # => 10
|
193
|
-
Jaina.evaluate('INIT AND (CHECK OR ADD)') # =>
|
230
|
+
Jaina.evaluate('INIT AND (CHECK OR ADD)') # => 10
|
231
|
+
|
232
|
+
# NOTE: with arguments
|
233
|
+
Jaina.evaluate('INIT[100] AND ADD') => # 112
|
194
234
|
```
|
195
235
|
|
196
236
|
---
|
@@ -64,13 +64,13 @@ class Jaina::Parser::AST::TreeBuilder
|
|
64
64
|
# @since 0.1.0
|
65
65
|
attr_reader :prefix_form
|
66
66
|
|
67
|
-
# @return [Array<
|
67
|
+
# @return [Array<Jaina::Parser::Tokenizer::Token>]
|
68
68
|
#
|
69
69
|
# @api private
|
70
70
|
# @since 0.1.0
|
71
71
|
attr_reader :tokens
|
72
72
|
|
73
|
-
# @param token_series [Array<
|
73
|
+
# @param token_series [Array<Jaina::Parser::Tokenizer::Token>]
|
74
74
|
# @return [Array<Jaina::Parser::Expression::Operator::Abstract>]
|
75
75
|
#
|
76
76
|
# @api private
|
@@ -80,14 +80,14 @@ class Jaina::Parser::AST::TreeBuilder
|
|
80
80
|
return if current_token.nil?
|
81
81
|
|
82
82
|
case
|
83
|
-
when terminal?(current_token)
|
83
|
+
when terminal?(current_token.token)
|
84
84
|
build_terminal_expression(current_token)
|
85
|
-
when non_terminal?(current_token)
|
85
|
+
when non_terminal?(current_token.token)
|
86
86
|
build_non_terminal_expression(current_token, token_series)
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
# @param token_series [Array<
|
90
|
+
# @param token_series [Array<Jaina::Parser::Tokenizer::Token>]
|
91
91
|
# @return [String, NilClass]
|
92
92
|
#
|
93
93
|
# @api private
|
@@ -96,7 +96,7 @@ class Jaina::Parser::AST::TreeBuilder
|
|
96
96
|
token_series.shift
|
97
97
|
end
|
98
98
|
|
99
|
-
# @param current_token [
|
99
|
+
# @param current_token [Jaina::Parser::Tokenizer::Token]
|
100
100
|
# @return [Jaina::Parser::Expression::Operator::Abstract]
|
101
101
|
#
|
102
102
|
# @api private
|
@@ -105,20 +105,20 @@ class Jaina::Parser::AST::TreeBuilder
|
|
105
105
|
Jaina::Parser::Expression.build(current_token)
|
106
106
|
end
|
107
107
|
|
108
|
-
# @param current_token [
|
109
|
-
# @param token_series [Array<
|
108
|
+
# @param current_token [Jaina::Parser::Tokenizer::Token]
|
109
|
+
# @param token_series [Array<Jaina::Parser::Tokenizer::Token>]
|
110
110
|
# @return [Jaina::Parser::Expression::Operator::Abstract]
|
111
111
|
#
|
112
112
|
# @api private
|
113
113
|
# @since 0.1.0
|
114
114
|
def build_non_terminal_expression(current_token, token_series)
|
115
115
|
case
|
116
|
-
when acts_as_unary_term?(current_token)
|
116
|
+
when acts_as_unary_term?(current_token.token)
|
117
117
|
Jaina::Parser::Expression.build(
|
118
118
|
current_token,
|
119
119
|
build_expression_tree(token_series)
|
120
120
|
)
|
121
|
-
when acts_as_binary_term?(current_token)
|
121
|
+
when acts_as_binary_term?(current_token.token)
|
122
122
|
Jaina::Parser::Expression.build(
|
123
123
|
current_token,
|
124
124
|
build_expression_tree(token_series),
|
@@ -51,7 +51,7 @@ class Jaina::Parser::CodeConverter::ToPostfixForm
|
|
51
51
|
# @since 0.1.0
|
52
52
|
attr_reader :program
|
53
53
|
|
54
|
-
# @return [Array<
|
54
|
+
# @return [Array<Jaina::Parser::Tokenizer::Token>]
|
55
55
|
#
|
56
56
|
# @api private
|
57
57
|
# @since 0.1.0
|
@@ -61,7 +61,7 @@ class Jaina::Parser::CodeConverter::ToPostfixForm
|
|
61
61
|
#
|
62
62
|
# @api private
|
63
63
|
# @since 0.1.0
|
64
|
-
def to_postfix_form
|
64
|
+
def to_postfix_form # rubocop:disable Metrics/AbcSize
|
65
65
|
final_expression = []
|
66
66
|
structure_operators = []
|
67
67
|
token_series = tokens.map(&:dup)
|
@@ -70,13 +70,13 @@ class Jaina::Parser::CodeConverter::ToPostfixForm
|
|
70
70
|
current_token = token_series.shift
|
71
71
|
|
72
72
|
case
|
73
|
-
when non_terminal?(current_token)
|
73
|
+
when non_terminal?(current_token.token)
|
74
74
|
process_non_terminal_token(final_expression, structure_operators, current_token)
|
75
|
-
when group_opener?(current_token)
|
75
|
+
when group_opener?(current_token.token)
|
76
76
|
process_group_opening_token(final_expression, structure_operators, current_token)
|
77
|
-
when group_closener?(current_token)
|
77
|
+
when group_closener?(current_token.token)
|
78
78
|
process_group_closing_token(final_expression, structure_operators, current_token)
|
79
|
-
when terminal?(current_token)
|
79
|
+
when terminal?(current_token.token)
|
80
80
|
process_terminal_token(final_expression, structure_operators, current_token)
|
81
81
|
end
|
82
82
|
end
|
@@ -89,7 +89,7 @@ class Jaina::Parser::CodeConverter::ToPostfixForm
|
|
89
89
|
|
90
90
|
# @param final_expression [Array<String>]
|
91
91
|
# @param structure_operators [Array<String>]
|
92
|
-
# @param current_token [
|
92
|
+
# @param current_token [Jaina::Parser::Tokenizer::Token]
|
93
93
|
# @return [void]
|
94
94
|
#
|
95
95
|
# @api private
|
@@ -98,12 +98,14 @@ class Jaina::Parser::CodeConverter::ToPostfixForm
|
|
98
98
|
if structure_operators.any? # NOTE: check assocaitivity with potential next token
|
99
99
|
potential_second_token = structure_operators.last
|
100
100
|
|
101
|
-
if non_terminal?(potential_second_token)
|
102
|
-
current_expression = Jaina::Parser::Expression.fetch(current_token)
|
103
|
-
next_expression = Jaina::Parser::Expression.fetch(potential_second_token)
|
101
|
+
if non_terminal?(potential_second_token.token)
|
102
|
+
current_expression = Jaina::Parser::Expression.fetch(current_token.token)
|
103
|
+
next_expression = Jaina::Parser::Expression.fetch(potential_second_token.token)
|
104
104
|
|
105
105
|
# NOTE: form infix to postfix form switching
|
106
|
-
|
106
|
+
if current_expression.lower?(next_expression)
|
107
|
+
final_expression.push(structure_operators.pop)
|
108
|
+
end
|
107
109
|
end
|
108
110
|
end
|
109
111
|
|
@@ -112,7 +114,7 @@ class Jaina::Parser::CodeConverter::ToPostfixForm
|
|
112
114
|
|
113
115
|
# @param final_expression [Array<String>]
|
114
116
|
# @param structure_operators [Array<String>]
|
115
|
-
# @param current_token [
|
117
|
+
# @param current_token [Jaina::Parser::Tokenizer::Token]
|
116
118
|
# @return [void]
|
117
119
|
#
|
118
120
|
# @api private
|
@@ -124,13 +126,13 @@ class Jaina::Parser::CodeConverter::ToPostfixForm
|
|
124
126
|
|
125
127
|
# @param final_expression [Array<String>]
|
126
128
|
# @param structure_operators [Array<String>]
|
127
|
-
# @param current_token [
|
129
|
+
# @param current_token [Jaina::Parser::Tokenizer::Token]
|
128
130
|
# @return [void]
|
129
131
|
#
|
130
132
|
# @api private
|
131
133
|
# @since 0.1.0
|
132
134
|
def process_group_closing_token(final_expression, structure_operators, current_token)
|
133
|
-
until group_opener?(structure_operators.last)
|
135
|
+
until group_opener?(structure_operators.last.token)
|
134
136
|
# NOTE: push all tokens to the final expression
|
135
137
|
final_expression.push(structure_operators.pop)
|
136
138
|
end
|
@@ -141,7 +143,7 @@ class Jaina::Parser::CodeConverter::ToPostfixForm
|
|
141
143
|
|
142
144
|
# @param final_expression [Array<String>]
|
143
145
|
# @param structure_operators [Array<String>]
|
144
|
-
# @param current_token [
|
146
|
+
# @param current_token [Jaina::Parser::Tokenizer::Token]
|
145
147
|
# @return [void]
|
146
148
|
#
|
147
149
|
# @api private
|
@@ -153,7 +155,7 @@ class Jaina::Parser::CodeConverter::ToPostfixForm
|
|
153
155
|
|
154
156
|
# @param final_expression [Array<String>]
|
155
157
|
# @param structure_operators [Array<String>]
|
156
|
-
# @param current_token [
|
158
|
+
# @param current_token [Jaina::Parser::Tokenizer::Token]
|
157
159
|
# @return [void]
|
158
160
|
#
|
159
161
|
# @api private
|
@@ -53,7 +53,7 @@ class Jaina::Parser::CodeConverter::ToPrefixForm
|
|
53
53
|
# @since 0.1.0
|
54
54
|
attr_reader :postfix_form
|
55
55
|
|
56
|
-
# @return [Array<
|
56
|
+
# @return [Array<Jaina::Parser::Tokenizer::Token>]
|
57
57
|
#
|
58
58
|
# @api private
|
59
59
|
# @since 0.1.0
|
@@ -71,15 +71,15 @@ class Jaina::Parser::CodeConverter::ToPrefixForm
|
|
71
71
|
current_token = token_series.shift
|
72
72
|
|
73
73
|
case
|
74
|
-
when terminal?(current_token)
|
74
|
+
when terminal?(current_token.token)
|
75
75
|
process_terminal_token(current_token, expression_stack)
|
76
|
-
when non_terminal?(current_token)
|
76
|
+
when non_terminal?(current_token.token)
|
77
77
|
process_non_terminal_token(current_token, expression_stack)
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
81
|
# NOTE: build prefixed program string
|
82
|
-
Jaina::Parser::Tokenizer.
|
82
|
+
Jaina::Parser::Tokenizer.raw_join(expression_stack)
|
83
83
|
end
|
84
84
|
|
85
85
|
# @param current_token [String]
|
@@ -89,7 +89,7 @@ class Jaina::Parser::CodeConverter::ToPrefixForm
|
|
89
89
|
# @api private
|
90
90
|
# @since 0.1.0
|
91
91
|
def process_terminal_token(current_token, expression_stack)
|
92
|
-
expression_stack.push(current_token)
|
92
|
+
expression_stack.push(current_token.raw_token)
|
93
93
|
end
|
94
94
|
|
95
95
|
# @param current_token [String]
|
@@ -99,22 +99,22 @@ class Jaina::Parser::CodeConverter::ToPrefixForm
|
|
99
99
|
# @api private
|
100
100
|
# @since 0.1.0
|
101
101
|
def process_non_terminal_token(current_token, expression_stack)
|
102
|
-
expression = Jaina::Parser::Expression.fetch(current_token)
|
102
|
+
expression = Jaina::Parser::Expression.fetch(current_token.token)
|
103
103
|
|
104
104
|
case # TODO: dry
|
105
105
|
when expression.acts_as_binary_term?
|
106
106
|
first_operand = expression_stack.pop
|
107
107
|
second_operand = expression_stack.pop
|
108
108
|
|
109
|
-
prefixed_expression_parts = [
|
110
|
-
prefixed_expression = Jaina::Parser::Tokenizer.
|
109
|
+
prefixed_expression_parts = [current_token.raw_token, second_operand, first_operand]
|
110
|
+
prefixed_expression = Jaina::Parser::Tokenizer.raw_join(prefixed_expression_parts)
|
111
111
|
|
112
112
|
expression_stack.push(prefixed_expression)
|
113
113
|
when expression.acts_as_unary_term?
|
114
114
|
operand = expression_stack.pop
|
115
115
|
|
116
|
-
prefixed_expression_parts = [
|
117
|
-
prefixed_expression = Jaina::Parser::Tokenizer.
|
116
|
+
prefixed_expression_parts = [current_token.raw_token, operand]
|
117
|
+
prefixed_expression = Jaina::Parser::Tokenizer.raw_join(prefixed_expression_parts)
|
118
118
|
|
119
119
|
expression_stack.push(prefixed_expression)
|
120
120
|
end
|
@@ -34,18 +34,26 @@ class Jaina::Parser::Expression::Operator::Abstract
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
+
# @return [Array<Any>]
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
# @since 0.4.0
|
41
|
+
attr_reader :arguments
|
42
|
+
|
37
43
|
# @return [Array<Jaina::Parser::Expressions::Operator::Abstract>]
|
38
44
|
#
|
39
45
|
# @api private
|
40
46
|
# @since 0.1.0
|
41
47
|
attr_reader :expressions
|
42
48
|
|
43
|
-
# @
|
49
|
+
# @option arguments [Array<Any>]
|
50
|
+
# @option expressions [Array<Jaina::Parser::Expression::Operator::Abstract>]
|
44
51
|
# @return [void]
|
45
52
|
#
|
46
53
|
# @api private
|
47
54
|
# @since 0.1.0
|
48
|
-
def initialize(
|
55
|
+
def initialize(arguments: [], expressions: [])
|
56
|
+
@arguments = arguments
|
49
57
|
@expressions = expressions
|
50
58
|
end
|
51
59
|
|
@@ -21,15 +21,15 @@ module Jaina::Parser::Expression
|
|
21
21
|
register(Operator::RightCorner)
|
22
22
|
|
23
23
|
class << self
|
24
|
-
# @param expression_token [
|
24
|
+
# @param expression_token [Jaina::Parser::Tokenizer::Token]
|
25
25
|
# @param arguments [Array<Any>]
|
26
26
|
# @return [Jaina::Parser::Expression::Operator::Abstract]
|
27
27
|
#
|
28
28
|
# @api private
|
29
29
|
# @since 0.1.0
|
30
|
-
def build(expression_token, *
|
31
|
-
expression = fetch(expression_token)
|
32
|
-
|
30
|
+
def build(expression_token, *expression_arguments)
|
31
|
+
expression = fetch(expression_token.token)
|
32
|
+
expression.new(arguments: expression_token.arguments, expressions: expression_arguments)
|
33
33
|
end
|
34
34
|
|
35
35
|
# @param expression_token [String]
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.4.0
|
5
|
+
class Jaina::Parser::Tokenizer::Token
|
6
|
+
# @return [String]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.4.0
|
10
|
+
attr_reader :raw_token
|
11
|
+
|
12
|
+
# @return [String]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.4.0
|
16
|
+
attr_reader :token
|
17
|
+
|
18
|
+
# @return [Array<String>]
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
# @since 0.4.0
|
22
|
+
attr_reader :arguments
|
23
|
+
|
24
|
+
# @param raw_token [String]
|
25
|
+
# @param token [String]
|
26
|
+
# @param arguments [Array<Any>]
|
27
|
+
# @return [void]
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
# @since 0.4.0
|
31
|
+
def initialize(raw_token, token, *arguments)
|
32
|
+
@raw_token = raw_token
|
33
|
+
@token = token
|
34
|
+
@arguments = arguments
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Jaina::Parser::Tokenizer::Token]
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
# @since 0.4.0
|
41
|
+
def dup
|
42
|
+
self.class.new(raw_token.dup, token.dup, *arguments)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [String]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
# @since 0.4.0
|
49
|
+
def to_s
|
50
|
+
raw_token
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.4.0
|
5
|
+
module Jaina::Parser::Tokenizer::TokenBuilder::ArgumentTypeCaster
|
6
|
+
# @return [Regexp]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.4.0
|
10
|
+
INTEGER_PATTERN = /\A\d+\z/.freeze
|
11
|
+
|
12
|
+
# @return [Regexp]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.4.0
|
16
|
+
FLOAT_PATTERN = /\A\d+\.\d+\z/.freeze
|
17
|
+
|
18
|
+
# @return [Regexp]
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
# @since 0.4.0
|
22
|
+
TRUE_PATTERN = /\A(t|true)\z/i.freeze
|
23
|
+
|
24
|
+
# @return [Regexp]
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
# @since 0.4.0
|
28
|
+
FALSE_PATTERN = /\A(f|false)\z/i.freeze
|
29
|
+
|
30
|
+
# @return [Regexp]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
# @since 0.4.0
|
34
|
+
ARRAY_PATTERN = /\A[^'"].*\s*,\s*.*[^'"]\z/.freeze
|
35
|
+
|
36
|
+
# @return [Regexp]
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
# @since 0.4.0
|
40
|
+
QUOTED_STRING_PATTERN = /\A['"].*['"]\z/.freeze
|
41
|
+
|
42
|
+
class << self
|
43
|
+
# @param arguments [Array<String>]
|
44
|
+
# @return [Array<Any>]
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
# @since 0.4.0
|
48
|
+
def cast(*arguments)
|
49
|
+
arguments.map { |argument| cast_argument_type(argument) }
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# @param value [String]
|
55
|
+
# @return [Object]
|
56
|
+
#
|
57
|
+
# @api private
|
58
|
+
# @since 0.4.0
|
59
|
+
def cast_argument_type(value)
|
60
|
+
case value
|
61
|
+
when INTEGER_PATTERN
|
62
|
+
Integer(value)
|
63
|
+
when FLOAT_PATTERN
|
64
|
+
Float(value)
|
65
|
+
when TRUE_PATTERN
|
66
|
+
true
|
67
|
+
when FALSE_PATTERN
|
68
|
+
false
|
69
|
+
when ARRAY_PATTERN
|
70
|
+
value.split(/\s*,\s*/).map(&method(:convert_argument))
|
71
|
+
when QUOTED_STRING_PATTERN
|
72
|
+
value.gsub(/(\A['"]|['"]\z)/, '')
|
73
|
+
else
|
74
|
+
value
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.4.0
|
5
|
+
class Jaina::Parser::Tokenizer::TokenBuilder
|
6
|
+
require_relative './token_builder/argument_type_caster'
|
7
|
+
|
8
|
+
# @return [Regexp]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.4.0
|
12
|
+
PARTS_SPLITTER = /[^,\[\]]+|\[|\]+/.freeze
|
13
|
+
|
14
|
+
# @return [String]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
# @since 0.4.0
|
18
|
+
OPENING_ATTRIBUTE_GROUP_SYMBOL = '['
|
19
|
+
|
20
|
+
# @return [String]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
# @since 0.4.0
|
24
|
+
CLOSING_ATTRIBUTE_GROUP_SYMBOL = ']'
|
25
|
+
|
26
|
+
# @since 0.4.0
|
27
|
+
Error = Class.new(StandardError)
|
28
|
+
# @since 0.4.0
|
29
|
+
IncorrectTokenDefinitionError = Class.new(Error)
|
30
|
+
|
31
|
+
class << self
|
32
|
+
# @param raw_token [String]
|
33
|
+
# @return [Jaina::Parser::Tokenizer::Token]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
# @since 0.4.0
|
37
|
+
def build(raw_token)
|
38
|
+
new(raw_token).build
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param raw_token [String]
|
43
|
+
# @return [void]
|
44
|
+
#
|
45
|
+
# @api private
|
46
|
+
# @since 0.4.0
|
47
|
+
def initialize(raw_token)
|
48
|
+
@raw_token = raw_token
|
49
|
+
@parts = raw_token.scan(PARTS_SPLITTER)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Jaina::Parser::Tokenizer::Token]
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
# @since 0.4.0
|
56
|
+
def build
|
57
|
+
check_for_correctness!
|
58
|
+
|
59
|
+
# NOTE:
|
60
|
+
# format: token [ attr1, attr2, attr3 ]
|
61
|
+
# TOKEN => parts[0]
|
62
|
+
# OPENING BRAKET => parts[1]
|
63
|
+
# CLOSING BRAKET => parts[-1]
|
64
|
+
# ARGUMENTS => parts[2..-2]
|
65
|
+
token = parts[0]
|
66
|
+
arguments = parts.count.zero? ? [] : parts[2..-2]
|
67
|
+
arguments = ArgumentTypeCaster.cast(*arguments)
|
68
|
+
|
69
|
+
Jaina::Parser::Tokenizer::Token.new(raw_token, token, *arguments)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# @return [String]
|
75
|
+
#
|
76
|
+
# @api private
|
77
|
+
# @since 0.4.0
|
78
|
+
attr_reader :raw_token
|
79
|
+
|
80
|
+
# @return [Array<String>]
|
81
|
+
#
|
82
|
+
# @api private
|
83
|
+
# @since 0.4.0
|
84
|
+
attr_reader :parts
|
85
|
+
|
86
|
+
# @return [void]
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
# @since 0.4.0
|
90
|
+
def check_for_correctness! # rubocop:disable Metrics/AbcSize
|
91
|
+
# NOTE: parts array contains a token only
|
92
|
+
return if parts.count == 1
|
93
|
+
|
94
|
+
# NOTE: prats should contain at least: "__token__", "[", and "]"
|
95
|
+
raise(
|
96
|
+
IncorrectTokenDefinitionError,
|
97
|
+
"Incorrect token definition `#{raw_token}`: " \
|
98
|
+
"token should contain `[` and `]` if `[` is provided)"
|
99
|
+
) if parts.count < 3
|
100
|
+
|
101
|
+
# NOTE:
|
102
|
+
# format: token [ attr1 attr2 attr3 ]
|
103
|
+
# TOKEN => parts[0]
|
104
|
+
# OPENING BRAKET => parts[1]
|
105
|
+
# CLOSING BRAKET => parts[-1]
|
106
|
+
# ARGUMENTS => parts[2..-2]
|
107
|
+
|
108
|
+
opening_corner = parts[1]
|
109
|
+
closing_corner = parts[-1]
|
110
|
+
arguments = parts[2..-2]
|
111
|
+
|
112
|
+
# rubocop:disable Metrics/LineLength
|
113
|
+
if arguments.include?(OPENING_ATTRIBUTE_GROUP_SYMBOL) || opening_corner != OPENING_ATTRIBUTE_GROUP_SYMBOL
|
114
|
+
raise(
|
115
|
+
IncorrectTokenDefinitionError,
|
116
|
+
"Incorrect token definition `#{raw_token}`: `[` should be the first arguments opening symbol."
|
117
|
+
)
|
118
|
+
end
|
119
|
+
|
120
|
+
if arguments.include?(CLOSING_ATTRIBUTE_GROUP_SYMBOL) || closing_corner != CLOSING_ATTRIBUTE_GROUP_SYMBOL
|
121
|
+
raise(
|
122
|
+
IncorrectTokenDefinitionError,
|
123
|
+
"Incorrect token definition `#{raw_token}`: `]` should be the last arguments closing symbol."
|
124
|
+
)
|
125
|
+
end
|
126
|
+
# rubocop:enable Metrics/LineLength
|
127
|
+
end
|
128
|
+
end
|
@@ -3,11 +3,14 @@
|
|
3
3
|
# @api private
|
4
4
|
# @since 0.1.0
|
5
5
|
module Jaina::Parser::Tokenizer
|
6
|
+
require_relative './tokenizer/token'
|
7
|
+
require_relative './tokenizer/token_builder'
|
8
|
+
|
6
9
|
# @return [Regexp]
|
7
10
|
#
|
8
11
|
# @api private
|
9
12
|
# @since 0.1.0
|
10
|
-
TOKEN_SCAN_PATTERN = /\(|\)|[\w
|
13
|
+
TOKEN_SCAN_PATTERN = /\(|\)|[\"\'\.\,\w\[\]]+/.freeze
|
11
14
|
|
12
15
|
# @return [String]
|
13
16
|
#
|
@@ -17,21 +20,32 @@ module Jaina::Parser::Tokenizer
|
|
17
20
|
|
18
21
|
class << self
|
19
22
|
# @param program [String]
|
20
|
-
# @return [Array<
|
23
|
+
# @return [Array<Jaina::Parser::Tokenizer::Token>]
|
21
24
|
#
|
22
25
|
# @api private
|
23
26
|
# @since 0.1.0
|
24
27
|
def tokenize(program)
|
25
|
-
program.scan(TOKEN_SCAN_PATTERN)
|
28
|
+
program.scan(TOKEN_SCAN_PATTERN).map do |raw_token|
|
29
|
+
TokenBuilder.build(raw_token)
|
30
|
+
end
|
26
31
|
end
|
27
32
|
|
28
|
-
# @param tokens [Array<
|
33
|
+
# @param tokens [Array<Jaina::Parser::Tokenizer::Token>]
|
29
34
|
# @return [String]
|
30
35
|
#
|
31
36
|
# @api private
|
32
37
|
# @since 0.1.0
|
33
38
|
def join(tokens)
|
34
|
-
tokens.
|
39
|
+
raw_join(tokens.map(&:raw_token))
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param raw_tokens [Array<String>]
|
43
|
+
# @return [String]
|
44
|
+
#
|
45
|
+
# @api private
|
46
|
+
# @since 0.4.0
|
47
|
+
def raw_join(raw_tokens)
|
48
|
+
raw_tokens.join(TOKEN_SPLITTER)
|
35
49
|
end
|
36
50
|
end
|
37
51
|
end
|
data/lib/jaina/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jaina
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rustam Ibragimov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-06-
|
11
|
+
date: 2019-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: coveralls
|
@@ -154,6 +154,9 @@ files:
|
|
154
154
|
- lib/jaina/parser/expression/registry.rb
|
155
155
|
- lib/jaina/parser/expression/registry/access_interface_mixin.rb
|
156
156
|
- lib/jaina/parser/tokenizer.rb
|
157
|
+
- lib/jaina/parser/tokenizer/token.rb
|
158
|
+
- lib/jaina/parser/tokenizer/token_builder.rb
|
159
|
+
- lib/jaina/parser/tokenizer/token_builder/argument_type_caster.rb
|
157
160
|
- lib/jaina/version.rb
|
158
161
|
homepage: https://github.com/0exp/jaina
|
159
162
|
licenses: []
|