~repos /plum
git clone https://pyrossh.dev/repos/plum.git
A statically typed, imperative programming language inspired by rust, python
19b8452f
—
pyrossh 1 year ago
improve syn
- .gitignore +1 -0
- readme.md +126 -236
- scratch.pc +218 -0
- std/bool.mi +1 -30
- std/coding/base64.mi +0 -170
- std/html.mi +43 -0
- std/http.mi +15 -53
- std/list.mi +4 -33
- std/map.mi +0 -2
- std/os.mi +5 -0
- std/ranges.mi +0 -75
- std/regex.mi +1 -0
- std/time.mi +3 -0
- std/uuid.mi +1 -0
.gitignore
CHANGED
|
@@ -9,3 +9,4 @@ static/bundle/
|
|
|
9
9
|
static/monaco-editor-workers/
|
|
10
10
|
static/worker/
|
|
11
11
|
syntaxes/
|
|
12
|
+
website
|
readme.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# 👾 Pacos Programming Language
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
- A statically typed, imperative programming language inspired by rust, koka.
|
|
4
|
-
|
|
4
|
+
- The compiler users the tree-sitter parser so has out of the box syntax highlighting support for helix and zed editor.
|
|
5
5
|
|
|
6
6
|
**Rules**
|
|
7
|
+
|
|
7
|
-
|
|
8
|
+
- Function parameters are passed by value only. You cannot modify a parameter. The compiler will throw an error if you try to.
|
|
8
|
-
|
|
9
|
+
- Strict naming convention
|
|
9
|
-
|
|
10
|
+
- Only one way of doing things ex: loops, condition
|
|
10
11
|
|
|
11
12
|
**Todo**
|
|
12
13
|
linter, formatter, test runner, language server, package manager
|
|
13
14
|
|
|
14
|
-
|
|
15
15
|
Here is some sample code, please enjoy.
|
|
16
16
|
|
|
17
17
|
```go
|
|
@@ -47,21 +47,21 @@ fn factorial(n: int): int =
|
|
|
47
47
|
a -> 1
|
|
48
48
|
_ -> n * factorial(n - 1)
|
|
49
49
|
|
|
50
|
-
fn
|
|
50
|
+
fn firstItem(l: list[int]): int? =
|
|
51
51
|
l[0]
|
|
52
52
|
|
|
53
|
-
fn
|
|
53
|
+
fn firstItem(l: list[int]): int? =
|
|
54
54
|
match l
|
|
55
55
|
[] -> nil
|
|
56
56
|
[head, ...rest] -> head
|
|
57
57
|
|
|
58
|
-
fn
|
|
58
|
+
fn firstItem(l: list[int]): int? =
|
|
59
59
|
if l.size > 0
|
|
60
60
|
l[0]
|
|
61
61
|
else
|
|
62
62
|
nil
|
|
63
63
|
|
|
64
|
-
fn
|
|
64
|
+
fn toCelsius(f: float): float =
|
|
65
65
|
(f - 32) * (5 / 9)
|
|
66
66
|
|
|
67
67
|
record Cat[A: Comparable & Stringable](
|
|
@@ -69,16 +69,16 @@ record Cat[A: Comparable & Stringable](
|
|
|
69
69
|
age: int
|
|
70
70
|
)
|
|
71
71
|
|
|
72
|
-
fn Cat.
|
|
72
|
+
fn Cat.withName(name: str): Cat =
|
|
73
73
|
Cat(name: name, age: 0)
|
|
74
74
|
|
|
75
75
|
fn (c: Cat) fullname(): str =
|
|
76
76
|
c.name + c.age.to_str()
|
|
77
77
|
|
|
78
78
|
fn (c: Cat) talk() =
|
|
79
|
-
|
|
79
|
+
printLn("cat ${c.name} says meow")
|
|
80
80
|
|
|
81
|
-
fn (c: Cat)
|
|
81
|
+
fn (c: Cat) toStr(): str =
|
|
82
82
|
"Cat<{c.fullname()}, ${c.age}>"
|
|
83
83
|
|
|
84
84
|
type MapCallback = fn(v: a): v
|
|
@@ -115,12 +115,12 @@ test("to_str") |t|
|
|
|
115
115
|
items := [Cat("Molly", 9), Cat("Fenton", 6)]
|
|
116
116
|
.retain(|p| p.name.size > 5)
|
|
117
117
|
.map(|p| describe(p))
|
|
118
|
-
.each(|d|
|
|
118
|
+
.each(|d| printLn(d))
|
|
119
119
|
assert items[0].to_str() == "Cat<Fenton, 6>"
|
|
120
120
|
|
|
121
121
|
bench("1231") |n|
|
|
122
122
|
for i := range n
|
|
123
|
-
|
|
123
|
+
printLn(i)
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
## Language Reference
|
|
@@ -128,7 +128,7 @@ bench("1231") |n|
|
|
|
128
128
|
**Keywords**
|
|
129
129
|
|
|
130
130
|
```rs
|
|
131
|
-
for,while,if,else,record,enum,fn,assert,match,type
|
|
131
|
+
for,while,if,else if,else,record,enum,fn,assert,match,type
|
|
132
132
|
```
|
|
133
133
|
|
|
134
134
|
### Types
|
|
@@ -158,7 +158,7 @@ if true || false
|
|
|
158
158
|
|
|
159
159
|
**byte**
|
|
160
160
|
|
|
161
|
-
A byte represents an unsigned 8
|
|
161
|
+
A byte represents an unsigned 8-bit number. It is mainly used to represent strings and binary data.
|
|
162
162
|
|
|
163
163
|
```rb
|
|
164
164
|
let data: []byte?
|
|
@@ -167,10 +167,13 @@ data = [104, 101, 197, 130, 197, 130, 111, 0]
|
|
|
167
167
|
|
|
168
168
|
**int**
|
|
169
169
|
|
|
170
|
-
An int is a signed 64
|
|
170
|
+
An int is a signed 64-bit number. It can be represented in various ways,
|
|
171
|
+
|
|
172
|
+
```
|
|
171
173
|
0b - Binary (Base 2)
|
|
172
174
|
0x - Hexadecimal (Base 16)
|
|
173
175
|
27 - Standard (Base 10)
|
|
176
|
+
```
|
|
174
177
|
|
|
175
178
|
```rb
|
|
176
179
|
0b00101010
|
|
@@ -184,11 +187,25 @@ An int is a signed 64 bit number. It can be represented in various ways,
|
|
|
184
187
|
|
|
185
188
|
**float**
|
|
186
189
|
|
|
187
|
-
A float represents a 64-bit floating point
|
|
190
|
+
A float represents a 64-bit floating point [IEEE-754-2008](https://en.wikipedia.org/wiki/Double-precision_floating-point_format).
|
|
191
|
+
|
|
192
|
+
```java
|
|
193
|
+
1.2
|
|
194
|
+
-0.4
|
|
195
|
+
12.0f
|
|
196
|
+
15.03f
|
|
197
|
+
```
|
|
188
198
|
|
|
189
199
|
**dec**
|
|
190
200
|
|
|
191
|
-
A
|
|
201
|
+
A dec is a decimal floating-point numbering format which is 64-bit data type.
|
|
202
|
+
It is intended for applications where it is necessary to emulate decimal rounding exactly, such as financial and tax computations.
|
|
203
|
+
It supports 16 decimal digits of significand and an exponent range of −383 to +384.
|
|
204
|
+
|
|
205
|
+
```java
|
|
206
|
+
2.4d
|
|
207
|
+
-13.3d
|
|
208
|
+
```
|
|
192
209
|
|
|
193
210
|
**str**
|
|
194
211
|
|
|
@@ -199,7 +216,7 @@ It supports interpolation of variables/values that implement the ToStr interface
|
|
|
199
216
|
"Hello World"
|
|
200
217
|
name := "Pacos"
|
|
201
218
|
age := 1
|
|
202
|
-
|
|
219
|
+
printLn("Name ${name} age ${age}")
|
|
203
220
|
```
|
|
204
221
|
|
|
205
222
|
**list**
|
|
@@ -207,11 +224,15 @@ println("Name ${name} age ${age}")
|
|
|
207
224
|
```py
|
|
208
225
|
import pacos/list
|
|
209
226
|
|
|
210
|
-
a :=
|
|
227
|
+
a := List.of(1, 2, 3) # List[int]
|
|
211
|
-
b :=
|
|
228
|
+
b := List.of( # List[List[int]]
|
|
229
|
+
List.of(1),
|
|
230
|
+
List.of(2),
|
|
231
|
+
List.of(3),
|
|
232
|
+
)
|
|
212
|
-
c :=
|
|
233
|
+
c := List.of(1, 2, 3 * 4, 8, n)
|
|
213
234
|
|
|
214
|
-
actors :=
|
|
235
|
+
actors := List.of("Krabs", "Squidward")
|
|
215
236
|
actors.add("Spongebob")
|
|
216
237
|
actors.length() // ==> 3
|
|
217
238
|
actors.contains("Krabs") // ==> true
|
|
@@ -220,58 +241,41 @@ actors.get(5) // => nil
|
|
|
220
241
|
|
|
221
242
|
items
|
|
222
243
|
.map(|v| v + 1)
|
|
223
|
-
.each(|v|
|
|
244
|
+
.each(|v| printLn("v", v))
|
|
224
245
|
.reduce(0, |v| v + 1)
|
|
225
246
|
```
|
|
226
247
|
|
|
227
248
|
**map**
|
|
228
249
|
|
|
229
|
-
```
|
|
250
|
+
```rs
|
|
230
251
|
import pacos/map
|
|
231
252
|
|
|
232
|
-
nums := Map.
|
|
253
|
+
nums := Map.of(:one => 1, :two => 2)
|
|
233
|
-
map.get(:one) // =>
|
|
254
|
+
map.get(:one) // => 1
|
|
234
255
|
map.get(:unknown) // => nil
|
|
235
|
-
|
|
236
|
-
friends_tree :=
|
|
256
|
+
friends_tree := Map.of(
|
|
237
257
|
:value => "Fred",
|
|
238
|
-
:left =>
|
|
258
|
+
:left => Map.of(
|
|
239
259
|
:value => "Jim",
|
|
240
|
-
|
|
260
|
+
),
|
|
241
|
-
:right =>
|
|
261
|
+
:right => Map.of(
|
|
242
262
|
:value => "Shiela",
|
|
243
|
-
:left =>
|
|
263
|
+
:left => Map.of(
|
|
244
264
|
:value => "Alice",
|
|
245
|
-
|
|
265
|
+
),
|
|
246
|
-
:right =>
|
|
266
|
+
:right => Map.of(
|
|
247
267
|
:value => "Bob"
|
|
248
|
-
|
|
268
|
+
),
|
|
249
|
-
|
|
269
|
+
),
|
|
250
|
-
|
|
270
|
+
)
|
|
251
271
|
|
|
252
272
|
friends_tree
|
|
253
273
|
.map(|k, v| v)
|
|
254
|
-
.each(|k, v|
|
|
274
|
+
.each(|k, v| printLn("v", v))
|
|
255
275
|
.reduce(0, |k, v| v + 1)
|
|
256
276
|
```
|
|
257
277
|
|
|
258
|
-
**time**
|
|
259
|
-
|
|
260
|
-
TBD
|
|
261
|
-
|
|
262
|
-
**duration**
|
|
263
|
-
|
|
264
|
-
TBD
|
|
265
|
-
|
|
266
|
-
**regex**
|
|
267
|
-
|
|
268
|
-
TBD
|
|
269
|
-
|
|
270
|
-
**uuid**
|
|
271
|
-
|
|
272
|
-
TBD
|
|
273
|
-
|
|
274
|
-
**
|
|
278
|
+
**constants**
|
|
275
279
|
|
|
276
280
|
Constants can be declared at the top level of a program. They cannot be reassigned.
|
|
277
281
|
|
|
@@ -279,15 +283,17 @@ Constants can be declared at the top level of a program. They cannot be reassign
|
|
|
279
283
|
- Reference values like `list, map, records` are initialized at program start and passed by reference when used. Their data can be modified.
|
|
280
284
|
|
|
281
285
|
```rb
|
|
286
|
+
const START_YEAR = 2101
|
|
282
|
-
const PI = 3.
|
|
287
|
+
const PI = 3.14159
|
|
283
|
-
const
|
|
288
|
+
const NAME = "pacos"
|
|
289
|
+
const DEBUG_ENABLED = true
|
|
284
290
|
const COUNT = count(10)
|
|
285
|
-
const COUNTRIES_LIST =
|
|
291
|
+
const COUNTRIES_LIST = ist.of("US", "INDIA", "CANADA")
|
|
286
|
-
const COUNTRY_CODES =
|
|
292
|
+
const COUNTRY_CODES = map.of(
|
|
287
|
-
|
|
293
|
+
"in" => "INDIA",
|
|
288
|
-
|
|
294
|
+
"us" => "United States",
|
|
289
|
-
|
|
295
|
+
"ca" => "Canada"
|
|
290
|
-
|
|
296
|
+
)
|
|
291
297
|
|
|
292
298
|
fn count(n: int): int = n * 1
|
|
293
299
|
```
|
|
@@ -305,22 +311,6 @@ assoc_list[:a]
|
|
|
305
311
|
assoc_list["b"]
|
|
306
312
|
```
|
|
307
313
|
|
|
308
|
-
**Assignment Operators**
|
|
309
|
-
|
|
310
|
-
```go
|
|
311
|
-
x := 5
|
|
312
|
-
y := 3
|
|
313
|
-
x += y // 8
|
|
314
|
-
x -= y // 5
|
|
315
|
-
x *= y // 15
|
|
316
|
-
x /= y // 5
|
|
317
|
-
x %= y // 2
|
|
318
|
-
x &= y // 2
|
|
319
|
-
x |= y // 3
|
|
320
|
-
x <<= y // 24
|
|
321
|
-
x >>= y // 3
|
|
322
|
-
```
|
|
323
|
-
|
|
324
314
|
**While statement**
|
|
325
315
|
|
|
326
316
|
```rb
|
|
@@ -338,10 +328,6 @@ while a > b
|
|
|
338
328
|
|
|
339
329
|
**For statement**
|
|
340
330
|
|
|
341
|
-
type Seq0 = fn(yield: fn(): bool): bool
|
|
342
|
-
type Seq1[V] = fn(yield: fn(V): bool): bool
|
|
343
|
-
type Seq2[K, V] = fn(yield: fn(K, V): bool): bool
|
|
344
|
-
|
|
345
331
|
```rb
|
|
346
332
|
for i := range 10
|
|
347
333
|
sum += i
|
|
@@ -355,24 +341,45 @@ for k, v := range json_map
|
|
|
355
341
|
|
|
356
342
|
for v := range list
|
|
357
343
|
sum += k + v
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**For Custom Iterator**
|
|
347
|
+
|
|
348
|
+
```rb
|
|
349
|
+
type Seq0 = fn(yield: fn(): bool): bool
|
|
350
|
+
type Seq1[V] = fn(yield: fn(V): bool): bool
|
|
351
|
+
type Seq2[K, V] = fn(yield: fn(K, V): bool): bool
|
|
358
352
|
|
|
359
353
|
record Tree[E](
|
|
360
354
|
value E,
|
|
361
|
-
left: Tree[E]
|
|
355
|
+
left: optional[Tree[E]],
|
|
362
|
-
right: Tree[E]
|
|
356
|
+
right: optional[Tree[E]],
|
|
363
357
|
)
|
|
364
358
|
|
|
365
359
|
fn (t Tree[E]) op_range(yld: fn(E): bool): bool =
|
|
366
|
-
t ? true : t.left.in_order(yld) && yld(t.val) && t.right.in_order(yld)
|
|
360
|
+
t ? true : t.left?.in_order(yld) && yld(t.val) && t.right?.in_order(yld)
|
|
367
361
|
|
|
368
|
-
|
|
362
|
+
tree := Tree(
|
|
369
363
|
value: 10,
|
|
370
364
|
left: Tree(20, Tree(30), Tree(39)),
|
|
371
365
|
right: Tree(40),
|
|
372
366
|
)
|
|
373
367
|
|
|
374
|
-
for t := range
|
|
368
|
+
for t := range tree
|
|
375
|
-
|
|
369
|
+
printLn(v)
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**if expression/statement**
|
|
373
|
+
|
|
374
|
+
```py
|
|
375
|
+
if name == "Andreas"
|
|
376
|
+
printLn("What a nice name!")
|
|
377
|
+
else if name == "Pacman"
|
|
378
|
+
printLn("Game from the 80s")
|
|
379
|
+
else if name == ""
|
|
380
|
+
printLn("Don't you have a name?")
|
|
381
|
+
else
|
|
382
|
+
printLn("Boring name...")
|
|
376
383
|
```
|
|
377
384
|
|
|
378
385
|
**When expression/statement**
|
|
@@ -394,26 +401,13 @@ when number
|
|
|
394
401
|
1 | 3 | 5 | 7 -> "This is an odd number"
|
|
395
402
|
_ -> "I'm not sure"
|
|
396
403
|
|
|
397
|
-
|
|
404
|
+
when xs
|
|
398
405
|
[] -> "This list is empty"
|
|
399
406
|
[a] -> "This list has 1 element"
|
|
400
407
|
[a, b] -> "This list has 2 elements"
|
|
401
408
|
_ -> "This list has more than 2 elements"
|
|
402
409
|
```
|
|
403
410
|
|
|
404
|
-
Arithmetic (+, -, /, \*, @divFloor, @sqrt, @ceil, @log, etc.)
|
|
405
|
-
Bitwise operators (>>, <<, &, |, ~, etc.)
|
|
406
|
-
Comparison operators (<, >, ==, etc.)
|
|
407
|
-
|
|
408
|
-
**if expression/statement**
|
|
409
|
-
|
|
410
|
-
```rb
|
|
411
|
-
if post.valid()
|
|
412
|
-
render_create_view()
|
|
413
|
-
else
|
|
414
|
-
render_post(post: post)
|
|
415
|
-
```
|
|
416
|
-
|
|
417
411
|
### Conditional operators
|
|
418
412
|
|
|
419
413
|
**not operator**
|
|
@@ -474,13 +468,29 @@ paint := Paint()
|
|
|
474
468
|
..strokeWidth = 5.0
|
|
475
469
|
```
|
|
476
470
|
|
|
477
|
-
**variadic
|
|
471
|
+
**variadic operator**
|
|
478
472
|
|
|
479
473
|
```go
|
|
480
474
|
fn add(items ...str) =
|
|
481
475
|
list.add(items)
|
|
482
476
|
```
|
|
483
477
|
|
|
478
|
+
**assignment operator**
|
|
479
|
+
|
|
480
|
+
```go
|
|
481
|
+
x := 5
|
|
482
|
+
y := 3
|
|
483
|
+
x += y // 8
|
|
484
|
+
x -= y // 5
|
|
485
|
+
x *= y // 15
|
|
486
|
+
x /= y // 5
|
|
487
|
+
x %= y // 2
|
|
488
|
+
x &= y // 2
|
|
489
|
+
x |= y // 3
|
|
490
|
+
x <<= y // 24
|
|
491
|
+
x >>= y // 3
|
|
492
|
+
```
|
|
493
|
+
|
|
484
494
|
**generics**
|
|
485
495
|
|
|
486
496
|
```
|
|
@@ -488,134 +498,14 @@ fn add[T: int | float](a: List[T], b: List[T]): List[T] =
|
|
|
488
498
|
pass
|
|
489
499
|
```
|
|
490
500
|
|
|
491
|
-
```
|
|
492
|
-
fn create_post_action(req: Request): Result[Response] =
|
|
493
|
-
post := Post(title = req.params.title, body = req.params.body)
|
|
494
|
-
if post.valid()
|
|
495
|
-
RenderNewView()
|
|
496
|
-
else
|
|
497
|
-
post := createRecord(post)
|
|
498
|
-
setSuccessMessage("Post created")
|
|
499
|
-
redirectTo("/posts")
|
|
500
|
-
|
|
501
|
-
fn divide(dividend: u32, divisor: u32) !u32 =
|
|
502
|
-
if divisor == 0
|
|
503
|
-
error.DivideByZero
|
|
504
|
-
else
|
|
505
|
-
dividend / divisor
|
|
506
|
-
dispatch(action, req, res) catch |err|
|
|
507
|
-
BodyTooBig -> Response(
|
|
508
|
-
status: 431,
|
|
509
|
-
body: "Request body is too big",
|
|
510
|
-
)
|
|
511
|
-
BrokenPipe, ConnectionResetByPeer -> return false
|
|
512
|
-
_ -> error_handler(req, res, err)
|
|
513
|
-
|
|
514
|
-
error FileOpenError
|
|
515
|
-
| AccessDenied(str)
|
|
516
|
-
| OutOfMemory(str)
|
|
517
|
-
| FileNotFound(str)
|
|
518
|
-
|
|
519
|
-
fn parse_version(header: List[int]): result[Version, error] =
|
|
520
|
-
header.get(0) != nil ? v : error.InvalidHeaderLength(header.get(0))
|
|
521
|
-
|
|
522
|
-
fn main(): result[unit, unit] =
|
|
523
|
-
version := parse_version(list.of(1, 2))
|
|
524
|
-
match pg.connect()
|
|
525
|
-
ok(c) -> return 0
|
|
526
|
-
err(e) -> return e
|
|
527
|
-
|
|
528
|
-
greeting_file := file.open("hello.txt").unwrap_or_else() |error|
|
|
529
|
-
match error
|
|
530
|
-
ErrorKind::NotFound ->
|
|
531
|
-
File::create("hello.txt").unwrap_or_else() |error|
|
|
532
|
-
panic!("Problem creating the file: {:?}", error)
|
|
533
|
-
_ ->
|
|
534
|
-
panic!("Problem opening the file: {:?}", error)
|
|
535
|
-
|
|
536
|
-
conn := match pg.connect()?
|
|
537
|
-
create_post_action(req)
|
|
538
|
-
.map(|v| 0)
|
|
539
|
-
.map_err(|err| 1)
|
|
540
|
-
|
|
541
|
-
create_post_action(req)?
|
|
542
|
-
ok()
|
|
543
|
-
|
|
544
|
-
greeting_file := file.open("hello.txt")?
|
|
545
|
-
ok()
|
|
546
|
-
|
|
547
|
-
import std/str
|
|
548
|
-
import std/result
|
|
549
|
-
|
|
550
|
-
fn double_number(s: str): result[i32, unit] =
|
|
551
|
-
number_str.parse_int().map(|n| 2 * n)
|
|
552
|
-
|
|
553
|
-
fn main(): result<i32, unit> =
|
|
554
|
-
double_number("10")
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
use std::num::ParseIntError;
|
|
558
|
-
|
|
559
|
-
fn double_number(number_str: &str) -> Result<i32, ParseIntError> {
|
|
560
|
-
number_str.parse::<i32>().map(|n| 2 * n)
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
fn main() {
|
|
564
|
-
match double_number("10") {
|
|
565
|
-
Ok(n) => assert_eq!(n, 20),
|
|
566
|
-
Err(err) => println!("Error: {:?}", err),
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
trait error: debug + display (
|
|
571
|
-
fn description(): str
|
|
572
|
-
fn cause(): option<error>
|
|
573
|
-
)
|
|
574
|
-
|
|
575
|
-
trait From<T>(
|
|
576
|
-
fn from(v: T) -> Self;
|
|
577
|
-
)
|
|
578
|
-
|
|
579
|
-
trait Debug(
|
|
580
|
-
fn fmt(f: Formateer): result[unit, unit]
|
|
581
|
-
)
|
|
582
|
-
|
|
583
|
-
trait Display(
|
|
584
|
-
fn fmt(f: Formateer): result[unit, unit]
|
|
585
|
-
)
|
|
586
|
-
|
|
587
|
-
record Car(wheels: int)
|
|
588
|
-
|
|
589
|
-
fn getWheels() =
|
|
590
|
-
returns 4
|
|
591
|
-
|
|
592
|
-
fn main() =
|
|
593
|
-
let c1 = some(Car(wheels: 2))
|
|
594
|
-
let c2: option<Car> = none
|
|
595
|
-
|
|
596
|
-
let c1 = Some(Car(wheels: 2))
|
|
597
|
-
let c2: Option<Car> = None
|
|
598
|
-
|
|
599
|
-
match c
|
|
600
|
-
none -> print("no car")
|
|
601
|
-
some(car) -> car.getWheels()
|
|
602
|
-
|
|
603
|
-
Car c2 = null
|
|
604
|
-
c2.getWheels() // Null pointer
|
|
605
|
-
```
|
|
606
|
-
|
|
607
501
|
### General naming convention
|
|
608
502
|
|
|
609
|
-
| Item
|
|
503
|
+
| Item | Convention |
|
|
610
|
-
| ----------------------- | -----------------------
|
|
504
|
+
| ------------------------ | ----------------------- |
|
|
611
|
-
| Modules
|
|
505
|
+
| Modules | snake_case |
|
|
612
|
-
| Types | UpperCamelCase |
|
|
613
|
-
| Traits
|
|
506
|
+
| Types/Traits/Enum | UpperCamelCase |
|
|
614
|
-
|
|
|
507
|
+
| Fields/Functions/Methods | lowerCamelCase |
|
|
615
|
-
| Functions | snake_case |
|
|
616
|
-
| Methods | snake_case |
|
|
617
|
-
| General constructors | new or with_more_details |
|
|
618
|
-
| Conversion constructors
|
|
508
|
+
| Conversion constructors | from_some_other_type |
|
|
619
|
-
| Local variables
|
|
509
|
+
| Local variables | snake_case |
|
|
620
|
-
| Constants
|
|
510
|
+
| Constants | SCREAMING_SNAKE_CASE |
|
|
621
|
-
| Generics
|
|
511
|
+
| Generics | single uppercase letter |
|
scratch.pc
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
data.price?.take_if(|a| a != "")
|
|
2
|
+
trait Iterator(
|
|
3
|
+
fun has_next(): Bool
|
|
4
|
+
fun next(): T?
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
trait HasEq[A](
|
|
8
|
+
fn eq(that: a): bool
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
trait Equatable[A]
|
|
12
|
+
`A trait that defines equal and not equal operations
|
|
13
|
+
|
|
14
|
+
fn eq(b: Equatable[A]): bool
|
|
15
|
+
fn ne(b: Equatable[A]): bool
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
enum Compare
|
|
19
|
+
| Less
|
|
20
|
+
| Equal
|
|
21
|
+
| Greater
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
trait Comparable[A] is Equatable[A]
|
|
25
|
+
fn lt(that: A): bool
|
|
26
|
+
fn le(that: A): bool
|
|
27
|
+
fn ge(that: A): bool
|
|
28
|
+
fn gt(that: A): bool
|
|
29
|
+
fn compare(that: a): Compare
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
enum list<T> =
|
|
33
|
+
| empty
|
|
34
|
+
| link(v: T, rest: list<T>)
|
|
35
|
+
|
|
36
|
+
fn list.of[A](values: ...A): list[A] =
|
|
37
|
+
list[A]().add(values)
|
|
38
|
+
|
|
39
|
+
fn (o list<T>) each(cb: fn(v: T)) =
|
|
40
|
+
match o
|
|
41
|
+
empty -> return
|
|
42
|
+
link(a, rest) ->
|
|
43
|
+
cb(a)
|
|
44
|
+
rest.each(cb)
|
|
45
|
+
|
|
46
|
+
fn (l: list) append(values: ...V) =
|
|
47
|
+
`adds the specified elements to the start of the list
|
|
48
|
+
range(values) |v|
|
|
49
|
+
l.add(v)
|
|
50
|
+
|
|
51
|
+
fn (l: list) append(v: V) =
|
|
52
|
+
`adds the specified elements to the start of the list
|
|
53
|
+
l.last().rest = link(v, empty)
|
|
54
|
+
|
|
55
|
+
fn (l: list) prepend(v: V) =
|
|
56
|
+
`adds the specified elements to the start of the list
|
|
57
|
+
l.first() = link(v, l.first())
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
fn range(start: int, e: int, cb: fn(int)) =
|
|
61
|
+
match start < end ->
|
|
62
|
+
cb(start)
|
|
63
|
+
range(start + 1, end, cb)
|
|
64
|
+
|
|
65
|
+
fn repeat(n: int, cb: fn(int): void) =
|
|
66
|
+
match n != 0
|
|
67
|
+
cb()
|
|
68
|
+
repeat(n - 1, cb)
|
|
69
|
+
|
|
70
|
+
fn repeat(cb: fn(int): void) =
|
|
71
|
+
cb()
|
|
72
|
+
repeat(cb)
|
|
73
|
+
|
|
74
|
+
fn iterate[T](l: list[T], cb: fn(t: T)) =
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
fn main() =
|
|
78
|
+
repeat(10) |i|
|
|
79
|
+
println(i)
|
|
80
|
+
range(10, 20) |i|
|
|
81
|
+
println(i)
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
fn create_post_action(req: Request): Result[Response] =
|
|
85
|
+
post := Post(title = req.params.title, body = req.params.body)
|
|
86
|
+
if post.valid()
|
|
87
|
+
RenderNewView()
|
|
88
|
+
else
|
|
89
|
+
post := createRecord(post)
|
|
90
|
+
setSuccessMessage("Post created")
|
|
91
|
+
redirectTo("/posts")
|
|
92
|
+
|
|
93
|
+
fn divide(dividend: u32, divisor: u32) !u32 =
|
|
94
|
+
if divisor == 0
|
|
95
|
+
error.DivideByZero
|
|
96
|
+
else
|
|
97
|
+
dividend / divisor
|
|
98
|
+
|
|
99
|
+
error FileOpenError
|
|
100
|
+
| AccessDenied(str)
|
|
101
|
+
| OutOfMemory(str)
|
|
102
|
+
| FileNotFound(str)
|
|
103
|
+
|
|
104
|
+
fn parse_version(header: List[int]): result[Version, error] =
|
|
105
|
+
header.get(0) != nil ? v : error.InvalidHeaderLength(header.get(0))
|
|
106
|
+
|
|
107
|
+
fn main(): result[unit, unit] =
|
|
108
|
+
version := parse_version(list.of(1, 2))
|
|
109
|
+
match pg.connect()
|
|
110
|
+
ok(c) -> return 0
|
|
111
|
+
err(e) -> return e
|
|
112
|
+
|
|
113
|
+
greeting_file := file.open("hello.txt").unwrap_or_else() |error|
|
|
114
|
+
match error
|
|
115
|
+
ErrorKind::NotFound ->
|
|
116
|
+
File::create("hello.txt").unwrap_or_else() |error|
|
|
117
|
+
panic!("Problem creating the file: {:?}", error)
|
|
118
|
+
_ ->
|
|
119
|
+
panic!("Problem opening the file: {:?}", error)
|
|
120
|
+
|
|
121
|
+
conn := match pg.connect()?
|
|
122
|
+
create_post_action(req)
|
|
123
|
+
.map(|v| 0)
|
|
124
|
+
.map_err(|err| 1)
|
|
125
|
+
|
|
126
|
+
create_post_action(req)?
|
|
127
|
+
ok()
|
|
128
|
+
|
|
129
|
+
greeting_file := file.open("hello.txt")?
|
|
130
|
+
ok()
|
|
131
|
+
|
|
132
|
+
import std/str
|
|
133
|
+
import std/result
|
|
134
|
+
|
|
135
|
+
fn double_number(s: str): result[i32, unit] =
|
|
136
|
+
number_str.parse_int().map(|n| 2 * n)
|
|
137
|
+
|
|
138
|
+
fn main(): result<i32, unit> =
|
|
139
|
+
double_number("10")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
use std::num::ParseIntError;
|
|
143
|
+
|
|
144
|
+
fn double_number(number_str: &str) -> Result<i32, ParseIntError> {
|
|
145
|
+
number_str.parse::<i32>().map(|n| 2 * n)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
fn main() {
|
|
149
|
+
match double_number("10") {
|
|
150
|
+
Ok(n) => assert_eq!(n, 20),
|
|
151
|
+
Err(err) => printLn!("Error: {:?}", err),
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
trait error: debug + display (
|
|
156
|
+
fn description(): str
|
|
157
|
+
fn cause(): option<error>
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
trait From<T>(
|
|
161
|
+
fn from(v: T) -> Self;
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
trait Debug(
|
|
165
|
+
fn fmt(f: Formateer): result[unit, unit]
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
trait Display(
|
|
169
|
+
fn fmt(f: Formateer): result[unit, unit]
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
record Car(wheels: int)
|
|
173
|
+
|
|
174
|
+
fn getWheels() =
|
|
175
|
+
returns 4
|
|
176
|
+
|
|
177
|
+
fn main() =
|
|
178
|
+
let c1 = some(Car(wheels: 2))
|
|
179
|
+
let c2: option<Car> = none
|
|
180
|
+
|
|
181
|
+
let c1 = Some(Car(wheels: 2))
|
|
182
|
+
let c2: Option<Car> = None
|
|
183
|
+
|
|
184
|
+
match c
|
|
185
|
+
none -> print("no car")
|
|
186
|
+
some(car) -> car.getWheels()
|
|
187
|
+
|
|
188
|
+
Car c2 = null
|
|
189
|
+
c2.getWheels() // Null pointer
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
(1 + 2).mod(3).pow(2).sqrt()
|
|
193
|
+
|
|
194
|
+
math.sqrt(math.pow(math.mod(1 + 2, 3)))
|
|
195
|
+
|
|
196
|
+
repeat(10) |i|
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
repeat(n) |i|
|
|
200
|
+
pass
|
|
201
|
+
|
|
202
|
+
data.each() |k, v|
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
list.of(1, 2, 3).each() |v, i|
|
|
206
|
+
pass
|
|
207
|
+
|
|
208
|
+
map.of("a" => 1, "b" => 2).each() |k, v|
|
|
209
|
+
pass
|
|
210
|
+
|
|
211
|
+
repeat() |_|
|
|
212
|
+
pass
|
|
213
|
+
|
|
214
|
+
repeat(|_| i < 9) |i|
|
|
215
|
+
pass
|
|
216
|
+
|
|
217
|
+
repeat(|_| a > b && c < d) |i|
|
|
218
|
+
pass
|
std/bool.mi
CHANGED
|
@@ -4,38 +4,9 @@ enum bool =
|
|
|
4
4
|
| true
|
|
5
5
|
| false
|
|
6
6
|
|
|
7
|
+
fn (x: bool) op_eq(y: bool) = x == y
|
|
7
8
|
fn (x: bool) op_ne(y: bool) = x != y
|
|
8
9
|
fn (x: bool) op_and(y: bool) = x == false || y == false ? false : true
|
|
9
10
|
fn (x: bool) op_or(y: bool) = x == true || y == true ? true : false
|
|
10
11
|
fn (x: bool) op_not(): bool = x == true ? false : true
|
|
11
12
|
fn (x: bool) to_str() = x ? "true" : "false"
|
|
12
|
-
|
|
13
|
-
trait Iterator(
|
|
14
|
-
fun has_next(): Bool
|
|
15
|
-
fun next(): T?
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
trait HasEq[A](
|
|
19
|
-
fn eq(that: a): bool
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
trait Equatable[A]
|
|
23
|
-
`A trait that defines equal and not equal operations
|
|
24
|
-
|
|
25
|
-
fn eq(b: Equatable[A]): bool
|
|
26
|
-
fn ne(b: Equatable[A]): bool
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
enum Compare
|
|
30
|
-
| Less
|
|
31
|
-
| Equal
|
|
32
|
-
| Greater
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
trait Comparable[A] is Equatable[A]
|
|
36
|
-
fn lt(that: A): bool
|
|
37
|
-
fn le(that: A): bool
|
|
38
|
-
fn ge(that: A): bool
|
|
39
|
-
fn gt(that: A): bool
|
|
40
|
-
fn compare(that: a): Compare
|
|
41
|
-
end
|
std/coding/base64.mi
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
module base64
|
|
2
|
-
|
|
3
|
-
import std/collections
|
|
4
|
-
import std/testing/assert
|
|
5
|
-
|
|
6
|
-
fun encode_pem(data: ReadSeq[U8]): str =
|
|
7
|
-
`Encode for PEM (RFC 1421)
|
|
8
|
-
encode(data, '+', '/', '=', 64)
|
|
9
|
-
|
|
10
|
-
fun encode_mime(data: ReadSeq[U8]): str =
|
|
11
|
-
`Encode for MIME (RFC 2045)
|
|
12
|
-
encode(data, '+', '/', '=', 76)
|
|
13
|
-
|
|
14
|
-
fun encode_url[A: Seq[U8]](data: ReadSeq[U8], pad: Bool = false): A =
|
|
15
|
-
`Encode for URLs (RFC 4648). Padding characters are stripped by default
|
|
16
|
-
c := pad ? '=' else 0
|
|
17
|
-
encode[A](data, '-', '_', c)
|
|
18
|
-
|
|
19
|
-
fun encode[A: Seq[U8]](data: ReadSeq[U8], at62: U8 = '+', at63: U8 = '/', pad: U8 = '=', linelen: USize = 0, linesep: str = "\r\n"): A =
|
|
20
|
-
`Configurable encoding. The defaults are for RFC 4648.
|
|
21
|
-
len := ((data.size() + 2) / 3) * 4
|
|
22
|
-
out := recover A(len) end
|
|
23
|
-
lineblocks := linelen / 4
|
|
24
|
-
|
|
25
|
-
srclen := data.size()
|
|
26
|
-
blocks := USize(0)
|
|
27
|
-
i := USize(0)
|
|
28
|
-
|
|
29
|
-
try
|
|
30
|
-
while srclen >= 3 do
|
|
31
|
-
let in1 = data(i)?
|
|
32
|
-
let in2 = data(i + 1)?
|
|
33
|
-
let in3 = data(i + 2)?
|
|
34
|
-
|
|
35
|
-
let out1 = in1 >> 2
|
|
36
|
-
let out2 = ((in1 and 0x03) << 4) + (in2 >> 4)
|
|
37
|
-
let out3 = ((in2 and 0x0f) << 2) + (in3 >> 6)
|
|
38
|
-
let out4 = in3 and 0x3f
|
|
39
|
-
|
|
40
|
-
out.push(_enc_byte(out1, at62, at63)?)
|
|
41
|
-
out.push(_enc_byte(out2, at62, at63)?)
|
|
42
|
-
out.push(_enc_byte(out3, at62, at63)?)
|
|
43
|
-
out.push(_enc_byte(out4, at62, at63)?)
|
|
44
|
-
|
|
45
|
-
i = i + 3
|
|
46
|
-
blocks = blocks + 1
|
|
47
|
-
srclen = srclen - 3
|
|
48
|
-
|
|
49
|
-
if (lineblocks > 0) and (blocks == lineblocks) then
|
|
50
|
-
out.append(linesep)
|
|
51
|
-
blocks = 0
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
if srclen >= 1 then
|
|
56
|
-
let in1 = data(i)?
|
|
57
|
-
let in2 = if srclen == 2 then data(i + 1)? else 0 end
|
|
58
|
-
|
|
59
|
-
let out1 = in1 >> 2
|
|
60
|
-
let out2 = ((in1 and 0x03) << 4) + (in2 >> 4)
|
|
61
|
-
let out3 = (in2 and 0x0f) << 2
|
|
62
|
-
|
|
63
|
-
out.push(_enc_byte(out1, at62, at63)?)
|
|
64
|
-
out.push(_enc_byte(out2, at62, at63)?)
|
|
65
|
-
|
|
66
|
-
if srclen == 2 then
|
|
67
|
-
out.push(_enc_byte(out3, at62, at63)?)
|
|
68
|
-
else
|
|
69
|
-
out.push(pad)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
out.push(pad)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
if lineblocks > 0 then
|
|
76
|
-
out.append(linesep)
|
|
77
|
-
end
|
|
78
|
-
else
|
|
79
|
-
out.clear()
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
out
|
|
83
|
-
|
|
84
|
-
fun decode_url[A: Seq[U8] iso = Array[U8] iso](data: ReadSeq[U8]): A^ ? =>
|
|
85
|
-
"""
|
|
86
|
-
Decode for URLs (RFC 4648).
|
|
87
|
-
"""
|
|
88
|
-
decode[A](data, '-', '_')?
|
|
89
|
-
|
|
90
|
-
fun decode[A: Seq[U8] iso = Array[U8] iso](
|
|
91
|
-
data: ReadSeq[U8],
|
|
92
|
-
at62: U8 = '+',
|
|
93
|
-
at63: U8 = '/',
|
|
94
|
-
pad: U8 = '=')
|
|
95
|
-
: A^ ?
|
|
96
|
-
=>
|
|
97
|
-
"""
|
|
98
|
-
Configurable decoding. The defaults are for RFC 4648. Missing padding is
|
|
99
|
-
not an error. Non-base64 data, other than whitespace (which can appear at
|
|
100
|
-
any time), is an error.
|
|
101
|
-
"""
|
|
102
|
-
let len = (data.size() * 4) / 3
|
|
103
|
-
let out = recover A(len) end
|
|
104
|
-
|
|
105
|
-
var state = U8(0)
|
|
106
|
-
var input = U8(0)
|
|
107
|
-
var output = U8(0)
|
|
108
|
-
|
|
109
|
-
for i in Range(0, data.size()) do
|
|
110
|
-
input = data(i)?
|
|
111
|
-
|
|
112
|
-
let value =
|
|
113
|
-
match input
|
|
114
|
-
| ' ' | '\t' | '\r' | '\n' => continue
|
|
115
|
-
| pad => break
|
|
116
|
-
| at62 => 62
|
|
117
|
-
| at63 => 63
|
|
118
|
-
| if (input >= 'A') and (input <= 'Z') =>
|
|
119
|
-
(input - 'A')
|
|
120
|
-
| if (input >= 'a') and (input <= 'z') =>
|
|
121
|
-
((input - 'a') + 26)
|
|
122
|
-
| if (input >= '0') and (input <= '9') =>
|
|
123
|
-
((input - '0') + 52)
|
|
124
|
-
else
|
|
125
|
-
error
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
match state
|
|
129
|
-
| 0 =>
|
|
130
|
-
output = value << 2
|
|
131
|
-
state = 1
|
|
132
|
-
| 1 =>
|
|
133
|
-
out.push(output or (value >> 4))
|
|
134
|
-
output = (value and 0x0f) << 4
|
|
135
|
-
state = 2
|
|
136
|
-
| 2 =>
|
|
137
|
-
out.push(output or (value >> 2))
|
|
138
|
-
output = (value and 0x03) << 6
|
|
139
|
-
state = 3
|
|
140
|
-
| 3 =>
|
|
141
|
-
out.push(output or value)
|
|
142
|
-
state = 0
|
|
143
|
-
else
|
|
144
|
-
error
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
if output != 0 then
|
|
149
|
-
Fact(input != pad)?
|
|
150
|
-
|
|
151
|
-
match state
|
|
152
|
-
| 1 | 2 => out.push(output)
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
out
|
|
157
|
-
|
|
158
|
-
fun _enc_byte(i: U8, at62: U8, at63: U8): U8 ? =>
|
|
159
|
-
"""
|
|
160
|
-
Encode a single byte.
|
|
161
|
-
"""
|
|
162
|
-
match i
|
|
163
|
-
| 62 => at62
|
|
164
|
-
| 63 => at63
|
|
165
|
-
| if i < 26 => 'A' + i
|
|
166
|
-
| if i < 52 => ('a' - 26) + i
|
|
167
|
-
| if i < 62 => ('0' - 52) + i
|
|
168
|
-
else
|
|
169
|
-
error
|
|
170
|
-
end
|
std/html.mi
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module std
|
|
2
|
+
|
|
3
|
+
test("HTML DSL") |_|
|
|
4
|
+
tree := html(class: "123")
|
|
5
|
+
head() |_|
|
|
6
|
+
title("Pacos")
|
|
7
|
+
link(href: "https://pacos.dev")
|
|
8
|
+
body(class: "bg-white") |_|
|
|
9
|
+
div(class: "box") |_|
|
|
10
|
+
text("Hello")
|
|
11
|
+
div() |_|
|
|
12
|
+
text("World")
|
|
13
|
+
assert tree.to_str() == ```
|
|
14
|
+
<html>
|
|
15
|
+
<head>
|
|
16
|
+
<title>Pacos</title>
|
|
17
|
+
<link href="https://pacos.dev" />
|
|
18
|
+
</head>
|
|
19
|
+
<body>
|
|
20
|
+
<div class="box">
|
|
21
|
+
Hello
|
|
22
|
+
</div>
|
|
23
|
+
<div>
|
|
24
|
+
World
|
|
25
|
+
</div>
|
|
26
|
+
</body>
|
|
27
|
+
</html>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
html(class: "123") {
|
|
31
|
+
head() {
|
|
32
|
+
title("name")
|
|
33
|
+
link(href: "123")
|
|
34
|
+
}
|
|
35
|
+
body(class: "class") {
|
|
36
|
+
div(class: "132") {
|
|
37
|
+
text("Hello")
|
|
38
|
+
}
|
|
39
|
+
div() {
|
|
40
|
+
text("World")
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
std/http.mi
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import std/path
|
|
2
2
|
import std/os
|
|
3
|
-
import http/content_type
|
|
3
|
+
import std/http/content_type
|
|
4
|
+
|
|
5
|
+
#[builder]
|
|
6
|
+
record Response(
|
|
7
|
+
headers: map[str, str]
|
|
8
|
+
body: Buffer
|
|
9
|
+
status: int
|
|
10
|
+
)
|
|
4
11
|
|
|
5
12
|
fn file_response(file: str): Response! =
|
|
6
13
|
ext := path::ext(file)
|
|
7
|
-
content :=
|
|
14
|
+
content := contentTypeFromExt(ext)
|
|
8
15
|
data := try os.read_file(file)
|
|
9
|
-
Response(
|
|
10
|
-
status: 200,
|
|
11
|
-
headers: map.of(
|
|
12
|
-
"Content-Type" => content,
|
|
13
|
-
"Content-Length" => "2"
|
|
14
|
-
),
|
|
15
|
-
body: data
|
|
16
|
-
)
|
|
17
|
-
|
|
18
16
|
Response()
|
|
19
17
|
.header("Content-Type", content)
|
|
20
18
|
.header("Content-Length", "2")
|
|
@@ -28,45 +26,9 @@ fn index(): Response! =
|
|
|
28
26
|
#[get("/public/<...>")]
|
|
29
27
|
fn public(file: str): Response! =
|
|
30
28
|
ext := path::ext(file)
|
|
31
|
-
content :=
|
|
29
|
+
content := contentTypeFromExt(ext)
|
|
32
|
-
data :=
|
|
30
|
+
data := os.readFile?(file)
|
|
33
|
-
Response(
|
|
31
|
+
Response()
|
|
32
|
+
.header("Content-Type", content)
|
|
33
|
+
.body(data)
|
|
34
|
-
status
|
|
34
|
+
.status(200)
|
|
35
|
-
headers: [
|
|
36
|
-
:ContentType => content,
|
|
37
|
-
],
|
|
38
|
-
body: data,
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
/// Http similar to rocket
|
|
43
|
-
/// GET,DELETE Request -> fn params are got from query
|
|
44
|
-
/// POST,PUT,PATCH Request -> fn params got from multi part form body
|
|
45
|
-
/// DI at compiler level
|
|
46
|
-
/// Controller similar to IHP
|
|
47
|
-
|
|
48
|
-
#[path("/counters")]
|
|
49
|
-
record CounterController(db: DB)
|
|
50
|
-
static path = "/counters"
|
|
51
|
-
|
|
52
|
-
fn all(): HttpResponse =
|
|
53
|
-
counters := db.query(Counter).fetchAll()
|
|
54
|
-
render(CounterList(counters))
|
|
55
|
-
|
|
56
|
-
fn view() =
|
|
57
|
-
counter := fetchOne(Counter) ?? createOne(Counter)
|
|
58
|
-
if counter := fetchOne(Counter)
|
|
59
|
-
render CounterView(counter)
|
|
60
|
-
else
|
|
61
|
-
counter <- newRecord @Counter |> set #count 0 |> createRecord
|
|
62
|
-
render CounterView(counter)
|
|
63
|
-
|
|
64
|
-
fn increment(id: str) =
|
|
65
|
-
counter <- fetch counterId
|
|
66
|
-
updatedCounter <- counter |> incrementField #count |> updateRecord
|
|
67
|
-
respondHtml $ counterView updatedCounter
|
|
68
|
-
|
|
69
|
-
fn decrement(id: str) =
|
|
70
|
-
counter <- fetch counterId
|
|
71
|
-
updatedCounter <- counter |> decrementField #count |> updateRecord
|
|
72
|
-
respondHtml $ counterView updatedCounter
|
std/list.mi
CHANGED
|
@@ -15,7 +15,7 @@ record node[V](
|
|
|
15
15
|
next: node[V]?
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
-
fn
|
|
18
|
+
fn listOf[A](values: ...A): list[A] =
|
|
19
19
|
list[A]().add(values)
|
|
20
20
|
|
|
21
21
|
fn (l: list) get(i: int) -> A =
|
|
@@ -44,7 +44,7 @@ fn (l: list) add(values: ...V) =
|
|
|
44
44
|
l.head.next.prev = l.head
|
|
45
45
|
l._size += 1
|
|
46
46
|
|
|
47
|
-
fn (l: list)
|
|
47
|
+
fn (l: list) removeAt(i int) =
|
|
48
48
|
`removes the element at i'th index of the list
|
|
49
49
|
l.tail?.prev?.next = nil
|
|
50
50
|
`old tail node gets deallocated due to zero reference count
|
|
@@ -102,7 +102,7 @@ fn (l: list) map[B](cb: fn(v: V): B): list[A] =
|
|
|
102
102
|
nl.push(item)
|
|
103
103
|
nl
|
|
104
104
|
|
|
105
|
-
fn (l: list)
|
|
105
|
+
fn (l: list) flatMap() =
|
|
106
106
|
`returns a new list with all elements shuffled`
|
|
107
107
|
pass
|
|
108
108
|
|
|
@@ -165,7 +165,7 @@ fn (l: list) chunk() =
|
|
|
165
165
|
`returns a new list with all elements shuffled`
|
|
166
166
|
pass
|
|
167
167
|
|
|
168
|
-
fn (l: list)
|
|
168
|
+
fn (l: list) groupBy() =
|
|
169
169
|
`returns a new list with all elements grouped`
|
|
170
170
|
pass
|
|
171
171
|
|
|
@@ -177,32 +177,3 @@ fn (l: list) join(sep: str = ","): str =
|
|
|
177
177
|
else
|
|
178
178
|
res.write(@TypeToString(v), sep)
|
|
179
179
|
res.to_str()
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
enum list<T> =
|
|
184
|
-
| empty
|
|
185
|
-
| link(v: T, rest: list<T>)
|
|
186
|
-
|
|
187
|
-
fn list.of[A](values: ...A): list[A] =
|
|
188
|
-
list[A]().add(values)
|
|
189
|
-
|
|
190
|
-
fn (o list<T>) each(cb: fn(v: T)) =
|
|
191
|
-
match o
|
|
192
|
-
empty -> return
|
|
193
|
-
link(a, rest) ->
|
|
194
|
-
cb(a)
|
|
195
|
-
rest.each(cb)
|
|
196
|
-
|
|
197
|
-
fn (l: list) append(values: ...V) =
|
|
198
|
-
`adds the specified elements to the start of the list
|
|
199
|
-
range(values) |v|
|
|
200
|
-
l.add(v)
|
|
201
|
-
|
|
202
|
-
fn (l: list) append(v: V) =
|
|
203
|
-
`adds the specified elements to the start of the list
|
|
204
|
-
l.last().rest = link(v, empty)
|
|
205
|
-
|
|
206
|
-
fn (l: list) prepend(v: V) =
|
|
207
|
-
`adds the specified elements to the start of the list
|
|
208
|
-
l.first() = link(v, l.first())
|
std/map.mi
CHANGED
|
@@ -29,5 +29,3 @@ fn (m: Map) update() =
|
|
|
29
29
|
|
|
30
30
|
fn (m: Map) update_if() =
|
|
31
31
|
pass
|
|
32
|
-
|
|
33
|
-
data.price?.take_if(|a| a != "")
|
std/os.mi
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
module std
|
|
2
|
+
|
|
3
|
+
fn printLn(): result[unit, unit] =
|
|
4
|
+
`Writes the specified data, followed by the current line terminator, to the standard output stream.
|
|
5
|
+
pass
|
std/ranges.mi
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
fn range(start: int, e: int, cb: fn(int)) =
|
|
2
|
-
match start < end ->
|
|
3
|
-
cb(start)
|
|
4
|
-
range(start + 1, end, cb)
|
|
5
|
-
|
|
6
|
-
fn repeat(n: int, cb: fn(int): void) =
|
|
7
|
-
match n != 0
|
|
8
|
-
cb()
|
|
9
|
-
repeat(n - 1, cb)
|
|
10
|
-
|
|
11
|
-
fn repeat(cb: fn(int): void) =
|
|
12
|
-
cb()
|
|
13
|
-
repeat(cb)
|
|
14
|
-
|
|
15
|
-
fn iterate[T](l: list[T], cb: fn(t: T)) =
|
|
16
|
-
pass
|
|
17
|
-
|
|
18
|
-
fn main() =
|
|
19
|
-
repeat(10) |i|
|
|
20
|
-
println(i)
|
|
21
|
-
range(10, 20) |i|
|
|
22
|
-
println(i)
|
|
23
|
-
|
|
24
|
-
//support unocss
|
|
25
|
-
|
|
26
|
-
html(class: "123") |_|
|
|
27
|
-
head() |_|
|
|
28
|
-
title("name")
|
|
29
|
-
link(href: "123")
|
|
30
|
-
body(class: "bg-white") |_|
|
|
31
|
-
div(class: "132") |_|
|
|
32
|
-
text("Hello")
|
|
33
|
-
div()
|
|
34
|
-
|
|
35
|
-
html(class: "123") {
|
|
36
|
-
head() {
|
|
37
|
-
title("name")
|
|
38
|
-
link(href: "123")
|
|
39
|
-
}
|
|
40
|
-
body(class: "class") {
|
|
41
|
-
div(class: "132") {
|
|
42
|
-
text("Hello")
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
`kotlin
|
|
48
|
-
html("lang": "en") {
|
|
49
|
-
head {
|
|
50
|
-
meta("charset": "UTF-8")
|
|
51
|
-
title {
|
|
52
|
-
text("Kotlin HTML DSL")
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
body {
|
|
56
|
-
h1 {
|
|
57
|
-
text("Hello, World!")
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
`dart
|
|
63
|
-
Html(
|
|
64
|
-
class: "123",
|
|
65
|
-
head: list.of(
|
|
66
|
-
titl("name")
|
|
67
|
-
link(href: "123")
|
|
68
|
-
),
|
|
69
|
-
body: list.of(
|
|
70
|
-
div(class: "class", list.of(
|
|
71
|
-
text("Hello")
|
|
72
|
-
),
|
|
73
|
-
)
|
|
74
|
-
)
|
|
75
|
-
)
|
std/regex.mi
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
TBD
|
std/time.mi
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
record time()
|
|
2
|
+
|
|
3
|
+
record duration()
|
std/uuid.mi
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
type uuid = string
|