N26 is a redesign of N19, and the seventh attempt to design and implement the Nectar programming language. As a "better C", Nectar is designed to do low-level ergonomically, something that most self-proclaimed better Cs fail at. Nectar does not excuse ignorance, so unless the programmer keeps to very high-level concepts only, he/she must still understand the intricacies of the target architecture. A Nectar program is composed of separate modules, each in its own namespace. A module may define global functions or variables. By default, no definitions are exposed to the outside. To do so a definition must be "exported", which locks it to a given ABI. What is not exported however, has no guarantee of making it to the finished binary or looking the way it is written. Non-exported items should be seen as being in a wibbly-wobbly, high-energy state (later on these wobbles will be explained in detail). The only standardized library Nectar has is fully compile-time, in the module `N`. Nectar does not expect any runtime environment such as POSIX or even libc, however compatibility should be trivial: to write a C-compatible main function, simply do: export main: (i32 argc, u8[?]*[?]* argv) -> () @N.sysc { return; } The main function must be exported to be visible to the C runtime. We specify the N.sysc to use whatever the target operating system specifies. In the Unix world, this will be the System V ABI. In Windows it is System V for IA-32, and x64 for AMD64. When we get to low-level features such as the ABI, you will see a lot of @ symbols, which were chosen to prevent most words from being keywords. In general, a Nectar function may have any number of inputs and outputs and need not be assigned an ABI, for example: div: (i32 a, i32 b) -> (i32 q, u32 r) { q = a / b; r = a % b; return; } An equivalent function body would be `return a / b, a % b;`. # Type system N26 supports the following types: 1. Scalar types (e.g. `u8`, `i16`) 2. Array types, which may either have a fixed length or `?` 3. Structure types, which are effectively tuples and may be optionally locked to a specific memory layout 4. Function types, which accept structures and emit structures (e.g. `u8 -> u32`) 5. Reference types (e.g. `u8[?]*` which may be a C string) String literals have no concrete type and must be immediately casted to either a constant-size array or an integer. Likewise, integer literals have no concrete type and must be immediately casted to a scalar type. The undefined value literal `?` must be immediately casted into any type. # Wobbles ## Guaranteeed Editor's note: from a programmer's PoV I dislike when optimizations aren't part of the language specification because this basically leaves me to the mercy of the compiler, and we've all heard the horror stories of GCC and clang both wreaking havoc with how they distort the C standard SCOTUS-style. This can influence programmers in one of two horrible ways: either it encourages programmers to remain ignorant because they are told to "trust the compiler" without even knowing what it is doing, or they *don't* trust the compiler to the point they manually optimize everything too hard and leave behind unreadable code. By enforcing the most common optimizations I hope these would be minimized. Within the bounds of a single Nectar object, the following wobbles are guaranteed: 1. Variables that are unused are removed from the code. 2. Functions that are unused are removed from the code. 3. Structure fields that are unused are removed from their definition. 4. Structure fields may be reordered to minimize padding. 5. Constant integer expressions within a compilation unit are propagated and evaluated. 6. Values of variables are propagated to subsequent expressions if known, if such a move is profitable. 7. Individual operations might not use instructions intended for them, depending on the discretion of the compiler. For example, a `x * 15` may actually be computed as `x << 4 - x`. Any of the above are disabled if they are to interfere with the specified ABI. ## Optional The following are considered too risque for a compiler to perform without consent of the programmer. 1. `@bitpack` boosts structure packing to the bit-level (for example two `u3`s and a `u1` will all fit into one `u8`). Fields may still be reordered to minimize crossings of byte boundaries. 2. `@ucinline` enforces function call inlining # ABI specification Editor's note: all of the following is still experimental and unimplemented due to compile-time evaluation not being ready. To achieve high portability, Nectar does not require a specific ABI. Each function can be locked to a given calling convention as long as it is formally specified. There are many ways calling conventions can differ: 1. Callee cleanup or caller cleanup 2. Variable-length arguments 3. How exactly parameters are passed around: 3.1. One parameter per register 3.2. Multiple parameters per register 3.3. Structures in multiple registers 3.4. Whether padding is included 3.5. Whether values are upcasted before being passed 3.6. Whether passed buffers may overlap The above is too much complexity to handle in a oneliner.