kumi 0.0.8 → 0.0.9
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/README.md +13 -0
- data/docs/AST.md +7 -0
- data/docs/features/README.md +7 -0
- data/docs/features/s-expression-printer.md +77 -0
- data/lib/kumi/support/s_expression_printer.rb +161 -0
- data/lib/kumi/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5f9f087001a482c906f041da8cb229511966a11c56e3ced99930b59d4141543
|
4
|
+
data.tar.gz: fe5fafe03a5ca1e414b5bc3af9eccd830553aa8f44762e9d9b38e589fa342fd1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b26279c08a9387616104252d24147c920f27b0f66574cde1987024325c5fd1ba87aff876b39fdc3d4f472d9aba2c713b19a5105890b66ac3ef10a3018a0b1bca
|
7
|
+
data.tar.gz: d4c8db755880cd7088fd4b6087d2f38e86a4fdee11f867d58a480e143494ca2b85440abd9249b8c02a469a9ad21642c287d5ef8f30948bdc6d67695a895c1c13
|
data/README.md
CHANGED
@@ -325,6 +325,19 @@ Kumi::Explain.call(FederalTax2024, :fed_tax, inputs: {income: 100_000, filing_st
|
|
325
325
|
# = 15,099.50
|
326
326
|
```
|
327
327
|
|
328
|
+
**Debug AST Structure**: Visualize the parsed schema as S-expressions:
|
329
|
+
|
330
|
+
```ruby
|
331
|
+
puts Kumi::Support::SExpressionPrinter.print(FederalTax2024.__syntax_tree__)
|
332
|
+
# => (Root
|
333
|
+
# inputs: [
|
334
|
+
# (InputDeclaration :income :float)
|
335
|
+
# (InputDeclaration :filing_status :string domain: ["single", "married_joint"])
|
336
|
+
# ]
|
337
|
+
# traits: [...]
|
338
|
+
# attributes: [...])
|
339
|
+
```
|
340
|
+
|
328
341
|
</details>
|
329
342
|
|
330
343
|
<details>
|
data/docs/AST.md
CHANGED
@@ -39,6 +39,13 @@ InputReference = Struct.new(:name)
|
|
39
39
|
# Has operator methods: >=, <=, >, <, ==, != that create CallExpression nodes
|
40
40
|
```
|
41
41
|
|
42
|
+
**InputElementReference**: Access of nested input fields (`input.field_name.element.subelement.subsubelement`)
|
43
|
+
```ruby
|
44
|
+
InputElementReference = Struct.new(:path)
|
45
|
+
# Represents nested input access
|
46
|
+
# DSL: input.address.street → InputElementReference([:address, :street])
|
47
|
+
```
|
48
|
+
|
42
49
|
**DeclarationReference**: References to other declarations
|
43
50
|
```ruby
|
44
51
|
DeclarationReference = Struct.new(:name)
|
data/docs/features/README.md
CHANGED
@@ -37,6 +37,13 @@ Processes large schemas with optimized algorithms.
|
|
37
37
|
- Result caching
|
38
38
|
- Selective evaluation
|
39
39
|
|
40
|
+
### [S-Expression Printer](s-expression-printer.md)
|
41
|
+
Debug and inspect AST structures with readable S-expression notation output.
|
42
|
+
|
43
|
+
- Visitor pattern implementation for all node types
|
44
|
+
- Proper indentation and hierarchical structure
|
45
|
+
- Useful for debugging schema parsing and AST analysis
|
46
|
+
|
40
47
|
## Integration
|
41
48
|
|
42
49
|
- Type inference uses input declarations
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# S-Expression Printer
|
2
|
+
|
3
|
+
Debug and inspect Kumi AST structures with readable S-expression notation output.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
The S-Expression Printer provides a clean, structured way to visualize Kumi's Abstract Syntax Tree (AST) nodes in traditional Lisp-style S-expression format. This is particularly useful for debugging schema parsing, understanding AST structure, and analyzing complex expressions.
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
require 'kumi/support/s_expression_printer'
|
13
|
+
|
14
|
+
# Print any AST node
|
15
|
+
Kumi::Support::SExpressionPrinter.print(node)
|
16
|
+
|
17
|
+
# Print a complete schema AST
|
18
|
+
module MySchema
|
19
|
+
extend Kumi::Schema
|
20
|
+
|
21
|
+
schema do
|
22
|
+
input do
|
23
|
+
integer :age
|
24
|
+
string :name
|
25
|
+
end
|
26
|
+
|
27
|
+
trait :adult, (input.age >= 18)
|
28
|
+
value :greeting, fn(:concat, "Hello ", input.name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
puts Kumi::Support::SExpressionPrinter.print(MySchema.__syntax_tree__)
|
33
|
+
```
|
34
|
+
|
35
|
+
## Output Format
|
36
|
+
|
37
|
+
The printer produces indented S-expressions that clearly show the hierarchical structure:
|
38
|
+
|
39
|
+
```lisp
|
40
|
+
(Root
|
41
|
+
inputs: [
|
42
|
+
(InputDeclaration :age :integer)
|
43
|
+
(InputDeclaration :name :string)
|
44
|
+
]
|
45
|
+
attributes: [
|
46
|
+
(ValueDeclaration :greeting
|
47
|
+
(CallExpression :concat
|
48
|
+
(Literal "Hello ")
|
49
|
+
(InputReference :name)
|
50
|
+
)
|
51
|
+
)
|
52
|
+
]
|
53
|
+
traits: [
|
54
|
+
(TraitDeclaration :adult
|
55
|
+
(CallExpression :>=
|
56
|
+
(InputReference :age)
|
57
|
+
(Literal 18)
|
58
|
+
)
|
59
|
+
)
|
60
|
+
]
|
61
|
+
)
|
62
|
+
```
|
63
|
+
|
64
|
+
## Supported Node Types
|
65
|
+
|
66
|
+
The printer handles all Kumi AST node types:
|
67
|
+
|
68
|
+
- **Root** - Schema container with inputs, attributes, and traits
|
69
|
+
- **Declarations** - InputDeclaration, ValueDeclaration, TraitDeclaration
|
70
|
+
- **Expressions** - CallExpression, ArrayExpression, CascadeExpression, CaseExpression
|
71
|
+
- **References** - InputReference, InputElementReference, DeclarationReference
|
72
|
+
- **Literals** - Literal values (strings, numbers, booleans)
|
73
|
+
- **Collections** - Arrays and HashExpression nodes
|
74
|
+
|
75
|
+
## Implementation
|
76
|
+
|
77
|
+
Built as a visitor pattern class that traverses AST nodes recursively, with each node type having its own specialized formatting method. The printer preserves proper indentation and handles nested structures gracefully.
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kumi
|
4
|
+
module Support
|
5
|
+
class SExpressionPrinter
|
6
|
+
def initialize(indent: 0)
|
7
|
+
@indent = indent
|
8
|
+
end
|
9
|
+
|
10
|
+
def visit(node)
|
11
|
+
return node.inspect unless node.respond_to?(:class)
|
12
|
+
|
13
|
+
case node
|
14
|
+
when nil then "nil"
|
15
|
+
when Array then visit_array(node)
|
16
|
+
when Kumi::Syntax::Root then visit_root(node)
|
17
|
+
when Kumi::Syntax::ValueDeclaration then visit_value_declaration(node)
|
18
|
+
when Kumi::Syntax::TraitDeclaration then visit_trait_declaration(node)
|
19
|
+
when Kumi::Syntax::InputDeclaration then visit_input_declaration(node)
|
20
|
+
when Kumi::Syntax::CallExpression then visit_call_expression(node)
|
21
|
+
when Kumi::Syntax::ArrayExpression then visit_array_expression(node)
|
22
|
+
when Kumi::Syntax::CascadeExpression then visit_cascade_expression(node)
|
23
|
+
when Kumi::Syntax::CaseExpression then visit_case_expression(node)
|
24
|
+
when Kumi::Syntax::InputReference then visit_input_reference(node)
|
25
|
+
when Kumi::Syntax::InputElementReference then visit_input_element_reference(node)
|
26
|
+
when Kumi::Syntax::DeclarationReference then visit_declaration_reference(node)
|
27
|
+
when Kumi::Syntax::Literal then visit_literal(node)
|
28
|
+
when Kumi::Syntax::HashExpression then visit_hash_expression(node)
|
29
|
+
else visit_generic(node)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.print(node, indent: 0)
|
34
|
+
new(indent: indent).visit(node)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def visit_array(node)
|
40
|
+
return "[]" if node.empty?
|
41
|
+
|
42
|
+
elements = node.map { |child| child_printer.visit(child) }
|
43
|
+
"[\n#{indent_str(2)}#{elements.join("\n#{indent_str(2)}")}\n#{indent_str}]"
|
44
|
+
end
|
45
|
+
|
46
|
+
def visit_root(node)
|
47
|
+
fields = %i[inputs attributes traits].map do |field|
|
48
|
+
value = node.public_send(field)
|
49
|
+
"#{field}: #{child_printer.visit(value)}"
|
50
|
+
end.join("\n#{indent_str(2)}")
|
51
|
+
|
52
|
+
"(Root\n#{indent_str(2)}#{fields}\n#{indent_str})"
|
53
|
+
end
|
54
|
+
|
55
|
+
def visit_value_declaration(node)
|
56
|
+
"(ValueDeclaration :#{node.name}\n#{child_indent}#{child_printer.visit(node.expression)}\n#{indent_str})"
|
57
|
+
end
|
58
|
+
|
59
|
+
def visit_trait_declaration(node)
|
60
|
+
"(TraitDeclaration :#{node.name}\n#{child_indent}#{child_printer.visit(node.expression)}\n#{indent_str})"
|
61
|
+
end
|
62
|
+
|
63
|
+
def visit_input_declaration(node)
|
64
|
+
fields = [":#{node.name}"]
|
65
|
+
fields << ":#{node.type}" if node.respond_to?(:type) && node.type
|
66
|
+
fields << "domain: #{node.domain.inspect}" if node.respond_to?(:domain) && node.domain
|
67
|
+
|
68
|
+
if node.respond_to?(:children) && !node.children.empty?
|
69
|
+
children_str = child_printer.visit(node.children)
|
70
|
+
"(InputDeclaration #{fields.join(' ')}\n#{child_indent}#{children_str}\n#{indent_str})"
|
71
|
+
else
|
72
|
+
"(InputDeclaration #{fields.join(' ')})"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def visit_call_expression(node)
|
77
|
+
return "(CallExpression :#{node.fn_name})" if node.args.empty?
|
78
|
+
|
79
|
+
args = node.args.map { |arg| child_printer.visit(arg) }
|
80
|
+
"(CallExpression :#{node.fn_name}\n#{indent_str(2)}#{args.join("\n#{indent_str(2)}")}\n#{indent_str})"
|
81
|
+
end
|
82
|
+
|
83
|
+
def visit_array_expression(node)
|
84
|
+
return "(ArrayExpression)" if node.elements.empty?
|
85
|
+
|
86
|
+
elements = node.elements.map { |elem| child_printer.visit(elem) }
|
87
|
+
"(ArrayExpression\n#{indent_str(2)}#{elements.join("\n#{indent_str(2)}")}\n#{indent_str})"
|
88
|
+
end
|
89
|
+
|
90
|
+
def visit_cascade_expression(node)
|
91
|
+
cases = node.cases.map do |case_expr|
|
92
|
+
"(#{visit(case_expr.condition)} #{visit(case_expr.result)})"
|
93
|
+
end.join("\n#{indent_str(2)}")
|
94
|
+
|
95
|
+
"(CascadeExpression\n#{indent_str(2)}#{cases}\n#{indent_str})"
|
96
|
+
end
|
97
|
+
|
98
|
+
def visit_case_expression(node)
|
99
|
+
"(CaseExpression #{visit(node.condition)} #{visit(node.result)})"
|
100
|
+
end
|
101
|
+
|
102
|
+
def visit_input_reference(node)
|
103
|
+
"(InputReference :#{node.name})"
|
104
|
+
end
|
105
|
+
|
106
|
+
def visit_input_element_reference(node)
|
107
|
+
"(InputElementReference #{node.path.map(&:to_s).join('.')})"
|
108
|
+
end
|
109
|
+
|
110
|
+
def visit_declaration_reference(node)
|
111
|
+
"(DeclarationReference :#{node.name})"
|
112
|
+
end
|
113
|
+
|
114
|
+
def visit_literal(node)
|
115
|
+
"(Literal #{node.value.inspect})"
|
116
|
+
end
|
117
|
+
|
118
|
+
def visit_hash_expression(node)
|
119
|
+
return "(HashExpression)" if node.pairs.empty?
|
120
|
+
|
121
|
+
pairs = node.pairs.map do |pair|
|
122
|
+
"(#{visit(pair.key)} #{visit(pair.value)})"
|
123
|
+
end.join("\n#{indent_str(2)}")
|
124
|
+
|
125
|
+
"(HashExpression\n#{indent_str(2)}#{pairs}\n#{indent_str})"
|
126
|
+
end
|
127
|
+
|
128
|
+
def visit_generic(node)
|
129
|
+
class_name = node.class.name&.split('::')&.last || node.class.to_s
|
130
|
+
|
131
|
+
if node.respond_to?(:children) && !node.children.empty?
|
132
|
+
children = node.children.map { |child| child_printer.visit(child) }
|
133
|
+
"(#{class_name}\n#{indent_str(2)}#{children.join("\n#{indent_str(2)}")}\n#{indent_str})"
|
134
|
+
elsif node.respond_to?(:members)
|
135
|
+
fields = node.members.reject { |m| m == :loc }.map do |member|
|
136
|
+
value = node[member]
|
137
|
+
"#{member}: #{child_printer.visit(value)}"
|
138
|
+
end
|
139
|
+
|
140
|
+
return "(#{class_name})" if fields.empty?
|
141
|
+
|
142
|
+
"(#{class_name}\n#{indent_str(2)}#{fields.join("\n#{indent_str(2)}")}\n#{indent_str})"
|
143
|
+
else
|
144
|
+
"(#{class_name} #{node.inspect})"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def child_printer
|
149
|
+
@child_printer ||= self.class.new(indent: @indent + 2)
|
150
|
+
end
|
151
|
+
|
152
|
+
def indent_str(extra = 0)
|
153
|
+
' ' * (@indent + extra)
|
154
|
+
end
|
155
|
+
|
156
|
+
def child_indent
|
157
|
+
indent_str(2)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/lib/kumi/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kumi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- André Muta
|
@@ -48,6 +48,7 @@ files:
|
|
48
48
|
- docs/features/array-broadcasting.md
|
49
49
|
- docs/features/input-declaration-system.md
|
50
50
|
- docs/features/performance.md
|
51
|
+
- docs/features/s-expression-printer.md
|
51
52
|
- docs/schema_metadata.md
|
52
53
|
- docs/schema_metadata/broadcasts.md
|
53
54
|
- docs/schema_metadata/cascades.md
|
@@ -143,6 +144,7 @@ files:
|
|
143
144
|
- lib/kumi/registry.rb
|
144
145
|
- lib/kumi/schema.rb
|
145
146
|
- lib/kumi/schema_metadata.rb
|
147
|
+
- lib/kumi/support/s_expression_printer.rb
|
146
148
|
- lib/kumi/syntax/array_expression.rb
|
147
149
|
- lib/kumi/syntax/call_expression.rb
|
148
150
|
- lib/kumi/syntax/cascade_expression.rb
|