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 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: []