Kumi::Parser
Text parser for Kumi schemas: a single-pass lexer feeding a recursive-descent + Pratt parser that builds kumi-core's AST directly, with located, framed parse errors.
Installation
gem 'kumi-parser'
Usage
require 'kumi/parser'
schema = <<~KUMI
schema do
input do
float :income
string :status
end
trait :adult, input.age >= 18
value :tax, fn(:calculate_tax, input.income)
end
KUMI
# Parse to AST
ast = Kumi::Parser::TextParser.parse(schema)
# Validate
Kumi::Parser::TextParser.valid?(schema) # => true
API
parse(text)→ ASTvalid?(text)→ Booleanvalidate(text)→ Array of error hashes
Syntax
schema do
input do
<type> :<name>[, domain: <spec>]
end
trait :<name>, <expression>
value :<name>, <expression>
value :<name> do
on <condition>, <result>
base <result>
end
end
Function calls: fn(:name, arg1, arg2, ...)
Operators: + - * ** `/%><>=<===!=&|
**References**:input.field,value_name,array[index]
**Strings**: Both"double"and'single'` quotes supported
Ruby DSL Differences
String concatenation: Ruby DSL evaluates "Hello" + "World" → Literal("HelloWorld"), text parser → CallExpression(:add, [...]).
Semantically equivalent - both should execute identically.
Architecture
The pipeline is source → Lexer → tokens → Parser → AST, where the AST is
kumi-core's Kumi::Syntax::* nodes.
lexer.rb— single-passStringScannerlexer producing a flat array of typedTokens, each carrying only its kind, value, and start offset.parser.rb— recursive descent for declarations, Pratt for expressions.grammar.rb— the lookup tables (keywords, type keywords, function sugar, operator precedence/associativity) shared by the lexer and parser.source.rb/parse_error.rb— turn a byte offset into afile:line:collocation and a caret-annotated code frame for error messages.text_parser.rb— the publicparse/valid?/validatefacade.
Error reporting
Parse errors report an exact location, a plain-English description of what was expected versus what was found, and a source frame:
demo.kumi:2:3: expected an `input do` block, but found `value`
➤ 2 | value :y, input.x
| ^
Errors are confined to the parse phase. Resolving names, checking types, and reasoning about axes are semantic concerns handled later by kumi-core's analyzer, not by this gem.
License
MIT