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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
|
# Ek
Ek (oak in Swedish) is my toy programming language.
Currently far from a usable state, but I thought I might
still put it up on GitHub. Get them green squares, yo.
# Building
+ `make`
Add `RELEASE=1` to enable optimizations.
# Ek features and target use case
Ek is inteded to be a simple language, similar to C with little to no runtime but with
some quality of life improvements, mainly stuff already seen in other languages.
Nothing particularly ground-breaking.
# Tests
Tests are currently broken, I'm going to have to come up with some half decent
testing framework.
## Feature showcase
In no particular order of importance or significance.
Check the tests for some more examples.
### C++-style comments
```
// inline comment
/*
multi line comment
*/
```
Additionally, comments are allowed in comments:
```
/* comment 1
/* comment2 */
*/
```
### Right to left type declarations
```
// an array of eight pointers to u32
a [8]'u32;
// a pointer to an array of eight u32
b '[8]u32;
```
### Unified function declaration syntax
```
// function definition
some_func(a u8, b u16 => i32){
// function pointer to some_func
a_callback '(u8, u16 => i32) = some_func;
}
```
### More legible casts
```
b f32;
a u32 = b as u32;
```
### Immutable by default
```
// immutable i64
a i64 = 32;
// mutable u32
b mut u32 = 32;
```
### Type inference
```
// immutable i64 (integer constants are by default the largest signed type)
a const = 32;
// mutable u32
b mut = 32 as u32;
```
### Strong(er) type system
```
// ERROR
a u32 = 20.0f;
// OK
a u32 = 20.0f as u32;
```
### Defer
```
a const = alloc(16);
defer(dealloc(a));
```
### Shadowing is disallowed
```
a u32;
{
// ERROR
a f32;
}
```
Possibly controversial, but I've only ever run into shadowing bugs,
haven't really found it to be a useful feature.
### Function overloading
```
some_func(u32, u64){}
some_func(u32, u32){}
```
### Interfaces
```
// accepts any type, but you're not allowed to do anything with it
typedef any {}
// any type where there exists a function add_one(some_type => some_type)
// fullfills the interface
typedef addable {
add_one(addable => addable);
}
```
Interfaces are a bit more complex with peculiarities I'm still not entirely sure about,
TODO. For example,
```
typedef some_type {
add(some_type, some_type => some_type)
}
// does this fullfill some_type for u32 and u64? Or neither?
add(u32, u32 => u64){}
```
These could be fairly easily solved with some extra syntax, but I would really prefer
keeping function syntax in the top context without extra decorations. Maybe just me.
However, as long as I pick an interpretation and stick to it, should be fine.
### Generics through interfaces
```
struct some_struct (A some_interface, B some_other_interface) {
a T;
b B;
};
```
Note that each type must implement some interface. Similarly, procedures
can take an interface as the type which makes it 'generic', but type parameters are not
supported:
```
do_something(a some_interface){
// you can only call functions and read members that the interface defines
}
```
This type of interfaces and overloading are tricky to support at the same time, but
I currently use a left-to-right actualization. That is, in each parameter 'index'
there can only be actual types (`u32`, `i64`, `some_struct`), or a fallback 'generic'
interface. Each parameter type is matched to the argument type, and if none of the actual types match,
the fallback is checked. If it also fails, no function implements the requested types.
This is to avoid cases where two interfaces would both be implemented by a type, leading
to unambiguous function resolutions. This system is inherently less powerful than what
C++ for example has, but might be more flexible than Rust, for example.
### Hidden by default
```
// visible through import
pub some_func(){}
// not visible through import
some_other_func(){}
```
### Public imports
```
// visible in this file and through import
pub import "some_file.ek";
// only visible in this file
import "some_file.ek";
```
### AST macros
```
define stuff(x, y) {
x + y;
}
...
// equivalent to a u32 = (200 + 200) as u32;
a u32 = stuff(20, 200) as u32;
```
Having macros is a useful feature, but they should be limited.
Having the macros operate on the AST gives better control, and for example
defining new functions is disallowed.
By default macros are unhygienic, but you can create a new context to make
them hygienic:
```
define hygienic(){
{// this is hygienic}
// this isn't
}
```
### Compile time variadics
```
some_func(a u32, b u32, ...args) {
// const for is a compiletime loop that iterates over ...args
const for x : ...args {
...
}
}
```
Macros also support variadics. Macros and functions are in the same space, so
```
// OK, macro that doesn't take arguments
define some_thing(){}
// ERR, an object with matching signature already exists
some_thing(){}
// OK, the same name is used but the signature is different
some_thing(a u32){}
```
# Architecture
At the moment I'm aiming for transpiling Ek to C. I'm hoping that it will be possible
to add other transpilation targets, for example JavaScript.
# Name
Ek is the current name, mainly because I like oaks and didn't seem to step on anyone's toes.
I had a number of names I toyed around with before pushing to GitHub, any mentions of other
names should be disregarded and preferrably fixed.
|