jaina 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|