Skip to content

Grammar And Language Status

Nilakan is a small language with Python-like tokens, not a Python implementation. Similar spelling does not imply that a Python construct is supported. This page records the accepted surface grammar and the important parser/static-check restrictions in the current release.

The notation below uses ? for optional, * for repetition, + for one-or-more, and | for alternatives. Newlines and indentation delimit a block; a block may instead contain simple statements on the header line separated by semicolons.

program := (function_def | if_stmt | pragma_stmt | simple_stmt)*
function_def := "def" NAME "(" parameters? ")" ("->" type)? ":" block
parameters := parameter ("," parameter)*
parameter := NAME (":" type)? ("=" expression)?
if_stmt := "if" expression ":" block
("elif" expression ":" block)*
("else" ":" block)?
init_stmt := "init" ":" init_block
pragma_stmt := "pragma" NAME ":" NAME
while_stmt := "while" expression ":" block
simple_stmt := "return" expression ("," expression)* ","?
| NAME call
| target ("[" expression "]")+ "=" expression
| NAME (":" type)? "=" expression
| target "," destructure_tail "=" expression
| "(" target "," destructure_tail ")" "=" expression
destructure_tail := (target ("," target)* ","?)?
target := NAME | PRIMED_NAME
PRIMED_NAME := NAME "'" +

Parsing is only the first layer. init, while, primed targets, return, nested definitions, and pragma have placement rules described under Statements and Commit Loops. In particular, a Nilakan while is always a commit loop; there is no separate imperative while statement.

From lowest to highest precedence:

expression := conditional ("catch" conditional)*
conditional := or_expr ("if" or_expr "else" or_expr)*
or_expr := and_expr ("or" and_expr)*
and_expr := not_expr ("and" not_expr)*
not_expr := "not"* equality
equality := comparison (("==" | "!=") comparison)*
comparison := bit_or (("<" | "<=" | ">" | ">=" | "in" | "not" "in") bit_or)*
bit_or := bit_xor ("|" bit_xor)*
bit_xor := bit_and ("^" bit_and)*
bit_and := sum ("&" sum)*
sum := product (("+" | "-") product)*
product := unary (("*" | "/" | "%") unary)*
unary := ("try" | "-")* postfix
postfix := primary (("." NAME call) | subscript)*
primary := NUMBER | STRING | "True" | "False" | PRIMED_NAME
| NAME call?
| list_literal | brace_literal | parenthesized
call := "(" (argument ("," argument)* ","?)? ")"
argument := NAME "=" expression | expression
parenthesized := "(" (expression ("," expression)* ","?)? ")"

The parser can recover a comparison-shaped token sequence containing multiple comparison operators, but static AST construction rejects chained comparisons with NLK1005. Write left < middle and middle < right.

Calls support positional and named arguments, with positional arguments first. A direct call suffix is accepted only as part of a NAME primary; it is not a general postfix, so (str)(1) is invalid. After a primary, the parser accepts only method calls and subscripts. Parenthesized expressions, lists, sets, dicts, tuples, calls, and returns accept a trailing comma. Subscripts support indexes and start:end slices; slice steps are unsupported.

type := primary_type ("|" primary_type)*
primary_type := NAME ("[" (type ("," type)* ","?)? "]")?

The recognized scalar names are num, bool, str, and err. Container forms are list[T], set[T], dict[K, V], and fixed tuples such as tuple[num, str]. See Type Contracts for validation rules. The parser also accepts empty type arguments such as list[]. An empty argument list is the same unconstrained container contract as the bare form list.

Deliberately Unsupported Or Not Yet Implemented

Section titled “Deliberately Unsupported Or Not Yet Implemented”

| Construct | Status and replacement | | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | for, break, continue | Unsupported. Express iteration as commit-loop state. | | class, import, modules | Unsupported. Nilakan programs are single files of functions and top-level code. | | lambda, comprehensions, generators | Unsupported. Use named functions and explicit commit loops. | | None, null values, bytes | Unsupported. Some Python defaults that require None are therefore unavailable. | | try/except, raise | Unsupported spelling. Nilakan uses error values, prefix try, and infix catch. | | chained comparisons | Rejected with NLK1005; join explicit comparisons with and. | | augmented assignment (+=) | Unsupported. Write a normal assignment or primed assignment. | | exponentiation (**) | Unsupported. | | exponent/hex/octal/binary number literals | Unsupported; use decimal integers or fractions. | | slice steps (a:b:c) | Rejected with NLK1005. | | nested function definitions | Parsed for recovery but rejected statically. | | branch-local return | Rejected; every function has one final return statement. | | direct and mutual recursion | Allowed; non-terminating calls are stopped by the configured call-depth limit. | | general mutation methods | Methods are pure and return a new value. Raw list/dict index assignment is available in limited statement positions. | | effectful calls in expressions | Rejected. Call print and transitively effectful user functions as statements. |

“Unsupported” means programs must not rely on the construct. It is not a promise that the construct will be added or keep Python semantics if it is added later.

An untitled nlk code fence anywhere in the documentation is a complete executable program and is run by the documentation test suite. A fence whose title begins with Intentional failure states and tests the expected diagnostic code written in its title. Fences titled Fragment or Expression forms are syntax illustrations that explicitly require the surrounding context named in the title. This convention prevents pseudo-code from being mistaken for a runnable lesson.