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 Type
C Equivalent
u8
uint8_t
i8
int8_t
u16
uint16_t
i16
int16_t
u32
uint32_t
i32
int32_t
u64
uint64_t
i64
int64_t
f32
float
f64
double
isize
ssize_t
usize
size_t
Quark Type
C Equivalent
char
char
ichar
signed char
uchar
unsigned char
Short
short
UShort
unsigned short
Int
int
UInt
unsigned int
Long
long
ULong
unsigned long
bool
bool
File
FILE
void
void
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.
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.