======== TYPES ======== Built-in types: unit bool u8 u16 u32 u64 i8 i16 i32 i64 f16 f32 f64 N.B.: there is no dedicated 'char' type, strings operate on u8 (utf-8) or u32 (utf-32) Pointer types: T* (pointer to const T) T mut* (pointer to mutable T) T** (pointer to const pointer to const T) T* mut* (pointer to mutable pointer to const T) T mut** (pointer to const pointer to mutable T) T mut* mut* (pointer to mutable pointer to mutable T) Array types: T[N] array of N elements of type T (N must be a compile-time value) Function types: (arg1, arg2, arg3) -> result (arg1, arg2) -> unit // no return value arg -> result // simplified syntax for single-argument case ======== LITERALS ======== Literals: 56b -> i8 42ub -> u8 456s -> i16 456us -> u16 98765 -> i32 98765u -> u32 123l -> i64 123ul -> u64 3.14h -> f16 3.14 -> f32 3.14l -> f64 'a' -> u8 (ascii only?) '猫'u -> u32 TODO: string literals? fixed-size arrays? built-in spans? Probably built-in spans (potentially defines in prelude.psl) "hello, world" -> utf-8 string "здарова, братки"u -> utf-32 string ======== VARIABLES ======== Variable declaration: const x = ... compile-time value, type inferred const x: T = ... compile-time value of type T let x = ... immutable value, type inferred let x: T = ... immutable value of type T mut x = ... mutable, ... mut x: T = ... Array declaration: let arr: i32[4] = [12, 15, 65, 42] let arr = [2, 5, 6] // size and type inferred as i32[3] let arr: i32[0] = [] // need special empty array literal, type cannot be inferred Null pointer literal: let p: u32* = null // special like empty array literal, type cannot be inferred Variables must always be initialized. (TODO: really? What about arrays? Maybe need special syntax for zero-initialization or mass-initialization. Alternative: default to zero-initialization) Const variables must be initialized with a const expression (any expression that doesn't include non-const values). ======== OPERATORS ======== Logical (only bool type): !x x & y x | y x && y // short-circuit x || y // short-circuit x ^ y Equality (all built-in types, all struct types, only same type): x == y x != y Comparison (all built-in types, all struct types, only same type): x < y x > y x <= y x >= y Bitwise (integer types, only same type): !x x & y x | y x && y // short-circuit x || y // short-circuit x ^ y Bitwise shift (any pair of integer types): x >> y x << y Arithmetic (only same integer/floating-point type): -x x + y x * y x / y x % y Pointer arithmetic (any pointer type + any integer type): p + x p - x p - q // returns i64 Pointer arithmetic works element-wise (like C or C++), i.e. p + n advances by n * sizeof(T) when typeof(p) is *T Casting: x as u32 // always explicit, no implicit casts allowed Any integer/floating-point types can be cast to each other. Any pointer types can be cast to each other (TODO: alignment? UB or safe fallback? Probably UB.) Address: &x // returns *T, fails if x is a const variable &mut x // returns *mut T, fails if x is non-mut variable Assignment: x = 15 // requires x to be a mut variable *p = 15 // p must be a pointer to mut ======== FLOW CONTROL ======== Flow control: if condition: statements else if condition: statements else: statements while condition: statements TODO: for loops? iterator/range interface? ======== STRUCTS ======== Struct types: struct rect: width: u32 height: u32 Creating a struct value: let x = rect(10u, 20u) let y = rect(width = 10u, height = 20u) Struct field access: let r = rect(1u, 2u) let x = r.width let p = &r let y = p.height // field access through pointer is the same ======== FUNCTIONS ======== Function definition: func foo(x: i32, y: i32) -> i32: return x * y func bar(x: f32): // deduced return type unit print(x) TODO: function overloading? Probably requires selecting a specific overload using `as` operator to save to a value (but not on call site) ======== TYPE OF TYPES ======== Types are also considered to be values. The keyword `type` denotes the type of all types. I.e. `typeof(16) == i32` and `typeof(i32) == type`. Incidentally, `typeof(type) == type` as well; there are no type kinds or etc. `type` can be used in any place where a type is required (variable types, function arguments, function return value, etc). E.g. func foo(x: type) -> type: return x[4] // type of arrays of 4 elements of type x let y: type = u32 if foo(y) == u32[4]: do_smth() ======== CONST EXPRESSIONS ======== // TODO // Auto-upgrading values to compile-time when a pure function is executed from const-only values? ======== METAPROGRAMMING ======== // TODO // Functions returning functions/structs // Syntactic sugar for common cases // Figure out: max(a,b) - how to deduce type parameters? How does it play with overloading? // func max(t: type): // return func(x : t, y : t): // if x > y: // return x // else: // return y ======== MODULES AND IMPORTS ======== // TODO ======== STANDARD LIBRARY ======== // TODO: containers, memory management, strings?