Kex.AST
module Kex.AST
Parses Kex source code into a structured AST, at run time.
Opt-in: nothing here is in scope until using Kex.AST.
This is the entry point for tools that read Kex source: linters, formatters, documentation generators, code search. The AST includes module definitions, function signatures, type/record definitions, traits, make blocks, and the doc-comments extracted from # lines, which is how the standard library's own documentation is generated.
using Kex.AST
main do
match Kex.AST.parseFile("src/main.kex") do
Ok(program) => IO.printLine("${program.items.count} top-level items")
Error(e) => IO.printError(e.message)
end
end
Everything answers a Result, so a source file that does not parse is a value you handle rather than an exception.
record Location
Where in a source file something appeared.
Line and column are 1-based, for reporting to a person; the offsets are 0-based byte positions, for slicing the source.
Fields
fileStringlineIntegercolumnIntegerstartOffsetIntegerendOffsetInteger
record Program
A parsed source file: its schema version, and its top-level items.
Fields
schemaVersionIntegeritems[Node]
record ParseError
Why a source file could not be parsed.
Fields
messageStringlocationLocation?
function parse
Parses Kex source text into a structured AST.
Pass filename when you have one: it is what appears in every Location and in the error message, so diagnostics can point at a real file.
parse(source) : String -> Result<Program, ParseError>
parse(source) : String -> String -> Result<Program, ParseError>
Parameters
sourceString- the Kex source text
filenameString- the name to report locations against; optional
Returns: Result<Program, ParseError> — the parsed program, or why it failed
Examples
Kex.AST.parse("let double(n: Integer) -> Integer = n * 2\n")
# => Ok(Program { schemaVersion: 2, items: [...] })
Kex.AST.parse("let x =").error? # => true
Counting a file's top-level items
Kex.AST.parse(source, "main.kex").map { |p| p.items.count }
function parseFile
Reads a file and parses it, reporting locations against its path.
A file that cannot be read is an Error like one that cannot be parsed.
parseFile(path) : FS.FilePath -> Result<Program, ParseError>
Parameters
pathFS.FilePath- the file to read and parse
Returns: Result<Program, ParseError> — the parsed program, or why it failed
Examples
match Kex.AST.parseFile("src/main.kex") do
Ok(program) => IO.printLine("${program.items.count} items")
Error(e) => IO.printError(e.message)
end
Parsing every source in a directory
FS.Directory.files(dir)
.or([])
.filter { |f| FS.Path.extension(f) == ".kex" }
.map { |f| Kex.AST.parseFile(FS.Path.join(dir, f)) }
function parseType
Parses a type expression on its own, without a surrounding program.
Use it to read a type written in data: a signature in a config file, a type named on a command line. typeRefText renders the result back.
parseType(source) : String -> Result<TypeRef, ParseError>
Parameters
sourceString- the type expression
Returns: Result<TypeRef, ParseError> — the parsed type, or why it failed
Examples
Kex.AST.parseType("[Integer]").map { |t| Kex.AST.typeRefText(t) }
# => Ok("[Integer]")
Kex.AST.parseType("Map<String, Integer>").map { |t| Kex.AST.typeRefText(t) }
# => Ok("Map<String, Integer>")
function parseExpression
Parses a single expression on its own, without a surrounding program.
This gives you the expression's SHAPE. To evaluate one instead, use Evaluator.runExpression.
parseExpression(source) : String -> Result<Expression, ParseError>
Parameters
sourceString- the expression text
Returns: Result<Expression, ParseError> — the parsed expression, or why it failed
Examples
Kex.AST.parseExpression("1 ` 2").ok? # => true
Kex.AST.parseExpression("1 `").ok? # => false
type TypeRef
A type as it was written in source.
typeRefText renders one back to the source spelling.
Variants
NamedType(String, [TypeRef])FunctionType([TypeRef], TypeRef)TupleType([TypeRef])ListType(TypeRef)MapType(TypeRef, TypeRef)UnionType([TypeRef])IntersectionType([TypeRef])RecordType([(String, TypeRef)])NullableType(TypeRef)BlockType(TypeRef)AtomType(String)TypeQuery(String, Expression)TypeVar(String)AnyTypeNoneType
function typeRefText
Renders a TypeRef back to the way it is written in source.
A list reads as [Integer], a map as {String: Integer}, an optional as String?: the spelling a reader would recognise, not the constructor tree behind it.
typeRefText(NamedType(name, []))
Parameters
typeRefTypeRef- the parsed type
Returns: String — the type, as source
Examples
Kex.AST.parseType("[Integer]").map { |t| Kex.AST.typeRefText(t) }
# => Ok("[Integer]")
Kex.AST.parseType("String?").map { |t| Kex.AST.typeRefText(t) }
# => Ok("String?")
type PatternRef
Structured representation of patterns.
Pattern nodes describe what a declaration or match arm accepts; they do not contain runtime values. A linter can distinguish a wildcard from a binding, for example, without reparsing source text.
Variants
BindPattern(String)LiteralPattern(String)ConstructorPattern(String, [PatternRef])TuplePattern([PatternRef])ListPattern([PatternRef], PatternRef?)RecordPattern(String?, [PatternField])RangePattern(PatternRef, PatternRef)ThisPattern(PatternRef)WildcardPattern
record PatternField
One field inside a record or map-shaped pattern.
pattern is None for shorthand such as { name }. stringKey keeps {"name": value} distinct from the atom-key spelling { name: value }.
Fields
nameStringpatternPatternRef?stringKeyBool
function patternRefText
Renders a PatternRef back to the way it is written in source.
patternRefText(BindPattern(name))
Parameters
patternRefPatternRef- the parsed pattern
Returns: String — the pattern, as source
Examples
Kex.AST.patternRefText(WildcardPattern) # => "_"
Kex.AST.patternRefText(BindPattern("n")) # => "n"
function patternFieldText
patternFieldText(PatternField { name, pattern, stringKey })
function referenceText
Renders either a type or a pattern back to source.
The one call to reach for when a node may carry either: it dispatches to typeRefText or patternRefText as appropriate.
referenceText(NamedType(name, args))
Parameters
referenceTypeRef | PatternRef- the parsed node
Returns: String — the node, as source
Examples
Kex.AST.referenceText(WildcardPattern) # => "_"
Kex.AST.referenceText(AnyType) # => "Any"
type Expression
Structured representation of expression AST nodes.
Expressions retain syntax-level distinctions that matter to tools: a method call is not flattened into a generic call, var is distinct from let, and a trailing if remains recognizable. Walk these constructors when writing a linter or code search; use Evaluator when the goal is to execute an expression rather than inspect it.
Variants
LitInt(Int)LitFloat(Float)LitChar(Int)LitString(String)InterpolatedString([String], [Expression])LitBool(Bool)LitAtom(String)LitNoneIdentifier(String)ThisBinaryOp(Expression, String, Expression)UnaryOp(String, Expression)Call(Expression, [Expression], [NamedArgument], Expression?)TaggedLiteral(String, [String], [Expression])MethodCall(Expression, String, [Expression], [NamedArgument], Expression?, Bool, Bool, TypeRef?)If(Expression, PatternRef?, [Expression], [ElseIf], [Expression]?)Match(Expression, String?, [MatchArm])Receive(String?, [MatchArm], Expression?, Expression?)ListLit([Expression], Expression?)MapLit([MapItem])RecordLit(String, [RecordField])TupleLit([Expression])Block([Expression])Lambda([LambdaParam], [Expression], TypeRef?, RescueInfo?)Let(PatternRef, TypeRef?, Expression)Var(String, TypeRef?, Expression)Assign(String, Expression)Return(Expression)BreakNextSpawn([Expression])Try(Expression)Spread(Expression)TrailingIf(Expression, Expression)ThenElse(Expression, Expression, Expression)ShorthandLambda(String, [Expression], Bool)CurryPlaceholderCurry(String, String?, Bool, [[Expression]])Using(String, String?, [String], [String], [Expression])With(String, Expression, [Expression])GeneratedDeclaration(Expression, GeneratedTemplate)ErrorExpression(String)Trying([Expression], RescueInfo)While(Expression, [Expression])Loop([String], [Expression])RangeLit(Expression, Expression)
record NamedArgument
One name: value argument at a call site.
Fields
nameStringvalueExpression
record MatchArm
One arm of match, receive, or rescue.
Multiple patterns are the comma-separated alternatives on the left of the arrow. guard is absent when the arm has no when condition.
Fields
patterns[PatternRef]guardExpression?bodyExpression
record LambdaParam
One lambda parameter and its optional source annotation.
Fields
nameStringtypeTypeRef?
record RescueInfo
The structured recovery clauses attached to a function or expression.
Named rescue arms live in arms; a catch-all rescue keeps its optional binding and body separately. inlineReturn represents the compact rescue form rather than inventing a synthetic block.
Fields
arms[MatchArm]catchAllNameString?catchAllBody[Expression]inlineReturnExpression?
record ElseIf
One elif branch, in source order.
Fields
conditionExpressionbody[Expression]
type MapItem
One entry in a map literal: either a key/value pair or ...spread.
Variants
MapEntry(Expression, Expression)MapSpread(Expression)
record RecordField
One explicitly initialized field in a record literal.
Fields
nameStringvalueExpression
type GeneratedTemplate
A declaration template whose name (and, for a make block, target) is computed by a compiled do expression.
Tools normally encounter this only while inspecting metaprogramming code. After expansion, generated declarations appear as ordinary Nodes.
Variants
GeneratedNode(Node)GeneratedMake(GeneratedMakeInfo)
record GeneratedMakeInfo
The fixed portion of a generated make declaration.
Fields
isFinalBoolimplements[TypeRef]body[CompiledItem]locationLocation
record MainInfo
The program entry point, including documentation and recovery clauses.
Fields
docString?params[ParamInfo]body[Expression]rescueInfoRescueInfo?locationLocation
record ParamInfo
One declared function parameter.
name is absent for a destructuring parameter; pattern preserves that destructuring shape. hasDefault records whether an initializer appeared.
Fields
nameString?patternPatternRef?typeTypeRef?hasDefaultBool
record ClauseInfo
One clause of a function, including its patterns and body.
Multi-clause functions place all clauses in one FunctionInfo, preserving source order so tooling can reason about which pattern is tried first.
Fields
params[ParamInfo]body[Expression]returnTypeTypeRef?rescueInfoRescueInfo?hasParamListBool
record FunctionInfo
A named function and all of its pattern-matching clauses.
doc contains the normalized # comment immediately attached to the declaration. Documentation generators can therefore share the same parsed structure as linters instead of scanning comments independently.
Fields
nameStringdocString?isFoulBoolpredicateBoolclauses[ClauseInfo]locationLocation
record AnnotationInfo
A standalone function or method type signature.
implicitThis distinguishes :> methods from module-level : functions without making a tool inspect punctuation in the original source.
Fields
record VariantInfo
One constructor of an algebraic data type.
Fields
nameStringfields[TypeRef]
record TypeInfo
A type alias or algebraic data type declaration.
variants is present for an ADT and absent for an alias or abstract type. parents preserves declared bounds and inherited type relationships.
Fields
nameStringdocString?typeParams[String]parents[TypeRef]variants[VariantInfo]?locationLocation
record FieldInfo
One field declared by a record type.
Fields
nameStringtypeTypeRefhasDefaultBool
record RecordInfo
A record declaration with fields in source order.
Fields
record TraitInfo
A trait declaration and the signatures or default methods in its body.
Fields
nameStringdocString?typeParams[String]body[Node]locationLocation
record MakeInfo
A make implementation block.
target is the receiver type, implements lists explicit traits, and body retains methods and visibility sections in declaration order.
Fields
record PragmaInfo
A compiler pragma and its optional value.
Fields
nameStringvalueString?locationLocation
record ModuleInfo
A module and its declarations in source order.
Fields
nameStringdocString?items[Node]locationLocation
record ConstantInfo
A named constant declaration. The AST reader never evaluates its value.
Fields
record VisibilityInfo
A public or private section and the declarations it contains.
Fields
isPublicBoolitems[Node]locationLocation
record UsingInfo
A using import, including aliasing, filters, and an optional scoped body.
Fields
moduleNameStringaliasString?onlyNames[String]exceptNames[String]body[Expression]locationLocation
record ExportInfo
An export declaration and its public-name filters.
Fields
moduleNameStringaliasString?onlyNames[String]exceptNames[String]locationLocation
type CompiledItem
One item inside a compiled do block before expansion.
Variants
CompiledNode(Node)CompiledExpression(Expression)
record CompiledInfo
A compile-time block and its declarations or expressions in source order.
Fields
items[CompiledItem]locationLocation
type Node
Any top-level or declaration-level AST node.
A source tool can match only the declarations it understands and leave the rest alone. The program's schemaVersion lets persisted consumers reject a tree whose possible node shapes have changed.
Variants
ModuleDef(ModuleInfo)FunctionDef(FunctionInfo)TypeAnnotation(AnnotationInfo)TypeDef(TypeInfo)RecordDef(RecordInfo)TraitDef(TraitInfo)MakeDef(MakeInfo)PragmaDef(PragmaInfo)ConstantDef(ConstantInfo)MainDef(MainInfo)Visibility(VisibilityInfo)UsingDef(UsingInfo)ExportDef(ExportInfo)Compiled(CompiledInfo)
make TypeRef | PatternRef
Source-like conversion through the standard to(String) spelling.
Useful in diagnostics: a tool can interpolate the type or pattern it found without manually dispatching between the two reference families.
to
to(String)