Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

TypeExampleNotes
Integer12364-bit signed
Float3.14IEEE-754 double precision
String"hello\n"Double quotes; \n, \\", \\t escapes
Char'a'Single quotes
Booleantrue, falseKeywords
None/nullnoneRepresents 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:

CategorySymbols
Arithmetic+, -, *, /, // (floor div), %, **
Comparison==, !=, <, <=, >, >=
Booleanand, or, not
Bitwise&, `
Assignment=, +=, -=, *=, /=, //=, %=, **=
Increment/Dec++x, --x
Member accessobj.field *, arr[index]
Function callfn(arg1, arg2)

Precedence (highest → lowest):

  1. Primary

    • identifiers, literals, parenthesized expressions
    • array indexing arr[i], field access obj.f, argument-less calls fn()
  2. Call

    • function calls: fn(arg1, arg2)
  3. Exponentiation

    • **
  4. Unary

    • prefix -, not
  5. Factor

    • *, /, //, %
  6. Term

    • +, -
  7. Bitwise AND

    • &
  8. Bitwise OR

    • |
  9. Bitwise XOR

    • ^
  10. Comparison

    • <, <=, >, >=
  11. Equality

    • ==, !=
  12. Logical AND

    • and
  13. Logical OR

    • or
  14. 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/else statements like python.
  • The condition must be wrapped around parenthesis
if (condition):
    # true branch
    print("Yes")
else:
    # false branch
    print("No")

while

  • Tundra supports regular while statements 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 for statements like python.
for i in range(3):
    print(i)

break and continue

    • Tundra supports regular break and continue statements like python.
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 fun keyword
  • 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:

NameArgumentsDescription
print(x)1Print a value to stdout with newline
input()0Read a line from stdin as string
parseInt(s)1Convert string s to integer
parseFloat(s)1Convert string s to float
len(arr)1Length of array
Array(n)1Create 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