-
Notifications
You must be signed in to change notification settings - Fork 0
/
tutorial.kek
322 lines (233 loc) · 6.15 KB
/
tutorial.kek
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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# Single Line Comment
##
Multi Line Comment
Multi Line Comment
##
##
newlines and multiple whitespaces are ignored
Commas are not needed and are removed by the tokenizer,
you can however use them if you want for readability
##
import std:out as out
# this file is parsed
# all exported types (marked as pub), get a prefix
# then we go over this file and
# place the prefix in front of the "my_function"
# in every use
# we keep track of already imported files
# so we dont import the same file twice
import "path/to/file":my_function as my_function
# if the file cannot be found locally the compiler
# looks in te global paths, etc..
# scopes can be nested
{}
# scopes behave like in c
# variables declared inside a scope are not accessible outside the scope
# the default scope can see and use all variables declared
# and available in the parent scope
a :: 124 # const
{
b :: 123
c :: a + b
}
# c and b are not accessible here
# adding () makes the scope into a capture scope
# now only the names in the capture list are accessible
b :: 123
(a){
c :: a + a
# b is not accessible here
}
# scopes can return values
c :: ->Int {
return 123
}
# returning scopes can be capturing as well
d :: (c) -> Int {
return c + 1
}
##
define a variable
with :=
Change a variable with "set"
"set" is mandatory, since mutation of state should
made explicit.
##
my_var := 123
set my_var = 124 # set statement
##
Control flow
Kek only has if statements and for loops
##
if a == 1 {
# do something
}
elif a == 2 {
# do something else
}
else {
# do something else
}
start :: 0
end :: 10
stepsize :: 1
for i := start .. end; stepsize {
# do something
}
for i := 0 .. 10; 1{
# do something
}
##
Functions
Functions are NOT first class citizens in Kek, they are
more like C functions, but without function pointers.
The need to be declared as const (as ::).
##
fn foob(a:Int b:Int) -> Int {
ret a + b
}
out(foob(1 3)) # 4
##
enums
Enums are simple structs, where you only can declare
const values of the same type.
All types need to start with an upper case letter.
Only types are allowed to start with an upper case letter.
##
enum MyEnum{
A :: 1
B :: 2
}
##
structs
Structs are used to structure data.
Structs and functions can only be defined on the top level.
##
struct MyStruct {
a: Int
b: Int
myList: [MyStruct]
}
a :: MyStruct.static_const_field # 12
mys :: MyStruct(a=1 b=2) # calling a type -> construction literal
##
Methods
Structs can have methods, this allows for a simpler
call syntax.
A method is a function in a struct that has the
"self" argument as the first argument.
Other than that methods are normal functions.
##
struct MyName {
a: Int
fn myMethod(self: MyStruct){
out(self.a)
}
}
mys :: MyStruct(a=1)
mys:my_method() # 1 # call methods with :
p:apply(_(x=69 y=69))
##
optionals
Kek code is statically typed.
So we need a type for values that can be nil.
To unpack an option, we need to use the ? operator
with the unpack syntax in the if statement.
##
fn foo(a: Bool) -> Player|nil {
if a {
ret Player(x=1 y=2)
}
ret nil
}
option :: foo(true) # type: Player?
if p :: option ?? Player { # unpack syntax -> can also be "p := ?option"
# p is now like defined in this scope and cannot leave it
out(p.x)
}
else{
out("nil")
}
##
unions
We can return union-types.
This is used instead of try-catch blocks, but
can also be used for other things.
##
fn handle(a: Bool) -> Player|String {
if a {
ret Player(x=1 y=2)
}
ret "error"
}
result :: handle(true) # type: Player | String
if player :: result ?? Player {
out(player.x)
# do more with player
}
elif error :: result ?? String {
out(error)
}
##
Array and Map(Dict)
Kek has a built in array and map type.
If a value that is not a type is followed by a .(
this is interpreted as accessing the value at the index.
It qualifies as a indentifier can can stand on
the left side of an assignment.
Since we only have list and map as generic types
we can only expect ( and ) if it is preceded by a type.
##
{
arr := [Int](1 2 3 4 5)
map :: [String Int]("a"=1 "b"=2 "c"=3)
out(arr:(0))
out(arr:(1))
out(map:("a"))
out(map:("b"))
set arr 0 = 123
}
##
const & local
Function arguments and return types can be denoted as const and local.
If an argument is denoted as const, it cannot be changed within
the function - even if its is mutable outside the function.
IF an argument is denoted as local, this argument cannot be set to any
value that does leave the function scope, expect into function that
have the same local argument.
So local ensures, that there is no new state containing the local reference
after the function has concluded.
If a return type is const, the return value can only be set to
a const field or passed into a const argument. Const is part of the type.
If a return type is local, the return value can only be set to
a local field or passed into a local argument. Local is part of the type.
##
# todo: add example
##
Scope Rules & rule-scopes
Each scope can have compile time rules set.
Each scope inherits the rules from the parent scope.
Rules can also be added together and applied to multiple scopes.
##
{
ruleset only_const = true
a :: 123
# not allowed
# b := 123
{
# not allowed
# c := 123
}
{
rule only_const = false
b := 123
}
}
MyFunctionConstraints :: rules{
only_const :: true
only_local_args:: true
}
my_c_f :: fn(){
ruleset MyFunctionConstraints
# now all rules are applied here ...
}