Introduction

Languages are cool! Compilers are cool! How do they work? That's why this project exists: Kyanite is a statically-typed, compiled programming language to learn more about how PLs are created. There are two backends: LLVM and a custom IR, kyir. The latter currently supports more language features, but is much less stable than the former.

Welcome to the Kyanite docs! See the sidebar for an overview of what's here, particularly the language reference.

Command Line Interface

The Kyanite command-line interface is a Clap crate within the workspace that provides an interface for users to interact with the underlying compiler. This is a quick guide to its functionality.

run

This is a convenience command for building a .kya program and then executing it. With the current implementation, it blocks for all output produced by the program before displaying it. If you want to run a program without blocking, use the build command and run the compiled binary directly.

For a more detailed explanation, see the help command: kyanite help

Reference

This section contains a comprehensive reference for the Kyanite language. A representative Kyanite program might look something like this:

% Comments are defined with the `%` character.
const PI: float = 3.14;

Class Coordinate {
    x: int,
    y: int,
}

fun sum(c: Coordinate): int {
    return c.x + c.y;
}

fun main() {
    let coordinate: Coordinate = Coordinate:init(
        x: 1,
        y: 2,
    );

    % The `+=` form is not currently supported
    coordinate.x = coordinate.x + 1;

    println_int(coordinate.x);
    println_int(coordinate.y);

    println_int(sum(coordinate));
}

Primitive Types

int: A 64-bit signed integer, represented as a sequence of one or more digits: e.g. 3, 4, 17800.

float: A 64-bit floating-point number, represented as a int followed by a decimal point and zero or more digits: e.g., 3.14, 3..

bool: true or false.

str: A constant, immutable sequence of characters, enclosed with double quotes: "Hello, world!".

void: A non-constructible type that represents the absence of a value.

Classes

Classes are a non-recursive 1 collection of named fields and methods used to represent structured data and associated operations. Classes are declared with the class keyword, followed by a comma-separated list of field names and their types, and optionally a collection of methods:

class Coordinate {
    x: int,
    y: int

    fun add(other: Coordinate): Coordinate {
        return Coordinate:init(
            x: self.x + other.x,
            y: self.y + other.y,
        );
    }
}

Classes are constructed as follows:

let coordinate: Coordinate = Coordinate:init(
    x: 1,
    y: 2,
);
1

Classes cannot contain fields of their own type, either directly (as a field on itself) or indirectly (as a field on another class that contains a field of the original class's type).

Inheritance and Generics

Kyanite has preliminary support for inheritance and generics. The following is the syntax for extending a class:

class X {
    x: int

    fun show(self) {
        println_int(self.x);
    }
}

class Z: X {
    y: int

    fun show(self) {
        println_int(self.x);
        println_int(self.y);
    }
}

As shown, child classes inherit fields of parent classes. Additionally, the child class's show function overrides the parent class's show function.

Generics

Suppose we have a class Print:

class Print {
    fun print(self) { /* not implemented */ }
}

Then, a class Foo can be declared like so:

class Foo<T: Print>: Print {
    val: T

    fun print(self) {
        self.val.print();
    }
}

The type parameter T has a bound of Print, meaning Foo can act as a container of sorts for any Printable object. Becuase T is bounded, we can access val.print() inside the body of Foo's print method.

Unbounded type parameters are also supported when the type of the object is not important to the program's logic.

Functions

Functions are declared with the fun keyword as follows:

fun foo() {}

foo takes no arguments and returns no value as output (referred to as a void function). The return type may also be specified explicitly (e.g. fun foo(): void {}).

Formal parameters are specified as follows:

fun foo(x: int, y: int): int {
    return x + y;
}

Calling a function

Functions are called using their name followed by a list of arguments:

foo(1, 2);

Or with no arguments:

foo();

Expressions

Literals

"Hello, world!", 3, 3.14, true

Identifiers

Identifiers are a sequence of one or more characters, where the first character may not be a digit 0-9.

foo, bar, baz

Calls

foo(), foo(1, 2, 3)

Binary Operators

+, -, *, /, ==, !=, >, <, >=, <=

Logical operators are not currently supported.

Unary Operators

-, !

Grouping

(1 + 2) * 3

Statements

Variable Declaration

let name: type = value;

Assignment

expression = value;

Valid left-hand sides of assignment are identifiers (e.g. foo, or an access expression foo.bar.baz).

Return

return value;

Expression

expression;