218 lines
5.5 KiB
Text
218 lines
5.5 KiB
Text
======== 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?
|