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,
);
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 Print
able 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;