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) → AST
  • valid?(text) → Boolean
  • validate(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-pass StringScanner lexer producing a flat array of typed Tokens, 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 a file:line:col location and a caret-annotated code frame for error messages.
  • text_parser.rb — the public parse / valid? / validate facade.

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