WalkLang v1 Specification
This document is the stable WalkLang language contract for the v1 line. v1.9 keeps the v1.8 compatibility policy and adds string interpolation for display text.
Core rule:
If it is not in SPEC.md, it is not stable WalkLang.
If the compiler disagrees with SPEC.md, either the compiler or the spec must change.
---
1. Source Files
WalkLang source files are UTF-8 text files ending in .walk.
main.walk
A source file contains zero or more statements. Blank lines are ignored. # starts a comment outside strings.
# comment
var: x = 1 # inline comment
out: x
---
2. Compilation Contract
The stable compiler pipeline is:
.walk source -> lexer -> parser -> AST -> type checker -> C emitter -> native executable
The walk build command writes generated C next to the executable path unless --emit-c chooses another path. Native builds use cc by default and link with -lm.
---
3. Blocks And Indentation
Indentation owns blocks.
if: true
out: 'inside'
out: 'outside'
Rules:
- spaces define indentation
- tabs are invalid
- a greater indentation level starts a child block
- a lower indentation level closes blocks
- formatter output uses 4 spaces per block level
---
4. Lexical Rules
Stable tokens:
names letters, digits, and _
numbers int and float literals
strings single-quoted strings
symbols ( ) [ ] , : = + - * / ^ > < ? .
comments # outside strings
Strings support these escapes:
\' single quote
\\ backslash
\n newline
\t tab
Strings support interpolation with {expression}. Interpolation formats int, float, bool, string, and nullable string values into the surrounding string.
var: name = 'Walker'
var: length = string.len(name)
out: 'name {name} has {length} characters'
Use doubled braces for literal braces:
out: '{{name}}'
Double-quoted strings are not valid WalkLang.
---
5. Statements
Stable statements:
imp:
exp:
var:
const:
assignment
out:
test:
assert:
func:
return:
if:
else:
while:
repeat:
for:
break:
continue:
Draft implemented statement:
do:
do: is the current compiler's draft effect-call statement. It is documented so draft IO programs can be tested, but it is not part of the v1.9 stable compatibility contract yet.
One statement appears on each physical line. Semicolons are not part of v1.1 syntax.
---
6. Reserved Words
These words cannot be user-defined names:
var const out if else while for repeat break continue
func return imp exp true false null and or not in test assert
---
7. Types
Stable value types:
int
float
bool
string
array[T]
func(T...) R
void is used internally for functions with no return type. It is not a value type.
null is stable for nullable strings in native v1.1 programs:
var: name string? = null
Other nullable scalar forms are not part of the v1.1 stable native contract.
---
8. Type Inference And Type Lock
var: and const: infer a type from their initializer unless an explicit type annotation is present.
var: x = 1
var: y float = 1
const: name = 'Walker'
Once a name is declared, its type is locked.
var: x = 1
x = 2 # ok
x = 'two' # type error
int values may initialize or assign to float values. Other implicit conversions are not stable.
Function parameters and return types may also be inferred from the function body when the result is local and unambiguous. Omitted function parameter types are not inferred from call sites.
---
9. Variables And Constants
var: creates a mutable binding.
var: count = 0
count = + count 1
const: creates an immutable binding.
const: limit = 10
Reassigning a const: name is a type error. Assigning through an indexed target rooted at a const: array is also a type error.
---
10. Output
out: writes one value and a trailing newline.
out: 'hello'
out: + 1 2
Stable output types:
int
float
bool
string
nullable string
Arrays, functions, and void values cannot be output.
---
11. Input
in: reads one required line from stdin and returns a string.
var: name = in:
in: may take an optional prompt expression. The prompt must be string.
var: prompt = 'Name? '
var: name = in: prompt
Prompt behavior:
write prompt to stdout
add no newline
flush stdout before reading
Input behavior:
reads from stdin only
consumes exactly one line
strips the final \n or \r\n line ending
preserves all other whitespace
returns '' for an empty line
has no language-level line length limit
returns final unterminated input as a line
runtime-stops if EOF happens before any text is read
runtime-stops on stdin read failure or allocation failure
in: is an expression and may appear anywhere an expression is valid.
out: in: 'Say something: '
Typed input is not part of in:. Read text first, then parse it explicitly when a parse API exists.
---
12. Expressions
Stable expressions:
literals
names
in:
prefix operators
grouped expressions
function calls
qualified module calls
array literals
index expressions
block expressions under commands
Index expressions work on arrays and strings.
var: nums = [1, 2, 3]
out: nums[0]
out: 'walk'[1]
String indexing returns a one-character string by zero-based byte index and runtime-stops when the index is out of range.
Grouping uses parentheses:
var: x = * (+ 1 2) (- 9 4)
---
13. Operators
Numeric operators:
+ 2 or more args
* 2 or more args
- exactly 2 args
/ exactly 2 args, returns float
^ exactly 2 args
Comparison operators:
> < >= <= == !=
Boolean operators:
and 2 or more bool args
or 2 or more bool args
not exactly 1 bool arg
Negative numeric literals are supported:
var: x = -4
Unary negation of variables is not v1.1 syntax. Use subtraction from zero:
var: y = - 0 x
---
14. Arrays
Array literals are homogeneous. Empty arrays need an explicit array annotation.
var: nums = [1, 2, 3]
var: words = ['a', 'b']
var: guessed array[string] = []
Stable native array element types:
int
float
bool
string
Indexing is zero-based.
out: nums[0]
nums[1] = 9
Nested array emission is not a stable v1.8 native feature.
---
15. Functions
Function declarations may use typed parameters.
func: add(a int, b int) int
return: + a b
Obvious local functions may omit parameter and return types. Whole-number arithmetic infers int; float contexts infer float; boolean contexts infer bool.
func: power_four(n)
return: ^ n 4
If a parameter type cannot be inferred from the function body, the parameter needs an explicit annotation. Function types are not inferred from later call sites.
func: identity(value) # type error until value is annotated
return: value
If a return type is omitted and the function has no return: value, the function returns void and should end normally.
func: say(message string)
out: message
return: requires a value and is valid only inside a function with a compatible return type.
Non-void functions must return on all paths.
---
16. Function Values
Named functions may be passed as values.
func: inc(x int) int
return: + x 1
func: apply(f func(int) int, x int) int
return: f(x)
out: apply(inc, 4)
Anonymous functions and closures are not v1.1 syntax.
---
17. Control Flow
if: conditions must be bool.
if: > age 18
out: 'adult'
else:
out: 'minor'
while: conditions must be bool.
while: < count 3
count = + count 1
repeat: counts must be int.
repeat: 3
out: 'again'
for: iterates arrays.
for: n in nums
out: n
break: and continue: are valid only inside loops.
---
18. Modules
Built-in modules:
math
string
array
time
random
testing
Draft built-in modules in the current compiler:
io
parse
process
Draft modules are importable for experimentation, but they are not compatibility-protected by the v1.9 stable contract.
Stable built-in functions:
math.sqrt(number) -> float
math.pow(number, number) -> float
string.len(string) -> int
string.at(string, int) -> string
string.contains(string, string) -> bool
string.concat(string, string) -> string
array.len(array[T]) -> int
array.contains(array[T], T) -> bool
array.push(array[T], T) -> array[T]
time.now() -> int
random.int(int, int) -> int
random.choice(array[T]) -> T
testing.assert(bool) -> bool
array.contains, array.push, and random.choice are stable for arrays whose elements are int, float, bool, or string.
User modules are sibling .walk files imported by bare module name.
imp: calc
out: calc.square(5)
calc resolves to calc.walk in the importing file's directory.
User module rules:
- imported calls stay namespaced
- only functions listed with
exp:are callable from another file - module files may contain only
imp:,func:, andexp:at top level - import cycles are errors
---
19. Tests
test: defines a test block for walk test.
test: 'add works'
assert: == add(2, 3) 5
assert: requires a bool expression. Failed assertions make the generated test executable exit non-zero.
testing.assert(bool) is a stable v1.3 stdlib helper that returns its bool argument unchanged. It is intended to be paired with assert::
imp: testing
test: 'wrapped assertion works'
assert: testing.assert(true)
Normal walk build ignores test: blocks.
---
20. Formatter
walk fmt emits stable spacing and indentation:
var:x=+ 1 2
becomes:
var: x = + 1 2
Formatter output uses 4 spaces for indentation.
---
21. Diagnostics
Compiler diagnostics use this shape:
file.walk:line:column: category: message
The command-line display may add a source snippet, caret, and focused suggestion under that stable first line:
main.walk:1:16: type error: age is int, got string
var: age int = 'old'
^ string cannot initialize int
Stable categories:
syntax error
type error
name error
module error
warning
internal error
Warnings do not fail by default. --warnings=error promotes warnings to errors. Stable v1.4 warnings cover shadowing an outer name and unreachable statements after return:, break, or continue.
---
22. Non-Goals
These are not stable v1 features:
classes
structs
methods
generic functions
inheritance
interfaces
traits
anonymous functions
closures
try/catch
networking
package manager
debugger
full LSP
file/json/matrix stdlib APIs
empty arrays
nested arrays in native output
---
23. Minimal Valid Program
out: 'hello'
---
24. Representative v1.1 Program
imp: math
func: add(a int, b int) int
return: + a b
func: distance(x1 float, y1 float, x2 float, y2 float) float
return:
math.sqrt(
+:
^ (- x2 x1) 2
^ (- y2 y1) 2
)
var: nums = [1, 2, 3]
for: n in nums
out: add(n, 10)
var: d = distance(0, 0, 3, 4)
if: == d 5
out: 'distance is 5'
else:
out: 'distance is not 5'