kumi-parser 0.0.2 โ†’ 0.0.4

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.
@@ -0,0 +1,146 @@
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.attributes.count}"
74
+ ast.attributes.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
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/kumi/parser/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'kumi-parser'
7
+ spec.version = Kumi::Parser::VERSION
8
+ spec.authors = ['Kumi Team']
9
+ spec.email = ['dev@kumi.ai']
10
+
11
+ spec.summary = 'Text parser for Kumi'
12
+ spec.description = 'Allows Kumi schemas to be written as plain text with syntax validation and editor integration.'
13
+ spec.homepage = 'https://github.com/amuta/kumi-parser'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.0.0'
16
+
17
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = 'https://github.com/amuta/kumi-parser'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/amuta/kumi-parser/blob/main/CHANGELOG.md'
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ spec.files = Dir.chdir(__dir__) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (File.expand_path(f) == __FILE__) ||
26
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile]) ||
27
+ f.end_with?('.gem')
28
+ end
29
+ end
30
+ spec.bindir = 'exe'
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ['lib']
33
+
34
+ # Dependencies
35
+ spec.add_dependency 'kumi', '~> 0.0.7'
36
+ spec.add_dependency 'parslet', '~> 2.0'
37
+ spec.add_dependency 'zeitwerk', '~> 2.6'
38
+
39
+ # Development dependencies
40
+ spec.add_development_dependency 'bundler', '~> 2.0'
41
+ spec.add_development_dependency 'rake', '~> 13.0'
42
+ spec.add_development_dependency 'rspec', '~> 3.0'
43
+ spec.add_development_dependency 'rubocop', '~> 1.21'
44
+ spec.add_development_dependency 'simplecov', '~> 0.22'
45
+ end
@@ -0,0 +1,51 @@
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