{"id":22815907,"url":"https://github.com/amari-calipso/skye-lang","last_synced_at":"2025-05-05T16:43:33.150Z","repository":{"id":254759120,"uuid":"847459251","full_name":"amari-calipso/skye-lang","owner":"amari-calipso","description":"Skye ~ The retrofuturistic programming language","archived":false,"fork":false,"pushed_at":"2024-12-08T20:02:33.000Z","size":536,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-08T21:23:27.484Z","etag":null,"topics":["c","c-programming-language","compiler","language-design","language-engineering","low-level","programming-language","rust","transpiler"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/amari-calipso.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-25T21:55:58.000Z","updated_at":"2024-12-08T20:02:36.000Z","dependencies_parsed_at":"2025-01-19T03:19:43.548Z","dependency_job_id":"a8741de8-abb6-449c-8db6-82d092236e07","html_url":"https://github.com/amari-calipso/skye-lang","commit_stats":null,"previous_names":["thatsoven/skye-lang","amari-calipso/skye-lang"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amari-calipso%2Fskye-lang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amari-calipso%2Fskye-lang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amari-calipso%2Fskye-lang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amari-calipso%2Fskye-lang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amari-calipso","download_url":"https://codeload.github.com/amari-calipso/skye-lang/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252535251,"owners_count":21763921,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","c-programming-language","compiler","language-design","language-engineering","low-level","programming-language","rust","transpiler"],"created_at":"2024-12-12T14:05:40.618Z","updated_at":"2025-05-05T16:43:33.130Z","avatar_url":"https://github.com/amari-calipso.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"![](logo.png)\n\n# Skye\nSkye's programming language (Skye, for short) is the retrofuturistic systems programming language.\n\nNote: the language is currently in a very early stage! the standard library is very limited in functionality, and the language is widely untested.\n\n# Who is Skye?\nSkye loves programming, and they enjoy writing their programs from scratch, just like you would do using C. However, they also think that while the C programming language is great, it's missing some tools and constructs to make their life easier. They do like manual memory allocation, but sometimes it's too much to handle. They like having control over all the code they write, but they would also like to have some more abstraction, as long as it doesn't hurt the runtime performance! If this sounds like you, then you have your answer: you are Skye.\n\n# Tell me more!\nSkye tries to give you a similar experience to writing code in C, but with some handy tools like type inference, generics, sum types, a more modern syntax, and a type system that's way more robust than C's, as well as a more coherent ecosystem. In some way, Skye is covering the use case for C++, but it isn't as annoying to use. At the same time, Skye is also a fairly simple language in its structure, that means that every component of it is hackable and accessible: Skye loves open source!\n\n# Installation\nTo install Skye, you can either jump to the releases and download the latest version for your platform, or download the source and compile it using `cargo build --release`.\n\nWhen using the Skye compiler, the `SKYE_PATH` environment variable should be set. It has to be set to the path of the compiler executable and the `lib` folder. If not set, Skye will try to infer it from the compiler executable location.\n\nNOTE: Windows is not supported yet, sorry! I will work on it\u003c3\n\n# Hello, World!\n```\nfn main() {\n    @println(\"Hello, World!\");\n}\n```\n\n# Projects\nCreating a new project in Skye is simple!\n\nIf your project is a simple one that doesn't need any specific compiler flag, you can just create a new file containing your Skye code, and then compile it or run it directly by using `skye compile \u003cfile\u003e` or `skye run \u003cfile\u003e` respectively.\n\nIf you're working with a bigger project (this is the most common case, since you'll be working with C compilers) you can create a Skye project by using the `skye new` command. At this stage, you should choose if you want to create a standalone program (`skye new standalone \u003cproject_name\u003e`), or a Skye package (`skye new package \u003cproject_name\u003e`).\n\nStandalone projects can be built by using the `skye build` command, and Skye packages can be exported using `skye export`. The result of `skye export` is a `zip` file that can be installed using `skye install \u003cpackage_file\u003e`. To remove an installed package, use `skye remove \u003cpackage_name\u003e`.\n\nThe Skye package manager has no notion of versions, so feature-wise versioning should be performed by the developer through different package names (for example \"myPackage-v1_0\", \"myPackage-v1_1\"...). This way, projects that require a specific version of a package as a dependency don't collide with a different version of the same package while the required one is being installed.\n\n# Comments\n```\n// This is a comment\n\n/*\n    This is a multiline comment\n    It can't be nested\n*/\n```\n\n# Variables\n```\nlet a = 0; // Skye will infer the type for this variable\nlet b: u64 = 0; // You can manually specify types\nconst c = 3; // This variable is immutable, it cannot be modified\nlet d: f32; // Variables can be left undefined, but the type needs to be specified\n```\n\n# Primitive types\n```\nIntegers:\ni8 i16 i32 i64\nu8 u16 u32 u64\nusz (equivalent to size_t)\n\nFloats:\nf32 f64\n\nOther:\nchar\nvoidptr (void*, mostly for C interop)\n```\nNo implicit casting is performed, every cast must be performed explictly using the `@cast` macro.\n```\nlet a: i32 = 0;\nlet b = @cast(u64, a);\n```\n\nThe default integer type is `i32`, but it's possible to specify the integer type on the literal level, by putting the type after the number. For example:\n```\nlet a = 10u64;\nlet b = 255u8;\nlet c = 1f32;\nlet d = 2.19083f64;\n```\nIt's also possible to create integers in binary, octal, and hexadecimal bases.\n```\nlet bin = 0b111010;\nlet oct = 0o17356;\nlet hex = 0x37f8A;\n```\n# Arrays\nThere are two main types of arrays in Skye: the slice, and the array.\n\nA slice is a read-only view inside another collection. You can create a slice using this syntax:\n```\nlet mySlice: Slice[i32] = {1, 2, 3};\n```\n\nAn array is a dynamically sized list allocated on the heap. To create one, you can use this syntax:\n```\nlet myArray: Array[f32] = [1.0, 2.0, 3.0];\n```\n\nCreating empty slices and arrays with this syntax is not permitted. To create an empty array:\n```\nlet myEmptyArray = Array::new[f32]();\n```\n\n# Strings\nThere are two main types of strings in Skye: raw strings, and strings.\n\nA String is defined by using quotes (\") around your text:\n```\nlet myString: String = \"This is a string\";\nlet stringLength = myString.length; // 16\n```\nThe String type in Skye is not null terminated and stores its length separately. Effectively, a Skye string is just a `Slice` of `char`s.\n\nA raw string is mostly used for C interop. It's like a C string, but not null terminated.\n```\nlet myRawString: *const char = `This is a raw string\\0`;\nlet rawStringLength = core::utils::cStringLength(myRawString); // 22\n```\n\n# Conditionals\nConditionals in Skye accept any numeric type as their condition, just like in C.\n## If statements\n```\nif 2 + 2 == 4 {\n\tconst a = true;\n\tif (a) @println(\"True!\");\n}\n```\n## While loops\n```\nlet a = 2;\nwhile a-- {\n    @println(\"Looping\");\n}\n\na = 3;\ndo {\n    @println(\"Looping yet again\");\n} while a--;\n```\n## For loops\nThere are two types of for loops in Skye.\n### C-like for\n```\nfor let i = 0; i \u003c 10; i++ {\n    @println(\"This will be printed 10 times\");\n}\n```\n### Foreach\n```\nconst mySlice = {1, 2, 3};\nfor element; mySlice {\n    @println(\"This will go through all the elements of the slice\");\n}\n```\nForeach loops can iterate any type that either contains a `next` method returning an `Option`, or an `iter` method that returns a valid type containing a `next` method;\n\nAll loops can use `continue` and `break` statements.\n## Switch statements\n```\nlet a: u8 = 2;\nswitch a {\n    3 | 4 | 5 {\n        @println(\"Nope!\");\n    }\n    // you can use an arrow instead of a block if you want to use a single statement for a case\n    0 -\u003e @println(\"Still nope\");\n    2 -\u003e @println(\"Here!\");\n    default {\n        @println(\"Something else\");\n    }\n}\n```\n\nUsing types as conditions for a switch statement allows you to compare a type against other types at compile time. An example of this is in the section about [generics](#generics);\n# Functions\nTo create a function, you can use the `fn` keyword, like so:\n```\nfn add(a: i32, b: i32) i32 {\n    return a + b;\n}\n\nfn sayHello() {\n    @println(\"Hello!\");\n}\n```\n\nFunctions can be declared, in case you need to reference one before it's actually defined\n```\nfn b(x: i32);\n\nfn a(x: i32) {\n    b(x - 1);\n}\n\nfn b(x: i32) {\n    if x \u003c 2 {\n        a(x);\n    }\n}\n```\nYou can create function bindings for existing C functions by using the `#bind` qualifier\n```\n#bind fn malloc(size: usz) voidptr;\n```\nOverloading is not allowed, however it's possible to bind different behaviors to the same function called with different types through [generics](#generics).\n\nVariable parameter length is not allowed, however it's possible to create [macros](#macros) to call functions with a variable amount of arguments:\n```\nfn printAllFunction(strings: Slice[String]) {\n    for string; strings {\n        @println(\"{string}\");\n    }\n}\n\nmacro printAll(strings*) printAllFunction(strings);\n```\n\nIt's possible to create function pointers either by referencing an existing function or using the function pointer type.\n```\nlet aFunctionPointer: fn (i32) void = a;\naFunctionPointer(3);\n```\n# Pointers\nThere are two types of pointers in Skye: the raw pointer, and the reference.\n\nPointers are their own type. They point to a location in memory, support pointer arithmetics, and behave as an indipendent type.\nOn the other hand, references internally work like pointers, but they just operate as the underlying data type. For example, if you have two references to `i32`s, you can add them directly without dereferencing them, because the compiler does it automatically.\n\nPointers and references also have their own `const`ness associated to them. If a pointer or a reference are `const`, the value they point to cannot be mutated.\n\nPointer and reference types are created with the prefix `*` and `\u0026` operators respectively, and a `const` keyword can be added to create a `const` pointer or reference.\n\n```\nlet a = 2;\n// the address pointed by these pointers can be mutated\nlet aPtr: *i32 = \u0026a; // a can be mutated through this pointer\nlet aConstPtr: *const i32 = \u0026a; // a cannot be mutated through this pointer\nlet anotherConstPtr: *const i32 = \u0026const a; // you can also use the `\u0026const` operator to create a const reference, which can be casted to a pointer\n\n// the address pointed by these pointers cannot be mutated\nconst constAPtr: *i32 = \u0026a; // a can be mutated through this pointer\nconst constAConstPtr: *const i32 = \u0026a; // a cannot be mutated through this pointer\n\nlet b = 3;\nconst refA: \u0026const i32 = \u0026a; // a cannot be mutated through this reference\nconst refB: \u0026i32 = \u0026b; // b can be mutated through this reference\n\nlet result = refA + refB; // equivalent to a + b (= 5)\n```\n\nIf a function parameter is defined as a reference, the compiler will automatically create a reference for you if the function gets passed the value directly.\n\n```\nfn add(const a: \u0026const i32, const b: \u0026const i32) i32 {\n    return a + b;\n}\n\nfn main() {\n    const a = 2;\n    const b = 3;\n\n    // these are both valid\n    const result = add(a, b); // here, the compiler will automatically pass the values by reference\n    const resultAgain = add(\u0026a, \u0026b);\n}\n```\n\n# Qualifiers\nIt's possible to use C qualifiers on function and variable declarations using the `#` operator\n```\n#inline\nfn add(a: i32, b: i32) i32 {\n    return a + b;\n}\n\n#volatile let a = 3;\n```\n# Defer\nThe `defer` statement is used to execute a statement while exiting the current scope.\n```\nfn test() f32 {\n    let anArray = Array::new[f32]();\n    defer anArray.free();\n\n    anArray.push(1.0);\n    anArray.push(2.0);\n\n    return anArray[0];\n    // anArray.free() will be called here\n}\n```\n# Structs\n```\nstruct MyStruct {\n    myField: i32,\n    const anotherField: u64\n}\n```\n\n# Bitfields\n```\nbitfield MyBitfield {\n    a: 10, // bitfields fields can be 0-64 bits\n    b: 48,\n    d: 0,\n    c: 1,\n}\n```\n# Unions\nUnions are mostly meant for C interop.\n```\nunion MyUnion {\n    a: i32,\n    b: f32\n}\n```\n# Enums\n```\nenum ClassicEnum {\n    Variant1,\n    Variant2\n}\n\n// by default, enum variants are typed `i32`,\n// but you can specify a custom time using the `as` keyword\nenum U64Enum as u64 {\n    Variant1,\n    Variant2\n}\n\nenum SumTypeEnum {\n    Variant1(i32),\n    Variant2(f64)\n}\n```\nAny sum type includes a `kind` field that indicates the active variant.\n```\nstruct Dog {}\nstruct Cat {}\n\nenum Animal {\n    DogVariant(Dog),\n    CatVariant(Cat),\n    AnotherAnimal\n}\n\nfn test() {\n    let var = Animal::DogVariant(Dog.{});\n    let kind = var.kind; // Animal::Kind::DogVariant;\n    let dog = var.DogVariant;\n\n    var = Animal::AnotherAnimal;\n    kind = var.kind; // Animal::Kind::AnotherAnimal;\n}\n```\n\nIt's possible to bind all user defined types to C defined types with the following syntax:\n```\nstruct MyStructBinding: CStructName {\n    x: f32,\n    y: f32\n}\n\nenum MyEnumBinding: CEnumName {\n    FIRST_FIELD,\n    SECOND_FIELD\n}\n\nbitfield MyBitfieldBinding: CBitfieldName {\n    a: 23,\n    b: 1\n}\n\nunion MyUnionBinding: CUnionName {\n    a: i32,\n    b: f32\n}\n```\nStructs, bitfields, and unions can be initialized through a compound literal:\n```\nlet myStructInstance = MyStructBinding.{ x: 1.0, y: 2.0 };\nlet a = 2;\n// field name can be omitted when it collides with the expression name\nlet myBitfieldInstance = MyBitfieldBinding.{ a, b: 1 };\nlet myUnionInstance = MyUnionBinding.{ a }; // only one field of a union can be initialized\n```\n# Impl\nStructs and sum type enums can have methods, and they can be implemented using the `impl` keyword.\n```\nstruct MyStruct {\n    myField: i32,\n    const anotherField: u64\n}\n\nimpl MyStruct {\n    fn new(myField: i32, anotherField: u64) Self {\n        return MyStruct.{ myField, anotherField };\n    }\n\n    // self doesn't need type specifiers!\n    fn add(const self) i32 {\n        return self.myField + @cast(i32, self.anotherField);\n    }\n\n    fn setMyField(self, field: i32) {\n        self.myField = field;\n    }\n\n    fn staticMethod() {\n        @println(\"This method does not depend on the instance\");\n    }\n}\n```\nMethods can be called either through the type with a `::` operator, or through its instances, through the `.` operator.\n```\nMyStruct::staticMethod();\nlet instance = MyStruct::new(10, 10);\nlet result = instance.add();\ninstance::setMyField(\u0026instance, result);\n```\n# Namespaces\nNamespaces can be created to avoid name conflicts and organize code. They can be accessed through the `::` operator and defined like this:\n```\nnamespace myNamespace {\n    fn test() {\n        @println(\"test!\");\n    }\n}\n\n// myNamespace::test();\n```\n# Use\nThe `use` statement is used to create aliases for types and identifiers.\n```\nuse f32 | f64 as Floats;\nuse myNamespace::test; // in case of namespaces accesses, `as` can be omitted and the alias will be bound to the outermost name, in this case, \"test\"\n\nuse myNamespace::test as myTestAlias;\n\nmacro defineAdd(constant) {\n    fn addValue[T: AnyFloat](x: T) T {\n        return a + constant;\n    }\n}\n\n// using \"_\" as an identifier forces the compiler to evaluate the expression without creating an alias.\nuse @defineAdd(1) as _; // this is especially useful for metaprogramming with macros\nuse addValue[f32] as _; // or, for instance, this syntax will create the necessary code for add[f32], adding to the resulting C source\n```\n# Import\nThe import statement can import both Skye packages and C libraries.\n```\nimport \"os\"; // using the name with no extension will assume this is an installed package\nimport \"otherFile.skye\"; // using the full file name will search in the project folder\nimport \"anotherFile.h\";\nimport \u003c\"math.h\"\u003e; // using angular brackets is equivalent to doing the same in C through an #include\nimport \u003c\u003c\"core/internals.h\"\u003e\u003e; // using double angular brackets forces the import to address to the installed packages\n```\n# Generics\nStructs, sum type enums, and functions can use generics to accept multiple types\n```\nstruct MyStruct[T] {\n    a: T,\n    b: T\n}\n\nimpl[T] MyStruct[T] {\n    fn new(a: T, b: T) Self[T] {\n        return Self.{ a, b };\n    }\n}\n\n// generics can have type bounds\nfn add[T: AnyInt | AnyFloat](a: T, b: T) T {\n    return a + b;\n}\n\n// it's possible to specify a default type for generics\nenum Result[T, U = i32] {\n    Ok(T),\n    Error(U)\n}\n\nlet myStruct = MyStruct::new(1i32, 2i32); // Skye can infer generic types...\nlet result = add[i32](2, 2); // ...but you can also specify types manually\n```\n\nYou can use generics to give the function different behaviors depending on types.\n\n```\nfn which32[T: u32 | i32 | f32](x: T) {\n    switch T {\n        u32 -\u003e @println(\"got a u32\");\n        i32 -\u003e @println(\"got a i32\");\n        f32 -\u003e @println(\"got a f32\");\n        default -\u003e @unreachable;\n    }\n}\n```\n\n# Results and Options\nSkye avoids the usage of `null` types and propagates errors by value.\n```\nfn someIfPositive(x: i32) ?i32 { // ?i32 corresponds to core::Option[i32]\n    if x \u003c 0 {\n        return (?i32)::None;\n    }\n\n    return (?i32)::Some(x);\n}\n\nfn errorIfNegative(x: i32) u32!i32 { // u32!i32 corresponds to core::Result[u32, i32]\n    if x \u003c 0 {\n        return (u32!i32)::Error(x);\n    }\n\n    return (u32!i32)::Ok(x);\n}\n\nfn main() !i32 { // omitting the left value makes the compiler assume it's `void`\n   let result = try errorIfNegative(-2); // the try operator propagates the error if there is one\n   // when using the try operator, error types need to match\n\n   return (!i32)::Ok;\n}\n```\n# Macros\nIt's possible to create macros in Skye, and unlike in C, they are based on the AST instead of using a preprocessor. It's also possible to bind to C macros.\n```\nmacro constantNumber 32;\nmacro count(n) {\n    for let i = @cast(@typeOf(n), 0); i \u003c n; i++ {\n        @println(\"{i}\");\n    }\n}\n\nmacro addTwo(x) x + 2;\n\n// C macro bindings\nmacro __WORDSIZE -\u003e u8;\nmacro A_C_MACRO(x, y) -\u003e i32;\n```\nTo reference macros, the `@` operator must be used.\n```\nlet number = @costantNumber;\n@saySomething(\"hello!\");\nlet result = @A_C_MACRO(1, 1);\n```\n\nTo reference macros inside namespaces, this syntax is used:\n```\nnamespace myNamespace {\n    macro constantNumber 32;\n}\n\n// myNamespace::@constantNumber\n```\n\nYou can create macros with variable parameter length using the following syntax:\n```\nmacro variableArgumentsMacro(args*) {\n    // `args` will be bound to a `Slice` of whatever arguments it got passed\n}\n```\n# Interfaces\nIt is possible to create interfaces with types known at compile time. Interfaces allow to group shared behavior to a shared data type. Internally, this is just syntax sugar around sum types, implementing enum dispatch.\n```\nstruct Dog {}\nimpl Dog {\n    fn speak(const self) {\n        @println(\"Woof!\");\n    }\n}\n\nstruct Cat {}\nimpl Cat {\n    fn speak(const self) {\n        @println(\"Meow!\");\n    }\n}\n\ninterface Animal {\n    fn speak(const self);\n} for Dog, Cat;\n\nfn main() {\n    let animal = @cast(Animal, Dog.{}); // you can convert an instance of a type to a compatible interface using a cast\n    const dog = @cast(Dog, animal).unwrap(); // casting the interface back to its type can fail, so it may return none\n\n    animal.speak(); // Woof!\n\n    animal = @cast(Animal, Cat.{});\n    animal.speak(); // Meow!\n}\n```\nYou can also provide a default implementation:\n```\n...\n\nstruct AnotherAnimal {}\n\ninterface Animal {\n    fn speak(const self) {\n        @println(\"\u003cinsert animal noise here\u003e\");\n    }\n} for Dog, Cat, AnotherAnimal;\n\nfn main() {\n    const animal = @cast(Animal, AnotherAnimal.{});\n    animal.speak(); // \u003cinsert animal noise here\u003e\n}\n```\nInterfaces can be forward declared when needed, by just omitting default implementations and the `for types...` part.\n\nThis approach to type dispatching has been experimented with in Rust, and has shown up to a 10x speed increase over Rust's native dynamic dispatching, as well as much better possibility for compiler optimizations ([reference](https://gitlab.com/antonok/enum_dispatch)).\n\n# Main operators\n| name | syntax | additional notes |\n| ---- | ------ | ----------- |\n| Prefix increment | `++x` | Increments `x` before it's used [*1](#additional-information) |\n| Suffix increment | `x++` | Increments `x` after it's used [*1](#additional-information) |\n| Prefix decrement | `--x` | Decrements `x` before it's used [*1](#additional-information) |\n| Suffix decrement | `x--` | Decrements `x` after it's used [*1](#additional-information) |\n| Unary plus | `+x` | Same behavior as C |\n| Negation | `-x` | ... |\n| Boolean not | `!x` | Can also define a `Result` type with `Ok = void` |\n| Bitwise not | `~x` | ... |\n| Reference | `\u0026x` | Returns a reference to `x`. Can also define a reference type if applied to a type |\n| Const reference | `\u0026const x` | Returns a const reference to `x` (`x` cannot be modified through that reference). Can also define a const reference type if applied to a type |\n| Dereference | `*x` | Dereferences a pointer. Can also define a pointer type if applied to a type [*4](#additional-information) |\n| Const dereference | `*const x` | Dereferences a pointer and returns a `const` value. Can also define a const pointer type if applied to a type [*4](#additional-information) |\n| Option | `?x` | Defines an `Option[x]` type where `x` is a type |\n| Try | `try x` | Returns the `Ok` or `Some` value of `x` where `x` is a `Result` or `Option`. Propagates the `Error` or `None` if the set variant is not `Ok` or `Some` |\n| Access | `x.y` | Accesses the `y` property of `x`, whether it's a method or a field, where `x` is an instance of a struct, sum type enum, union or bitfield. Automatically dereferences pointers if necessary [*4](#additional-information) |\n| Static access | `x::y` | Accesses the `y` property of `x` statically, where `x` is a namespace, a struct type, an enum type, or an instance of the above. This operator will automatically follow pointers at compile time if necessary |\n| Addition | `x + y` `x += y` | ... |\n| Subtraction | `x - y` `x -= y` | ... |\n| Multiplication | `x * y` `x *= y` | ... |\n| Division | `x / y` `x /= y` | [*3](#additional-information) |\n| Modulo | `x % y` `x %= y` | [*3](#additional-information) |\n| Shift left | `x \u003c\u003c y` `x \u003c\u003c= y` | Shifts `x` left `y` times |\n| Shift right | `x \u003e\u003e y` `x \u003e\u003e= y` | Shifts `x` right `y` times |\n| Boolean or | \u003ccode\u003ex \u0026#124;\u0026#124; y\u003c/code\u003e | ... |\n| Boolean and | `x \u0026\u0026 y` | ... |\n| Bitwise xor | `x ^ y` `x ^= y` | ... |\n| Bitwise and | `x \u0026 y` `x \u0026= y` | ... |\n| Bitwise or | \u003ccode\u003ex \u0026#124; y\u003c/code\u003e \u003ccode\u003ex \u0026#124;= y\u003c/code\u003e | Can define a type group if the operands are types |\n| Greater | `x \u003e y` | ... |\n| Greater or equal | `x \u003e= y` | ... |\n| Less | `x \u003c y` | ... |\n| Less or equal | `x \u003c= y` | ... |\n| Equality | `x == y` | ... |\n| Inequality | `x != y` | ... |\n| Result | `x ! y` | Defines a `Result[x, y]` type where `x` and `y` are types |\n| Ternary | `x ? y : z` | Returns `y` if `x` is truthy, otherwise returns `z` |\n\n## Operator overloading\nIt's possible to perform operator overloading by creating some special functions in your types.\n```\nstruct Vector {\n    x: f32,\n    y: f32\n}\n\nimpl Vector {\n    fn __add__(const self, const other: \u0026const Self) Self {\n        return Vector.{ x: self.x + other.x, y: self.y + other.y };\n    }\n}\n```\n\nHere is a list of operators that can be overloaded\n\n| operator | method | n. of arguments (except self) | return type |\n| -------- | ------ | ----------------------------- | ----------- |\n| `++{}` or `{}++` | `__inc__` | 0 | void |\n| `--{}` or `{}--` | `__dec__` | 0 | void |\n| `+{}` | `__pos__` | 0 | any |\n| `-{}` | `__neg__` | 0 | any |\n| `!{}` | `__not__` | 0 | any |\n| `~{}` | `__inv__` | 0 | any |\n| `*{}` | `__deref__` [*2](#additional-information) | 0 | any |\n| `*const {}` | `__constderef__` | 0 | any |\n| `{} + {}` | `__add__` | 1 | any |\n| `{} - {}` | `__sub__` | 1 | any |\n| `{} / {}` | `__div__` | 1 | any |\n| `{} * {}` | `__mul__` | 1 | any |\n| `{} % {}` | `__mod__` | 1 | any |\n| `{} \u003c\u003c {}` | `__shl__` | 1 | any |\n| `{} \u003e\u003e {}` | `__shr__` | 1 | any |\n| \u003ccode\u003e{} \u0026#124;\u0026#124; {}\u003c/code\u003e | `__or__` | 1 | any |\n| `{} \u0026\u0026 {}` | `__and__` | 1 | any |\n| `{} ^ {}` | `__xor__` | 1 | any |\n| \u003ccode\u003e{} \u0026#124; {}\u003c/code\u003e | `__bitor__` | 1 | any |\n| `{} \u0026 {}` | `__bitand__` | 1 | any |\n| `{} \u003e {}` | `__gt__` | 1 | any |\n| `{} \u003e= {}` | `__ge__` | 1 | any |\n| `{} \u003c {}` | `__lt__` | 1 | any |\n| `{} \u003c= {}` | `__le__` | 1 | any |\n| `{} == {}` | `__eq__` | 1 | any |\n| `{} != {}` | `__ne__` | 1 | any |\n| `{} += {}` | `__setadd__` | 1 | any |\n| `{} -= {}` | `__setsub__` | 1 | any |\n| `{} /= {}` | `__setdiv__` | 1 | any |\n| `{} *= {}` | `__setmul__` | 1 | any |\n| `{} %= {}` | `__setmod__` | 1 | any |\n| `{} \u003c\u003c= {}` | `__setshl__` | 1 | any |\n| `{} \u003e\u003e= {}` | `__setshr__` | 1 | any |\n| `{} ^= {}` | `__setxor__` | 1 | any |\n| \u003ccode\u003e{} \u0026#124;= {}\u003c/code\u003e | `__setor__` | 1 | any |\n| `{} \u0026= {}` | `__setand__` | 1 | any |\n| `{}[{}]` | `__subscript__` or `__constsubscript__` [*5](#additional-information) | any | pointer to any |\n\nAdditionally, Skye offers you copy constructors and destructors, mostly used for special types like smart pointers. They are respectively the `__copy__` method and the `__destruct__` method. The Skye compiler will warn you when it inserts calls to those methods inside the code, so that eventual debugging is easier.\n\n### Additional information\n1) Prefix and suffix increments and decrements are handled by the Skye compiler, and thus prevent undefined behavior for cases where multiple increments are used in the same expression or statement. Every expression is evaluated from left to right, and the outcome is always predictable.\n2) The `__deref__` method is used to bind the unary `*` operator to a different behavior. This means, for example, that dereferencing the type and assigning to the dereferenced output will not be possible with the standard syntax. To achieve that kind of behavior, a `__asptr__` method, taking no arguments and returning a pointer to any type, has to be implemented.\n3) In debug mode (the default compilation mode), division and modulo operators do not cause undefined behavior, but rather they panic the program if division by zero is performed. This check is disabled in release mode for performance reasons.\n4) Unlike in C, in debug mode, dereferencing a `null` pointer, either explicitly or implictly, will result in a panic rather than undefined behavior. This check is disabled in release mode for performance reasons.\n5) `__constsubscript__` is used when subscripting from a const source, which in some cases throws an error if using `__subscript__`.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famari-calipso%2Fskye-lang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famari-calipso%2Fskye-lang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famari-calipso%2Fskye-lang/lists"}