~repos /plum
git clone https://pyrossh.dev/repos/plum.git
A statically typed, imperative programming language inspired by rust, python
5de508e7
—
pyrossh 11 months ago
changes
- libs/std/bool.plum +19 -36
- libs/std/{encode/base64.plum → encoding/Base64.plum} +41 -66
- libs/std/err.plum +3 -4
- libs/std/float.plum +24 -24
- libs/std/http.plum +20 -21
- libs/std/json.plum +88 -84
- libs/std/option.plum +2 -7
- libs/std/result.plum +11 -2
- test/add.plum +4 -3
- test/aoc_2020_1.plum +2 -2
- test/aoc_2020_2.plum +2 -3
- test/sample.plum +183 -115
- tooling/tree-sitter-plum/grammar.js +19 -11
- tooling/tree-sitter-plum/src/grammar.json +0 -0
- tooling/tree-sitter-plum/src/node-types.json +0 -0
- tooling/tree-sitter-plum/src/parser.c +0 -0
- tooling/tree-sitter-plum/test/corpus/assert.txt +1 -1
- tooling/tree-sitter-plum/test/corpus/enum.txt +5 -4
- tooling/tree-sitter-plum/test/corpus/if.txt +1 -1
- tooling/tree-sitter-plum/test/corpus/match.txt +1 -52
- tooling/tree-sitter-plum/test/corpus/type.txt +28 -49
- tooling/vscode-plum/syntaxes/plum.tmLanguage.json +42 -2
libs/std/bool.plum
CHANGED
|
@@ -1,57 +1,40 @@
|
|
|
1
1
|
module std
|
|
2
2
|
|
|
3
|
-
type Int
|
|
3
|
+
type Int
|
|
4
|
-
add(b: Int) -> Int = this + b
|
|
5
|
-
sub(b: Int) -> Int = this - b
|
|
6
4
|
|
|
5
|
+
fn (i: Int) add(b: Int) -> Int =
|
|
6
|
+
return this + b
|
|
7
|
+
|
|
8
|
+
fn (i: Int) sub(b: Int) -> Int =
|
|
9
|
+
return this - b
|
|
7
10
|
|
|
8
11
|
enum Bool =
|
|
9
12
|
| True
|
|
10
13
|
| False
|
|
11
14
|
|
|
15
|
+
fromStr(s: Str) -> Result[Bool, Err] =>
|
|
16
|
+
if s == "true"
|
|
17
|
+
True
|
|
18
|
+
else if s == "false"
|
|
19
|
+
False
|
|
20
|
+
else
|
|
21
|
+
Err("could not parse bool from '{s}'")
|
|
22
|
+
|
|
12
|
-
and(
|
|
23
|
+
and(self, o: Bool) -> Bool =>
|
|
13
|
-
match this,
|
|
24
|
+
match this, o
|
|
14
25
|
True, True => True
|
|
15
26
|
True, False => False
|
|
16
27
|
False, True => False
|
|
17
28
|
False, False => False
|
|
18
29
|
|
|
19
|
-
or(
|
|
30
|
+
or(self, o: Bool) -> Bool =>
|
|
20
|
-
match this,
|
|
31
|
+
match this, o
|
|
21
32
|
True, True => True
|
|
22
33
|
True, False => True
|
|
23
34
|
False, True => True
|
|
24
35
|
False, False => False
|
|
25
36
|
|
|
26
|
-
toStr() -> Str =
|
|
37
|
+
toStr(self) -> Str =>
|
|
27
38
|
match this
|
|
28
39
|
True => "True"
|
|
29
40
|
False => "False"
|
|
30
|
-
|
|
31
|
-
fromStr(s: Str) -> Result(Bool, Err) =
|
|
32
|
-
if s == "true"
|
|
33
|
-
True
|
|
34
|
-
else if s == "false"
|
|
35
|
-
False
|
|
36
|
-
else
|
|
37
|
-
Err("could not parse bool from '{s}'")
|
|
38
|
-
|
|
39
|
-
readFile() -> Result(String, Err) =
|
|
40
|
-
return Err
|
|
41
|
-
|
|
42
|
-
data = try readFile()
|
|
43
|
-
|
|
44
|
-
if Ok(d) = readFile()
|
|
45
|
-
println(d)
|
|
46
|
-
|
|
47
|
-
match readFile()
|
|
48
|
-
Ok(d) =>
|
|
49
|
-
printLn(d)
|
|
50
|
-
Err(e) =>
|
|
51
|
-
printErr(e)
|
|
52
|
-
|
|
53
|
-
enum List =
|
|
54
|
-
| Empty
|
|
55
|
-
| Node(a)
|
|
56
|
-
|
|
57
|
-
add() =
|
libs/std/{encode/base64.plum → encoding/Base64.plum}
RENAMED
|
@@ -1,39 +1,20 @@
|
|
|
1
|
-
```
|
|
2
|
-
# Base64 package
|
|
3
|
-
|
|
4
|
-
The Base64 package contains support for doing Base64 binary-to-text encodings.
|
|
1
|
+
`The Base64 package contains support for doing Base64 binary-to-text encodings.
|
|
5
|
-
We currently have support 3 encodings: PEM, MIME and URL.
|
|
6
|
-
|
|
7
|
-
To learn more about Base64, we suggest you check out the
|
|
8
|
-
[wikipedia entry](https://en.wikipedia.org/wiki/Base64).
|
|
9
|
-
|
|
10
|
-
## Example code
|
|
11
|
-
|
|
12
|
-
use "encode/base64"
|
|
13
|
-
|
|
14
|
-
actor Main
|
|
15
|
-
new create(env: Env) =>
|
|
16
|
-
env.out.print(Base64.encode("foobar"))
|
|
17
|
-
try
|
|
18
|
-
env.out.print(Base64.decode[String iso]("Zm9vYmFy")?)
|
|
19
|
-
end
|
|
20
|
-
```
|
|
21
|
-
module
|
|
2
|
+
module Base64
|
|
22
|
-
|
|
3
|
+
|
|
23
|
-
|
|
4
|
+
`Encode for PEM (RFC 1421).
|
|
24
5
|
encodePEM(data: ReadSeq[U8]) -> String =
|
|
25
6
|
encode(data, '+', '/', '=', 64)
|
|
26
7
|
|
|
27
|
-
|
|
8
|
+
`Encode for MIME (RFC 2045).
|
|
28
9
|
encodeMIME(data: ReadSeq[U8]) -> String =
|
|
29
10
|
encode(data, '+', '/', '=', 76)
|
|
30
11
|
|
|
31
|
-
|
|
12
|
+
`Encode for URLs (RFC 4648). Padding characters are stripped by default.
|
|
32
13
|
encodeURL(data: ReadSeq[U8], pad: Bool = False) -> String =
|
|
33
14
|
let c: U8 = if pad then '=' else 0 end
|
|
34
15
|
encode[A](data, '-', '_', c)
|
|
35
16
|
|
|
36
|
-
|
|
17
|
+
`Configurable encoding. The defaults are for RFC 4648.
|
|
37
18
|
encode(
|
|
38
19
|
data: ReadSeq[U8],
|
|
39
20
|
at62: U8 = '+',
|
|
@@ -96,32 +77,21 @@ encode(
|
|
|
96
77
|
out.clear()
|
|
97
78
|
out
|
|
98
79
|
|
|
99
|
-
fun decode_url[A: Seq[U8] iso = Array[U8] iso](data: ReadSeq[U8]): A^ ? =>
|
|
100
|
-
"""
|
|
101
|
-
|
|
80
|
+
`Decode for URLs (RFC 4648).
|
|
102
|
-
|
|
81
|
+
decode_url(data: ReadSeq[U8]) -> Option[A] =
|
|
103
82
|
decode[A](data, '-', '_')?
|
|
104
83
|
|
|
105
|
-
fun decode[A: Seq[U8] iso = Array[U8] iso](
|
|
106
|
-
data: ReadSeq[U8],
|
|
107
|
-
at62: U8 = '+',
|
|
108
|
-
at63: U8 = '/',
|
|
109
|
-
pad: U8 = '=')
|
|
110
|
-
: A^ ?
|
|
111
|
-
=>
|
|
112
|
-
"""
|
|
113
|
-
|
|
84
|
+
`Configurable decoding. The defaults are for RFC 4648. Missing padding is
|
|
114
|
-
|
|
85
|
+
`not an error. Non-base64 data, other than whitespace (which can appear at
|
|
115
|
-
|
|
86
|
+
`any time), is an error.
|
|
116
|
-
|
|
87
|
+
decode(data: ReadSeq[U8], at62: U8 = '+', at63: U8 = '/', pad: U8 = '=') -> Option[A] =
|
|
117
|
-
|
|
88
|
+
len = (data.size() * 4) / 3
|
|
118
|
-
|
|
89
|
+
out = recover A(len) end
|
|
119
|
-
|
|
120
|
-
|
|
90
|
+
state = U8(0)
|
|
121
|
-
|
|
91
|
+
input = U8(0)
|
|
122
|
-
|
|
92
|
+
output = U8(0)
|
|
123
|
-
|
|
93
|
+
|
|
124
|
-
for i in Range(0, data.size())
|
|
94
|
+
for i in Range(0, data.size())
|
|
125
95
|
input = data(i)?
|
|
126
96
|
|
|
127
97
|
let value =
|
|
@@ -170,23 +140,12 @@ encode(
|
|
|
170
140
|
|
|
171
141
|
out
|
|
172
142
|
|
|
173
|
-
# Encode a single byte
|
|
174
|
-
encodeByte(i: Byte, at62: Byte, at63: Byte) -> Byte =
|
|
175
|
-
match
|
|
176
|
-
i == 62 => at62
|
|
177
|
-
i == 63 => at63
|
|
178
|
-
i < 26 => 'A' + i
|
|
179
|
-
i < 52 => ('a' - 26) + i
|
|
180
|
-
i < 62 => ('0' - 52) + i
|
|
181
|
-
|
|
182
|
-
# Test base64 encoding.
|
|
183
|
-
|
|
143
|
+
test "encode and decode" =>
|
|
184
|
-
encodeDecodeTest() =
|
|
185
|
-
|
|
144
|
+
expect:
|
|
186
145
|
encode(a) == b
|
|
187
146
|
decode(b) == a
|
|
188
147
|
where:
|
|
189
|
-
a | b
|
|
148
|
+
a | b
|
|
190
149
|
"" | ""
|
|
191
150
|
"f" | "Zg=="
|
|
192
151
|
"fo" | "Zm8="
|
|
@@ -195,5 +154,21 @@ encodeDecodeTest() =
|
|
|
195
154
|
"fooba" | "Zm9vYmE="
|
|
196
155
|
"foobar" | "Zm9vYmFy"
|
|
197
156
|
|
|
198
|
-
|
|
157
|
+
encodeByte(i: Byte, at62: Byte, at63: Byte) -> Byte =
|
|
199
|
-
|
|
158
|
+
doc: "encode a single byte"
|
|
159
|
+
match i
|
|
160
|
+
i == 62 => at62
|
|
161
|
+
i == 63 => at63
|
|
162
|
+
i < 26 => 'A' + i
|
|
163
|
+
i < 52 => ('a' - 26) + i
|
|
164
|
+
i < 62 => ('0' - 52) + i
|
|
165
|
+
check:
|
|
166
|
+
expect:
|
|
167
|
+
encodeByte(i, at62, at63) == b
|
|
168
|
+
where:
|
|
169
|
+
i | at62 | at63
|
|
170
|
+
1 | 0 | 0
|
|
171
|
+
2 | 0 | 0
|
|
172
|
+
3 | 0 | 0
|
|
173
|
+
62 | 0 | 0
|
|
174
|
+
63 | 0 | 0
|
libs/std/err.plum
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
module std
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`This is used to represent an error value across the language
|
|
4
|
-
Err =
|
|
4
|
+
trait Err =
|
|
5
5
|
code() -> Int
|
|
6
6
|
msg() -> Str
|
|
7
|
-
cause() -> Err | Nil
|
|
7
|
+
cause() -> Err | Nil
|
|
8
|
-
)
|
libs/std/float.plum
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
module std
|
|
2
2
|
|
|
3
3
|
type Float =
|
|
4
|
-
E = 2.718f
|
|
4
|
+
E = 2.718f `Euler's number, the base of natural logarithms, e, https://oeis.org/A001113
|
|
5
|
-
LN10 = 2.302f
|
|
5
|
+
LN10 = 2.302f `The natural logarithm of 10, https://oeis.org/A002392
|
|
6
|
-
LN2 = 0.693f
|
|
6
|
+
LN2 = 0.693f `The natural logarithm of 2 https://oeis.org/A002162
|
|
7
|
-
LOG10E = 0.434f
|
|
7
|
+
LOG10E = 0.434f `The base 10 logarithm of e `formula: 1 / LN10
|
|
8
|
-
LOG2E = 1.442f
|
|
8
|
+
LOG2E = 1.442f `The base 2 logarithm of e `formula: 1 / LN2
|
|
9
|
-
PI = 3.14159f
|
|
9
|
+
PI = 3.14159f `The ratio of the circumference of a circle to its diameter https://oeis.org/A000796
|
|
10
|
-
PHI = 1.6180f
|
|
10
|
+
PHI = 1.6180f `https://oeis.org/A001622
|
|
11
|
-
SQRT1_2 = 0.707f
|
|
11
|
+
SQRT1_2 = 0.707f `The square root of 1/2
|
|
12
|
-
SQRT2 = 1.414f
|
|
12
|
+
SQRT2 = 1.414f `The square root of 2 https://oeis.org/A002193
|
|
13
|
-
SQRT_E = 1.64872f
|
|
13
|
+
SQRT_E = 1.64872f `https://oeis.org/A019774
|
|
14
|
-
SQRT_PI = 1.77245f
|
|
14
|
+
SQRT_PI = 1.77245f `https://oeis.org/A002161
|
|
15
|
-
SQRT_PHI = 1.27201f
|
|
15
|
+
SQRT_PHI = 1.27201f `https://oeis.org/A139339
|
|
16
|
-
EPSILON = 2.220446049250313e-16f
|
|
16
|
+
EPSILON = 2.220446049250313e-16f `The difference between 1 and the smallest floating point number greater than 1 formula: 7/3 - 4/3 - 1
|
|
17
|
-
MIN_FLOAT_VALUE = 4.9406564584124654417656879286822137236505980e-324
|
|
17
|
+
MIN_FLOAT_VALUE = 4.9406564584124654417656879286822137236505980e-324 `Lowest value of float
|
|
18
|
-
MAX_FLOAT_VALUE = 1.79769313486231570814527423731704356798070e+308
|
|
18
|
+
MAX_FLOAT_VALUE = 1.79769313486231570814527423731704356798070e+308 `Highest value of float
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
|
|
20
|
+
|
|
21
|
-
|
|
21
|
+
static fromStr(s: Str) -> Result(Float, Err) =
|
|
22
22
|
todo
|
|
23
23
|
|
|
24
24
|
acosh(v: Float) -> Float =
|
|
@@ -31,19 +31,19 @@ type Float =
|
|
|
31
31
|
t =
|
|
32
32
|
((2*t+t*t).sqrt() + t).log1p() 2 >= x > 1
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
`Check whether this number is finite, ie not +/-infinity and not NaN.
|
|
35
|
-
|
|
35
|
+
`True if exponent is not all 1s
|
|
36
36
|
isFinite() -> Bool =
|
|
37
37
|
(bits() && 0x7FF0_0000_0000_0000) != 0x7FF0_0000_0000_0000
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
`Check whether this number is +/-infinity
|
|
40
|
-
|
|
40
|
+
`True if exponent is all 1s and mantissa is 0
|
|
41
41
|
isInfinite() -> Bool =
|
|
42
42
|
((bits() && 0x7FF0_0000_0000_0000) == 0x7FF0_0000_0000_0000) && # exp
|
|
43
43
|
((bits() && 0x000F_FFFF_FFFF_FFFF) == 0) # mantissa
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
`Check whether this number is NaN.
|
|
46
|
-
|
|
46
|
+
`True if exponent is all 1s and mantissa is non-0
|
|
47
47
|
isNaN() -> Bool =
|
|
48
48
|
((bits() && 0x7FF0_0000_0000_0000) == 0x7FF0_0000_0000_0000) && # exp
|
|
49
49
|
((bits() && 0x000F_FFFF_FFFF_FFFF) != 0) # mantissa
|
libs/std/http.plum
CHANGED
|
@@ -4,29 +4,28 @@ import std/path
|
|
|
4
4
|
import std/os
|
|
5
5
|
import std/http/content_type
|
|
6
6
|
|
|
7
|
-
Response =
|
|
7
|
+
type Response =
|
|
8
|
-
headers: Map
|
|
8
|
+
headers: Map[Str, Str]
|
|
9
9
|
body: Str | Buffer | IO
|
|
10
10
|
status: Int
|
|
11
|
-
)
|
|
12
11
|
|
|
13
|
-
createFileResponse(path: Str) -> Response | IOError =
|
|
12
|
+
createFileResponse(path: Str) -> Response | IOError =
|
|
14
|
-
|
|
13
|
+
content = content_type.fromExt(path.ext())
|
|
15
|
-
|
|
14
|
+
data = os.readFile(file)?
|
|
16
|
-
|
|
15
|
+
Response::create()
|
|
17
|
-
|
|
16
|
+
.header("Content-Type", content)
|
|
18
|
-
|
|
17
|
+
.contentType(.ApplicationJS)
|
|
19
|
-
|
|
18
|
+
.body(data)
|
|
20
|
-
|
|
19
|
+
.status(200)
|
|
21
20
|
|
|
22
|
-
index() -> Response | IOError =
|
|
21
|
+
index() -> Response | IOError =
|
|
23
|
-
|
|
22
|
+
createFileResponse("index.html")
|
|
24
23
|
|
|
25
|
-
serveFile(file: Str) -> Response | IOError =
|
|
24
|
+
serveFile(file: Str) -> Response | IOError =
|
|
26
|
-
|
|
25
|
+
ext := path::ext(file)
|
|
27
|
-
|
|
26
|
+
content := contentTypeFromExt(ext)
|
|
28
|
-
|
|
27
|
+
data := os.readFile(file)?
|
|
29
|
-
|
|
28
|
+
Response()
|
|
30
|
-
|
|
29
|
+
.header("Content-Type", content)
|
|
31
|
-
|
|
30
|
+
.body(data)
|
|
32
|
-
|
|
31
|
+
.status(200)
|
libs/std/json.plum
CHANGED
|
@@ -1,64 +1,97 @@
|
|
|
1
1
|
module std/json
|
|
2
2
|
|
|
3
|
+
import std.{Option, Bool, Float, Str, List, Map, Result}
|
|
4
|
+
|
|
3
|
-
Json =
|
|
5
|
+
enum Json =
|
|
4
6
|
| None
|
|
5
|
-
| Str
|
|
6
7
|
| Bool
|
|
7
8
|
| Float
|
|
8
|
-
|
|
|
9
|
+
| Str
|
|
9
10
|
| List
|
|
10
11
|
| Map
|
|
11
12
|
|
|
13
|
+
^parse(src: Readable) -> Result(Json, JsonParseError) =
|
|
14
|
+
JsonParser.withSrc(src).parse()
|
|
15
|
+
|
|
12
|
-
JsonParseError =
|
|
16
|
+
class JsonParseError(Error) =
|
|
13
17
|
line: Int
|
|
14
18
|
pos: Int
|
|
15
19
|
token: Char
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
Json::parse(src: Str | Buffer | IO) -> Json | JsonParseError =
|
|
19
|
-
JsonParser.withSrc(src).parse()
|
|
20
|
-
|
|
21
|
-
Json\parseNone() -> None | JsonParseError =
|
|
22
|
-
parser.find("null")
|
|
23
|
-
|
|
24
|
-
Json\parseStr() -> Str | JsonParseError =
|
|
25
|
-
parser.find("null")
|
|
26
|
-
|
|
27
|
-
Json\parseFloat() -> Float | JsonParseError =
|
|
28
|
-
parser.find("null")
|
|
29
|
-
|
|
30
|
-
Json\parseInt() -> Int | JsonParseError =
|
|
31
|
-
parser.find("null")
|
|
32
|
-
|
|
33
|
-
Json\parseList() -> List | JsonParseError =
|
|
34
|
-
parser.find("null")
|
|
35
|
-
|
|
36
|
-
Json\parseMap() -> Map | JsonParseError =
|
|
37
|
-
parser.find("null")
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
JsonParser = (
|
|
41
|
-
cur: Int = ' '
|
|
42
|
-
_pos: Int = 0
|
|
43
|
-
_next: Int = ' '
|
|
44
|
-
_buf: Str | Buffer | IO
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
JsonParser::withSrc(src: Str | Buffer | IO) -> JsonParser =
|
|
48
|
-
JsonParser(_buf = src)
|
|
49
|
-
|
|
50
|
-
fn isSpace(c: U32): Bool => (c == ' ') or ((c >= '\t') and (c <= '\r'))
|
|
51
|
-
|
|
52
|
-
fn isDelim(c: U32): Bool =>
|
|
53
|
-
(c == ',') || (c == '}') || (c == ':') || (c == ']') || (_is_space(c)) || (c == 0)
|
|
54
20
|
|
|
21
|
+
data JsonParser =
|
|
22
|
+
cur: Int = ""
|
|
23
|
+
pos: Int = 0
|
|
24
|
+
next: Int = ""
|
|
25
|
+
src: Readable
|
|
26
|
+
|
|
27
|
+
^withSrc(src: Readable) =
|
|
28
|
+
JsonParser::create()
|
|
29
|
+
.src(src)
|
|
30
|
+
|
|
31
|
+
parse() -> Result(Json, JsonParseError) =
|
|
32
|
+
match getNext()
|
|
33
|
+
'{' -> parseObject()
|
|
34
|
+
'[' -> parseList()
|
|
35
|
+
`"` -> parseStr()
|
|
36
|
+
'n' -> parseNone()
|
|
37
|
+
`t` | `f` -> parseBool()
|
|
38
|
+
0..9 -> parseFloat()
|
|
39
|
+
_ -> Err(createErr())
|
|
40
|
+
|
|
41
|
+
createErr() =
|
|
42
|
+
JsonParseError::create()
|
|
43
|
+
.line(0)
|
|
44
|
+
.pos(pos)
|
|
45
|
+
.token(cur)
|
|
46
|
+
|
|
47
|
+
parseNone() -> Result[None, JsonParseError] =
|
|
48
|
+
find("null")
|
|
49
|
+
check:
|
|
50
|
+
assert Json.parse("null") == None
|
|
51
|
+
|
|
52
|
+
parseBool() -> Result[Bool, JsonParseError] =
|
|
53
|
+
if find("true")
|
|
54
|
+
True
|
|
55
|
+
else if find("false")
|
|
56
|
+
False
|
|
57
|
+
check:
|
|
58
|
+
assert Json.parse("true") == True
|
|
59
|
+
assert Json.parse("false") == False
|
|
60
|
+
|
|
61
|
+
parseStr() -> Result[Str, JsonParseError] =
|
|
62
|
+
find("null")
|
|
63
|
+
check:
|
|
64
|
+
assert Json.parse(`"hello"`) == "hello"
|
|
65
|
+
|
|
66
|
+
parseFloat() -> Result[Float, JsonParseError] =
|
|
67
|
+
find("null")
|
|
68
|
+
|
|
69
|
+
parseInt() -> Result[Int, JsonParseError] =
|
|
70
|
+
find("Int")
|
|
71
|
+
check:
|
|
72
|
+
assert Json.parse("123") == 123
|
|
73
|
+
|
|
74
|
+
parseList() -> Result[List, JsonParseError] =
|
|
75
|
+
find("null")
|
|
76
|
+
check:
|
|
77
|
+
assert Json.parse("[]") == List[Json]()
|
|
78
|
+
assert Json.parse("[1, 2]") == List[Json](1, 2)
|
|
79
|
+
|
|
80
|
+
parseMap() -> Result[Map, JsonParseError] =
|
|
81
|
+
find("null")
|
|
82
|
+
check:
|
|
83
|
+
assert Json.parse("{}") == Map[Str, Json]()
|
|
84
|
+
assert Json.parse(`{"a": 1, "b": 2}`) == Map[Str, Json]("a" => 1, "b" => 2)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
isSpace(c: Int) -> Bool =
|
|
88
|
+
{c == ' '} || {{c >= '\t'} && {c <= '\r'}}
|
|
89
|
+
|
|
90
|
+
isDelim(c: Int) -> Bool =
|
|
91
|
+
(c == ',') || (c == '}') || (c == ':') || (c == ']') || (_is_space(c)) || (c == 0)
|
|
92
|
+
|
|
55
|
-
|
|
93
|
+
isDigit(c: Int) -> Bool =
|
|
56
|
-
|
|
94
|
+
(c >= '0') && (c <= '9')
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
fn _is_digit(c: U32): Bool => (c >= '0') and (c <= '9')
|
|
60
|
-
|
|
61
|
-
fn (p: Parser) _err(msg: str) => printLn(msg)
|
|
62
95
|
|
|
63
96
|
fn _get_next_raw(): U32 =>
|
|
64
97
|
try
|
|
@@ -94,16 +127,10 @@ fn _expect(chr: U32, msg: String): Bool =>
|
|
|
94
127
|
|
|
95
128
|
fn _inc_pos(i: ISize = 1) => _pos = _pos.add(i)
|
|
96
129
|
|
|
97
|
-
fn (p JsonParser) parse(): Json =
|
|
98
|
-
match parser.get_next()
|
|
99
|
-
'{' -> p.parseObject()
|
|
100
|
-
'[' -> p.parseList()
|
|
101
|
-
_ -> nil
|
|
102
|
-
|
|
103
130
|
fn (p JsonParser) parseObject(): Map[str, Json] =
|
|
104
131
|
object := Map[str, Json]()
|
|
105
|
-
p.
|
|
132
|
+
p.getNext() # '{'
|
|
106
|
-
while
|
|
133
|
+
while True
|
|
107
134
|
if p.cur != '"'
|
|
108
135
|
p._err("JSON: Object: expected '\"'\n")
|
|
109
136
|
break
|
|
@@ -113,7 +140,7 @@ fn (p JsonParser) parseObject(): Map[str, Json] =
|
|
|
113
140
|
break
|
|
114
141
|
|
|
115
142
|
val_val := Json(nil)
|
|
116
|
-
|
|
143
|
+
match p.cur
|
|
117
144
|
'{' ->
|
|
118
145
|
val_val = parse_object(p)
|
|
119
146
|
p._expect('}', "JSON: Object: expected '}'\n")
|
|
@@ -175,9 +202,9 @@ fn (p: Parser) parseUtf16(): int =
|
|
|
175
202
|
p._get_next_raw()
|
|
176
203
|
if _is_digit(cur)
|
|
177
204
|
result = ((result << 4) or (cur - '0').i32())
|
|
178
|
-
else if
|
|
205
|
+
else if {cur >= 'a'} && {cur <= 'f'}
|
|
179
206
|
result = ((result << 4) or ((cur - 'a').i32() + 10))
|
|
180
|
-
else if (cur >= 'A')
|
|
207
|
+
else if (cur >= 'A') && (cur <= 'F')
|
|
181
208
|
result = ((result << 4) or ((cur - 'A').i32() + 10))
|
|
182
209
|
count = count+1
|
|
183
210
|
result
|
|
@@ -345,27 +372,4 @@ fn _get_fals(): bool? =
|
|
|
345
372
|
false
|
|
346
373
|
else
|
|
347
374
|
error
|
|
348
|
-
end
|
|
375
|
+
end
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
Json\test("parseInt") =
|
|
353
|
-
assert Json.parse("123") == 123
|
|
354
|
-
|
|
355
|
-
Json\test("parseStr") =
|
|
356
|
-
assert Json.parse(`"hello"`) == "hello"
|
|
357
|
-
|
|
358
|
-
Json\test("parseBool") =
|
|
359
|
-
assert Json.parse("true") == true
|
|
360
|
-
assert Json.parse("false") == false
|
|
361
|
-
|
|
362
|
-
Json\test("parse null") =
|
|
363
|
-
assert Json.parse("null") == nil
|
|
364
|
-
|
|
365
|
-
Json\test("parse array") =
|
|
366
|
-
assert Json.parse(`[]`) == List[Json]()
|
|
367
|
-
assert Json.parse(`[1, 2]`) == List.of[Json](1, 2)
|
|
368
|
-
|
|
369
|
-
Json\test("parse object") =
|
|
370
|
-
assert Json.parse(`{}`) == Map[str, Json]()
|
|
371
|
-
assert Json.parse(`{"a": 1, "b": 2}`) == Map.of[str, Json]("a" => 1, "b" => 2)
|
libs/std/option.plum
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
module std
|
|
2
2
|
|
|
3
|
-
import std.{
|
|
3
|
+
import std.{Int, Str, Result, Option}
|
|
4
|
-
Int,
|
|
5
|
-
Str,
|
|
6
|
-
Result,
|
|
7
|
-
Option
|
|
8
|
-
}
|
|
9
4
|
|
|
10
|
-
|
|
5
|
+
enum Option =
|
|
11
6
|
| Some(a)
|
|
12
7
|
| None
|
libs/std/result.plum
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
module std
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
enum Result =
|
|
4
4
|
| Ok(a)
|
|
5
5
|
| Err(b)
|
|
6
|
+
|
|
6
|
-
|
|
7
|
+
isOk() -> Bool =
|
|
8
|
+
doc: "Checks whether the result is an `Ok` value"
|
|
9
|
+
if Ok(_) == this
|
|
10
|
+
True
|
|
11
|
+
else if Err(_) = this
|
|
12
|
+
False
|
|
13
|
+
check:
|
|
14
|
+
Ok("data").isOk() == True
|
|
15
|
+
Err("no data").isOk() == False
|
test/add.plum
CHANGED
|
@@ -5,7 +5,7 @@ import fs
|
|
|
5
5
|
add(a: Int, b: Int) -> Int =
|
|
6
6
|
a + b
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
give42() -> Int =
|
|
9
9
|
42
|
|
10
10
|
|
|
11
11
|
main() -> Int =
|
|
@@ -61,8 +61,9 @@ factorial(num: Int) -> Int =
|
|
|
61
61
|
main() -> Bool
|
|
62
62
|
5.squared() == 25
|
|
63
63
|
|
|
64
|
+
extend Int
|
|
64
|
-
|
|
65
|
+
squared() -> Int =
|
|
65
|
-
|
|
66
|
+
this * this
|
|
66
67
|
|
|
67
68
|
main() -> Unit =
|
|
68
69
|
input = fs::readFile("./examples/test/demos/aoc2023/1.txt")
|
test/aoc_2020_1.plum
CHANGED
|
@@ -11,8 +11,8 @@ main() -> Unit =
|
|
|
11
11
|
printLn(a * b)
|
|
12
12
|
return
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
parseNumbers(input: Str) -> List<Int> =
|
|
15
|
-
numbers =
|
|
15
|
+
numbers = List<Int>()
|
|
16
16
|
current_number = 0
|
|
17
17
|
for i in 0..input.len()
|
|
18
18
|
c = input.charAt(i)
|
test/aoc_2020_2.plum
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
Step =
|
|
1
|
+
enum Step =
|
|
2
2
|
| READ_MIN_OCCURANCES = 0
|
|
3
3
|
| READ_MAX_OCCURANCES = 1
|
|
4
4
|
| READ_CHAR_TO_COUNT = 2
|
|
5
5
|
| COUNT_OCCURANCES = 3
|
|
6
6
|
|
|
7
|
-
PasswordCheckState =
|
|
7
|
+
class PasswordCheckState =
|
|
8
8
|
step: Step
|
|
9
9
|
min_occurances: Int
|
|
10
10
|
max_occurances: Int
|
|
11
11
|
current_occurances: Int
|
|
12
12
|
char_to_counst: Int
|
|
13
|
-
}
|
|
14
13
|
|
|
15
14
|
initialCheckState() -> PasswordCheckState =
|
|
16
15
|
PasswordCheckState(
|
test/sample.plum
CHANGED
|
@@ -1,126 +1,225 @@
|
|
|
1
1
|
module sample
|
|
2
2
|
|
|
3
|
-
import std.
|
|
3
|
+
import std.(List, Map, Math)
|
|
4
|
+
import std/encoding.(Base64)
|
|
4
5
|
|
|
5
6
|
Int.random()
|
|
6
7
|
Float.random()
|
|
7
8
|
Float.PI
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
type Int =
|
|
11
|
+
random() -> Int = 20
|
|
12
|
+
add(self, other: Int) -> Int = self + other
|
|
13
|
+
sub(self, other: Int) -> Int = self - other
|
|
14
|
+
|
|
15
|
+
type User(ToStr) =
|
|
10
16
|
name: Str
|
|
11
|
-
|
|
17
|
+
age: Int
|
|
12
18
|
todos: List(Todo)
|
|
13
|
-
}
|
|
14
19
|
|
|
20
|
+
fromJson(s: Str) -> Result(Self, Err) =
|
|
21
|
+
v = try Json.parse(s) as Map(Str, Json)
|
|
22
|
+
User::create()
|
|
23
|
+
.name(v.get("name").asStr())
|
|
24
|
+
.email(v.get("email").asStr())
|
|
25
|
+
.todos(v.get("todos").asList().map(\t ->
|
|
26
|
+
Todo::create().title(t.get("title").asStr()))
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
queryAll(self, noise: Int) -> Str =
|
|
30
|
+
printLn(noise)
|
|
15
|
-
|
|
31
|
+
""
|
|
32
|
+
|
|
33
|
+
findOne(self) -> Self =
|
|
34
|
+
printLn(noise)
|
|
35
|
+
|
|
36
|
+
toStr(self) -> Str =
|
|
37
|
+
"User({u.name}, {u.age})"
|
|
38
|
+
|
|
39
|
+
type Cat(ToStr) =
|
|
16
|
-
|
|
40
|
+
name: Str
|
|
17
|
-
|
|
41
|
+
age: Int
|
|
42
|
+
|
|
43
|
+
fromJson(s: Str) -> Result(Self, Err) =
|
|
44
|
+
v = try Json.parse(s) as Map(Str, Json)
|
|
45
|
+
Cat::create()
|
|
46
|
+
.name(v.get("name").asStr())
|
|
47
|
+
.age(v.get("age").asInt())
|
|
48
|
+
|
|
49
|
+
withName(self, name: Str) -> Cat =
|
|
50
|
+
Cat(name = name, age = self.age)
|
|
51
|
+
|
|
52
|
+
withAge(self, age: Int) -> Cat =
|
|
53
|
+
Cat(name = self.name, age = age)
|
|
18
54
|
|
|
55
|
+
toStr(self) -> Str =
|
|
56
|
+
"Cat(1, 2)"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
2.sqrt().sin().cos()
|
|
60
|
+
|
|
61
|
+
# Less indentation
|
|
62
|
+
|
|
19
|
-
User()
|
|
63
|
+
u = User::create()
|
|
20
64
|
.name("John Doe")
|
|
21
65
|
.email("john@example.com")
|
|
22
|
-
.
|
|
66
|
+
.todos(Todo::create().title("Make pizza"))
|
|
23
|
-
.
|
|
67
|
+
.todos(Todo::create().title("Finish Toasty"))
|
|
24
|
-
.
|
|
68
|
+
.todos(Todo::create().title("Sleep"))
|
|
69
|
+
|
|
25
|
-
|
|
70
|
+
# User(
|
|
71
|
+
# name: "John Doe",
|
|
72
|
+
# email: "john@example.com",
|
|
73
|
+
# todos: List(
|
|
74
|
+
# Todo(title: "Make pizza"),
|
|
75
|
+
# Todo(title: "Finish Toasty"),
|
|
76
|
+
# Todo(title: "Sleep"),
|
|
77
|
+
# )
|
|
78
|
+
# )
|
|
79
|
+
|
|
80
|
+
u2 = try User.fromJson(`{
|
|
81
|
+
"name": "John Doe",
|
|
82
|
+
"email": "john@example.com",
|
|
83
|
+
"todos": [
|
|
84
|
+
{"title": "Make pizza"},
|
|
85
|
+
{"title": "Finish Toasty"},
|
|
86
|
+
{"title": "Sleep"}
|
|
87
|
+
]
|
|
88
|
+
}`)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
26
|
-
stoplightColor(something: Int) -> Color =
|
|
92
|
+
func stoplightColor(something: Int) -> Color =
|
|
27
93
|
if something > 0
|
|
28
|
-
|
|
94
|
+
Red
|
|
29
95
|
else if something == 0
|
|
30
|
-
|
|
96
|
+
Yellow
|
|
31
97
|
else
|
|
32
|
-
|
|
98
|
+
Green
|
|
99
|
+
|
|
100
|
+
func toCelsius(f: Float) -> Float =
|
|
101
|
+
doc: "Convert fahrenheit temperature reading to celsius"
|
|
102
|
+
return {f - 32} * {5 / 9}
|
|
103
|
+
check:
|
|
104
|
+
toCelsius(0) == 32
|
|
105
|
+
toCelsius(100) == 212
|
|
106
|
+
toCelsius(100) == 392
|
|
107
|
+
|
|
108
|
+
type Response(Reader, Writer) =
|
|
109
|
+
val body: Buffer = Buffer()
|
|
110
|
+
val headers: Map(Str, Str) = Map()
|
|
111
|
+
val status: Int = 0
|
|
112
|
+
|
|
113
|
+
static fn empty() -> Self =
|
|
114
|
+
Response::create()
|
|
115
|
+
.body(Buffer())
|
|
116
|
+
.headers(Map())
|
|
117
|
+
.status(0)
|
|
118
|
+
|
|
119
|
+
fn createFileResponse(path: Str) -> Result(Response, IOError) =
|
|
120
|
+
content_type = try Mime::fromExt(path.ext())
|
|
121
|
+
body = try os.readFile(file)
|
|
122
|
+
Response::create()
|
|
123
|
+
.header("Content-Type", content_type)
|
|
124
|
+
.body(body)
|
|
125
|
+
.status(200)
|
|
126
|
+
|
|
127
|
+
fn serveFile(file: Str) -> Result(Response, IOError) =
|
|
128
|
+
ext = try Path::fromExt(file)
|
|
129
|
+
content_type = try Mime::fromExt(ext)
|
|
130
|
+
body = os.readFile(file)
|
|
131
|
+
Response::create()
|
|
132
|
+
.header("Content-Type", content_type)
|
|
133
|
+
.body(body)
|
|
134
|
+
.status(200)
|
|
135
|
+
|
|
136
|
+
fn index() -> Result(Response, IOError) =
|
|
137
|
+
createFileResponse("index.html")
|
|
33
138
|
|
|
34
|
-
Response = {
|
|
35
|
-
body: Buffer = emptyBuffer()
|
|
36
|
-
|
|
139
|
+
g = Greeter(name: "abc")
|
|
37
|
-
|
|
140
|
+
g = Greeter(...g, name: "123")
|
|
38
|
-
}
|
|
39
141
|
|
|
40
|
-
|
|
142
|
+
func readFile() -> Result(String, Err) =
|
|
41
|
-
|
|
143
|
+
return Err("123")
|
|
42
144
|
|
|
43
|
-
main() ->
|
|
145
|
+
func main() -> Unit =
|
|
146
|
+
printLn("123")
|
|
147
|
+
createFileResponse("./src/sample.plum")
|
|
148
|
+
index()
|
|
44
|
-
|
|
149
|
+
serveFile("./src/sample.plum")
|
|
150
|
+
content = try readFile()
|
|
45
151
|
|
|
46
|
-
|
|
152
|
+
if Ok(d) = readFile()
|
|
47
|
-
Int\toStr() -> Str =
|
|
48
|
-
|
|
153
|
+
println(d)
|
|
49
154
|
|
|
155
|
+
match readFile()
|
|
156
|
+
Ok(d) =>
|
|
157
|
+
printLn(d)
|
|
50
|
-
|
|
158
|
+
Err(e) =>
|
|
51
|
-
|
|
159
|
+
printErr(e)
|
|
52
160
|
|
|
53
|
-
sub(arg1: Int, arg2: Int) -> Int =
|
|
161
|
+
func sub(arg1: Int, arg2: Int) -> Int =
|
|
54
162
|
local1 = arg1
|
|
55
163
|
local2 = arg2
|
|
56
164
|
local1 - local2
|
|
57
165
|
|
|
58
|
-
addAndStringify(num1: Int, num2: Int) -> Str =
|
|
166
|
+
func addAndStringify(num1: Int, num2: Int) -> Str =
|
|
59
|
-
(num1 + num2).toStr()
|
|
167
|
+
return (num1 + num2).toStr()
|
|
60
168
|
|
|
61
|
-
factorial(x: Int) -> Int =
|
|
169
|
+
func factorial(x: Int) -> Int =
|
|
62
170
|
if x < 2
|
|
63
171
|
x
|
|
64
172
|
else
|
|
65
173
|
x * factorial(x - 1)
|
|
66
174
|
|
|
67
|
-
Result(a, b) =
|
|
68
|
-
| Ok(a)
|
|
69
|
-
| Err(b)
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
Checks whether the result is an `Ok` value
|
|
73
|
-
isOk(Ok(1)) # => True
|
|
74
|
-
isOk(Err(Nil)) # => False
|
|
75
|
-
```
|
|
76
|
-
Result\isOk() -> Bool =
|
|
77
|
-
r! # will bubble back the result similar to try catch
|
|
78
|
-
o! # will bubble back the option/result
|
|
79
|
-
if self == Ok(v) then
|
|
80
|
-
True
|
|
81
|
-
else if self == Err(_) then
|
|
82
|
-
False
|
|
83
|
-
|
|
84
|
-
birds = 3
|
|
175
|
+
var birds = 3
|
|
85
|
-
iguanas = 2
|
|
176
|
+
var iguanas = 2
|
|
86
|
-
total = addAndStringify(birds, iguanas)
|
|
177
|
+
var total = addAndStringify(birds, iguanas)
|
|
87
|
-
|
|
178
|
+
|
|
88
|
-
pluralize(singular: Str, plural: Str, count: Int) -> Str =
|
|
179
|
+
func pluralize(singular: Str, plural: Str, count: Int) -> Str =
|
|
89
180
|
countstr = Num.toStr(count)
|
|
90
181
|
if count == 1 then
|
|
91
182
|
"$(countstr) $(singular)"
|
|
92
183
|
else
|
|
93
184
|
"$(countstr) $(plural)"
|
|
94
185
|
|
|
95
|
-
pluralize_test(t: Test) -> Unit =
|
|
186
|
+
func pluralize_test(t: Test) -> Unit =
|
|
96
187
|
assert pluralize("cactus", "cacti", 1) == "1 cactus"
|
|
97
188
|
assert pluralize("cactus", "cacti", 2) == "2 cacti"
|
|
98
189
|
|
|
99
|
-
User =
|
|
190
|
+
type User =
|
|
100
191
|
name: String
|
|
101
192
|
age: String
|
|
102
|
-
}
|
|
103
193
|
|
|
104
|
-
user = User(1, 2, 3)
|
|
105
|
-
params = Map("one" => 1, "two" => 2, "three" => 3)
|
|
106
|
-
arr = List(1, 2, 3, 4)
|
|
107
|
-
|
|
108
|
-
|
|
194
|
+
isAuthorized() -> Bool =
|
|
109
195
|
False
|
|
110
196
|
|
|
111
|
-
|
|
197
|
+
map(cb: (a: User) -> b) -> b =
|
|
112
198
|
cb(u)
|
|
113
199
|
|
|
200
|
+
|
|
201
|
+
type Teacher(
|
|
202
|
+
name: String,
|
|
203
|
+
subject: String
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
user = User(
|
|
207
|
+
name: "John Doe",
|
|
208
|
+
age: 30
|
|
209
|
+
)
|
|
210
|
+
params = Map("one" => 1, "two" => 2, "three" => 3)
|
|
211
|
+
arr = List(1, 2, 3, 4)
|
|
212
|
+
|
|
114
|
-
name(
|
|
213
|
+
func name(d: Option(Str)) -> Str =
|
|
115
|
-
if
|
|
214
|
+
if d == Some(v) then
|
|
116
215
|
v.subString(0, 3)
|
|
117
216
|
else if a == b then
|
|
118
217
|
"123"
|
|
119
218
|
else
|
|
120
219
|
"1231"
|
|
121
220
|
|
|
122
|
-
delta(
|
|
221
|
+
delta(d: Option(Str)) -> Str =
|
|
123
|
-
if
|
|
222
|
+
if d == Some(v)
|
|
124
223
|
v.subString(0, 3)
|
|
125
224
|
else if a == b
|
|
126
225
|
"123"
|
|
@@ -130,20 +229,19 @@ delta(data: Option(Str)) -> Str =
|
|
|
130
229
|
else
|
|
131
230
|
println("No Data")
|
|
132
231
|
|
|
133
|
-
|
|
134
|
-
|
|
232
|
+
d.price?.takeIf(\a -> a != "")
|
|
135
233
|
(1 + 2).mod(3).pow(2).sqrt()
|
|
136
234
|
|
|
137
235
|
if v == None
|
|
138
236
|
error.Error
|
|
139
|
-
else if
|
|
237
|
+
else if Some(c) = v
|
|
140
238
|
c * 20
|
|
141
239
|
|
|
142
240
|
match v
|
|
143
241
|
None -> error.Error
|
|
144
242
|
Some(c) -> c * 20
|
|
145
243
|
|
|
146
|
-
names =
|
|
244
|
+
names = List("Sam", "Lee", "Ari")
|
|
147
245
|
names.append("Jess")
|
|
148
246
|
names.map(\num -> num * 2)
|
|
149
247
|
|
|
@@ -157,50 +255,20 @@ import std/libc
|
|
|
157
255
|
extern sqrt(x: libc.Double) -> libc.Double
|
|
158
256
|
extern pow(x: libc.Double, y: libc.Double) -> libc.Double
|
|
159
257
|
|
|
160
|
-
WriteError = EOF | Closed
|
|
161
|
-
|
|
162
|
-
Writer = (
|
|
163
|
-
write(p: Str | Buffer) -> Int | WriteError
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
ReadError = EOF | Closed
|
|
167
258
|
|
|
168
|
-
|
|
259
|
+
trait Writer =
|
|
169
|
-
|
|
260
|
+
write(p: Str | Buffer) -> Result[Int, WriteError]
|
|
170
|
-
)
|
|
171
261
|
|
|
172
|
-
|
|
262
|
+
enum ReadError =
|
|
263
|
+
| EOF
|
|
173
|
-
|
|
264
|
+
| Closed
|
|
174
265
|
|
|
266
|
+
enum WriteError =
|
|
267
|
+
| EOF
|
|
175
|
-
|
|
268
|
+
| Closed
|
|
176
|
-
headers: Map[Str, Str]
|
|
177
|
-
body: Str | Buffer | IO
|
|
178
|
-
status: Int
|
|
179
|
-
)
|
|
180
269
|
|
|
181
|
-
createFileResponse(path: Str) -> Response | IOError =
|
|
182
|
-
content := content_type.fromExt(path.ext())
|
|
183
|
-
data := os.readFile(file)?
|
|
184
|
-
Response()
|
|
185
|
-
.header("Content-Type", content)
|
|
186
|
-
.body(data)
|
|
187
|
-
.status(200)
|
|
188
|
-
|
|
189
|
-
|
|
270
|
+
trait Reader =
|
|
190
|
-
createFileResponse("index.html")
|
|
191
|
-
|
|
192
|
-
|
|
271
|
+
read(p: Str | Buffer) -> Result[Int, ReadError]
|
|
193
|
-
ext := path::ext(file)
|
|
194
|
-
content := contentTypeFromExt(ext)
|
|
195
|
-
data := os.readFile(file)?
|
|
196
|
-
Response()
|
|
197
|
-
.header("Content-Type", content)
|
|
198
|
-
.body(data)
|
|
199
|
-
.status(200)
|
|
200
|
-
|
|
201
|
-
http.createFileResponse("./src/sample.plum")
|
|
202
|
-
http.index()
|
|
203
|
-
http.serveFile("./src/sample.plum")
|
|
204
272
|
|
|
205
|
-
|
|
273
|
+
trait IO(Reader, Writer) =
|
|
206
|
-
|
|
274
|
+
pass
|
tooling/tree-sitter-plum/grammar.js
CHANGED
|
@@ -90,17 +90,22 @@ module.exports = grammar({
|
|
|
90
90
|
seq(
|
|
91
91
|
"type",
|
|
92
92
|
field("name", $.type_identifier),
|
|
93
|
-
field("implements", optional(seq("
|
|
93
|
+
field("implements", optional(seq("(", commaSep1($.type_identifier), ")"))),
|
|
94
94
|
field("generics", optional($.generics)),
|
|
95
95
|
"=",
|
|
96
96
|
$._indent,
|
|
97
|
-
optional(repeat($.record_field)),
|
|
97
|
+
field("fields", optional(repeat(alias($.record_field, $.field)))),
|
|
98
|
-
optional(repeat($.method)),
|
|
98
|
+
field("methods", optional(repeat($.method))),
|
|
99
99
|
$._dedent,
|
|
100
100
|
),
|
|
101
101
|
|
|
102
102
|
record_field: ($) => seq(field("name", $.identifier), ":", field("type", $.type)),
|
|
103
|
-
method: ($) =>
|
|
103
|
+
method: ($) => seq(
|
|
104
|
+
field("name", $.identifier),
|
|
105
|
+
field("params", seq("(", optional(commaSep1($.param)), ")")),
|
|
106
|
+
field("returns", optional(seq("->", $.return_type))),
|
|
107
|
+
field("body", seq("=", choice($.expression, $.body))),
|
|
108
|
+
),
|
|
104
109
|
trait: ($) =>
|
|
105
110
|
seq(
|
|
106
111
|
"trait",
|
|
@@ -119,11 +124,14 @@ module.exports = grammar({
|
|
|
119
124
|
),
|
|
120
125
|
|
|
121
126
|
param: ($) =>
|
|
127
|
+
choice(
|
|
128
|
+
seq("self"),
|
|
122
|
-
|
|
129
|
+
seq(
|
|
123
|
-
|
|
130
|
+
field("name", $.var_identier),
|
|
124
|
-
|
|
131
|
+
":",
|
|
125
|
-
|
|
132
|
+
field("type", choice($.type, $.variadic_type)),
|
|
126
|
-
|
|
133
|
+
optional(seq("=", field("value", $.expression))),
|
|
134
|
+
),
|
|
127
135
|
),
|
|
128
136
|
|
|
129
137
|
return_type: ($) =>
|
|
@@ -135,7 +143,7 @@ module.exports = grammar({
|
|
|
135
143
|
field("name", $.type_identifier),
|
|
136
144
|
"=",
|
|
137
145
|
$._indent,
|
|
138
|
-
optional(repeat($.enum_field)),
|
|
146
|
+
optional(repeat(alias($.enum_field, $.field))),
|
|
139
147
|
optional(repeat($.method)),
|
|
140
148
|
$._dedent,
|
|
141
149
|
),
|
|
@@ -513,4 +521,4 @@ function newlineSep1(rule) {
|
|
|
513
521
|
|
|
514
522
|
function sep1(rule, separator) {
|
|
515
523
|
return seq(rule, repeat(seq(separator, rule)));
|
|
516
|
-
}
|
|
524
|
+
}
|
tooling/tree-sitter-plum/src/grammar.json
CHANGED
|
Binary file
|
tooling/tree-sitter-plum/src/node-types.json
CHANGED
|
Binary file
|
tooling/tree-sitter-plum/src/parser.c
CHANGED
|
Binary file
|
tooling/tree-sitter-plum/test/corpus/assert.txt
CHANGED
|
@@ -13,7 +13,7 @@ main() =
|
|
|
13
13
|
|
|
14
14
|
(source
|
|
15
15
|
(fn
|
|
16
|
-
(
|
|
16
|
+
(identifier)
|
|
17
17
|
(body
|
|
18
18
|
(assert
|
|
19
19
|
(expression
|
tooling/tree-sitter-plum/test/corpus/enum.txt
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
enum
|
|
3
3
|
================================================================================
|
|
4
4
|
|
|
5
|
-
enum Bool
|
|
5
|
+
enum Bool =
|
|
6
6
|
| True
|
|
7
7
|
| False
|
|
8
8
|
|
|
9
|
-
toStr() -> Str =
|
|
9
|
+
toStr(self) -> Str =
|
|
10
10
|
"Bool"
|
|
11
11
|
|
|
12
12
|
--------------------------------------------------------------------------------
|
|
@@ -14,12 +14,13 @@ enum Bool
|
|
|
14
14
|
(source
|
|
15
15
|
(enum
|
|
16
16
|
(type_identifier)
|
|
17
|
-
(
|
|
17
|
+
(field
|
|
18
18
|
(type_identifier))
|
|
19
|
-
(
|
|
19
|
+
(field
|
|
20
20
|
(type_identifier))
|
|
21
21
|
(method
|
|
22
22
|
(identifier)
|
|
23
|
+
(param)
|
|
23
24
|
(return_type
|
|
24
25
|
(type_identifier))
|
|
25
26
|
(body
|
tooling/tree-sitter-plum/test/corpus/if.txt
CHANGED
|
@@ -19,7 +19,7 @@ main() =
|
|
|
19
19
|
|
|
20
20
|
(source
|
|
21
21
|
(fn
|
|
22
|
-
(
|
|
22
|
+
(identifier)
|
|
23
23
|
(body
|
|
24
24
|
(if
|
|
25
25
|
(expression
|
tooling/tree-sitter-plum/test/corpus/match.txt
CHANGED
|
@@ -15,55 +15,4 @@ main() =
|
|
|
15
15
|
|
|
16
16
|
--------------------------------------------------------------------------------
|
|
17
17
|
|
|
18
|
-
(source
|
|
19
|
-
|
|
18
|
+
()
|
|
20
|
-
(string
|
|
21
|
-
(string_start)
|
|
22
|
-
(string_end))
|
|
23
|
-
(lambda
|
|
24
|
-
(identifier)
|
|
25
|
-
(body
|
|
26
|
-
(assignment_statement
|
|
27
|
-
(identifier)
|
|
28
|
-
(binary_operator
|
|
29
|
-
(parenthesized_expression
|
|
30
|
-
(binary_operator
|
|
31
|
-
(integer)
|
|
32
|
-
(integer)))
|
|
33
|
-
(parenthesized_expression
|
|
34
|
-
(binary_operator
|
|
35
|
-
(integer)
|
|
36
|
-
(integer)))))
|
|
37
|
-
(if_statement
|
|
38
|
-
(comparison_operator
|
|
39
|
-
(identifier)
|
|
40
|
-
(identifier))
|
|
41
|
-
(block
|
|
42
|
-
(assert_statement
|
|
43
|
-
(comparison_operator
|
|
44
|
-
(identifier)
|
|
45
|
-
(identifier))))
|
|
46
|
-
(else_if_clause
|
|
47
|
-
(comparison_operator
|
|
48
|
-
(identifier)
|
|
49
|
-
(integer))
|
|
50
|
-
(block
|
|
51
|
-
(assert_statement
|
|
52
|
-
(comparison_operator
|
|
53
|
-
(identifier)
|
|
54
|
-
(integer)))))
|
|
55
|
-
(else_if_clause
|
|
56
|
-
(comparison_operator
|
|
57
|
-
(identifier)
|
|
58
|
-
(integer))
|
|
59
|
-
(block
|
|
60
|
-
(assert_statement
|
|
61
|
-
(comparison_operator
|
|
62
|
-
(identifier)
|
|
63
|
-
(integer)))))
|
|
64
|
-
(else_clause
|
|
65
|
-
(block
|
|
66
|
-
(assert_statement
|
|
67
|
-
(comparison_operator
|
|
68
|
-
(identifier)
|
|
69
|
-
(integer))))))))))
|
tooling/tree-sitter-plum/test/corpus/type.txt
CHANGED
|
@@ -1,79 +1,54 @@
|
|
|
1
1
|
================================================================================
|
|
2
|
-
type
|
|
2
|
+
type
|
|
3
3
|
================================================================================
|
|
4
4
|
|
|
5
|
-
type Cat is Stringable =
|
|
6
|
-
name: Str
|
|
7
|
-
age: Int
|
|
8
|
-
|
|
9
5
|
type Dog =
|
|
10
|
-
name:
|
|
6
|
+
name: Str
|
|
11
|
-
age:
|
|
7
|
+
age: b
|
|
12
|
-
|
|
13
|
-
type Dog(a: Compare + Stringable, b) is Stringable =
|
|
14
|
-
name: a
|
|
15
|
-
age: b
|
|
16
8
|
|
|
17
9
|
type Int =
|
|
10
|
+
random() -> Int = 20
|
|
18
|
-
add(other: Int) -> Int =
|
|
11
|
+
add(self, other: Int) -> Int = self + other
|
|
19
|
-
sub(other: Int) -> Int =
|
|
12
|
+
sub(self, other: Int) -> Int = self - other
|
|
20
13
|
|
|
21
|
-
type Cat
|
|
14
|
+
type Cat(Stringable) =
|
|
22
15
|
name: Str
|
|
23
16
|
age: Int
|
|
24
17
|
|
|
25
|
-
withName(name: Str) -> Cat =
|
|
18
|
+
withName(self, name: Str) -> Cat =
|
|
26
19
|
Cat(name = name, age = 0)
|
|
27
20
|
|
|
28
|
-
withAge(age: Int) -> Cat =
|
|
21
|
+
withAge(self, age: Int) -> Cat =
|
|
29
22
|
Cat(name = "", age = age)
|
|
30
23
|
|
|
31
|
-
|
|
24
|
+
toStr(self) -> Str =
|
|
32
|
-
"Cat"
|
|
25
|
+
"Cat(1, 2)"
|
|
33
26
|
|
|
34
27
|
--------------------------------------------------------------------------------
|
|
35
28
|
|
|
36
29
|
(source
|
|
37
30
|
(record
|
|
38
31
|
(type_identifier)
|
|
39
|
-
(type_identifier)
|
|
40
|
-
(
|
|
32
|
+
(field
|
|
41
33
|
(identifier)
|
|
42
34
|
(type
|
|
43
35
|
(type_identifier)))
|
|
44
|
-
(
|
|
36
|
+
(field
|
|
45
|
-
(identifier)
|
|
46
|
-
(type
|
|
47
|
-
(type_identifier))))
|
|
48
|
-
(record
|
|
49
|
-
(type_identifier)
|
|
50
|
-
(record_field
|
|
51
|
-
(identifier)
|
|
52
|
-
(type
|
|
53
|
-
(a)))
|
|
54
|
-
(record_field
|
|
55
37
|
(identifier)
|
|
56
38
|
(type
|
|
57
39
|
(b))))
|
|
58
40
|
(record
|
|
59
41
|
(type_identifier)
|
|
60
|
-
(generics
|
|
61
|
-
(a)
|
|
62
|
-
(type_identifier)
|
|
63
|
-
(type_identifier)
|
|
64
|
-
(b))
|
|
65
|
-
(record_field
|
|
66
|
-
(identifier)
|
|
67
|
-
|
|
42
|
+
(method
|
|
68
|
-
(a)))
|
|
69
|
-
(record_field
|
|
70
43
|
(identifier)
|
|
71
|
-
(
|
|
44
|
+
(return_type
|
|
72
|
-
(b))))
|
|
73
|
-
(record
|
|
74
|
-
|
|
45
|
+
(type_identifier))
|
|
46
|
+
(expression
|
|
47
|
+
(primary_expression
|
|
48
|
+
(integer))))
|
|
75
49
|
(method
|
|
76
50
|
(identifier)
|
|
51
|
+
(param)
|
|
77
52
|
(param
|
|
78
53
|
(var_identier)
|
|
79
54
|
(type
|
|
@@ -84,11 +59,12 @@ type Cat is Stringable =
|
|
|
84
59
|
(primary_expression
|
|
85
60
|
(binary_operator
|
|
86
61
|
(primary_expression
|
|
87
|
-
(
|
|
62
|
+
(identifier))
|
|
88
63
|
(primary_expression
|
|
89
64
|
(identifier))))))
|
|
90
65
|
(method
|
|
91
66
|
(identifier)
|
|
67
|
+
(param)
|
|
92
68
|
(param
|
|
93
69
|
(var_identier)
|
|
94
70
|
(type
|
|
@@ -99,22 +75,23 @@ type Cat is Stringable =
|
|
|
99
75
|
(primary_expression
|
|
100
76
|
(binary_operator
|
|
101
77
|
(primary_expression
|
|
102
|
-
(
|
|
78
|
+
(identifier))
|
|
103
79
|
(primary_expression
|
|
104
80
|
(identifier)))))))
|
|
105
81
|
(record
|
|
106
82
|
(type_identifier)
|
|
107
83
|
(type_identifier)
|
|
108
|
-
(
|
|
84
|
+
(field
|
|
109
85
|
(identifier)
|
|
110
86
|
(type
|
|
111
87
|
(type_identifier)))
|
|
112
|
-
(
|
|
88
|
+
(field
|
|
113
89
|
(identifier)
|
|
114
90
|
(type
|
|
115
91
|
(type_identifier)))
|
|
116
92
|
(method
|
|
117
93
|
(identifier)
|
|
94
|
+
(param)
|
|
118
95
|
(param
|
|
119
96
|
(var_identier)
|
|
120
97
|
(type
|
|
@@ -138,6 +115,7 @@ type Cat is Stringable =
|
|
|
138
115
|
(integer)))))))))
|
|
139
116
|
(method
|
|
140
117
|
(identifier)
|
|
118
|
+
(param)
|
|
141
119
|
(param
|
|
142
120
|
(var_identier)
|
|
143
121
|
(type
|
|
@@ -163,6 +141,7 @@ type Cat is Stringable =
|
|
|
163
141
|
(identifier)))))))))
|
|
164
142
|
(method
|
|
165
143
|
(identifier)
|
|
144
|
+
(param)
|
|
166
145
|
(return_type
|
|
167
146
|
(type_identifier))
|
|
168
147
|
(body
|
tooling/vscode-plum/syntaxes/plum.tmLanguage.json
CHANGED
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"include": "#discards"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"include": "#raw_string_literals"
|
|
22
25
|
}
|
|
23
26
|
],
|
|
24
27
|
"repository": {
|
|
@@ -26,7 +29,7 @@
|
|
|
26
29
|
"patterns": [
|
|
27
30
|
{
|
|
28
31
|
"name": "keyword.control.plum",
|
|
29
|
-
"match": "\\b(module|import|trait|type|enum|
|
|
32
|
+
"match": "\\b(self|this|fn|impl|where|extern|func|try|var|val|module|import|trait|type|enum|in|return|continue|break|match|if|else|while|for|as|is|assert|panic)\\b"
|
|
30
33
|
},
|
|
31
34
|
{
|
|
32
35
|
"name": "keyword.operator.arrow.plum",
|
|
@@ -67,6 +70,10 @@
|
|
|
67
70
|
{
|
|
68
71
|
"name": "keyword.operator.assignment.plum",
|
|
69
72
|
"match": "="
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "keyword.operator.comparison.plum",
|
|
76
|
+
"match": "(<|>)"
|
|
70
77
|
}
|
|
71
78
|
]
|
|
72
79
|
},
|
|
@@ -85,7 +92,7 @@
|
|
|
85
92
|
"patterns": [
|
|
86
93
|
{
|
|
87
94
|
"name": "comment.line.plum",
|
|
88
|
-
"match": "
|
|
95
|
+
"match": "#.*"
|
|
89
96
|
}
|
|
90
97
|
]
|
|
91
98
|
},
|
|
@@ -132,6 +139,35 @@
|
|
|
132
139
|
"match": "\\b0[xX]0*[1-9a-zA-Z][0-9a-zA-Z]*\\b",
|
|
133
140
|
"patterns": []
|
|
134
141
|
},
|
|
142
|
+
"string_placeholder": {
|
|
143
|
+
"patterns": [
|
|
144
|
+
{
|
|
145
|
+
"match": "%(\\[\\d+\\])?([\\+#\\-0\\x20]{,2}((\\d+|\\*)?(\\.?(\\d+|\\*|(\\[\\d+\\])\\*?)?(\\[\\d+\\])?)?))?[vT%tbcdoqxXUbeEfFgGspw]",
|
|
146
|
+
"name": "constant.other.placeholder.go"
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
"raw_string_literals": {
|
|
151
|
+
"comment": "Raw string literals",
|
|
152
|
+
"begin": "`",
|
|
153
|
+
"beginCaptures": {
|
|
154
|
+
"0": {
|
|
155
|
+
"name": "punctuation.definition.string.begin.go"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"end": "`",
|
|
159
|
+
"endCaptures": {
|
|
160
|
+
"0": {
|
|
161
|
+
"name": "punctuation.definition.string.end.go"
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"name": "string.quoted.raw.go",
|
|
165
|
+
"patterns": [
|
|
166
|
+
{
|
|
167
|
+
"include": "#string_placeholder"
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
},
|
|
135
171
|
"entity": {
|
|
136
172
|
"patterns": [
|
|
137
173
|
{
|
|
@@ -155,6 +191,10 @@
|
|
|
155
191
|
{
|
|
156
192
|
"name": "entity.name.namespace.plum",
|
|
157
193
|
"match": "\\b([[:lower:]][[:word:]]*):"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"name": "entity.name.namespace.plum",
|
|
197
|
+
"match": "\\b(doc|check|expect|where)\\b"
|
|
158
198
|
}
|
|
159
199
|
]
|
|
160
200
|
},
|