viri

A small, statically typed programming language

Built from scratch to learn how languages work — a hand-written scanner, parser, type checker, compiler and bytecode VM, all in Go.

Try it — runs entirely in your browser

Input
Loading...
1
2
3
4
Output

Viri compiles to bytecode and runs on its own virtual machine, built here for WebAssembly — the same compiler the viri binary uses. Nothing is sent to a server.

A taste

fun fibonacci(n: number): number {
  if (n <= 1) return n;
  return fibonacci(n - 1)
       + fibonacci(n - 2);
}

print fibonacci(10);

var multiply: fun(number, number): number =
  fun(a: number, b: number): number {
    return a * b;
  };
print multiply(3, 4);

// IIFE
print fun(x: number): number { return x * x; }(5);
class Animal {
  name: string;

  init(name: string) {
    this.name = name;
  }
}

class Dog < Animal {
  init(name: string) {
    super.init(name);
  }

  speak() {
    print this.name + " barks";
  }
}

var rex: Dog = Dog("Rex");
rex.speak();

// every field is declared, and
// init must assign all of them
// Viri checks the whole program
// before any of it runs.

var count: number = 10;
var name: string = "Viri";

// Uncomment a line to see the
// checker reject it:

// var bad: number = "ten";
// print count + name;
// if (count) { print "hi"; }
// count.missing;

print name + " has " + "types";
import "std:math" as m;

print m.PI;
print m.pow(2, 3);
print m.sqrt(144);

// standard library calls are
// type-checked like any other:
// m.sqrt("144") is an error
var list: []number = [1, 2, 3];
print list[0];

var dict: map[string]string = {
  "name": "Viri",
  "ver": "1"
};
print dict["name"];

// an annotation is what gives an
// empty literal its type
var empty: []string = [];
print len(empty);

for (var i: number = 0; i < 10000; i = i + 1){
  print i;
}
  

What you get

Static types, checked ahead of timeevery variable, parameter, field and return type is declared; nothing runs until the whole program checks
No nilevery type holds a real value, so there is no null to guard against
Classes & inheritancedeclared fields, single inheritance, and constructors that must leave every field assigned
Functions & closuresfirst-class functions with typed signatures that capture their surrounding scope
Module systemfile-based modules with explicit exports and alias-based imports
Arrays & mapstyped collections — []number, map[string]bool — with no untyped escape hatch