Quick Start

All source code needed to build your first project can be found on the GitHub repository: quark-programming/quark

You can download the source code using the latest release or by cloning the repo

$ git clone https://github.com/quark-programming/quark.git

After downloading the source code, navigate to its folder and build the project using make. You can specify the output binary name using the out variable. By default, it will create a binary named qc.

$ make build
$ make build out=my_compiler

All source code is licensed under the MIT License. See the LICENSE file for more information.

When writing Quark code, source files should use the .qk file extension. You can compile your Quark source files using the compiler like so

$ ./qc path/to/source_file.qk -o output_code.c
$ cc output_code.c -o final_executable

Hello World

Let's write out first program in Quark. Start by creating a new filed with the .qk extension. Here I'll use hello.qk.

import lib::io;

print("Hello, World!");

We start by importing the standard I/O library so we can use the print function. You can see all of the source library files in the libs/ folder. As of 0.1.0, the lib::io library also imports the lib::str library, so we can use string literals and the str type.

Primitive Types

Quark has several built-in primitive types for representing data. Here are the default types and their C equivalents as of 0.1.0:

Quark TypeC Equivalent
u8uint8_t
i8int8_t
u16uint16_t
i16int16_t
u32uint32_t
i32int32_t
u64uint64_t
i64int64_t
f32float
f64double
isizessize_t
usizesize_t
Quark TypeC Equivalent
charchar
icharsigned char
ucharunsigned char
Shortshort
UShortunsigned short
Intint
UIntunsigned int
Longlong
ULongunsigned long
boolbool
FileFILE
voidvoid

You can reuse any other C type by using the extern keyword. For example, to use the C long long type, you can declare it like so:

type LongLong = extern "long long";

Quark also comes with helper types like auto and int. These types conform to whatever type they are matched with. For example, this is how you can use the auto type:

i32 number = 10;
auto another_number = number;

Here, the another_number variable will be of type i32 since it is being assigned the value of the number variable. int will do the same thing, but will only match number types.

Pointer Types

In Quark, pointer types are declared using the * symbol after a type or the & before a type. For example, to declare a pointer to an integer, you would write:

i32* ptr_to_int;

or

&i32 ptr_to_int;

Pointers can be referenced and dereferenced using the & and * operators, similar to C.

i32 number = 42;
i32* ptr_to_number = &number;
i32 dereferenced_number = *ptr_to_number;

Structures

Structures in Quark are similar to structs in C, but with added support for generics. You can define a structure using the struct keyword followed by the structure name and its fields.

struct Person {
   str name;
   u8 age;
;

You can create new instances of a structure using the following syntax:

auto john = Person {
   name: "John",
   age: 30,
};

Accessing structure fields is done in the same way as in C, and structures can be dereferenced while accessing fields if you have a pointer to a structure.

str johns_name = john.name;
Person* ptr_to_john = &john;
u8 johns_age = ptr_to_john->age;

Generics

Quark supports generics, allowing you to create data structures and functions that can operate on different types. You can define a generic type by using angle brackets < and > to specify type parameters.

struct Array<T> {
   T* data;
   usize size;
}

T echo<T>(T value) {
   return value;
}

The Quark compiler will generate an implementation of the generic type or function for each unique type it is used with. For example, if you create an Array<int> and an Array<char>, the compiler will generate two separate structures:

struct Array__number { int* data; size_t size; };
struct Array__char { char* data; size_t size; };

Control Statements

Quark provides standard control flow statements such as if and while similar to C. Here are some examples of how to use these control statements in Quark:

if( <condition> ) {
   // code to execute if condition is true
}

while( <condition> ) {
   // code to execute while condition is true
}

Functions

Functions in Quark are defined in a similar way to C, starting with the return type, followed by the function name and parameters. Here is an example of a simple function that returns the sum of two integers:

i32 add(i32 a, i32 b) {
   return a + b;
}

Functions can also use generics to operate on different types. Here is an example of a generic function that returns the value it receives:

T echo<T>(T value) {
   return value;
}

Notice that the return type is used before the declaration of the generic type parameter(s).

Importing Other .qk Files

Quark allows you to import other .qk source files using the import keyword. This is useful for organizing your code into separate modules and reusing code across different files.

Let's say we have a print_message() function defined in a file named other/utils.qk and we want to use it in our main file main.qk. Here's how you can do that:

import other::utils;

print_message("Hello World!");

In this example, we import the utils.qk file located in the other/ directory. After importing, we can directly use the print_message() function defined in that file.

other::utils is transformed into the relative path other/utils.qk when searching for the file to import.