Introduction

Ankoku is a lightweight embeddable scripting language written in Rust.

The main inspirations are JavaScript, Rust, and Java-style OOP.

If you know JavaScript, Ankoku should be easy to pick up.

Features

  • Cool name
  • Not Lua

Examples

Here are some basic examples to demonstrate the syntax and semantics.

Hello World

print("Hello, world!");

Recursive Fibonacci

fn fib(n) {
  if (n < 2) { n } else { fib(n - 1) + fib(n - 2) }
}
print("Hello, world!");

First Class Functions

fn do_10_times(f) {
	for i in 0..10 {
		f(i);
	}
}

do_10_times(fn(i) {
	print(i)
});

Objects

let object = {
	just_a: "A hashmap, dictionary, hash table, etc."
};

print(object.a_thing);

Classes

For more about OOP, read Object Oriented Programming in Ankoku.

class Animal {
	fn make_sound() {
		print("*crickets*");
	}
}

class Cow: Animal {
	fn make_sound() {
		if (this.loud) {
			print("MOOOO!!");
		} else {
			print("Mooo");
		}
	}
}

let cow = Cow.new();

cow.make_sound(); // Mooo
let animal = Animal.new();

Animal.make_sound(); // Animals.

Object Oriented Programming in Ankoku

OOP is implemented into Ankoku, in a mix of prototypal and traditional styles, with the goals of being fast without too much magic. When defining a class, you define all the methods like this:

class Animal {
	fn make_sound() {
		print("*crickets*");
	}
}

class Cow: Animal {
	fn make_sound() {
		if (this.loud) {
			print("MOOOO!!");
		} else {
			print("Mooo");
		}
	}
}

but they actually desugar into normal functions:

fn Animal.make_sound(this) {
	print("*crickets*");
}

fn Cow.make_sound(this) {
	if (this.loud) {
		print("MOOOO!!");
	} else {
		print("Mooo");
	}
}

(this is prepended as the first argument unless the function is a static fn)

And then it also creates constructor functions and a type:

type Animal(Object);

fn Animal.new() {
	// user code goes here...
	Object.new(Animal) // create a new object of type Animal
}

type Cow(Animal);

fn Cow.new() {
	Object.new(Cow) // create a new object of type Cow
}

Object is at the top of the inheritance hierarchy and includes static methods to create new objects.

Anyways, each object has a type:

let object = {}; // empty object, desugars to Object.new(Object)

print(object is Object); // true

let animal = Object.new(Animal); // animal

print(animal is Animal); // true

The type is accessible by calling typeof:

let object = {}; // empty object

print(typeof(object) == Object); // true
print(typeof(object) == Animal); // false

let animal = Object.new(Animal); // animal

print(typeof(object) == Animal); // true
print(typeof(animal) == Object); // false

However, typeof only gives the direct type, so use is for comparisons to allow inherited types.

Anyways, Object.new(type) will set properties on the object to the associated non-static functions (fn Animal.*):

let animal = Object.new(Animal); // finds all functions for animal (Animal.*)

print(animal.make_sound); // [function]
print(animal); // { make_sound: [function] }

But it will set it to bound versions of them (using .bind on Function), so the first parameter (this) is set to the created object.

type Example(Object);
fn Example.assoc_types(this) {
	return this;
}

let example = Object.new(Example);

print(example.assoc_types() == example); // true

And you can call associated functions yourself as well, if you'd like to use a specific version:

let cow = Cow.new();

Animal.make_sound(cow); // prints "*crickets*"

This is a reasonably elegant system that is sort of confusing, but combines some of the dynamicity of prototypes with the rigidity of OOP.

Reference

This is an API reference for the built in types.

Function

Functions are instances of the Function class, which is an FFI class and private. They expose a few methods, generally for making more functions.

Function.call(this, arguments)

Call this function with the specified arguments.

Implemented in the VM.

Function.bind(this, ...arguments)

Bind this function with a starting number of parameters. Most commonly used to bind this in Object.new.

Example implementation:

fn Function.bind(this, ...arguments) {
	return fn(...args) {
		return this.call(arguments.concat(args));
	}
}

Object

Object is the root class of the hierarchy. Everything is Object.

Object.new(type)

Creates an instance of a Type.

Type

Type is the type that specifies types. It's implemented in the VM, but is still totally meta.

type name(parent);

Not a function, but built in syntax. Creates type name that inherits from parent.

Example

type Cat(Object);