1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
type vec2 = [2]f32
type Sprite = enum u8 {
BLANK,
PLAYER
}
type Player = struct {
pos, vel vec2
}
extern {
blit proc(spr Sprite, x, y i32),
exp func(v f32) f32
}
proc frame(dt f32) {
/* unary .. can splat an array as parameters */
blit(.PLAYER, ..plr.pos)
plr.pos += plr.vel * dt
plr.vel *= exp(-dt * 5)
}
/* statements
type Ident = T Alias Ident to type T
extern Ident T Define an external variable (or function!) of the given type
extern { a A, b B... } Block of extern declarations, formatted like a struct
*/
/* expressions
N Integer
N.N | NeN | N.NeN Float
[-+~] A Unary operator on term A
A [+-/*%&|^] B Binary operator on term A and expression B
A := B Type inference assignment
A, B, C := D
T { ... } Literal of type T; mostly only makes sense for structs,
but this can be used to do something like u8 { 2 } too.
/* types
IDENT type of the given identifier
^T pointer to type T
enum { FOO, BAR... } enum
enum T { FOO, BAR... } enum with backing type T, which must be an integer type
struct { a A, b B... } struct
A..B range (kinda an anonymous enum)
A and B must be ordinal types (enum value, integer, or character)
[N]T array of T of length N
[E]T array of T indexed by enum or range E
[]T slice
(Ta, Tb...) tuple of given types; can be destructured to multiple assignment
set[E] a set of enum or range E
proc(a A, b B...) function pointer
func(a A, b B...) T function pointer with return value T
*/
Expr = LValue | Literal
LValue = Ident | LValue '.' Ident
Literal = Number
Number = Integer | Float
Integer = /[0-9]+/
Float = Integer 'e' Integer | Integer '.' Integer | Integer '.' Integer 'e' Integer
IDENT Variable
LVALUE . IDENT Member access
[0-9]+ Integer
[0-9]+ . [0-9]+ Float
[0-9]+ . [0-9]+ e [0-9]+ Float
F(E1, E2...) function call with the given expression arguments
*/
/* import first tries to scan local directory for the package, then standard
* system directory; in said directory it will look for an IR cache file,
* or compile a new one if it's not present (or if its last-modified time
* is older than the compiler executable), which is imported with a name
* equivalent to the last part of the path.
*
* codegen is only performed on procedures that get called from main and their
* own dependencies, recursively. this allows to keep output binaries small.
* maybe supply some sort of export qualifier to do something similar for
* procedures that aren't main.
*/
import "io"
proc main {
io.print("Hello, world!")
}
|