Complete reference for Zyn grammar commands and the TypedAST builder API.
Semantic actions follow the -> arrow and build TypedAST nodes from named bindings. There are five action kinds.
Construct — Build a TypedAST Node
The primary action. Field values are expressions over bindings:
fn_param = { name:identifier ~ ":" ~ ty:type_expr }
-> TypedParameter { name: intern(name), ty: ty }
integer_literal = @{ ASCII_DIGIT+ }
-> TypedExpression::IntLiteral { value: integer_literal }
Atomic rules (@) capture matched text automatically — the binding name holds the text.
PassThrough — Return a Binding
For wrapper/choice rules:
statement = { if_stmt | while_stmt | return_stmt | expr_stmt }
// no action needed — implicitly passes through the matched alternative
factor = { inner:paren_expr | inner:number }
-> inner
HelperCall — Built-in Helpers
fn_params = { first:fn_param ~ rest:fn_param_comma* }
-> prepend_list(first, rest)
additive_expr = { first:term ~ rest:additive_rest* }
-> fold_left_ops(first, rest)
additive_rest = { op:add_op ~ operand:term }
-> make_pair(op, operand)
type_name = { name:identifier }
-> intern(name)
Match — Branch on a String Binding
decl = { kind:("let" | "const") ~ name:identifier ~ "=" ~ val:expr }
-> match kind {
"let" => TypedStatement::Let { name: intern(name), init: val },
"const" => TypedStatement::Const { name: intern(name), init: val },
}
fn_decl = { "fn" ~ name:identifier ~ "(" ~ params:fn_params? ~ ")" ~ ret:type_expr? ~ body:block }
-> if ret.is_some() {
TypedDeclaration::Function { name: intern(name), params: params.unwrap_or([]), return_type: ret, body: Some(body) }
} else {
TypedDeclaration::Procedure { name: intern(name), params: params.unwrap_or([]), body: body }
}
Action Expression Reference
name // value of the binding 'name'
params // Vec<T> from 'params:rule*'
ret // Option<T> from 'ret:rule?'
Call
Signature
Description
intern(s)
text → InternedString
Intern a string into the arena
prepend_list(first, rest)
(T, Vec<T>) → Vec<T>
Prepend first to rest Vec
fold_left_ops(first, rest)
(Expr, Vec<(op,Expr)>) → Expr
Build left-assoc binary tree
make_pair(op, operand)
(op, Expr) → (op, Expr)
Package for fold_left_ops
Box::new(expr)
T → Box<T>
Heap-box a value
Some(value)
T → Option<T>
Wrap in Some
params.unwrap_or([]) // Option<Vec<T>> → Vec<T>
opt.is_some() // Option<T> → bool
binding.text // get matched text as String
binding.span // get source Span
-> TypedExpression::Call {
callee: Box::new(TypedExpression::Variable { name: intern(name) }),
args: args.unwrap_or([]),
}
path: [intern(name)] // single-element Vec
params: [] // empty Vec
TypedAST Variant Quick Reference
Expressions (TypedExpression::)
Variant
Key Fields
Description
IntLiteral
value
Integer literal
BoolLiteral
value
Boolean literal
StringLiteral
value
String literal
Variable
name
Variable reference
Binary
op, left, right
Binary operation
Unary
op, operand
Unary operation
Call
callee, args
Function call
FieldAccess
object, field
Field access
Index
object, index
Index access
StructLiteral
type_name, fields
Struct literal
ArrayLiteral
elements
Array literal
Statements (TypedStatement::)
Variant
Key Fields
Description
Let
name, init
Variable declaration
Const
name, init
Constant declaration
Return
value?
Return statement
Expr
expr
Expression statement
Assign
target, value
Assignment
If
condition, then_branch, else_branch?
If statement
While
condition, body
While loop
For
iterable, binding, body
For loop
Declarations (TypedDeclaration::)
Variant
Key Fields
Description
Function
name, params, return_type, body
Function declaration
Struct
name, fields
Struct declaration
Enum
name, variants
Enum declaration
Import
path, alias?
Import declaration
Variant
Key Fields
Description
Named
name
Named/primitive type
Pointer
pointee
Pointer type
Optional
inner
Optional type
Array
size?, element
Array type
Extern
name
Extern/opaque type
Syntax
Description
Creates Node
rule = { ... }
Normal rule
Yes
rule = @{ ... }
Atomic rule
Yes
rule = _{ ... }
Silent rule
No
Operator
Name
Description
~
Sequence
Match in order
|
Choice
First match wins
*
Zero or more
Repeat 0+ times
+
One or more
Repeat 1+ times
?
Optional
Match 0 or 1 time
!
Not
Succeed if doesn't match
&
And
Succeed if matches (no consume)
Rule
Matches
SOI
Start of input
EOI
End of input
ANY
Any character
ASCII
ASCII character (0x00-0x7F)
ASCII_DIGIT
0-9
ASCII_ALPHA
a-z, A-Z
ASCII_ALPHANUMERIC
a-z, A-Z, 0-9
ASCII_HEX_DIGIT
0-9, a-f, A-F
NEWLINE
\n or \r\n
Rule
Purpose
WHITESPACE
Define whitespace handling
COMMENT
Define comment syntax
use zyntax_typed_ast:: TypedASTBuilder ;
let mut builder = TypedASTBuilder :: new ( ) ;
builder. i32_type ( ) // Type::Primitive(PrimitiveType::I32)
builder. i64_type ( ) // Type::Primitive(PrimitiveType::I64)
builder. bool_type ( ) // Type::Primitive(PrimitiveType::Bool)
builder. string_type ( ) // Type::Primitive(PrimitiveType::String)
builder. unit_type ( ) // Type::Primitive(PrimitiveType::Unit)
builder. char_type ( ) // Type::Primitive(PrimitiveType::Char)
builder. f32_type ( ) // Type::Primitive(PrimitiveType::F32)
builder. f64_type ( ) // Type::Primitive(PrimitiveType::F64)
builder. span ( start, end) // Create span from byte offsets
builder. dummy_span ( ) // Create (0, 0) span for testing
// Literals
builder. int_literal ( 42 , span)
builder. string_literal ( "hello" , span)
builder. bool_literal ( true , span)
builder. char_literal ( 'x' , span)
builder. unit_literal ( span)
// References
builder. variable ( "name" , ty, span)
// Operations
builder. binary ( BinaryOp :: Add , left, right, result_ty, span)
builder. unary ( UnaryOp :: Minus , operand, result_ty, span)
// Access
builder. field_access ( object, "field" , field_ty, span)
builder. index ( object, index_expr, element_ty, span)
// Calls
builder. call_positional ( callee, args_vec, return_ty, span)
builder. call_named ( callee, named_args_vec, return_ty, span)
// Composite
builder. struct_literal ( "Name" , fields_vec, struct_ty, span)
builder. array_literal ( elements_vec, array_ty, span)
builder. tuple ( elements_vec, tuple_ty, span)
builder. lambda ( params_vec, body, lambda_ty, span)
// Special
builder. cast ( expr, target_ty, span)
builder. try_expr ( expr, result_ty, span)
builder. await_expr ( expr, result_ty, span)
builder. reference ( expr, mutability, ptr_ty, span)
builder. dereference ( expr, deref_ty, span)
// Declarations
builder. let_statement ( "name" , ty, mutability, init_opt, span)
// Control flow
builder. if_statement ( condition, then_block, else_opt, span)
builder. while_loop ( condition, body, span)
builder. for_loop ( "binding" , iterable, body, span)
builder. loop_stmt ( body, span)
// Jumps
builder. return_stmt ( value, span)
builder. return_void ( span)
builder. break_stmt ( span)
builder. break_with_value ( value, span)
builder. continue_stmt ( span)
// Other
builder. expression_statement ( expr, span)
builder. throw_stmt ( exception, span)
builder. block ( statements_vec, span)
builder. struct_pattern ( "Name" , fields_vec, span)
builder. enum_pattern ( "Enum" , "Variant" , fields_vec, span)
builder. array_pattern ( patterns_vec, span)
builder. slice_pattern ( prefix, middle_opt, suffix, span)
zyntax compile [OPTIONS] [INPUT]...
Options:
-s, --source < SOURCE> Source file (with --grammar)
-g, --grammar < GRAMMAR> ZynPEG grammar file (.zyn)
-o, --output < OUTPUT> Output file path
-b, --backend < BACKEND> Backend (jit, llvm) [default: jit]
-v, --verbose Verbose output
-O, --opt-level < LEVEL> Optimization (0-3) [default: 2]
-f, --format < FORMAT> Input format (auto, typed-ast, hir-bytecode, zyn)
--run Run immediately (JIT only)
# Compile and run Zig file
zyntax compile --grammar zig.zyn --source hello.zig --run
# Compile to object file
zyntax compile --grammar zig.zyn --source main.zig -o main.o
# Verbose compilation
zyntax compile --grammar zig.zyn --source test.zig -v --run
# Use LLVM backend
zyntax compile --grammar zig.zyn --source test.zig -b llvm -o test.o
Error
Cause
Solution
"Rule not found"
Reference to undefined rule
Define the rule or check spelling
"Left recursion detected"
a = { a ~ ... }
Rewrite using repetition
binding 'x' not found
Action references a binding not in the pattern
Add x:rule to the pattern
left recursion detected
Rule calls itself without consuming input
Refactor to use rest* style
Error
Cause
Solution
"Cannot access fields on non-struct type"
Field access on wrong type
Check object type
"Unknown variant"
Enum variant not found
Check variant name
"Type mismatch"
Incompatible types
Check expression types
Group related rules - Keep declarations, statements, expressions separate
Order choices correctly - Longer/more specific first
Use meaningful names - if_stmt not rule1
Comment complex rules - Explain non-obvious patterns
Avoid excessive backtracking - Use negative lookahead
Make atomic rules atomic - Use @{ } for tokens
Keep grammar focused - Don't over-generalize
Test incrementally - Add rules one at a time
Use verbose mode - --verbose shows parse tree
Use named bindings - name:rule makes patterns self-documenting
Start simple - Get basic cases working first