kumi-parser 0.0.32 โ 0.1.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 +4 -4
- data/.rubocop.yml +41 -0
- data/CHANGELOG.md +64 -0
- data/CLAUDE.md +59 -120
- data/README.md +28 -6
- data/examples/parse_and_inspect.rb +34 -0
- data/kumi-parser.gemspec +3 -4
- data/lib/kumi/parser/grammar.rb +120 -0
- data/lib/kumi/parser/lexer.rb +232 -0
- data/lib/kumi/parser/parse_error.rb +52 -0
- data/lib/kumi/parser/parser.rb +692 -0
- data/lib/kumi/parser/source.rb +76 -0
- data/lib/kumi/parser/text_parser.rb +37 -27
- data/lib/kumi/parser/token.rb +10 -71
- data/lib/kumi/parser/version.rb +1 -1
- data/lib/kumi-parser.rb +9 -10
- metadata +16 -37
- data/examples/debug_text_parser.rb +0 -41
- data/examples/debug_transform_rule.rb +0 -26
- data/examples/text_parser_comprehensive_test.rb +0 -333
- data/examples/text_parser_test_with_comments.rb +0 -146
- data/lib/kumi/parser/base.rb +0 -51
- data/lib/kumi/parser/direct_parser.rb +0 -698
- data/lib/kumi/parser/error_extractor.rb +0 -89
- data/lib/kumi/parser/errors.rb +0 -40
- data/lib/kumi/parser/helpers.rb +0 -154
- data/lib/kumi/parser/smart_tokenizer.rb +0 -373
- data/lib/kumi/parser/syntax_validator.rb +0 -21
- data/lib/kumi/parser/text_parser/api.rb +0 -60
- data/lib/kumi/parser/token_constants.rb +0 -467
- data/lib/kumi/text_parser.rb +0 -40
- data/lib/kumi/text_schema.rb +0 -31
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative '../lib/kumi'
|
|
4
|
-
|
|
5
|
-
# Comprehensive test of all text parser supported features
|
|
6
|
-
# This example tests what the text parser ACTUALLY supports
|
|
7
|
-
# (not what the Ruby DSL supports)
|
|
8
|
-
|
|
9
|
-
module TextParserComprehensiveTest
|
|
10
|
-
extend Kumi::Schema
|
|
11
|
-
|
|
12
|
-
schema do
|
|
13
|
-
input do
|
|
14
|
-
# Basic type declarations
|
|
15
|
-
integer :age, domain: 18..65
|
|
16
|
-
float :score, domain: 0.0..100.0
|
|
17
|
-
string :status, domain: %w[active inactive suspended]
|
|
18
|
-
boolean :verified
|
|
19
|
-
any :metadata
|
|
20
|
-
|
|
21
|
-
# Nested array declarations
|
|
22
|
-
array :items do
|
|
23
|
-
string :name
|
|
24
|
-
float :price
|
|
25
|
-
integer :quantity
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# ============================================================
|
|
30
|
-
# ARITHMETIC OPERATIONS - All supported operators
|
|
31
|
-
# ============================================================
|
|
32
|
-
|
|
33
|
-
# Addition and subtraction
|
|
34
|
-
value :total_price, input.items.price + input.items.quantity
|
|
35
|
-
value :price_diff, input.items.price - 10.0
|
|
36
|
-
|
|
37
|
-
# Multiplication and division
|
|
38
|
-
value :scaled_score, input.score * 1.5
|
|
39
|
-
value :average_score, input.score / 2
|
|
40
|
-
|
|
41
|
-
# Modulo
|
|
42
|
-
value :remainder, input.age % 10
|
|
43
|
-
|
|
44
|
-
# Complex arithmetic with parentheses
|
|
45
|
-
value :complex_calc, (input.score * 2.5) + (input.age / 2) - 10
|
|
46
|
-
|
|
47
|
-
# ============================================================
|
|
48
|
-
# COMPARISON OPERATIONS - All comparison operators
|
|
49
|
-
# ============================================================
|
|
50
|
-
|
|
51
|
-
trait :adult, input.age >= 18
|
|
52
|
-
trait :senior, input.age > 60
|
|
53
|
-
trait :young_adult, input.age <= 25
|
|
54
|
-
trait :is_teen, input.age < 20
|
|
55
|
-
trait :perfect_score, input.score == 100.0
|
|
56
|
-
trait :not_perfect, input.score != 100.0
|
|
57
|
-
|
|
58
|
-
# String comparisons
|
|
59
|
-
trait :is_active, input.status == 'active'
|
|
60
|
-
trait :not_suspended, input.status != 'suspended'
|
|
61
|
-
|
|
62
|
-
# Boolean comparisons
|
|
63
|
-
trait :is_verified, input.verified == true
|
|
64
|
-
trait :not_verified, input.verified == false
|
|
65
|
-
|
|
66
|
-
# ============================================================
|
|
67
|
-
# LOGICAL OPERATIONS - AND and OR
|
|
68
|
-
# ============================================================
|
|
69
|
-
|
|
70
|
-
# AND operations using &
|
|
71
|
-
trait :eligible, (input.age >= 18) & (input.verified == true) & (input.status == 'active')
|
|
72
|
-
trait :premium_user, is_verified & is_active & (input.score > 80.0)
|
|
73
|
-
|
|
74
|
-
# OR operations using |
|
|
75
|
-
trait :needs_attention, (input.status == 'suspended') | (input.verified == false)
|
|
76
|
-
trait :special_case, senior | (input.score >= 95.0)
|
|
77
|
-
|
|
78
|
-
# Complex logical expressions
|
|
79
|
-
trait :complex_logic, (adult & is_verified) | (senior & is_active)
|
|
80
|
-
|
|
81
|
-
# ============================================================
|
|
82
|
-
# FUNCTION CALLS - fn(:name, args) syntax
|
|
83
|
-
# ============================================================
|
|
84
|
-
|
|
85
|
-
# Math functions
|
|
86
|
-
value :absolute_diff, fn(:abs, fn(:subtract, input.score, 50.0))
|
|
87
|
-
value :rounded_score, fn(:round, input.score)
|
|
88
|
-
value :clamped_score, fn(:clamp, input.score, 20.0, 80.0)
|
|
89
|
-
|
|
90
|
-
# String functions (note: no method syntax like .length)
|
|
91
|
-
value :name_length, fn(:string_length, input.status)
|
|
92
|
-
value :uppercase_status, fn(:upcase, input.status)
|
|
93
|
-
value :lowercase_status, fn(:downcase, input.status)
|
|
94
|
-
|
|
95
|
-
# Collection/aggregation functions on arrays
|
|
96
|
-
value :total_items, fn(:sum, input.items.quantity)
|
|
97
|
-
value :item_count, fn(:size, input.items)
|
|
98
|
-
value :max_price, fn(:max, input.items.price)
|
|
99
|
-
value :min_price, fn(:min, input.items.price)
|
|
100
|
-
|
|
101
|
-
# ============================================================
|
|
102
|
-
# REFERENCES - Both bare identifiers and ref() NOT supported in text parser
|
|
103
|
-
# ============================================================
|
|
104
|
-
|
|
105
|
-
# Using previously defined values/traits (bare identifiers)
|
|
106
|
-
trait :super_eligible, eligible & premium_user
|
|
107
|
-
value :bonus_points, fn(:multiply, total_price, 0.1)
|
|
108
|
-
|
|
109
|
-
# ============================================================
|
|
110
|
-
# CASCADE EXPRESSIONS - on/base syntax
|
|
111
|
-
# ============================================================
|
|
112
|
-
|
|
113
|
-
value :user_tier do
|
|
114
|
-
on premium_user, 'premium'
|
|
115
|
-
on eligible, 'standard'
|
|
116
|
-
on is_verified, 'basic'
|
|
117
|
-
base 'guest'
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
value :discount_rate do
|
|
121
|
-
on senior, 0.25
|
|
122
|
-
on premium_user, 0.15
|
|
123
|
-
on is_active, 0.05
|
|
124
|
-
base 0.0
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Cascades using complex conditions
|
|
128
|
-
value :risk_level do
|
|
129
|
-
on complex_logic, 'low'
|
|
130
|
-
on needs_attention, 'high'
|
|
131
|
-
on is_active, 'medium'
|
|
132
|
-
base 'unknown'
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# ============================================================
|
|
136
|
-
# NESTED INPUT REFERENCES - Deep field access
|
|
137
|
-
# ============================================================
|
|
138
|
-
|
|
139
|
-
# Direct nested field access (parsed as multi-part input reference)
|
|
140
|
-
value :first_item_name, input.items.name
|
|
141
|
-
value :all_prices, input.items.price
|
|
142
|
-
|
|
143
|
-
# Operations on nested fields (broadcasting)
|
|
144
|
-
value :discounted_prices, input.items.price * 0.9
|
|
145
|
-
|
|
146
|
-
# ============================================================
|
|
147
|
-
# LITERALS - All supported literal types
|
|
148
|
-
# ============================================================
|
|
149
|
-
|
|
150
|
-
# Number literals (integers and floats)
|
|
151
|
-
value :constant_int, 42
|
|
152
|
-
value :constant_float, 3.14159
|
|
153
|
-
|
|
154
|
-
# String literals (double quotes only)
|
|
155
|
-
value :greeting, 'Hello, World!'
|
|
156
|
-
|
|
157
|
-
# Boolean literals
|
|
158
|
-
value :always_true, true
|
|
159
|
-
value :always_false, false
|
|
160
|
-
|
|
161
|
-
# ============================================================
|
|
162
|
-
# EDGE CASES AND LIMITATIONS
|
|
163
|
-
# ============================================================
|
|
164
|
-
|
|
165
|
-
# Complex parenthesized expressions
|
|
166
|
-
value :nested_parens, ((input.age + 10) * 2) / (input.score - 20)
|
|
167
|
-
|
|
168
|
-
# Multiple operators in sequence
|
|
169
|
-
value :operator_chain, input.age + 10 - 5 + 20 - 3
|
|
170
|
-
|
|
171
|
-
# Nested function calls
|
|
172
|
-
value :nested_functions, fn(:round, fn(:multiply, fn(:add, input.score, 10), 1.5))
|
|
173
|
-
|
|
174
|
-
# ============================================================
|
|
175
|
-
# NOT SUPPORTED (would fail in text parser)
|
|
176
|
-
# ============================================================
|
|
177
|
-
|
|
178
|
-
# These are commented out as they would cause parse errors:
|
|
179
|
-
|
|
180
|
-
# Array literals in expressions
|
|
181
|
-
# value :array_literal, [1, 2, 3, 4, 5]
|
|
182
|
-
# value :max_of_array, fn(:max, [input.age, 30, 50])
|
|
183
|
-
|
|
184
|
-
# Method-style function calls
|
|
185
|
-
# value :method_style, fn.add(input.age, 10)
|
|
186
|
-
|
|
187
|
-
# Sugar method calls on fields
|
|
188
|
-
# trait :long_status, input.status.length > 5
|
|
189
|
-
# value :name_chars, input.status.size
|
|
190
|
-
|
|
191
|
-
# Ternary/conditional expressions
|
|
192
|
-
# value :conditional, eligible ? 100 : 0
|
|
193
|
-
|
|
194
|
-
# Hash literals
|
|
195
|
-
# value :options, { min: 0, max: 100 }
|
|
196
|
-
|
|
197
|
-
# ref() syntax
|
|
198
|
-
# trait :ref_example, ref(:eligible) & ref(:premium_user)
|
|
199
|
-
|
|
200
|
-
# Symbol literals (except in fn() calls)
|
|
201
|
-
# value :symbol_value, :active
|
|
202
|
-
|
|
203
|
-
# Power operator
|
|
204
|
-
# value :squared, input.age ** 2
|
|
205
|
-
|
|
206
|
-
# Comments within the DSL
|
|
207
|
-
# value :test, 42 # this would fail
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
# Test the schema with the text parser
|
|
212
|
-
if __FILE__ == $0
|
|
213
|
-
require_relative '../lib/kumi/text_parser'
|
|
214
|
-
|
|
215
|
-
# Create a clean schema without comments for text parser testing
|
|
216
|
-
schema_text = <<~SCHEMA
|
|
217
|
-
schema do
|
|
218
|
-
input do
|
|
219
|
-
integer :age, domain: 18..65
|
|
220
|
-
float :score, domain: 0.0..100.0
|
|
221
|
-
string :status, domain: %w[active inactive suspended]
|
|
222
|
-
boolean :verified
|
|
223
|
-
any :metadata
|
|
224
|
-
#{' '}
|
|
225
|
-
array :items do
|
|
226
|
-
string :name
|
|
227
|
-
float :price
|
|
228
|
-
integer :quantity
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
value :total_price, input.items.price + input.items.quantity
|
|
233
|
-
value :price_diff, input.items.price - 10.0
|
|
234
|
-
value :scaled_score, input.score * 1.5
|
|
235
|
-
value :average_score, input.score / 2
|
|
236
|
-
value :remainder, input.age % 10
|
|
237
|
-
value :complex_calc, (input.score * 2.5) + (input.age / 2) - 10
|
|
238
|
-
#{' '}
|
|
239
|
-
trait :adult, input.age >= 18
|
|
240
|
-
trait :senior, input.age > 60
|
|
241
|
-
trait :young_adult, input.age <= 25
|
|
242
|
-
trait :is_teen, input.age < 20
|
|
243
|
-
trait :perfect_score, input.score == 100.0
|
|
244
|
-
trait :not_perfect, input.score != 100.0
|
|
245
|
-
trait :is_active, input.status == "active"
|
|
246
|
-
trait :not_suspended, input.status != "suspended"
|
|
247
|
-
trait :is_verified, input.verified == true
|
|
248
|
-
trait :not_verified, input.verified == false
|
|
249
|
-
#{' '}
|
|
250
|
-
trait :eligible, (input.age >= 18) & (input.verified == true) & (input.status == "active")
|
|
251
|
-
trait :premium_user, is_verified & is_active & (input.score > 80.0)
|
|
252
|
-
trait :needs_attention, (input.status == "suspended") | (input.verified == false)
|
|
253
|
-
trait :special_case, senior | (input.score >= 95.0)
|
|
254
|
-
trait :complex_logic, (adult & is_verified) | (senior & is_active)
|
|
255
|
-
#{' '}
|
|
256
|
-
value :absolute_diff, fn(:abs, fn(:subtract, input.score, 50.0))
|
|
257
|
-
value :rounded_score, fn(:round, input.score)
|
|
258
|
-
value :clamped_score, fn(:clamp, input.score, 20.0, 80.0)
|
|
259
|
-
value :name_length, fn(:string_length, input.status)
|
|
260
|
-
value :uppercase_status, fn(:upcase, input.status)
|
|
261
|
-
value :lowercase_status, fn(:downcase, input.status)
|
|
262
|
-
value :total_items, fn(:sum, input.items.quantity)
|
|
263
|
-
value :item_count, fn(:size, input.items)
|
|
264
|
-
value :max_price, fn(:max, input.items.price)
|
|
265
|
-
value :min_price, fn(:min, input.items.price)
|
|
266
|
-
#{' '}
|
|
267
|
-
trait :super_eligible, eligible & premium_user
|
|
268
|
-
value :bonus_points, fn(:multiply, total_price, 0.1)
|
|
269
|
-
#{' '}
|
|
270
|
-
value :user_tier do
|
|
271
|
-
on premium_user, "premium"
|
|
272
|
-
on eligible, "standard"
|
|
273
|
-
on is_verified, "basic"
|
|
274
|
-
base "guest"
|
|
275
|
-
end
|
|
276
|
-
#{' '}
|
|
277
|
-
value :discount_rate do
|
|
278
|
-
on senior, 0.25
|
|
279
|
-
on premium_user, 0.15
|
|
280
|
-
on is_active, 0.05
|
|
281
|
-
base 0.0
|
|
282
|
-
end
|
|
283
|
-
#{' '}
|
|
284
|
-
value :risk_level do
|
|
285
|
-
on complex_logic, "low"
|
|
286
|
-
on needs_attention, "high"
|
|
287
|
-
on is_active, "medium"
|
|
288
|
-
base "unknown"
|
|
289
|
-
end
|
|
290
|
-
#{' '}
|
|
291
|
-
value :first_item_name, input.items.name
|
|
292
|
-
value :all_prices, input.items.price
|
|
293
|
-
value :discounted_prices, input.items.price * 0.9
|
|
294
|
-
#{' '}
|
|
295
|
-
value :constant_int, 42
|
|
296
|
-
value :constant_float, 3.14159
|
|
297
|
-
value :greeting, "Hello, World!"
|
|
298
|
-
value :always_true, true
|
|
299
|
-
value :always_false, false
|
|
300
|
-
#{' '}
|
|
301
|
-
value :nested_parens, ((input.age + 10) * 2) / (input.score - 20)
|
|
302
|
-
value :operator_chain, input.age + 10 - 5 + 20 - 3
|
|
303
|
-
value :nested_functions, fn(:round, fn(:multiply, fn(:add, input.score, 10), 1.5))
|
|
304
|
-
end
|
|
305
|
-
SCHEMA
|
|
306
|
-
|
|
307
|
-
puts 'Testing text parser with comprehensive DSL...'
|
|
308
|
-
puts '=' * 60
|
|
309
|
-
|
|
310
|
-
begin
|
|
311
|
-
# Validate the schema
|
|
312
|
-
diagnostics = Kumi::TextParser.validate(schema_text)
|
|
313
|
-
|
|
314
|
-
if diagnostics.empty?
|
|
315
|
-
puts 'โ
Schema is valid!'
|
|
316
|
-
|
|
317
|
-
# Parse and show some info
|
|
318
|
-
ast = Kumi::TextParser.parse(schema_text)
|
|
319
|
-
puts "\nParsed successfully!"
|
|
320
|
-
puts "- Input fields: #{ast.inputs.map(&:name).join(', ')}"
|
|
321
|
-
puts "- Values: #{ast.values.count}"
|
|
322
|
-
puts "- Traits: #{ast.traits.count}"
|
|
323
|
-
else
|
|
324
|
-
puts 'โ Schema has errors:'
|
|
325
|
-
diagnostics.to_a.each do |diagnostic|
|
|
326
|
-
puts " Line #{diagnostic.line}, Column #{diagnostic.column}: #{diagnostic.message}"
|
|
327
|
-
end
|
|
328
|
-
end
|
|
329
|
-
rescue StandardError => e
|
|
330
|
-
puts "โ Parser error: #{e.message}"
|
|
331
|
-
puts e.backtrace.first(5)
|
|
332
|
-
end
|
|
333
|
-
end
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative '../lib/kumi/text_parser'
|
|
4
|
-
|
|
5
|
-
# Test schema with comments and verify AST structure
|
|
6
|
-
schema_text = <<~SCHEMA
|
|
7
|
-
schema do
|
|
8
|
-
# Input section with type declarations
|
|
9
|
-
input do
|
|
10
|
-
integer :age, domain: 18..65 # User's age
|
|
11
|
-
float :score, domain: 0.0..100.0 # Test score
|
|
12
|
-
string :status, domain: %w[active inactive] # User status
|
|
13
|
-
boolean :verified # Verification status
|
|
14
|
-
#{' '}
|
|
15
|
-
# Nested array example
|
|
16
|
-
array :items do
|
|
17
|
-
string :name # Item name
|
|
18
|
-
float :price # Item price
|
|
19
|
-
integer :quantity # Item quantity
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Basic arithmetic operations
|
|
24
|
-
value :total_price, input.items.price + input.items.quantity
|
|
25
|
-
value :scaled_score, input.score * 1.5
|
|
26
|
-
#{' '}
|
|
27
|
-
# Trait definitions with comparisons
|
|
28
|
-
trait :adult, input.age >= 18 # Is adult
|
|
29
|
-
trait :high_scorer, input.score > 80.0 # High score
|
|
30
|
-
trait :is_active, input.status == "active" # Active user
|
|
31
|
-
#{' '}
|
|
32
|
-
# Complex logical operations
|
|
33
|
-
trait :eligible, adult & is_active & (input.verified == true)
|
|
34
|
-
#{' '}
|
|
35
|
-
# Function calls
|
|
36
|
-
value :rounded_score, fn(:round, input.score)
|
|
37
|
-
value :item_count, fn(:size, input.items)
|
|
38
|
-
#{' '}
|
|
39
|
-
# Cascade expressions
|
|
40
|
-
value :user_level do
|
|
41
|
-
on high_scorer, "premium" # Premium users
|
|
42
|
-
on eligible, "standard" # Standard users#{' '}
|
|
43
|
-
base "basic" # Default level
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
SCHEMA
|
|
47
|
-
|
|
48
|
-
puts 'Testing text parser with comments...'
|
|
49
|
-
puts '=' * 50
|
|
50
|
-
|
|
51
|
-
begin
|
|
52
|
-
# Test parsing
|
|
53
|
-
diagnostics = Kumi::TextParser.validate(schema_text)
|
|
54
|
-
|
|
55
|
-
if diagnostics.empty?
|
|
56
|
-
puts 'โ
Schema parsed successfully!'
|
|
57
|
-
|
|
58
|
-
# Parse and examine AST structure
|
|
59
|
-
ast = Kumi::TextParser.parse(schema_text)
|
|
60
|
-
|
|
61
|
-
puts "\n๐ AST Structure:"
|
|
62
|
-
puts "- Root type: #{ast.class.name}"
|
|
63
|
-
puts "- Input fields: #{ast.inputs.count}"
|
|
64
|
-
ast.inputs.each_with_index do |input, i|
|
|
65
|
-
puts " #{i + 1}. #{input.name} (#{input.type})"
|
|
66
|
-
next unless input.children && input.children.any?
|
|
67
|
-
|
|
68
|
-
input.children.each do |child|
|
|
69
|
-
puts " - #{child.name} (#{child.type})"
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
puts "- Value declarations: #{ast.values.count}"
|
|
74
|
-
ast.values.each_with_index do |value, i|
|
|
75
|
-
puts " #{i + 1}. #{value.name}"
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
puts "- Trait declarations: #{ast.traits.count}"
|
|
79
|
-
ast.traits.each_with_index do |trait, i|
|
|
80
|
-
puts " #{i + 1}. #{trait.name}"
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# Test with actual Ruby DSL to verify it works end-to-end
|
|
84
|
-
puts "\n๐งช Testing with full Kumi analysis..."
|
|
85
|
-
begin
|
|
86
|
-
analyzer = Kumi::Analyzer.new
|
|
87
|
-
analysis_result = analyzer.analyze(ast)
|
|
88
|
-
|
|
89
|
-
if analysis_result.errors.any?
|
|
90
|
-
puts 'โ Analysis errors:'
|
|
91
|
-
analysis_result.errors.each do |error|
|
|
92
|
-
if error.respond_to?(:message)
|
|
93
|
-
puts " - #{error.message}"
|
|
94
|
-
elsif error.is_a?(Array)
|
|
95
|
-
puts " - #{error[1]}"
|
|
96
|
-
else
|
|
97
|
-
puts " - #{error}"
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
else
|
|
101
|
-
puts 'โ
Schema analysis successful!'
|
|
102
|
-
|
|
103
|
-
# Try to create a compiled schema
|
|
104
|
-
compiler = Kumi::Compiler.new
|
|
105
|
-
compiled = compiler.compile(ast, analysis_result)
|
|
106
|
-
puts 'โ
Schema compilation successful!'
|
|
107
|
-
|
|
108
|
-
# Test execution with sample data
|
|
109
|
-
test_data = {
|
|
110
|
-
age: 25,
|
|
111
|
-
score: 85.5,
|
|
112
|
-
status: 'active',
|
|
113
|
-
verified: true,
|
|
114
|
-
items: [
|
|
115
|
-
{ name: 'Item 1', price: 10.0, quantity: 2 },
|
|
116
|
-
{ name: 'Item 2', price: 15.0, quantity: 1 }
|
|
117
|
-
]
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
runner = Kumi::Runner.new(compiled, test_data)
|
|
121
|
-
|
|
122
|
-
# Test a few key calculations
|
|
123
|
-
puts "\n๐ฏ Test Results:"
|
|
124
|
-
puts "- total_price: #{runner.fetch(:total_price)}"
|
|
125
|
-
puts "- scaled_score: #{runner.fetch(:scaled_score)}"
|
|
126
|
-
puts "- adult: #{runner.fetch(:adult)}"
|
|
127
|
-
puts "- eligible: #{runner.fetch(:eligible)}"
|
|
128
|
-
puts "- user_level: #{runner.fetch(:user_level)}"
|
|
129
|
-
puts "- item_count: #{runner.fetch(:item_count)}"
|
|
130
|
-
|
|
131
|
-
end
|
|
132
|
-
rescue StandardError => e
|
|
133
|
-
puts "โ Analysis/compilation error: #{e.message}"
|
|
134
|
-
puts e.backtrace.first(3)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
else
|
|
138
|
-
puts 'โ Schema has parse errors:'
|
|
139
|
-
diagnostics.to_a.each do |diagnostic|
|
|
140
|
-
puts " Line #{diagnostic.line}, Column #{diagnostic.column}: #{diagnostic.message}"
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
rescue StandardError => e
|
|
144
|
-
puts "โ Unexpected error: #{e.message}"
|
|
145
|
-
puts e.backtrace.first(5)
|
|
146
|
-
end
|
data/lib/kumi/parser/base.rb
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'smart_tokenizer'
|
|
4
|
-
require_relative 'direct_parser'
|
|
5
|
-
require_relative 'errors'
|
|
6
|
-
|
|
7
|
-
module Kumi
|
|
8
|
-
module Parser
|
|
9
|
-
# Text parser using tokenizer + direct AST construction
|
|
10
|
-
class Base
|
|
11
|
-
def self.parse(source, source_file: '<input>')
|
|
12
|
-
tokens = SmartTokenizer.new(source, source_file: source_file).tokenize
|
|
13
|
-
Kumi::Parser::DirectParser.new(tokens).parse
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def self.valid?(source, source_file: '<input>')
|
|
17
|
-
parse(source, source_file: source_file)
|
|
18
|
-
true
|
|
19
|
-
rescue Errors::TokenizerError, Errors::ParseError
|
|
20
|
-
false
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def self.validate(source, source_file: '<input>')
|
|
24
|
-
parse(source, source_file: source_file)
|
|
25
|
-
[]
|
|
26
|
-
rescue Errors::TokenizerError, Errors::ParseError => e
|
|
27
|
-
[create_diagnostic(e, source_file)]
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def self.create_diagnostic(error, source_file)
|
|
33
|
-
location = if error.is_a?(Errors::ParseError) && error.token
|
|
34
|
-
error.token.location
|
|
35
|
-
elsif error.respond_to?(:location)
|
|
36
|
-
error.location
|
|
37
|
-
else
|
|
38
|
-
nil
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
{
|
|
42
|
-
line: location&.line || 1,
|
|
43
|
-
column: location&.column || 1,
|
|
44
|
-
message: error.message,
|
|
45
|
-
severity: :error,
|
|
46
|
-
type: :syntax
|
|
47
|
-
}
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|