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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 850df9cc5e4a4962b435569da878367493a801ac0f842760157083e8f5fba578
4
- data.tar.gz: 79cb4a3a71d81205b4d6647a04de688b084b6108373d9cc274a11ca390fffadd
3
+ metadata.gz: 6a90305fd0da0eae831f491b174247e4e057eec761c841615c81b61eb2950ad2
4
+ data.tar.gz: 9ffe0dd0616419c9dab9d626f5047955d23d9aaa9db3cdd4054d63d0d5acaa10
5
5
  SHA512:
6
- metadata.gz: 5253d6c057ef220178518f8793cbb7e40e091556a8efec5ee48d443c582408e2122b27cc098747f1b0d620d470604b041e1c8cf66ddd8eb7ffa4486fbd63f08a
7
- data.tar.gz: 54d7867f5ee7ec104e2a46d8311f69c6cf3cab25b5d635a64458ee18ce1f94778cd3794425474346e371d576f4a6183f62a4363fb181ebe27507c9b36c44f231
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: register new operand
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: register another new operand
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
- context.set(:current_value, 0)
211
+ initial_value = arguments[0] || 0
212
+
213
+ context.set(:current_value, initial_value)
177
214
  end
178
215
  end
179
216
 
180
- # step 3: register new oeprands
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 4: run your program
222
+ # step 6: run your program
186
223
 
187
224
  # NOTE: with initial context
188
- Jaina.evaluate('CHECK AND ADD', current_value: -1) # => false
189
- Jaina.evaluate('CHECK AND ADD', current_value: 2) # => 12
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)') # => 12
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<String>]
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<String>]
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<String>]
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 [String]
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 [String]
109
- # @param token_series [Array<String>]
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<String>]
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 [String]
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
- final_expression.push(structure_operators.pop) if current_expression.lower?(next_expression)
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 [String]
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 [String]
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 [String]
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 [String]
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<String>]
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.join(expression_stack)
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 = [expression.token, second_operand, first_operand]
110
- prefixed_expression = Jaina::Parser::Tokenizer.join(prefixed_expression_parts)
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 = [expression.token, operand]
117
- prefixed_expression = Jaina::Parser::Tokenizer.join(prefixed_expression_parts)
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
- # @param expressions [Array<Jaina::Parser::Expression::Operator::Abstract>]
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(*expressions)
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 [String]
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, *arguments)
31
- expression = fetch(expression_token)
32
- arguments.any? ? expression.new(*arguments) : expression.new
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\.\*]+/.freeze
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<String>]
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<String>]
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.join(TOKEN_SPLITTER)
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
@@ -5,5 +5,5 @@ module Jaina
5
5
  #
6
6
  # @api public
7
7
  # @since 0.0.0
8
- VERSION = '0.3.0'
8
+ VERSION = '0.4.0'
9
9
  end
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.3.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-21 00:00:00.000000000 Z
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: []