# Draconian Programming Language This document specifies what a valid implementation of Draconian must contain. This document will henceforth be referred to as "the Spec", and all conforming implementations will be generally referred to as “implementation” or “compiler”. The Spec is categorized into these groups: <insert table of contents here> ## Variables Variables are used to store data in Draconian. There are two keywords used to define a variable: `let` and `val`. Variables defined with `val` are immutable; meaning they cannot be changed after the definition. Because Draconian is statically typed, you must explicitly label the type of the variable with the label (`:`) operator. Here are a few examples: ``` # Defining a mutable variable: let x: int = 0; # Defining an immutable variable: val y: int = 1; # We can modify this variable just fine print(x); // 0 x = 420; print(x); // 420 # We cannot modify this constant variable print(y); // 1 y = 69; // IllegalAssignmentException: V-01 ``` ## Typing System Draconian is a static, strongly typed programming language with manifest typing. In layman terms, that means the compiler must know the type of all values at compile time, and that you must explicitly define the type when you declare a variable. The “strongly typed” part means the compiler will use types to ensure you’re not doing something stupid (for example adding a number to a variable). ## Data Types Datatypes consist of the built-ins, enums, classes, and structs. ### Built Ins ## Operators Draconian has standard operators: - Arithmetic - Infix - `+` Addition - `-` Subtraction - `*` Multiplication - `/` Division - `%` Modulus - `**` Exponentiation - `=>` Type Casting - Additional Information: Attempts to convert operand `LEFT` to `typeof(RIGHT)`. - Unary - `-` Negation - `!` Logical Negation - `~` Binary Negation - Postfix - `++` Incrementation - `--` Decrementation ## Collections Collections are data types that group together values that can be accessed at any point of the program. There are many different types of collections, each storing data in a different way with different use cases. Tuples are immutable, ordered collections of elements that **do not** have to be the same type. Since it is read-only, you should initialize it as a constant. ``` val tuple: (type1, type2, [...typeN]) = (type1, type2, [...typeN]); tuple[index]; ``` Arrays differ from tuples in the manner that they are **mutable** collections of elements that are of the **same type**. Arrays, like tuples, are fixed size. ``` let array: Array<T> = Array(length); array.get(index); # Array zero-indexed access array.length(); # Array length cheking ``` Lists are mutable, ordered collections of elements that are the **same type**. Unlike tuples and arrays, the list can change in size. ``` let list: List<int> = [1, 2, 3]; list.add(4); # [1, 2, 3, 4] list.insert(2, 5); # [1, 2, 5, 3, 4] list.remove(2); # [1, 2, 3, 4] list.get(1); # 2 ``` Stacks are mutable, ordered collections of data organized in a LIFO system (Last In, First Out) for stack-like manipulation. ``` let stack: Stack<int> = [1, 2, 3]; stack.push(4); # [1, 2, 3, 4] stack.pop(); # [1, 2, 3], returns 4 stack.peek(); # [1, 2, 3], returns 3 ``` Queues are mutable, ordered collections of data organized in a FIFO system (First In, First Out) for queue-like manipulation. ``` let queue: Queue<int> = [1, 2, 3]; queue.enqueue(4); # [1, 2, 3, 4] queue.dequeue(); # [2, 3, 4], returns 1 queue.peek(); # [2, 3, 4], returns 2 ``` ## Control Flow Control flow dictates the order that statements are executed. In Draconian, there are three methods of control flow: If/else statements, switch statements, and pattern statements. Switch statements execute the statement or block of the first match. ``` switch (0) { i % 15 -> print("fizzbuzz"); # Single statement i % 5 -> { print("buzz"); }; # Block statement i % 3 -> print("fizz"); _ -> print(i); # Default case } ``` Pattern matching matches the **shape** or **type** of the value provided. An example of matching shape: ``` val tuple: (int, int, int) = (1, 5, 7); pattern (tuple) { (1, _, _) -> print(1); # Only the first number is checked (1, _, 3) -> print(2); # The first and last numbers are checked (1, 5, 7) -> print(3); # All numbers are checked _ -> print(4); # Default case # The first pattern match evaluates, printing 1 to the console } ``` An example of matching type: ``` # Todo ``` ## Functions ``` val functionName: Function = (argument: type) -> returnType { } ``` ## Classes A class is simply a collection of data, and methods to operate on that data. ``` class Player { private x: float, y: float, speed: float; fn new(this.x, this.y, this.speed = 100, modifier: int) { // x, y, and speed are all automatically assigned // modifier is a local variable and will // be destroyed after it is out of scope this.speed *= modifier; } fn move() { this.x += this.speed; this.y += this.speed; } } ``` Class members without an access modifier are automatically made `public`. However, you may still use `public` for readability. ## Structures Structures are also a way of grouping values under a common namespace. That might sound daunting, so here's an example of a pretty common struct: ``` struct Coordinate { public int x, y, z; fn new(this.x, this.y, this.z); fn operator equals(right: Coordinate) -> bool { return right.x == this.x && right.y == this.y && right.z = this.z; } } ``` The difference between a `struct` and a `class` is that `struct`s cannot have any non-operator methods, making them strictly a data container and data type. Structs should not "do" anything by themselves. We'll cover operator overloads in a later section. ## Loops ``` # condition: boolean while (condition) { } ``` ``` # init; condition; w for (let i: int = 0; i < 10; i++) { } ``` > ## Common Exceptions Here are all the common exceptions that users may encounter frequently. An implementation must list the variant of the exception, and a stacktrace. To improve user friendiness, you may add additional text to the variant. For example, instead of `IllegalAssignmentException (V-01)`, an implementation may output `IllegalAssignmentException (V-01, ConstReassignment)`. - IllegalAssignmentException - V-01 - A value is attempting to be reassigned to a constant variable. Try changing `val` to `let` in your source code. - V-02 - A value of a differing type from the variable is being assigned to the variable. This is usually a mistake, check the type of the value you're assigning with `typeOf();`. - V-03 - A value is attempting to be assigned to a non-existent variable. This problem typically happens after forgetting to declare a variable. - IllegalArgumentException - V-01 - One or more of the arguments given in a function call differs from the type of the corresponding argument. Make sure to check the types of the arguments and parameters. - V-02 - A function given an incorrect number of arguments is attempting to be called. Make sure the number of arguments and parameters are both the same. - IndexOutOfBoundsException - V-01 - An array was indexed with a value greater than its length. Check to make sure that the index you're using fits within the array's `.length()` method.