kumi-parser 0.0.27 → 0.0.29

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: 2d1d68b8e74816f3e524a335d3f4897c34c0c15fa0127c20481a2d755d6800fe
4
- data.tar.gz: 97bd55ed194b0c33d22c74802c4c83b0a7a744f2d0a99998ecfe6c82a1b3b142
3
+ metadata.gz: 6662f28685ceb786c8ce4153325bf7bfa3456317b0cfa60d1a488bbc8889db8b
4
+ data.tar.gz: 67210cf3da7c670e7c5ad06027cd7c096cd7ea568f254ace4ebeec6996cd34ce
5
5
  SHA512:
6
- metadata.gz: b326e1174e1fc0053646ca64b4215c3e4577a8dd7ae34c564af4bec0851d5f3535542f60a5c2d7278e2c6de77691452044db3a49cc8b9f0cd9df409339902854
7
- data.tar.gz: 9bdbb95aa25400f8a1ef71587241c18e61df8dbea925f77cbcfb300448a54bed87a30f39c2c53425dc7b6540899bd8622b9f77dfe8ebbf7ca7494b98b4abebdd
6
+ metadata.gz: 41672379669848d7e722eb58446e7aa95e2fbead517e7654bb2b48dba8be5448c17082184ecbb6554ed764506f4215daf6dea42b7d1024754c5f5497726cddef
7
+ data.tar.gz: 64627c6a31190147044795cc4a458e5c29b927090b3572775f59117e5ed79c6158cbce4178b2f3d1cd83c744baaa887941f844c92f8e10303293879a739625ba
@@ -9,11 +9,24 @@ module Kumi
9
9
  def initialize(tokens)
10
10
  @tokens = tokens
11
11
  @pos = 0
12
+ @imported_names = Set.new
12
13
  end
13
14
 
14
15
  def parse
15
16
  skip_comments_and_newlines
17
+
18
+ # Parse root-level imports (before schema)
19
+ root_imports = parse_imports
20
+ @imported_names.merge(root_imports.flat_map(&:names))
21
+
16
22
  schema_node = parse_schema
23
+
24
+ # If we have root imports, add them to the schema
25
+ if root_imports.any?
26
+ # Merge root imports with schema imports
27
+ schema_node.imports.concat(root_imports)
28
+ end
29
+
17
30
  skip_comments_and_newlines
18
31
  expect_token(:eof)
19
32
  schema_node
@@ -49,6 +62,9 @@ module Kumi
49
62
  expect_token(:do)
50
63
 
51
64
  skip_comments_and_newlines
65
+ import_declarations = parse_imports
66
+ @imported_names.merge(import_declarations.flat_map(&:names))
67
+
52
68
  input_declarations = parse_input_block
53
69
 
54
70
  value_declarations = []
@@ -73,10 +89,76 @@ module Kumi
73
89
  input_declarations,
74
90
  value_declarations, # values
75
91
  trait_declarations,
92
+ import_declarations,
76
93
  loc: schema_token.location
77
94
  )
78
95
  end
79
96
 
97
+ # Parse import declarations: 'import' :symbol, from: Module
98
+ def parse_imports
99
+ imports = []
100
+ skip_comments_and_newlines
101
+
102
+ while current_token.type == :import
103
+ import_token = expect_token(:import)
104
+
105
+ names = []
106
+ names << expect_token(:symbol).value.to_sym
107
+
108
+ while current_token.type == :comma
109
+ expect_token(:comma)
110
+ skip_comments_and_newlines
111
+
112
+ # Check if this is the 'from:' keyword argument or another symbol to import
113
+ if current_token.type == :label && current_token.value == 'from'
114
+ # This is 'from:' - end of imports list
115
+ break
116
+ else
117
+ # Another symbol to import
118
+ names << expect_token(:symbol).value.to_sym
119
+ end
120
+ end
121
+
122
+ skip_comments_and_newlines
123
+
124
+ # Handle 'from:' keyword argument
125
+ if current_token.type == :label && current_token.value == 'from'
126
+ expect_token(:label) # consume 'from:'
127
+ else
128
+ raise_parse_error("Expected 'from:' keyword argument in import statement")
129
+ end
130
+
131
+ skip_comments_and_newlines
132
+
133
+ module_ref = parse_constant
134
+
135
+ imports << Kumi::Syntax::ImportDeclaration.new(
136
+ names,
137
+ module_ref,
138
+ loc: import_token.location
139
+ )
140
+
141
+ skip_comments_and_newlines
142
+ end
143
+
144
+ imports
145
+ end
146
+
147
+ # Parse a constant reference like Schemas::Tax
148
+ def parse_constant
149
+ const_parts = []
150
+ const_parts << expect_token(:constant).value
151
+
152
+ while current_token.type == :colon && peek_token.type == :colon
153
+ expect_token(:colon)
154
+ expect_token(:colon)
155
+ const_parts << expect_token(:constant).value
156
+ end
157
+
158
+ # Return the full constant path as a string that will be evaluated at runtime
159
+ const_parts.join('::')
160
+ end
161
+
80
162
  # Input block: 'input' 'do' ... 'end'
81
163
  def parse_input_block
82
164
  expect_token(:input)
@@ -341,6 +423,9 @@ module Kumi
341
423
  parse_input_reference
342
424
  elsif token.value == 'index' && peek_token.type == :lparen
343
425
  parse_index_intrinsic
426
+ elsif peek_token.type == :lparen
427
+ # This is a function call like tax(amount: input.amount)
428
+ parse_imported_function_call
344
429
  else
345
430
  advance
346
431
  Kumi::Syntax::DeclarationReference.new(token.value.to_sym, loc: token.location)
@@ -448,7 +533,51 @@ module Kumi
448
533
  args, opts = parse_args_and_opts_inside_parens
449
534
  end
450
535
  # expect_token(:rparen)
451
- Kumi::Syntax::CallExpression.new(fn_name_token.value, args, opts, loc: fn_name_token.location)
536
+
537
+ # Check if this is an imported function call
538
+ if @imported_names.include?(fn_name_token.value) && args.empty? && opts.any?
539
+ # Convert to ImportCall - opts become the input mapping
540
+ Kumi::Syntax::ImportCall.new(fn_name_token.value, opts, loc: fn_name_token.location)
541
+ else
542
+ # Regular call expression
543
+ Kumi::Syntax::CallExpression.new(fn_name_token.value, args, opts, loc: fn_name_token.location)
544
+ end
545
+ end
546
+
547
+ def parse_imported_function_call
548
+ fn_name_token = current_token
549
+ fn_name = fn_name_token.value.to_sym
550
+ advance # consume identifier
551
+ expect_token(:lparen)
552
+
553
+ # Parse keyword arguments for imported function calls
554
+ # Imported functions only accept keyword arguments
555
+ opts = {}
556
+
557
+ unless current_token.type == :rparen
558
+ # Parse keyword arguments with full expression values
559
+ while current_token.type == :label
560
+ key = current_token.value.to_sym
561
+ advance
562
+
563
+ opts[key] = parse_expression
564
+
565
+ break unless current_token.type == :comma
566
+ advance
567
+ skip_comments_and_newlines
568
+ end
569
+ end
570
+
571
+ expect_token(:rparen)
572
+
573
+ # Check if this is an imported function call
574
+ if @imported_names.include?(fn_name) && opts.any?
575
+ # Convert to ImportCall - opts become the input mapping
576
+ Kumi::Syntax::ImportCall.new(fn_name, opts, loc: fn_name_token.location)
577
+ else
578
+ # Regular call expression (shouldn't happen for imported functions)
579
+ Kumi::Syntax::CallExpression.new(fn_name, [], opts, loc: fn_name_token.location)
580
+ end
452
581
  end
453
582
 
454
583
  def parse_array_literal
@@ -163,7 +163,18 @@ module Kumi
163
163
  identifier_or_label_name = consume_while { |c| c.match?(/[a-zA-Z0-9_]/) }
164
164
  location = Kumi::Syntax::Location.new(file: @source_file, line: @line, column: start_column)
165
165
 
166
- # Check if the next character is a colon
166
+ # Check if it's a constant FIRST (e.g., Float::INFINITY or GoldenSchemas::Tax)
167
+ # This needs to be checked before label detection because labels also start with `:``
168
+ if current_char == ':' && peek_char == ':'
169
+ advance # consume first :
170
+ advance # consume second :
171
+ constant_name = consume_while { |c| c.match?(/[a-zA-Z0-9_]/) }
172
+ full_constant = "#{identifier_or_label_name}::#{constant_name}"
173
+ add_token(:constant, full_constant, Kumi::Parser::TOKEN_METADATA[:constant])
174
+ return
175
+ end
176
+
177
+ # Check if the next character is a single colon (label)
167
178
  if current_char == ':'
168
179
  # It's a hash key or a label (e.g., `name:`)
169
180
  advance # consume the colon
@@ -174,16 +185,6 @@ module Kumi
174
185
  # If it's not a label, proceed to check for keywords and identifiers
175
186
  # The logic below is adapted from your original `consume_identifier_or_keyword` method
176
187
 
177
- # Check if it's a constant (e.g., Float::INFINITY)
178
- if identifier_or_label_name == 'Float' && current_char == ':' && peek_char == ':'
179
- advance # consume first :
180
- advance # consume second :
181
- constant_name = consume_while { |c| c.match?(/[a-zA-Z0-9_]/) }
182
- full_constant = "#{identifier_or_label_name}::#{constant_name}"
183
- add_token(:constant, full_constant, Kumi::Parser::TOKEN_METADATA[:constant])
184
- return
185
- end
186
-
187
188
  # Check if it's a keyword
188
189
  if keyword_type = Kumi::Parser::KEYWORDS[identifier_or_label_name]
189
190
  metadata = Kumi::Parser::TOKEN_METADATA[keyword_type].dup
@@ -20,6 +20,8 @@ module Kumi
20
20
  INPUT = :input
21
21
  VALUE = :value
22
22
  TRAIT = :trait
23
+ IMPORT = :import
24
+ FROM = :from
23
25
  DO = :do
24
26
  END_KW = :end
25
27
  ON = :on
@@ -28,6 +30,7 @@ module Kumi
28
30
  # Type keywords
29
31
  INTEGER_TYPE = :integer_type # integer
30
32
  FLOAT_TYPE = :float_type # float
33
+ DECIMAL_TYPE = :decimal_type # decimal
31
34
  STRING_TYPE = :string_type # string
32
35
  BOOLEAN_TYPE = :boolean_type # boolean
33
36
  ANY_TYPE = :any_type # any
@@ -99,6 +102,14 @@ module Kumi
99
102
  expects_expression: true,
100
103
  declaration_type: :trait
101
104
  },
105
+ import: {
106
+ category: :keyword,
107
+ import_declaration: true
108
+ },
109
+ from: {
110
+ category: :keyword,
111
+ import_source: true
112
+ },
102
113
  do: {
103
114
  category: :keyword,
104
115
  block_opener: true
@@ -130,6 +141,11 @@ module Kumi
130
141
  starts_declaration: true,
131
142
  type_name: :float
132
143
  },
144
+ decimal_type: {
145
+ category: :type_keyword,
146
+ starts_declaration: true,
147
+ type_name: :decimal
148
+ },
133
149
  string_type: {
134
150
  category: :type_keyword,
135
151
  starts_declaration: true,
@@ -401,7 +417,11 @@ module Kumi
401
417
  'select' => '__select__',
402
418
  'shift' => 'shift',
403
419
  'roll' => 'roll',
404
- 'index' => 'index'
420
+ 'index' => 'index',
421
+ 'to_decimal' => 'to_decimal',
422
+ 'to_integer' => 'to_integer',
423
+ 'to_float' => 'to_float',
424
+ 'to_string' => 'to_string'
405
425
  }
406
426
 
407
427
  # Keywords mapping
@@ -411,6 +431,8 @@ module Kumi
411
431
  'value' => :value,
412
432
  'let' => :let,
413
433
  'trait' => :trait,
434
+ 'import' => :import,
435
+ 'from' => :from,
414
436
  'do' => :do,
415
437
  'end' => :end,
416
438
  'on' => :on,
@@ -420,6 +442,7 @@ module Kumi
420
442
  'false' => :boolean,
421
443
  'integer' => :integer_type,
422
444
  'float' => :float_type,
445
+ 'decimal' => :decimal_type,
423
446
  'string' => :string_type,
424
447
  'boolean' => :boolean_type,
425
448
  'any' => :any_type,
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Kumi
4
4
  module Parser
5
- VERSION = '0.0.27'
5
+ VERSION = '0.0.29'
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.27
4
+ version: 0.0.29
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-10-13 00:00:00.000000000 Z
11
+ date: 2025-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parslet