Cirru Syntax Essentials
1. Indentation = Nesting
Cirru uses 2-space indentation to represent nested structures:
defn add (a b)
&+ a b
Equivalent JSON:
["defn", "add", ["a", "b"], ["&+", "a", "b"]]
2. The $ Operator (Single-Child Expand)
$ creates a single nested expression on the same line:
; Without $: explicit nesting
let
x 1
println x
; With $: inline nesting
let (x 1)
println x
; Multiple $ chain right-to-left
println $ str $ &+ 1 2
; Equivalent to: (println (str (&+ 1 2)))
Rule: a $ b c → ["a", ["b", "c"]]
3. The | Prefix (String Literals)
| marks a string literal:
println |hello
println |hello-world
println "|hello world with spaces"
|hello→"hello"(string, not symbol)- Without
|:hellois a symbol/identifier - For strings with spaces:
"|hello world"
4. The , Operator (Expression Terminator)
, forces the end of current expression, starting a new sibling:
; Without comma - ambiguous
if true 1 2
; With comma - clear structure
if true
, 1
, 2
Useful in cond, case, let bindings:
cond
&< x 0
, |negative ; comma separates condition from result
(&= x 0) |zero
true |positive
5. Quasiquote, Unquote, Unquote-Splicing
For macros:
quasiquoteor backtick: template~(unquote): insert evaluated value~@(unquote-splicing): splice list contents
defmacro when-not (cond & body)
quasiquote $ if (not ~cond)
do ~@body
JSON equivalent:
[
"defmacro",
"when-not",
["cond", "&", "body"],
["quasiquote", ["if", ["not", "~cond"], ["do", "~@body"]]]
]
LLM Guidance & Optimization
To ensure high-quality code generation for Calcit, follow these rules:
1. Mandatory | Prefix for Strings
LLMs often forget the | prefix. Always use | for string literals, even short ones.
- ❌
println "hello" - ✅
println |hello - ✅
println "|hello with spaces"
2. Functional let Binding
let bindings must be a list of pairs ((name value)). Single brackets (name value) are invalid.
- ❌
let (x 1) x - ✅
let ((x 1)) x - ✅ Preferred: Use multi-line for clarity:
let x 1 y 2 + x y
3. Arity Awareness
Calcit uses strict arity checking. Many core functions like +, -, *, / have native counterparts &+, &-, &*, &/ which are binaries (2 arguments). The standard versions are often variadic macros.
- Use
&+,&-, etc. in tight loops or when 2 args are guaranteed.
4. No Inline Types in Parameters
Calcit does not support Clojure-style (defn name [^Type arg] ...).
- ❌
defn add (a :number) ... - ✅ Use
assert-typeinside the body for parameters. - ✅ Return types can be specified with
hint-fnor a trailing label after parameters:
; Parameter check inside body
defn square (n)
assert-type n :number
&* n n
; Return type as trailing label
defn get-pi () :number
3.14159
; Mixed style
defn add (a b) :number
assert-type a :number
assert-type b :number
+ a b
5. $ and , Usage
- Use
$to avoid parentheses on the same line. - Use
,to separate multiline pairs incondorcaseif indentation alone feels ambiguous.
6. Common Patterns
Function Definition
defn function-name (arg1 arg2)
body-expression
Let Binding
let
x 1
y $ &+ x 2
&* x y
Conditional
if condition
then-branch
else-branch
Multi-branch Cond
cond
(test1) result1
(test2) result2
true default-result
JSON Format Rules
When using -j or --json-input:
- Everything is arrays or strings:
["defn", "name", ["args"], ["body"]] - Numbers as strings:
["&+", "1", "2"]not["&+", 1, 2] - Preserve prefixes:
"|string","~var","~@list" - No objects: JSON
{}cannot be converted to Cirru
Common Mistakes
| ❌ Wrong | ✅ Correct | Reason |
|---|---|---|
println hello | println \|hello | Missing \| for string |
$ a b c at line start | a b c | A line is an expression, no need of $ for extra nesting |
a$b | a $ b | Missing space around $ |
["&+", 1, 2] | ["&+", "1", "2"] | Numbers in syntax tree must be strings in JSON |
| Tabs for indent | 2 spaces | Cirru requires spaces |