kumi-parser 0.0.14 → 0.0.16
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/README.md +1 -1
- data/lib/kumi/parser/direct_parser.rb +73 -21
- data/lib/kumi/parser/smart_tokenizer.rb +42 -25
- data/lib/kumi/parser/token.rb +1 -1
- data/lib/kumi/parser/token_metadata.rb +26 -7
- data/lib/kumi/parser/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7c82cf28e39c53592ff6acb03772bc9d346716cb1dee414f4d1bce450629b9e
|
4
|
+
data.tar.gz: e9464f66ff1aae2b9d38770bdf1c9fe9d96eef114838eaec532a4408f87b8435
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4902e5502a0298fd6f9f24a9f80701910f641fa8098917d40cf0daa84f0845861c1f97aebd940bd704d213d1bce91fc1ad6971796bef76bfb2f5382aee60840
|
7
|
+
data.tar.gz: 512f5f4b80d7be91c543e1ae4f85c95835f9b52e2eb5768b4b55d4f69f27e17e640392851f3e2ee1b40003254d6160e533559f24a8e7546951b4d507c5d55950
|
data/README.md
CHANGED
@@ -57,7 +57,7 @@ end
|
|
57
57
|
```
|
58
58
|
|
59
59
|
**Function calls**: `fn(:name, arg1, arg2, ...)`
|
60
|
-
**Operators**: `+` `-` `*` `/` `%` `>` `<` `>=` `<=` `==` `!=` `&` `|`
|
60
|
+
**Operators**: `+` `-` `*` `**` `` `/` `%` `>` `<` `>=` `<=` `==` `!=` `&` `|`
|
61
61
|
**References**: `input.field`, `value_name`, `array[index]`
|
62
62
|
**Strings**: Both `"double"` and `'single'` quotes supported
|
63
63
|
**Element syntax**: `element :type, :name` for array element specifications
|
@@ -25,6 +25,7 @@ module Kumi
|
|
25
25
|
def peek_token(offset = 1)
|
26
26
|
peek_pos = @pos + offset
|
27
27
|
return @tokens.last if peek_pos >= @tokens.length # Return EOF
|
28
|
+
|
28
29
|
@tokens[peek_pos]
|
29
30
|
end
|
30
31
|
|
@@ -89,6 +90,7 @@ module Kumi
|
|
89
90
|
|
90
91
|
until %i[end eof].include?(current_token.type)
|
91
92
|
break unless current_token.metadata[:category] == :type_keyword
|
93
|
+
|
92
94
|
declarations << parse_input_declaration
|
93
95
|
skip_comments_and_newlines
|
94
96
|
end
|
@@ -232,6 +234,7 @@ module Kumi
|
|
232
234
|
|
233
235
|
def convert_array_expression_to_ruby_array(array_expr)
|
234
236
|
return nil unless array_expr.is_a?(Kumi::Syntax::ArrayExpression)
|
237
|
+
|
235
238
|
array_expr.elements.map do |element|
|
236
239
|
if element.is_a?(Kumi::Syntax::Literal)
|
237
240
|
element.value
|
@@ -365,14 +368,15 @@ module Kumi
|
|
365
368
|
value = convert_literal_value(token)
|
366
369
|
advance
|
367
370
|
Kumi::Syntax::Literal.new(value, loc: token.location)
|
371
|
+
when :function_sugar
|
372
|
+
parse_function_sugar
|
368
373
|
|
369
374
|
when :identifier
|
375
|
+
|
370
376
|
if token.value == 'input' && peek_token.type == :dot
|
371
377
|
parse_input_reference
|
372
378
|
elsif peek_token.type == :lbracket
|
373
379
|
parse_array_access_reference
|
374
|
-
elsif token.value == 'fn'
|
375
|
-
parse_function_call
|
376
380
|
else
|
377
381
|
advance
|
378
382
|
Kumi::Syntax::DeclarationReference.new(token.value.to_sym, loc: token.location)
|
@@ -394,8 +398,12 @@ module Kumi
|
|
394
398
|
when :lbracket
|
395
399
|
parse_array_literal
|
396
400
|
|
401
|
+
when :left_brace
|
402
|
+
parse_hash_literal
|
403
|
+
|
397
404
|
when :fn
|
398
|
-
|
405
|
+
# expect_token(:fn)
|
406
|
+
parse_function_call
|
399
407
|
|
400
408
|
when :subtract
|
401
409
|
advance
|
@@ -460,26 +468,15 @@ module Kumi
|
|
460
468
|
Kumi::Syntax::CallExpression.new(:at, [base_ref, index_expr], loc: name_token.location)
|
461
469
|
end
|
462
470
|
|
463
|
-
def
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
fn_name = fn_name_token.value
|
469
|
-
args = []
|
470
|
-
while current_token.type == :comma
|
471
|
-
advance
|
472
|
-
args << parse_expression
|
473
|
-
end
|
474
|
-
expect_token(:rparen)
|
475
|
-
Kumi::Syntax::CallExpression.new(fn_name, args, loc: fn_name_token.location)
|
476
|
-
else
|
477
|
-
raise_parse_error("Expected '(' after 'fn'")
|
478
|
-
end
|
471
|
+
def parse_function_sugar
|
472
|
+
sugar_token = current_token
|
473
|
+
advance
|
474
|
+
args = parse_argument_list
|
475
|
+
Kumi::Syntax::CallExpression.new(sugar_token.value.to_sym, args, loc: sugar_token.location)
|
479
476
|
end
|
480
477
|
|
481
|
-
def
|
482
|
-
|
478
|
+
def parse_function_call
|
479
|
+
advance
|
483
480
|
if current_token.type == :lparen
|
484
481
|
advance
|
485
482
|
fn_name_token = expect_token(:symbol)
|
@@ -498,6 +495,8 @@ module Kumi
|
|
498
495
|
|
499
496
|
def parse_argument_list
|
500
497
|
args = []
|
498
|
+
|
499
|
+
expect_token(:lparen)
|
501
500
|
unless current_token.type == :rparen
|
502
501
|
args << parse_expression
|
503
502
|
while current_token.type == :comma
|
@@ -505,6 +504,8 @@ module Kumi
|
|
505
504
|
args << parse_expression
|
506
505
|
end
|
507
506
|
end
|
507
|
+
expect_token(:rparen)
|
508
|
+
|
508
509
|
args
|
509
510
|
end
|
510
511
|
|
@@ -522,6 +523,57 @@ module Kumi
|
|
522
523
|
Kumi::Syntax::ArrayExpression.new(elements, loc: start_token.location)
|
523
524
|
end
|
524
525
|
|
526
|
+
def parse_hash_literal
|
527
|
+
start_token = expect_token(:left_brace)
|
528
|
+
skip_comments_and_newlines
|
529
|
+
pairs = []
|
530
|
+
|
531
|
+
# Handle empty hash: {}
|
532
|
+
unless current_token.type == :right_brace
|
533
|
+
pairs << parse_hash_pair
|
534
|
+
skip_comments_and_newlines
|
535
|
+
|
536
|
+
while current_token.type == :comma
|
537
|
+
advance
|
538
|
+
skip_comments_and_newlines
|
539
|
+
# Allow trailing comma
|
540
|
+
break if current_token.type == :right_brace
|
541
|
+
|
542
|
+
pairs << parse_hash_pair
|
543
|
+
skip_comments_and_newlines
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
expect_token(:right_brace)
|
548
|
+
Kumi::Syntax::HashExpression.new(pairs, loc: start_token.location)
|
549
|
+
end
|
550
|
+
|
551
|
+
def parse_hash_pair
|
552
|
+
key_token_type = current_token.type
|
553
|
+
key = case key_token_type
|
554
|
+
when :label, :string
|
555
|
+
value = current_token.value.to_sym if key_token_type == :label
|
556
|
+
advance
|
557
|
+
Kumi::Syntax::Literal.new(value, loc: current_token.location)
|
558
|
+
else
|
559
|
+
raise_parse_error("Hash keys must be symbols (:key) or strings (\"key\"), got #{current_token.type}")
|
560
|
+
end
|
561
|
+
|
562
|
+
skip_comments_and_newlines
|
563
|
+
|
564
|
+
case current_token.type
|
565
|
+
when :arrow
|
566
|
+
advance
|
567
|
+
else
|
568
|
+
raise_parse_error("Expected '=>' in hash pair") unless key_token_type == :label
|
569
|
+
end
|
570
|
+
|
571
|
+
skip_comments_and_newlines
|
572
|
+
value = parse_expression
|
573
|
+
|
574
|
+
[key, value]
|
575
|
+
end
|
576
|
+
|
525
577
|
def convert_literal_value(token)
|
526
578
|
case token.type
|
527
579
|
when :integer then token.value.gsub('_', '').to_i
|
@@ -34,7 +34,7 @@ module Kumi
|
|
34
34
|
else
|
35
35
|
consume_operator_or_punctuation
|
36
36
|
end
|
37
|
-
when /[a-zA-Z_]/ then
|
37
|
+
when /[a-zA-Z_]/ then consume_identifier_or_label_or_keyword
|
38
38
|
when ':' then consume_symbol_or_colon
|
39
39
|
else
|
40
40
|
consume_operator_or_punctuation
|
@@ -158,24 +158,34 @@ module Kumi
|
|
158
158
|
@tokens << Token.new(token_type, number_str, location, Kumi::Parser::TOKEN_METADATA[token_type])
|
159
159
|
end
|
160
160
|
|
161
|
-
def
|
161
|
+
def consume_identifier_or_label_or_keyword
|
162
162
|
start_column = @column
|
163
|
-
|
163
|
+
identifier_or_label_name = consume_while { |c| c.match?(/[a-zA-Z0-9_]/) }
|
164
|
+
location = Kumi::Syntax::Location.new(file: @source_file, line: @line, column: start_column)
|
165
|
+
|
166
|
+
# Check if the next character is a colon
|
167
|
+
if current_char == ':'
|
168
|
+
# It's a hash key or a label (e.g., `name:`)
|
169
|
+
advance # consume the colon
|
170
|
+
add_token(:label, identifier_or_label_name, Kumi::Parser::TOKEN_METADATA[:label])
|
171
|
+
return
|
172
|
+
end
|
173
|
+
|
174
|
+
# If it's not a label, proceed to check for keywords and identifiers
|
175
|
+
# The logic below is adapted from your original `consume_identifier_or_keyword` method
|
164
176
|
|
165
177
|
# Check if it's a constant (e.g., Float::INFINITY)
|
166
|
-
if
|
178
|
+
if identifier_or_label_name == 'Float' && current_char == ':' && peek_char == ':'
|
167
179
|
advance # consume first :
|
168
180
|
advance # consume second :
|
169
181
|
constant_name = consume_while { |c| c.match?(/[a-zA-Z0-9_]/) }
|
170
|
-
full_constant = "#{
|
171
|
-
|
172
|
-
location = Kumi::Syntax::Location.new(file: @source_file, line: @line, column: start_column)
|
173
|
-
@tokens << Token.new(:constant, full_constant, location, Kumi::Parser::TOKEN_METADATA[:constant])
|
182
|
+
full_constant = "#{identifier_or_label_name}::#{constant_name}"
|
183
|
+
add_token(:constant, full_constant, Kumi::Parser::TOKEN_METADATA[:constant])
|
174
184
|
return
|
175
185
|
end
|
176
186
|
|
177
187
|
# Check if it's a keyword
|
178
|
-
if keyword_type = Kumi::Parser::KEYWORDS[
|
188
|
+
if keyword_type = Kumi::Parser::KEYWORDS[identifier_or_label_name]
|
179
189
|
metadata = Kumi::Parser::TOKEN_METADATA[keyword_type].dup
|
180
190
|
|
181
191
|
# Update context based on keyword
|
@@ -187,24 +197,26 @@ module Kumi
|
|
187
197
|
closed_context = @context_stack.pop if @context_stack.length > 1
|
188
198
|
metadata[:closes_context] = closed_context
|
189
199
|
end
|
200
|
+
add_token(keyword_type, identifier_or_label_name, metadata)
|
201
|
+
return
|
202
|
+
end
|
190
203
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
# Add context-specific metadata
|
198
|
-
case current_context
|
199
|
-
when :input
|
200
|
-
metadata[:context] = :input_declaration
|
201
|
-
when :schema
|
202
|
-
metadata[:context] = :schema_body
|
203
|
-
end
|
204
|
+
# Check if its a function sugar
|
205
|
+
if Kumi::Parser::FUNCTION_SUGAR[identifier_or_label_name]
|
206
|
+
metadata = Kumi::Parser::TOKEN_METADATA[:function_sugar].dup
|
207
|
+
add_token(:function_sugar, identifier_or_label_name, metadata)
|
208
|
+
return
|
209
|
+
end
|
204
210
|
|
205
|
-
|
206
|
-
|
211
|
+
# Otherwise is an Identifier
|
212
|
+
metadata = Kumi::Parser::TOKEN_METADATA[:identifier].dup
|
213
|
+
case current_context
|
214
|
+
when :input
|
215
|
+
metadata[:context] = :input_declaration
|
216
|
+
when :schema
|
217
|
+
metadata[:context] = :schema_body
|
207
218
|
end
|
219
|
+
add_token(:identifier, identifier_or_label_name, metadata)
|
208
220
|
end
|
209
221
|
|
210
222
|
def consume_symbol_or_colon
|
@@ -232,7 +244,12 @@ module Kumi
|
|
232
244
|
# Handle multi-character operators
|
233
245
|
case char
|
234
246
|
when '='
|
235
|
-
if peek_char == '
|
247
|
+
if peek_char == '>'
|
248
|
+
advance
|
249
|
+
advance
|
250
|
+
location = Kumi::Syntax::Location.new(file: @source_file, line: @line, column: start_column)
|
251
|
+
@tokens << Token.new(:arrow, '=>', location, Kumi::Parser::TOKEN_METADATA[:arrow])
|
252
|
+
elsif peek_char == '='
|
236
253
|
advance
|
237
254
|
advance
|
238
255
|
location = Kumi::Syntax::Location.new(file: @source_file, line: @line, column: start_column)
|
data/lib/kumi/parser/token.rb
CHANGED
@@ -38,8 +38,8 @@ module Kumi
|
|
38
38
|
FN = :fn
|
39
39
|
|
40
40
|
# Operators (by precedence)
|
41
|
-
EXPONENT = :exponent
|
42
|
-
MULTIPLY = :multiply
|
41
|
+
EXPONENT = :exponent # **
|
42
|
+
MULTIPLY = :multiply # *
|
43
43
|
DIVIDE = :divide # /
|
44
44
|
MODULO = :modulo # %
|
45
45
|
ADD = :add # +
|
@@ -67,7 +67,7 @@ module Kumi
|
|
67
67
|
# Special
|
68
68
|
NEWLINE = :newline
|
69
69
|
EOF = :eof
|
70
|
-
COMMENT = :comment
|
70
|
+
COMMENT = :comment # # comment
|
71
71
|
end
|
72
72
|
|
73
73
|
# Rich metadata for each token type
|
@@ -163,6 +163,11 @@ module Kumi
|
|
163
163
|
starts_expression: true
|
164
164
|
},
|
165
165
|
|
166
|
+
function_sugar: {
|
167
|
+
function_keyword: true,
|
168
|
+
starts_expression: true
|
169
|
+
},
|
170
|
+
|
166
171
|
# Operators with precedence and associativity
|
167
172
|
exponent: {
|
168
173
|
category: :operator,
|
@@ -256,7 +261,6 @@ module Kumi
|
|
256
261
|
arity: :binary,
|
257
262
|
requires_boolean: true
|
258
263
|
},
|
259
|
-
|
260
264
|
# Literals with type information
|
261
265
|
integer: {
|
262
266
|
category: :literal,
|
@@ -278,7 +282,6 @@ module Kumi
|
|
278
282
|
starts_expression: true,
|
279
283
|
ast_class: 'Kumi::Syntax::Literal'
|
280
284
|
},
|
281
|
-
|
282
285
|
# Identifiers and references
|
283
286
|
identifier: {
|
284
287
|
category: :identifier,
|
@@ -339,6 +342,15 @@ module Kumi
|
|
339
342
|
terminates_expression: true
|
340
343
|
},
|
341
344
|
|
345
|
+
left_brace: {
|
346
|
+
category: :punctuation,
|
347
|
+
opens_scope: :hash
|
348
|
+
},
|
349
|
+
right_brace: {
|
350
|
+
category: :punctuation,
|
351
|
+
closes_scope: :hash
|
352
|
+
},
|
353
|
+
|
342
354
|
# Special tokens
|
343
355
|
newline: {
|
344
356
|
category: :whitespace,
|
@@ -360,6 +372,8 @@ module Kumi
|
|
360
372
|
')' => :rparen,
|
361
373
|
'[' => :lbracket,
|
362
374
|
']' => :rbracket,
|
375
|
+
'{' => :left_brace,
|
376
|
+
'}' => :right_brace,
|
363
377
|
',' => :comma,
|
364
378
|
'.' => :dot,
|
365
379
|
':' => :colon,
|
@@ -369,9 +383,14 @@ module Kumi
|
|
369
383
|
'/' => :divide,
|
370
384
|
'%' => :modulo,
|
371
385
|
'&' => :and,
|
372
|
-
'|' => :or
|
386
|
+
'|' => :or,
|
387
|
+
'=>' => :arrow
|
373
388
|
}.freeze
|
374
389
|
|
390
|
+
FUNCTION_SUGAR = {
|
391
|
+
'select' => '__select__'
|
392
|
+
}
|
393
|
+
|
375
394
|
# Keywords mapping
|
376
395
|
KEYWORDS = {
|
377
396
|
'schema' => :schema,
|
@@ -401,4 +420,4 @@ module Kumi
|
|
401
420
|
rbracket: :lbracket
|
402
421
|
}.freeze
|
403
422
|
end
|
404
|
-
end
|
423
|
+
end
|
data/lib/kumi/parser/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kumi-parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kumi Team
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: kumi
|