Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Welcome to the KSL Docs!

Hopefully you learn something useful about KSL!

Think something is missing or could be improved? Contribute Now!

Here by accident? KSL Hopepage.

Introduction

KSL is a language designed to be strongly typed, aot-compiled, and memory safe. Along with some other neat features like function attributes and pretty much whatever else seems worthy of having.

Getting Started

Installing KSL

Configuring KSL

Tutorials

Hello World in KSL

Ah yes, the traditional "hello world" program. Luckily for you, my dear programmer, we have a fun little cheat for the world's most basic program. The hello world program seems to be something that a lot of developers end up building, and KSL wants to make everything easier.

So we decided to make a standard library module for the hello world program!

using std.t;

fn main() -> void {
	std.t.hw();
	return;
}

And that's it. The hw() function will print "Hello, World!"


Ok, but what if I want to actually say "World, Hello?"

Now there we have some innovation! Some progress! You're going to do incredible things in the future, that's truly some outside the box thinking!

Let's do it!

So first, we're going to need a main function, our entry point.

fn main() -> void {}

Since we don't need this function to return anything we can just set the return type to void.

We're also writing to the console, so we're going to need our good old standard io module. Go ahead and add it with: using std.io;

That should give us access to the writeln function, so let's finally finish off your "World, Hello?" program.

using std.io;

fn main() -> void {
	std.io.writeln("World, Hello?");
	return;
}

If Statement

For Loops

If you're here you probably want to learn how to make a for loop in KSL. If that's the case, welcome! Otherwise, uh, leave?

So, we want to make something interesting that expresses how KSL works. A great idea for that would be to make a simple program that generates a random decimal value, we'll then round that and count the number of 1's and 0's in a sample number!

The first thing we need to do, is consider our requirements. We need to generate a random number, round it, and we'll want to display the results at the end. So we need std.random, std.math, and std.io.

Let's start our new file, you can call it whatever you want but I'll call mine super_epic_for_loop_example_for_the_ksl_docs_tutorial.k! Without further adieu, let's begin.

using std.io;
using std.math;
using std.random;

This should get all our needs taken care of in regards to the standard library modules! Now we need to define our entry point.

fn main() -> int {
	return 0;
}

As covered in some other tutorials (hopefully) the main function is the entry point and the -> int defines the function derivative (basically the expected function return type, but KSL has to be sooooo special.)

Now let's define our zero and one counts. By default all variables in KSL are mutable, this is great news for us because that means we don't have to do any extra work to update these values!

fn main() -> int {
	int zeros = 0;
	int ones = 0;
	return 0;
}

We've made so much progress so far! Go ahead and get a drink of water, because this is where it gets real.

We need to add the famed for loop now.

fn main() -> int {
	int zeros = 0;
	int ones = 0;
	for (int i = 0; i < 100; i++;) {

	}
	return 0;
}

So, let's disect this a little bit. The first statement is our initializer, we're setting the i variable to an integer with a value of 0. The next is an expression, it's expected to be a boolean. If the value is true then it will run again, otherwise it will move on in the program. Finally, we have the iterative statement, the i++, which is just a shorthand to increase the variable i by 1 in this case.

Great, that was a lot, but it only gets better from here! Let's add the random number generation function!

fn main() -> int {
	int zeros = 0;
	int ones = 0;
	for (int i = 0; i < 100; i++;) {
		f64 random_float = random.rand() -> f64;
	}
	return 0;
}

This line of code actually covers a lot of topics in KSL, for now most of them aren't super necessary to know. If you want to read more later check out inline function calls, specifically inline hints. All you need to know for this tutorial is that this is getting a random number and saving it to the random_float variable, which we have set to a f64.

Now we need to round this value to figure out if it will be a 1 or a 0!

We can do this with the following code, go ahead and add it after the random_float variable line.

int random_rounded = math.round(random_float) -> int;

If you've figured that out we can move on to the if statement, which will be the part of our program that actually tells us which variable to increment (ones or zeros, if you've already forgotten.)

#![allow(unused)]
fn main() {
if (random_rounded == 1) {
	ones++;
} else {
	zeros++;
}
}

What this is doing is checking if the random_rounded variable is equal to 1, if it is, then we increment the ones variable (remember that shorthand from earlier?) Otherwise, we add 1 to the zeros variable. Luckily, since the result of our random number rounding can only be 1 or 0 in this situation, we don't have to add any other logic for counting!

If you ran this code right now it would work! But you wouldn't be able to see the results :( and that's kind of the point of this whole thing. So, let's use our awesome io module from the standard library to show our counts!

#![allow(unused)]
fn main() {
io.writeln(zeros);
io.writeln(ones);
}

Great, now it will print our counts to the console!

For those of you that don't care about learning the language and just want to make something quickly without thinking of the potential consequences, here is the full code:

using std.io;
using std.math;
using std.random;

fn main() -> int {
	int zeros = 0;
	int ones = 0;
	for (int i = 0; i < 100; i++;) {
		f64 random_float = random.rand() -> f64;
		int random_rounded = math.round(random_float) -> int;
		if (random_rounded == 1) {
			ones++;
		} else {
			zeros++;
		}
	}
	io.writeln(zeros);
	io.writeln(ones);
	return 0;
}

Oh yeah, if you wanted to check your work that's also good, no hate on that.

While Loops

Rock, Paper, Scissors

Comments

Comments in KSL are similar to any other modern language. For a single line comment you can do // and for a multi- line comment, the usual /* */ syntax.

// Below is a function, and this is a comment!
fn main() -> void {
	return;
}
/*
 Below is a function that returns void,
 and this is a multi-line comment!
*/
fn main() -> void {
	return;
}

You can even use this inline! (If you're crazy...)

fn /* create a function */ main /* call it `main` */ () /* no args */ -> void /* return nothing */ {
	return /* see, nothing! */;
}

note

Due to some current limitations, KSL has a hard time handling nested multi-line comments.

Understanding Types

Integers

Floats

Booleans

References

Others

Variables

Variable declarations are extremely simple. Simply pick a variable type and identifier, then set it to a value.

int x = 20;

important

You cannot defined a variable with no value, for example: int x; is not valid syntax. However, variables are treated as mutable, so to achieve the same effect just initialize the variable with dummy data.

Currently the supported types in KSL are:

TypeIdentifierSize (Bits)
int8i88
int16i1616
int32i3232
int64i64 || int64
float32f3232
floatf64 || float64
boolbool1
nullnullN|A
voidvoidN|A

note

KSL is designed for programmers of all skill levels. Some programmers might not care (or know) about int's of different sizes, so int and float are aliases for the largest supported int and float types. It makes it easier for beginners to understand and it's more readable for simple projects that don't need to worry about tight memory constraints.

warning

Documentation incomplete, pulled from unfinished ksl/ksl_syntax.md file.

Expressions

Operations (Binary and Unary)

Higher precedences are executed first.

Supported Binary Operations (and Precedence):

OperationSymbolPrecedence
Multiply*80
Divide/80
Add+70
Subtract-70
Less or Equal<=60
Greater or Equal>=60
Less Than<50
Greater Than>50
Equal==40
Not Equal!=40
Not (Bool)!30
And (Bool)&&20
Or (Bool)||10

Supported Unary Operations:

OperationSymbol
Negative-<num>
Not!<bool>
Cast<type>'<expr>

Operations are only allowed between supported types. KSL may automatically promote expressions (ex. 12 + 300 will turn into i16) but it will not demote them, in order to set a variable to a value smaller than the type size in the resulting expression please downcast.

Other examples of expression promotion:

// Since we're working with an integer
// and a float, we promote to a float
float x = 10.0 + 5;
#![allow(unused)]
fn main() {
// Since we're working with an i8 (10)
// and an i16 (400) the expression is
// promoted to an i16, we can still assign
// the expression result to an i32 though
// because the variable storage type is
// larger. If `x` was an i8 it would cause
// an error.
i32 x = 10 + 400;
}

warning

Information and methodology about expression promotion is subject to change as we evaluate other type safety and memory safety strategies.

warning

Documentation incomplete, pulled from unfinished ksl/ksl_syntax.md file.

Functions

Definition

For now, functions will be defined with the fn keyword, then comes the function identifier and typed parameters (inside parenthesis). As KSL is a strongly typed language, you will also need to define the function return type (or function derivative.) This can be done with -> and then the type identifier.

fn main(int a) -> int {
	return a;
}

warning

Documentation incomplete, pulled from unfinished ksl/ksl_syntax.md file.

Returns

Attributes

Inline Calls

Currently, functions in KSL are defined internally in two ways, by the function body declaration or a forward declaration. In the case of a forward declaration, KSL will attempt to interpret the most likely return type. For example, if a function is called inside of an integer operation then it's likely to be an integer. In order to override this behavior and define a return type for an inline function call you can use the derive keyword (->) on the function call. See below:

Example of inline call:

int x = add(1, 3) + 5;

Example of inline call w/ type hint:

int x = add(1, 3) -> int + 5;

Sometimes it can be easier to read if you move the type hint to right after the funciton:

int x = add(1, 3)->int + 5;

important

You cannot use this syntax to change the function return type. If you wish to use a different type (post-return) then look into type casting. This is only to hint to the compiler what the expected return type is.

caution

Using this syntax to claim a function returns void for a function that returns an int will result in an unresolved symbol. The same applies for other type differences as well.

Similarly, if a function call doesn't appear to have any context then KSL might assume it returns nothing (void or null.) This might cause an error so you can use the type hint syntax outside of algebraic operations as well.

Example:

add(1, 3) -> int;

warning

Documentation incomplete, pulled from unfinished ksl/ksl_syntax.md file.

Conditionals

Loops

For

While

The syntax for while loops is absurdly easy. All you need is the while keyword with some parenthesis and an expression.

while (<expression>) {
	// Do something? Maybe?
}

The only real requirement is that the expression type is a boolean. KSL will give you a nice error if you don't. If this is a problem, you can always cast to a boolean.

while (bool'127) {
	// Do something? Probably?
}

For those of you focused on performance, look, I get it. You want the fastest loop option, for loop or while loop? Which is it?

Well, the answer is pretty simple. Both the for loop and while loop are compiled down to the exact same code. So the speed of your program mostly lies with the quality of the code.

Type Casting

KSL has a potentially unintuitive type casting syntax. Just use a type identifier followed by an apostrophe:

float x = 10.0;
int y = int'x;

There are some type casts that will result in errors, for example, casting to null or void. But why do that anyway? It should be noted that casting only works for the term immediately right to the cast. See below:

Example where the entire expression is calcuated then cast:

// This turns into 2.0 and is then turned into 2.
int x = int'(1.0 + 1.0);

Example where only the first number value is cast:

// This is turned into 1 + 1 then turned into 2.
int x = int'1.0 + 1;

A quick note on casting to booleans:

In some languages casting integers or floats to a boolean may be different. In KSL all integers other than 0 are cast to true. Similarly, all floats other than 0.0 are cast to true:

bool'1;         // true
bool'0;         // false
bool'252466642; // true
bool'-35;       // true

bool'0.0;       // false
bool'135.03111; // true
bool'-135.77;   // true

warning

Documentation incomplete, pulled from unfinished ksl/ksl_syntax.md file.

Function Attributes

Entry

No Fail

Libraries

The syntax for this is strange so pay attention. Both libraries and files use the using keyword to be imported, but what follows the using keyword is different.

In order to import another .k or .ksl file, follow up the using keyword with a string that contains the relative path to the file:

using "src/api.k"; // Imports the contents of src/api.k to be used.
using "src/abi.k"; // Imports the contents of src/abi.k to be used.
using "lib/ffi.k"; // Imports the contents of lib/ffi.k to be used.

To use a library (like a standard-lib module) the syntax is a little different, follow up the using keyword with an extended identifier instead:

using std.io;  // Links the standard io library.
using std.fs;  // Links the standard file system library.
using std.env; // Links the standard environment library.

note

The biggest difference is that imports (using "") are .k or .ksl files, most likely part of your own codebase. Links, on the other hand (using <iden>) link object files to your final executable. These object files are expected to be in the same directory as the compiler itself, but project-specific support may be added in the future. Each period represents a directory. So std.io would link the std/io.o object file into the final executable.

warning

Documentation incomplete, pulled from unfinished ksl/ksl_syntax.md file.

Import

Links

Namespaces

FFI Concepts

Currently we're discussing adding a no mangle attribute. This would do exactly what it sounds like, it would maintain it's function name regardless of the name mangling schema.

We're also thinking about adding some kind of syntax that would allow raw/manual forward declarations without KSL doing any kind of type predictions.

extern fn func_in_another_language(int, float, float) -> void;

This is still pretty close to KSL syntax and it's relatively intuitive so it's likely.

This would enable developers to call functions in non-officially supported linked object files while maintaining the type and memory safety of KSL (assuming they don't get type translations wrong.)

Additionally, for declaring multiple external functions at once, there could be an optional block inside.

extern {
	fn func_in_another_lang_zero(int) -> void;
	fn func_in_another_lang_one(float) -> int;
	fn func_in_another_lang_two(int) -> float;
}

Standard Library

Standard Library Modules:

IO

warning

The STD-IO module is not stable and/or partially implemented.

using std.io;

writeln

Parameters:
1 = int

Returns:
int
null (void)

C Impl:

int std__io__writeln____int_int(int a);
void std__io__writeln____int_null(int a);

FS

warning

The STD-FS module is not stable and/or partially implemented.

using std.fs;

read

Parameters:
1 = string | path of file

Returns:
string | contents of file

write

Parameters: 1 = string | path of file 2 = string | new contents

Returns: bool | success status

ENV

warning

The STD-ENV module is not stable and/or partially implemented.

using std.env;

get

Parameters:
1 = string | environment variable name

Returns:
string | contents of environment variable

args

Parameters:

Returns:
string[] | command line parameters on launch

Math

Random

The KSL Standard

A standard for KSL features and language implementation. In the future all new features and language components will need to be submitted for review and added to the standard before being included in the compiler.

The using Keyword

04 / 09 / 25

Objective

The objective of the using keyword is to enable multi-file projects to be compiled without manually defining all included files from the command line, it also serves as a way to disclose which existing object files should be linked after codegen.

Dictionary

using - The using keyword

Examples

The using keyword can change it's meaning depending on the syntax that follows. Following a using keyword with a string will tell the compiler to include another source file at the path provided, relative paths are preferred, example:

/* file: main.k */
using "./api/weather.k";

This syntax will include the source of ./api/weather.k in the compiler. It's worth noting that each source file is compiled individually using a shared symbol table. The resulting object files for each individual source file are then linked together to produce the final executable.

The second way to use the using keyword is with identifiers. This syntax will tell the compiler to link an object file following the path of the identifier. For example:

/* file: main.k */
using std.io;

This example will tell the compiler to link std/io.o with the resulting object files at the end of compilation. By default the compiler will search it's own working directory for object files to link, however if it does not find any then it will switch to the project working directory. Periods in the syntax will denote folders, for example:

/* file: main.k */
using os.api.platform;

This example will attempt to link os/api/platform.o with your KSL code.

Conditionals

Proposal unfinished.

The while Keyword

Proposal unfinished.

The for Keyword

Proposal unfinished.

Templates

04 / 09 / 25

Objective

The objective of templates is to achieve an effect similar to polymorphism. For example, defining baseline logic (the template) without static types, then during compile time a clone of the template with static types is generated for each unique call.

Dictionary

template - A keyword denoting the definition of a template function.
$[a-zA-Z] - Syntax denoting the use of an unknown type, single letters only.

Examples

In the example below you can see a template add function, it takes two parameters of unknown types. The parameter names are x and y with types $A and $B, respectively. Templates must return/derive a single type.

template add($A x, $B y) -> int {
	return int'(x + y);
}

If this template was called here is an example generated function:

fn add(float x, int y) -> int {
	return int'(x + y);
}

add(10.0, 5);

Notice how in this example the template call (add(10.0, 5)) uses a float and an integer? This will dictate how to template generates the function. A template can also be used for multiple function calls with multiple dynamic types, ex:

fn add(float x, int y) -> int {
	return int'(x + y);
}

fn add(float x, float y) -> int {
	return int'(x + y);
}

Templates can also reference the dynamic types multiple times in it's function body as long as the result/derivitive of the function remains constant, ex:

template multiply($A x) -> int {
	$A amount = 2;
	$A result = x * amount;
	return int'result;
}

The fn, ->, & return Keywords

04 / 09 / 25

Objective

The objectives of the fn, ->, and return keywords are to add rich function support to KSL.

Dictionary

fn - Keyword used to denote the start of a function.
-> - Keyword used to denote the return type/derivative of a function.
return - Keyword used to return a value from a function.

Examples

The fn keyword followed by a single identifier and parenthesis defines a basic function. Functions also need a defined return type, which can be set using the derive keyword (->.) Once you're done in your function body you can include a return keyword to return a value. It's expected that the returned value matches the type defined after derive. Technically speaking, the return keyword is optional, KSL will insert one for you automatically if it doesn't see one.

fn main(int a) -> float {
	return 10.0;
}

In the example above we can see a function defined, this function has the name main, it takes one integer parameter called a, and it returns a float. This function has the return statement defined in it's function body.

Functions can have multiple parameters separated by a comma. They cannot have multiple return types though.

fn main(int a, int b) -> int {
	return a + b;
}

Casting

Proposal unfinished.

Compiler Internals

This series of documentation pages is entierly dedicated to explaining how the compiler works internally. If you're not actively working on the KSL compiler or building a KSL module then you probably don't want to worry about anything here.

Compiling the Compiler

On Windows

Requirements:

  • KSL Source Code
  • Rust 1.87^: Download Rust
  • LLVM 18.1.x: Download LLVM
  • The libxml2 requirement for LLVM seems to be missing on Windows, luckily you can download it with vcpkg install libxml2:x64-windows. Once it's downloaded, rename the file from libxml2 to libxml2s and put it in your llvm/lib folder.
    • If you need vcpkg click here.

You'll want to make sure Rust is installed fully. I've found that installing it globally just takes care of a lot of potential issues. Make sure the LLVM/bin folder is added to your path as well. On top of that, you'll want to make a new system variable:

VariableValue
LLVM_SYS_180_PREFIX<your_llvm_root_directory>

Once you have Rust and LLVM set up you shouldn't really need anything else! Just navigate to the ksl source code root directory and run cargo build or cargo build --release.

It sounds pretty simple but some of the config stuff can take a while to figure out, I'd say this process could take anywhere between 30 minutes and few hours, depending on your technical skills. Don't get discouraged if it takes a while.

On Linux

note

Documentation help wanted! The Linux build instructions are not finalized and may depend on distro, any contributions would be great!

Requirements:

  • KSL Source Code
  • Rust 1.87^: Download Rust
  • LLVM 18.1.x: Download LLVM

On MacOS

note

Documentation help wanted! The MacOS build instructions are not finalized. Any contributions toward this section would be super helpful!

  • KSL Source Code
  • Rust 1.87^: Download Rust
  • LLVM 18.1.x: Download LLVM

Compiling KSL-STDLIB

yeah good luck lol

Name Mangling Convention

The KSL name mangling convention is designed to prevent any kind of type confusion. That includes the return type.

This also means that you can provide function overloading in KSL modules and external object files. The main reason for the return type being included is so that there can be functions that serve the same purpose but the KSL return type predictor doesn't get too broken.

So, for example, at the time of writing both of these functions exist in the std.io module:

// std.io.writeln(int _) -> int
int std__io__writeln____int_int(int num) {
	printf("%d", num);
	return 0;
}

// std.io.writeln(int _) -> null
void std__io__writeln____int_null(int num) {
	printf("%d", num);
	return;
}

Functionally they're the exact same, but one returns an integer and one returns void ("null" in KSL at the time of writing.)

note

void is likely going to be added in the near future.

The convention for name mangling in KSL is as follows:

  1. Periods in identifiers are translated to two underscores.
  2. Space between the function name and types are four underscores.
  3. The rest of the name will have all parameter types and the return type all separated by one underscore.

So:

std.io.writeln(int num) -> null
// Becomes
std__io__writeln____i64__null

std.io.writeln(f32 x) -> int
// Becomes
std__io__writeln____f32__i64

You'll also see that the type keyword int turns into i64. This is because, as explained in variables, int and float are just aliases for i64 and f64 respectively. Reasoning for this choice are explained there.

fractstrike

fractstrike is ksl's runtime garbage collector [in development].

Expression Semantics

This page will go over how semantic analysis handles expressions. It will cover the technical details behind return type prediction in inline calls and type promotion.

Currently, KSL will travel the expression in the AST and determine the smallest possible type for all literals and the types defined for each variable. It will then cross reference these anywhere there is an operation between them. It will pick the larger type of the two and "promote" the entire expression to that type. This way we can calculate expressions for differing types without losing data from downcasts.


In the future, we're likely to change this system so that the entire expression recieves a type hint ahead of time. It will attempt to convert all literals (and potentially variables) to this type. If the expression ends up with a larger type than the hint it will simply throw an error.

Additionally, if there is a type that doesn't match the rest that cannot be implicitly changed it will throw a warning or error to the programmer explaining that in order to "do this operation" they must cast to an acceptable type.

Error Codes

A list of error codes in the KSL compiler, where the originate, why they were triggered, etc.


EL0000 = Error [Lexer]: Unexpected End-of-File Immediately After Comment Opening

EL0001 = Error [Lexer]: Unexpected End-of-File While Lexing Comment

EL0002 = Error [Lexer]: Unexpected End-of-File Immediately After Comment Opening

EL0003 = Error [Lexer]: Unexpected End-of-File In Closing of Multi-Line Comment

EL0004 = Error [Lexer]: Unexpected End-of-File While Lexing Comment

EL0005 = Error [Lexer]: Unexpected End-of-File Immediately After Identifier Opening

EL0006 = Error [Lexer]: Unexpected End-of-File While Lexing Identifier

EL0007 = Error [Lexer]: Unexpected End-of-File Immediately After Number Opening

EL0008 = Error [Lexer]: Unexpected End-of-File While Lexing Number

EL0009 = Error [Lexer]: Unexpected End-of-File Immediately After String Opening

EL0010 = Error [Lexer]: Unexpected End-of-File While Lexing String


EP0000 = Error [Parser]: Expected <token> but got <token>

EP0001 = Error [Parser [Iden]]: Expected identifier, found <token>

EP0002 = Error [Parser [Qden]]: Expected identifier, found <token>

EP0003 = Error [Parser]: Unexpected end-of-file while parsing block

EP0004 = Error [Parser]: Function <name> parameter <param name> must be a valid type, currently <invalid type name>

EP0005 = Error [Parser]: Function <name> must derive valid type, currently <invalid type name>

EP0006 = Error [Parser]: Invalid statement used to initialize for loop

EP0007 = Error [Parser]: Invalid statement used in for loop

EP0008 = Error [Parser]: Namespace Declaration without a Name

EP0009 = Error [Parser]: Expected identifier or string after using but got <token>

EP0010 = Error [Parser]: Unexpected Token in Expression, Found <token>

EP0011 = Error [Parser]: Expected Literal in Expression, Found <token>


ES0000 = Error [Semantics]: Expected variable <name> to be <type> but it's <type>, maybe cast?

ES0001 = Error [Semantics]: Reference to undefined variable <name>

ES0002 = Error [Semantics]: Invalid Binary Operation Types, Cannot use <type> With <type>, Consider Casting

ES0003 = Error [Semantics]: Inline Function Call with Type Hint Doesn't Match Function Return Type (Hint: <type>, Returns <type>)

ES0004 = Error [Semantics]: Unexpected Number of Parameters to Function <name>, Expected <value> but got <value>

ES0005 = Error [Semantics]: Type mismatch passed to function <name> in arg slot <value>, expected <type> but got <type>

ES0006 = Error [Semantics]: Different types passed to forward declared function <name> in arg slot <value>, previously used <type> but <type> was passed, verify this is correct

ES0007 = Error [Semantics]: Empty array literal, verify this is correct

ES0008 = Error [Semantics]: Array literal initialized with type <type> was passed a <type>

ES0009 = Error [Semantics]: Missing type hint for array literal declaration

ES0010 = Error [Semantics]: Invalid array index type, must be in the integer family, got <type>

ES0011 = Error [Semantics]: Expected <literal> to be Parsed as an Integer

ES0012 = Error [Semantics]: Expected <literal> to be Parsed as a Float

ES0013 = Error [Semantics]: Cannot use <type> in the context of <type< (attempted literal upcast)

ES0014 = Error [Semantics]: Cannot use <type> in the context of <type> (attempted type conversion)

ES0015 = Error [Semantics]: Cannot implicitly convert <type> to <type> (would lose fractional data)

ES0016 = Error [Semantics]: Cannot implicity convert <type> to <type>

ES0017 = Error [Semantics]: Type mismatch in variable declaration <name>, expected <type> but got <type>

ES0018 = Error [Semantics]: Type mismatch in variable reassignment <name>, expected <type> but got <type>

ES0019 = Error [Semantics]: Attempted to reassign variable that does not exist <name>

ES0020 = Error [Semantics]: Type mismatch in function return, expected <type> but got <type>

ES0021 = Error [Semantics]: Type mismatch in function return, expected <type> but got none

ES0022 = Error [Semantics]: Condition in if statement must be of type Boolean, got <type>

ES0023 = Error [Semantics]: Condition in while statement must be of type Boolean, got <type>

ES0024 = Error [Semantics]: Condition in for statement must be of type Boolean, got <type>


ET0000 = Error [Symbols]: Scope <scope_name> not found in path

ET0001 = Error [Symbols]: Symbol <name> already exists in current scope

ET0002 = Error [Symbols]: Symbol <name> exists and is not a scope

ET0003 = Error [Symbols]: Symbol <name> already exists in specified scope

ET0004 = Error [Symbols]: Invalid scope in path <scope_name>

ET0005 = Error [Symbols]: Cannot enter scope <scope_name> because it's occupied by a non-scope-containing symbol


EN0000 = Error [Linker]: KSL requires <os_linker> for linking, install it or add it to path

EN0001 = Error [Linker]: Failed to run linker: <linker_error>

EN0002 = Error [Linker]: Linker failed with status code <linker_status><linker_error>