-
-
Notifications
You must be signed in to change notification settings - Fork 0
02 Getting Started
github-actions[bot] edited this page Feb 27, 2026
·
2 revisions
Before using Zyn, ensure you have:
- Rust toolchain (1.70+)
- The
zyntaxCLI tool
# Build zyntax from source
cargo build --release
# Verify installation
./target/release/zyntax --helpLet's create a minimal calculator language that supports:
- Integer literals
- Addition and subtraction
- Parentheses for grouping
Create calc.zyn:
// Calculator Language Grammar
@language {
name: "Calc",
version: "1.0",
file_extensions: [".calc"],
entry_point: "main",
}
// Program structure: wrap the expression in a main function
program = { SOI ~ e:expr ~ EOI }
-> TypedProgram {
declarations: [
TypedDeclaration::Function {
name: intern("main"),
params: [],
return_type: Type::Named { name: intern("i64") },
body: Some(TypedBlock {
stmts: [TypedStatement::Return { value: Some(e) }],
}),
is_async: false,
}
],
}
// Expression with addition/subtraction (left-associative)
expr = { first:term ~ rest:expr_rest* }
-> fold_left_ops(first, rest)
expr_rest = { op:add_sub_op ~ operand:term }
-> make_pair(op, operand)
add_sub_op = @{ "+" | "-" }
// Terms are atoms or parenthesized expressions — passthrough
term = { integer | paren_expr }
// Parenthesized expression (silent rule - doesn't create node)
paren_expr = _{ "(" ~ expr ~ ")" }
// Integer literal — atomic rule captures text automatically
integer = @{ ASCII_DIGIT+ }
-> TypedExpression::IntLiteral { value: integer }
// Whitespace handling
WHITESPACE = _{ " " | "\t" | "\n" }
Create test.calc:
1 + 2 + 3
zyntax compile --grammar calc.zyn --source test.calc --run@language {
name: "Calc",
version: "1.0",
file_extensions: [".calc"],
entry_point: "main",
}
This block defines metadata about your language:
-
name: Language identifier -
version: Grammar version -
file_extensions: Associated file types -
entry_point: The function to execute (for JIT compilation)
Rules follow PEG syntax with optional semantic actions:
rule_name = { pattern }
-> TypedAST::Variant { field: value, ... }
| Syntax | Meaning |
|---|---|
{ } |
Normal rule (creates parse node) |
@{ } |
Atomic rule (no whitespace handling) |
_{ } |
Silent rule (matches but creates no node) |
| Operator | Meaning | Example |
|---|---|---|
~ |
Sequence |
a ~ b matches a then b |
| ` | ` | Choice |
* |
Zero or more |
a* matches "", "a", "aa", ... |
+ |
One or more |
a+ matches "a", "aa", ... |
? |
Optional |
a? matches "" or "a" |
! |
Not predicate |
!a succeeds if a fails |
& |
And predicate |
&a succeeds if a matches (no consume) |
| Rule | Matches |
|---|---|
SOI |
Start of input |
EOI |
End of input |
ANY |
Any single character |
ASCII_DIGIT |
0-9 |
ASCII_ALPHA |
a-z, A-Z |
ASCII_ALPHANUMERIC |
a-z, A-Z, 0-9 |
WHITESPACE |
Define whitespace handling |
COMMENT |
Define comment syntax |
Each rule can have a semantic action that builds a TypedAST node directly from parsed bindings:
// Atomic rule (@) — matched text is available via the binding name 'integer'
integer = @{ ASCII_DIGIT+ }
-> TypedExpression::IntLiteral { value: integer }
The -> arrow connects the grammar rule to its action:
-
TypedExpression::IntLiteralis the TypedAST variant to construct -
value: integersets the field using the captured text from the binding
Named bindings in the pattern make values available to the action:
fn_param = { name:identifier ~ ":" ~ ty:type_expr }
-> TypedParameter { name: intern(name), ty: ty }
// ^^^^ ^^
// binding 'name' binding 'ty'
| Pattern | Description |
|---|---|
TypedX::Y { field: binding } |
Construct a TypedAST node from bindings |
-> binding |
Passthrough — return a binding directly |
-> fold_left_ops(first, rest) |
Build a left-associative binary expression tree |
-> prepend_list(first, rest) |
Combine first element + rest Vec into a Vec |
-> intern(name) |
Intern a string binding into the arena |
-> if cond { ... } else { ... } |
Branch on a boolean binding |
A typical Zyn project looks like:
my-language/
├── grammar/
│ └── mylang.zyn # Grammar definition
├── examples/
│ ├── hello.mylang # Example source files
│ └── test.mylang
└── tests/
└── parser_tests.rs # Test cases
Now that you have a working grammar: