Introduction
Welcome to Tundra!
Tundra is a Python-like, register-based language implemented in Rust.
This Wiki is here to help users use, understand and contribute to Tundra.
Syntax Overview
Tundra uses a clean, Python‐inspired syntax with significant (indentation) whitespace and familiar control constructs. Below is the high‐level grammar:
<program> ::= ( <simple_stmt> | <compound_stmt> )* EOF
<simple_stmt> ::= <small_stmt> NEWLINE
<small_stmt> ::= <var_decl>
| <expression_stmt>
| <print_stmt>
<compound_stmt> ::= <if_stmt>
| <for_stmt>
| <while_stmt>
| <fun_decl>
| <class_decl>
<suite> ::= <simple_stmt>
| NEWLINE INDENT
( <simple_stmt> | <compound_stmt> )+
DEDENT
<var_decl> ::= "var" IDENTIFIER [ "=" <expression> ]
<fun_decl> ::= "fun" IDENTIFIER "(" [ <parameters> ] ")" ":" <suite>
<class_decl> ::= "class" IDENTIFIER ":" <suite>
<if_stmt> ::= "if" "(" <expression> ")" ":" <suite>
[ "else" ":" <suite> ]
<for_stmt> ::= "for" IDENTIFIER "in" <expression> ":" <suite>
<while_stmt> ::= "while" "(" <expression> ")" ":" <suite>
<print_stmt> ::= "print" "(" <expression> ")"
<expression_stmt>::= <expression>
<expression> ::= <assignment>
<assignment> ::= IDENTIFIER "=" <expression>
| <logic_or>
<logic_or> ::= <logic_and> { "or" <logic_and> }
<logic_and> ::= <equality> { "and" <equality> }
<equality> ::= <comparison> { ( "==" | "!=" ) <comparison> }
<comparison> ::= <term> { ( "<" | "<=" | ">" | ">=" ) <term> }
<term> ::= <factor> { ( "+" | "-" ) <factor> }
<factor> ::= <unary> { ( "*" | "/" | "//" | "%" ) <unary> }
<unary> ::= ( "!" | "-" ) <unary>
| <primary>
<primary> ::= <atom> { <trailer> }
<atom> ::= IDENTIFIER
| INT_LITERAL
| FLOAT_LITERAL
| STRING_LITERAL
| CHAR_LITERAL
| BOOL_LITERAL
| "none"
| "(" <expression> ")"
| <array_literal>
<array_literal> ::= "[" [ <expression> { "," <expression> } ] "]"
<trailer> ::= "(" [ <argument_list> ] ")"
| "." IDENTIFIER
| "[" <expression> "]"
<parameters> ::= IDENTIFIER { "," IDENTIFIER }
<argument_list> ::= <expression> { "," <expression> }
-
Statements end with a newline; compound statements introduce a suite indented one level deeper.
-
Expressions follow usual precedence (assignment, or/and, equality, comparison, term, factor, unary, exponent, call, primary).
-
Identifiers: start with a letter or underscore, followed by letters, digits, or underscores.
-
Block structure: changing indentation at the start of a line emits Indent/Dedent tokens; mixing tabs/spaces is disallowed.
Sample Code
var x = 10
fun greet(name):
print("Hello, " + name)
greet("World")
Disassembled Bytecode
== == Disassembled Bytecode == ==
0000 Line 2 LoadConstant(0, Value { value: Int(10) })
0001 Line 2 DefineGlobal(0, "x")
0002 Line 6 LoadConstant(0, Value { value: Function(RefCell { value: FunctionObject { name: "greet", arity: 1, chunk: RefCell { value: Chunk { code: [LoadConstant(1, Value { value: String("\"Hello, \"") }), Move(2, 1), GetLocal(3, 0), Add(4, 2, 3), Print(4), Return(0)], constants: [], lines: [4, 4, 4, 4, 4, 6], max_register: 5 } }, jitted: None } }) })
0003 Line 6 DefineGlobal(0, "greet")
0004 Line 6 GetGlobal(0, "greet")
0005 Line 6 LoadConstant(1, Value { value: String("\"World\"") })
0006 Line 6 Call(2, 0, 1)
0007 Line 6 Pop(2)
0008 Line 7 Return(2)
Output
""Hello, ""World""
Literals
Tundra supports the following literal types:
| Type | Example | Notes |
|---|---|---|
| Integer | 123 | 64-bit signed |
| Float | 3.14 | IEEE-754 double precision |
| String | "hello\n" | Double quotes; \n, \\", \\t escapes |
| Char | 'a' | Single quotes |
| Boolean | true, false | Keywords |
| None/null | none | Represents absence of value |
| Array | [1, 2, 3] | List literal; optional newlines inside |
var x = 24
print(x) # integer
var y = 12.21
print(y) # float
y = "hi"
print(y) # string
print('z') # char literal
print(true) # boolean
print(none) # none
var a =[1,2,3] # array
print(a[0])
Arrays may span multiple lines
var a = [
1,
2,
1111
]
Operators
Tundra’s operators fall into these categories:
| Category | Symbols |
|---|---|
| Arithmetic | +, -, *, /, // (floor div), %, ** |
| Comparison | ==, !=, <, <=, >, >= |
| Boolean | and, or, not |
| Bitwise | &, ` |
| Assignment | =, +=, -=, *=, /=, //=, %=, **= |
| Increment/Dec | ++x, --x |
| Member access | obj.field *, arr[index] |
| Function call | fn(arg1, arg2) |
Precedence (highest → lowest):
-
Primary
- identifiers, literals, parenthesized expressions
- array indexing
arr[i], field accessobj.f, argument-less callsfn()
-
Call
- function calls:
fn(arg1, arg2)
- function calls:
-
Exponentiation
**
-
Unary
- prefix
-,not
- prefix
-
Factor
*,/,//,%
-
Term
+,-
-
Bitwise AND
&
-
Bitwise OR
|
-
Bitwise XOR
^
-
Comparison
<,<=,>,>=
-
Equality
==,!=
-
Logical AND
and
-
Logical OR
or
-
Assignment
=,+=,-=, …,**=
Example:
var x = 2 + 3 * 4 # 14
var y = (2 + 3) * 4 # 20
var z = not true or false # false
print(x)
print(y)
print(z)
Bytecode
== == Disassembled Bytecode == ==
0000 Line 2 LoadConstant(0, Value { value: Int(2) })
0001 Line 2 Move(1, 0)
0002 Line 2 LoadConstant(2, Value { value: Int(3) })
0003 Line 2 Move(3, 2)
0004 Line 2 LoadConstant(4, Value { value: Int(4) })
0005 Line 2 Multiply(5, 3, 4)
0006 Line 2 Add(4, 1, 5)
0007 Line 2 DefineGlobal(4, "x")
0008 Line 3 LoadConstant(4, Value { value: Int(2) })
0009 Line 3 Move(5, 4)
0010 Line 3 LoadConstant(1, Value { value: Int(3) })
0011 Line 3 Add(3, 5, 1)
0012 Line 3 Move(1, 3)
0013 Line 3 LoadConstant(5, Value { value: Int(4) })
0014 Line 3 Multiply(6, 1, 5)
0015 Line 3 DefineGlobal(6, "y")
0016 Line 4 LoadConstant(6, Value { value: Bool(true) })
0017 Line 4 LoadConstant(1, Value { value: Bool(false) })
0018 Line 4 Equal(5, 6, 1)
0019 Line 4 Move(1, 5)
0020 Line 4 LoadConstant(6, Value { value: Bool(false) })
0021 Line 4 BitwiseOr(7, 1, 6)
0022 Line 4 DefineGlobal(7, "z")
0023 Line 5 GetGlobal(7, "x")
0024 Line 5 Print(7)
0025 Line 6 GetGlobal(7, "y")
0026 Line 6 Print(7)
0027 Line 7 GetGlobal(7, "z")
0028 Line 7 Print(7)
0029 Line 8 Return(7)
Output
14
20
false
Note
* Classes are not yet implemented in Tundra
Control Flow
if / else
- Tundra supports regular
if/elsestatements like python. - The condition must be wrapped around parenthesis
if (condition):
# true branch
print("Yes")
else:
# false branch
print("No")
while
- Tundra supports regular
whilestatements like python. - The condition must be wrapped around parenthesis
var i = 0
while (i < 5):
print(i)
i = i + 1
for over range()
- Tundra supports regular
forstatements like python.
for i in range(3):
print(i)
break and continue
-
- Tundra supports regular
breakandcontinuestatements like python.
- Tundra supports regular
for i in range(10):
if (i == 3):
break
if (i % 2 == 0):
continue
print(i)
Functions & Classes
Function Declaration
fun add(a, b):
return a + b
The functions in tundra follow a very pythonic syntax
- Need to begin with
funkeyword - Followed by function name and parameters
- Colon to begin definition
- Indent and followed by a regular code
Example
fun fib(n):
if (n < 2):
return n
return fib(n - 1) + fib(n - 2)
print(fib(10))
Bytecode
== == Disassembled Bytecode == ==
0000 Line 6 LoadConstant(0, Value { value: Function(RefCell { value: FunctionObject { name: "fib", arity: 1, chunk: RefCell { value: Chunk { code: [GetLocal(1, 0), Move(2, 1), LoadConstant(3, Value { value: Int(2) }), Less(4, 2, 3), JumpIfFalse(4, 2), GetLocal(3, 0), Return(3), GetGlobal(4, "fib"), GetLocal(3, 0), Move(2, 3), LoadConstant(5, Value { value: Int(1) }), Subtract(6, 2, 5), Move(5, 6), Call(5, 4, 1), Move(4, 5), GetGlobal(6, "fib"), GetLocal(2, 0), Move(7, 2), LoadConstant(8, Value { value: Int(2) }), Subtract(9, 7, 8), Move(7, 9), Call(8, 6, 1), Add(6, 4, 8), Return(6), Return(0)], constants: [], lines: [3, 3, 3, 3, 3, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6], max_register: 10 } }, jitted: None } }) })
0001 Line 6 DefineGlobal(0, "fib")
0002 Line 6 GetGlobal(0, "fib")
0003 Line 6 LoadConstant(1, Value { value: Int(10) })
0004 Line 6 Call(2, 0, 1)
0005 Line 6 Print(2)
0006 Line 7 Return(2)
Output
55
Class Declaration
- To be implemented
Standard Library
Tundra currently provides these built-ins:
| Name | Arguments | Description |
|---|---|---|
print(x) | 1 | Print a value to stdout with newline |
input() | 0 | Read a line from stdin as string |
parseInt(s) | 1 | Convert string s to integer |
parseFloat(s) | 1 | Convert string s to float |
len(arr) | 1 | Length of array |
Array(n) | 1 | Create new array of length n |
Example:
var name = input()
print("Hello, " + name)
var nums = Array(5)
print(len(nums)) # → 5
Bytecode
== == Disassembled Bytecode == ==
0000 Line 2 GetGlobal(0, "input")
0001 Line 2 Call(1, 0, 0)
0002 Line 2 DefineGlobal(1, "name")
0003 Line 3 LoadConstant(1, Value { value: String("\"Hello, \"") })
0004 Line 3 Move(0, 1)
0005 Line 3 GetGlobal(2, "name")
0006 Line 3 Add(3, 0, 2)
0007 Line 3 Print(3)
0008 Line 4 GetGlobal(3, "Array")
0009 Line 4 LoadConstant(2, Value { value: Int(5) })
0010 Line 4 Move(4, 2)
0011 Line 4 Call(0, 3, 1)
0012 Line 4 DefineGlobal(0, "nums")
0013 Line 5 GetGlobal(0, "len")
0014 Line 5 GetGlobal(3, "nums")
0015 Line 5 Move(1, 3)
0016 Line 5 Call(2, 0, 1)
0017 Line 5 Print(2)
0018 Line 6 Return(2)
Output
Dexter Morgan <-stdin
""Hello, "Dexter Morgan"
5