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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 83ae18a38c78f9513121f579f024309c162ebbc1fdf909aecbe8aceff04e100a
4
- data.tar.gz: 8bfab6fb7250f7e39289ceb07b84ae556958a77be19b66fe253b19c7284359ba
3
+ metadata.gz: b7c82cf28e39c53592ff6acb03772bc9d346716cb1dee414f4d1bce450629b9e
4
+ data.tar.gz: e9464f66ff1aae2b9d38770bdf1c9fe9d96eef114838eaec532a4408f87b8435
5
5
  SHA512:
6
- metadata.gz: 39f5759930377f813b10d8ce148dc5f26bd9a6b4a21a577b91bbb24fe7d47d923cf3967f25454666bb9c8c23f0190909f6283fe629a7bd935d51ca9033c25047
7
- data.tar.gz: d06bd58249f51ef7edafb51f291c2b18720ea259b3a375b9624ac593631f7786aa2c920da92bea99ec1ad27fc517c71c9b7bbd41e7626faa814949e317b56f29
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
- parse_function_call_from_fn_token
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 parse_function_call
464
- fn_token = expect_token(:identifier)
465
- if current_token.type == :lparen
466
- advance
467
- fn_name_token = expect_token(:symbol)
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 parse_function_call_from_fn_token
482
- fn_token = expect_token(:fn)
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 consume_identifier_or_keyword
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 consume_identifier_or_keyword
161
+ def consume_identifier_or_label_or_keyword
162
162
  start_column = @column
163
- identifier = consume_while { |c| c.match?(/[a-zA-Z0-9_]/) }
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 identifier == 'Float' && current_char == ':' && peek_char == ':'
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 = "#{identifier}::#{constant_name}"
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[identifier]
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
- location = Kumi::Syntax::Location.new(file: @source_file, line: @line, column: start_column)
192
- @tokens << Token.new(keyword_type, identifier, location, metadata)
193
- else
194
- # It's an identifier - determine its role based on context
195
- metadata = Kumi::Parser::TOKEN_METADATA[:identifier].dup
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
- location = Kumi::Syntax::Location.new(file: @source_file, line: @line, column: start_column)
206
- @tokens << Token.new(:identifier, identifier, location, metadata)
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)
@@ -81,4 +81,4 @@ module Kumi
81
81
  end
82
82
  end
83
83
  end
84
- end
84
+ end
@@ -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 # # 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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Kumi
4
4
  module Parser
5
- VERSION = '0.0.14'
5
+ VERSION = '0.0.16'
6
6
  end
7
7
  end
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.14
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-08-30 00:00:00.000000000 Z
11
+ date: 2025-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kumi