kumi-parser 0.0.28 → 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 +4 -4
- data/lib/kumi/parser/direct_parser.rb +130 -1
- data/lib/kumi/parser/smart_tokenizer.rb +12 -11
- data/lib/kumi/parser/token_constants.rb +12 -0
- 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: 6662f28685ceb786c8ce4153325bf7bfa3456317b0cfa60d1a488bbc8889db8b
|
|
4
|
+
data.tar.gz: 67210cf3da7c670e7c5ad06027cd7c096cd7ea568f254ace4ebeec6996cd34ce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
|
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
|
|
@@ -100,6 +102,14 @@ module Kumi
|
|
|
100
102
|
expects_expression: true,
|
|
101
103
|
declaration_type: :trait
|
|
102
104
|
},
|
|
105
|
+
import: {
|
|
106
|
+
category: :keyword,
|
|
107
|
+
import_declaration: true
|
|
108
|
+
},
|
|
109
|
+
from: {
|
|
110
|
+
category: :keyword,
|
|
111
|
+
import_source: true
|
|
112
|
+
},
|
|
103
113
|
do: {
|
|
104
114
|
category: :keyword,
|
|
105
115
|
block_opener: true
|
|
@@ -421,6 +431,8 @@ module Kumi
|
|
|
421
431
|
'value' => :value,
|
|
422
432
|
'let' => :let,
|
|
423
433
|
'trait' => :trait,
|
|
434
|
+
'import' => :import,
|
|
435
|
+
'from' => :from,
|
|
424
436
|
'do' => :do,
|
|
425
437
|
'end' => :end,
|
|
426
438
|
'on' => :on,
|
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.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-
|
|
11
|
+
date: 2025-10-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: parslet
|