@effekt-lang/effekt 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/effekt +0 -0
- package/libraries/common/buffer.effekt +3 -3
- package/libraries/common/bytearray.effekt +64 -1
- package/libraries/common/effekt.effekt +31 -9
- package/libraries/common/io/network.effekt +8 -5
- package/libraries/common/json.effekt +30 -22
- package/libraries/common/list.effekt +96 -18
- package/libraries/common/map.effekt +992 -0
- package/libraries/common/regex.effekt +64 -0
- package/libraries/common/set.effekt +234 -0
- package/libraries/common/stream.effekt +45 -0
- package/libraries/common/string.effekt +7 -0
- package/libraries/llvm/bytearray.c +36 -7
- package/libraries/llvm/forward-declare-c.ll +1 -0
- package/libraries/llvm/main.c +1 -0
- package/libraries/llvm/rts.ll +9 -0
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +2 -2
- package/licenses/mit license - mit-license.html +42 -17
- package/licenses/the bsd license - bsd-license.html +44 -19
- package/licenses/the mit license - mit.html +44 -19
- package/package.json +1 -1
- package/libraries/chez/common/text/regex.effekt +0 -31
- package/libraries/js/text/regex.effekt +0 -33
package/bin/effekt
CHANGED
|
Binary file
|
|
@@ -34,7 +34,7 @@ def emptyBuffer[T](capacity: Int): Buffer[T] at {global} = {
|
|
|
34
34
|
}
|
|
35
35
|
def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
|
|
36
36
|
// TODO allocate buffer (and array) into a region r.
|
|
37
|
-
val contents =
|
|
37
|
+
val contents = array::allocate[T](initialCapacity)
|
|
38
38
|
var head in global = 0
|
|
39
39
|
var tail in global = 0
|
|
40
40
|
|
|
@@ -51,7 +51,7 @@ def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
|
|
|
51
51
|
def read() = {
|
|
52
52
|
if (buffer.empty?) None()
|
|
53
53
|
else {
|
|
54
|
-
val result: T = contents.
|
|
54
|
+
val result: T = contents.unsafeGet(head);
|
|
55
55
|
head = mod(head + 1, initialCapacity)
|
|
56
56
|
Some(result)
|
|
57
57
|
}
|
|
@@ -59,7 +59,7 @@ def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
|
|
|
59
59
|
def write(el: T) = {
|
|
60
60
|
if (buffer.full?) <> // raise(BufferOverflow())
|
|
61
61
|
|
|
62
|
-
contents.
|
|
62
|
+
contents.unsafeSet(tail, el)
|
|
63
63
|
tail = mod(tail + 1, initialCapacity)
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -6,7 +6,7 @@ module bytearray
|
|
|
6
6
|
extern type ByteArray
|
|
7
7
|
// = llvm "%Pos"
|
|
8
8
|
// = js "Uint8Array"
|
|
9
|
-
|
|
9
|
+
// = chez "bytevector"
|
|
10
10
|
|
|
11
11
|
/// Allocates a new bytearray with the given `size`, its values are undefined.
|
|
12
12
|
extern global def allocate(size: Int): ByteArray =
|
|
@@ -15,6 +15,7 @@ extern global def allocate(size: Int): ByteArray =
|
|
|
15
15
|
%arr = call %Pos @c_bytearray_new(%Int ${size})
|
|
16
16
|
ret %Pos %arr
|
|
17
17
|
"""
|
|
18
|
+
chez "(make-bytevector ${size})"
|
|
18
19
|
|
|
19
20
|
extern pure def size(arr: ByteArray): Int =
|
|
20
21
|
js "${arr}.length"
|
|
@@ -22,6 +23,7 @@ extern pure def size(arr: ByteArray): Int =
|
|
|
22
23
|
%size = call %Int @c_bytearray_size(%Pos ${arr})
|
|
23
24
|
ret %Int %size
|
|
24
25
|
"""
|
|
26
|
+
chez "(bytevector-length ${arr})"
|
|
25
27
|
|
|
26
28
|
extern global def unsafeGet(arr: ByteArray, index: Int): Byte =
|
|
27
29
|
js "(${arr})[${index}]"
|
|
@@ -29,6 +31,7 @@ extern global def unsafeGet(arr: ByteArray, index: Int): Byte =
|
|
|
29
31
|
%byte = call %Byte @c_bytearray_get(%Pos ${arr}, %Int ${index})
|
|
30
32
|
ret %Byte %byte
|
|
31
33
|
"""
|
|
34
|
+
chez "(bytevector-u8-ref ${arr} ${index})"
|
|
32
35
|
|
|
33
36
|
extern global def unsafeSet(arr: ByteArray, index: Int, value: Byte): Unit =
|
|
34
37
|
js "bytearray$set(${arr}, ${index}, ${value})"
|
|
@@ -36,6 +39,7 @@ extern global def unsafeSet(arr: ByteArray, index: Int, value: Byte): Unit =
|
|
|
36
39
|
%z = call %Pos @c_bytearray_set(%Pos ${arr}, %Int ${index}, %Byte ${value})
|
|
37
40
|
ret %Pos %z
|
|
38
41
|
"""
|
|
42
|
+
chez "(bytevector-u8-set! ${arr} ${index} ${value})"
|
|
39
43
|
|
|
40
44
|
def resize(source: ByteArray, size: Int): ByteArray = {
|
|
41
45
|
val target = allocate(size)
|
|
@@ -75,6 +79,7 @@ extern pure def fromString(str: String): ByteArray =
|
|
|
75
79
|
llvm """
|
|
76
80
|
ret %Pos ${str}
|
|
77
81
|
"""
|
|
82
|
+
chez "(string->utf8 ${str})"
|
|
78
83
|
|
|
79
84
|
extern pure def toString(arr: ByteArray): String =
|
|
80
85
|
js "(new TextDecoder('utf-8').decode(${arr}))"
|
|
@@ -82,10 +87,68 @@ extern pure def toString(arr: ByteArray): String =
|
|
|
82
87
|
llvm """
|
|
83
88
|
ret %Pos ${arr}
|
|
84
89
|
"""
|
|
90
|
+
chez "(utf8->string ${arr})"
|
|
85
91
|
|
|
86
92
|
extern js """
|
|
87
93
|
function bytearray$set(bytes, index, value) {
|
|
88
94
|
bytes[index] = value;
|
|
89
95
|
return $effekt.unit;
|
|
90
96
|
}
|
|
97
|
+
|
|
98
|
+
function bytearray$compare(arr1, arr2) {
|
|
99
|
+
const len = Math.min(arr1.length, arr2.length);
|
|
100
|
+
|
|
101
|
+
for (let i = 0; i < len; i++) {
|
|
102
|
+
if (arr1[i] !== arr2[i]) {
|
|
103
|
+
return arr1[i] < arr2[i] ? -1 : 1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (arr1.length !== arr2.length) {
|
|
108
|
+
return arr1.length < arr2.length ? -1 : 1;
|
|
109
|
+
} else {
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
extern chez """
|
|
116
|
+
(define (bytearray$compare b1 b2)
|
|
117
|
+
(let ([len1 (bytevector-length b1)]
|
|
118
|
+
[len2 (bytevector-length b2)]
|
|
119
|
+
[minlen (min (bytevector-length b1) (bytevector-length b2))])
|
|
120
|
+
(let loop ([i 0])
|
|
121
|
+
(cond
|
|
122
|
+
[(= i minlen)
|
|
123
|
+
(cond [(< len1 len2) -1]
|
|
124
|
+
[(> len1 len2) 1]
|
|
125
|
+
[else 0])]
|
|
126
|
+
[(< (bytevector-u8-ref b1 i) (bytevector-u8-ref b2 i)) -1]
|
|
127
|
+
[(> (bytevector-u8-ref b1 i) (bytevector-u8-ref b2 i)) 1]
|
|
128
|
+
[else (loop (+ i 1))]))))
|
|
91
129
|
"""
|
|
130
|
+
|
|
131
|
+
extern pure def compareByteArrayImpl(b1: ByteArray, b2: ByteArray): Int =
|
|
132
|
+
js "bytearray$compare(${b1}, ${b2})"
|
|
133
|
+
llvm """
|
|
134
|
+
%x = call %Int @c_bytearray_compare(%Pos ${b1}, %Pos ${b2})
|
|
135
|
+
ret %Int %x
|
|
136
|
+
"""
|
|
137
|
+
chez "(bytearray$compare ${b1} ${b2})"
|
|
138
|
+
|
|
139
|
+
def compareByteArray(b1: ByteArray, b2: ByteArray): Ordering = {
|
|
140
|
+
val ret = compareByteArrayImpl(b1, b2)
|
|
141
|
+
if (ret == 0) {
|
|
142
|
+
Equal()
|
|
143
|
+
} else if (ret < 0) {
|
|
144
|
+
Less()
|
|
145
|
+
} else { // ret > 0
|
|
146
|
+
Greater()
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
def compareStringBytes(left: String, right: String): Ordering = {
|
|
151
|
+
val l = left.fromString
|
|
152
|
+
val r = right.fromString
|
|
153
|
+
compareByteArray(l, r)
|
|
154
|
+
}
|
|
@@ -90,6 +90,7 @@ extern pure def show(value: Char): String =
|
|
|
90
90
|
|
|
91
91
|
extern pure def show(value: Byte): String =
|
|
92
92
|
js "'' + ${value}"
|
|
93
|
+
chez "(string ${value})"
|
|
93
94
|
llvm """
|
|
94
95
|
%z = call %Pos @c_bytearray_show_Byte(i8 ${value})
|
|
95
96
|
ret %Pos %z
|
|
@@ -166,17 +167,24 @@ type Ordering {
|
|
|
166
167
|
Greater()
|
|
167
168
|
}
|
|
168
169
|
|
|
170
|
+
def compareInt(n: Int, m: Int) =
|
|
171
|
+
if (n == m) Equal()
|
|
172
|
+
else if (n < m) Less()
|
|
173
|
+
else Greater()
|
|
174
|
+
|
|
169
175
|
extern pure def genericCompareImpl[R](x: R, y: R): Int =
|
|
170
176
|
js "$effekt.compare(${x}, ${y})"
|
|
171
177
|
|
|
172
178
|
/// Compares two values of the same type, returning an `Ordering`.
|
|
173
179
|
/// Only available on the JavaScript backend.
|
|
174
180
|
def genericCompare[R](x: R, y: R): Ordering = {
|
|
175
|
-
genericCompareImpl(x, y)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
181
|
+
val ret = genericCompareImpl(x, y)
|
|
182
|
+
if (ret == 0) {
|
|
183
|
+
Equal()
|
|
184
|
+
} else if (ret < 0) {
|
|
185
|
+
Less()
|
|
186
|
+
} else { // ret > 0
|
|
187
|
+
Greater()
|
|
180
188
|
}
|
|
181
189
|
}
|
|
182
190
|
|
|
@@ -344,21 +352,25 @@ extern pure def cos(x: Double): Double =
|
|
|
344
352
|
js "Math.cos(${x})"
|
|
345
353
|
chez "(cos ${x})"
|
|
346
354
|
vm "effekt::cos(Double)"
|
|
355
|
+
llvm "%z = call %Double @llvm.cos.f64(double ${x}) ret %Double %z"
|
|
347
356
|
|
|
348
357
|
extern pure def sin(x: Double): Double =
|
|
349
358
|
js "Math.sin(${x})"
|
|
350
359
|
chez "(sin ${x})"
|
|
351
360
|
vm "effekt::sin(Double)"
|
|
361
|
+
llvm "%z = call %Double @llvm.sin.f64(double ${x}) ret %Double %z"
|
|
352
362
|
|
|
353
363
|
extern pure def atan(x: Double): Double =
|
|
354
364
|
js "Math.atan(${x})"
|
|
355
365
|
chez "(atan ${x})"
|
|
356
366
|
vm "effekt::atan(Double)"
|
|
367
|
+
llvm "%z = call %Double @atan(double ${x}) ret %Double %z"
|
|
357
368
|
|
|
358
369
|
extern pure def tan(x: Double): Double =
|
|
359
370
|
js "Math.tan(${x})"
|
|
360
371
|
chez "(tan ${x})"
|
|
361
372
|
vm "effekt::tan(Double)"
|
|
373
|
+
llvm "%z = call %Double @tan(double ${x}) ret %Double %z"
|
|
362
374
|
|
|
363
375
|
extern pure def sqrt(x: Double): Double =
|
|
364
376
|
js "Math.sqrt(${x})"
|
|
@@ -381,15 +393,18 @@ extern pure def log(x: Double): Double =
|
|
|
381
393
|
js "Math.log(${x})"
|
|
382
394
|
chez "(log ${x})"
|
|
383
395
|
vm "effekt::log(Double)"
|
|
396
|
+
llvm "%z = call %Double @llvm.log.f64(double ${x}) ret %Double %z"
|
|
384
397
|
|
|
385
398
|
extern pure def log1p(x: Double): Double =
|
|
386
399
|
js "Math.log1p(${x})"
|
|
387
400
|
chez "(log (+ ${x} 1))"
|
|
401
|
+
llvm "%z = call %Double @log1p(double ${x}) ret %Double %z"
|
|
388
402
|
|
|
389
403
|
extern pure def exp(x: Double): Double =
|
|
390
404
|
js "Math.exp(${x})"
|
|
391
405
|
chez "(exp ${x})"
|
|
392
406
|
vm "effekt::exp(Double)"
|
|
407
|
+
llvm "%z = call %Double @llvm.exp.f64(double ${x}) ret %Double %z"
|
|
393
408
|
|
|
394
409
|
def pow(base: Double, exponent: Int): Double = {
|
|
395
410
|
def loop(base: Double, exponent: Int, acc: Double): Double = {
|
|
@@ -408,12 +423,14 @@ extern pure def pow(base: Double, exponent: Double): Double =
|
|
|
408
423
|
js "Math.pow(${base}, ${exponent})"
|
|
409
424
|
chez "(expt ${base} ${exponent})"
|
|
410
425
|
vm "effekt::pow(Double, Double)"
|
|
426
|
+
llvm "%z = call %Double @llvm.pow.f64(double ${base}, double ${exponent}) ret %Double %z"
|
|
411
427
|
|
|
412
428
|
// since we do not have "extern val", yet
|
|
413
429
|
extern pure def _pi(): Double =
|
|
414
430
|
js "Math.PI"
|
|
415
431
|
chez "(* 4 (atan 1))"
|
|
416
432
|
vm "effekt::pi()"
|
|
433
|
+
llvm "ret double 3.14159265358979323846264338327950288419716939937510582097494459"
|
|
417
434
|
|
|
418
435
|
val PI: Double = _pi()
|
|
419
436
|
|
|
@@ -562,19 +579,19 @@ extern pure def infixGte(x: Double, y: Double): Bool =
|
|
|
562
579
|
// TODO do we really need those? if yes, move to string.effekt
|
|
563
580
|
extern pure def infixLt(x: String, y: String): Bool =
|
|
564
581
|
js "(${x} < ${y})"
|
|
565
|
-
chez "(
|
|
582
|
+
chez "(string<? ${x} ${y})"
|
|
566
583
|
|
|
567
584
|
extern pure def infixLte(x: String, y: String): Bool =
|
|
568
585
|
js "(${x} <= ${y})"
|
|
569
|
-
chez "(
|
|
586
|
+
chez "(string<=? ${x} ${y})"
|
|
570
587
|
|
|
571
588
|
extern pure def infixGt(x: String, y: String): Bool =
|
|
572
589
|
js "(${x} > ${y})"
|
|
573
|
-
chez "(
|
|
590
|
+
chez "(string>? ${x} ${y})"
|
|
574
591
|
|
|
575
592
|
extern pure def infixGte(x: String, y: String): Bool =
|
|
576
593
|
js "(${x} >= ${y})"
|
|
577
|
-
chez "(
|
|
594
|
+
chez "(string>=? ${x} ${y})"
|
|
578
595
|
|
|
579
596
|
|
|
580
597
|
// Boolean operations
|
|
@@ -638,10 +655,13 @@ extern pure def bitwiseXor(x: Int, y: Int): Int =
|
|
|
638
655
|
extern pure def toByte(n: Int): Byte =
|
|
639
656
|
js "${n}"
|
|
640
657
|
llvm "%z = trunc %Int ${n} to %Byte ret %Byte %z"
|
|
658
|
+
chez "${n}"
|
|
641
659
|
|
|
642
660
|
extern pure def toInt(n: Byte): Int =
|
|
643
661
|
js "${n}"
|
|
644
662
|
llvm "%z = zext %Byte ${n} to %Int ret %Int %z"
|
|
663
|
+
vm "effekt::toInt(Byte)"
|
|
664
|
+
chez "${n}"
|
|
645
665
|
|
|
646
666
|
|
|
647
667
|
// Undefined and Null
|
|
@@ -657,11 +677,13 @@ extern pure def toInt(n: Byte): Int =
|
|
|
657
677
|
extern pure def undefined[A](): A =
|
|
658
678
|
js "undefined"
|
|
659
679
|
chez "#f"
|
|
680
|
+
vm "effekt::undefined()"
|
|
660
681
|
|
|
661
682
|
/// Is an FFI value undefined?
|
|
662
683
|
extern pure def isUndefined[A](value: A): Bool =
|
|
663
684
|
js "(${value} === undefined || ${value} === null)"
|
|
664
685
|
chez "(eq? ${value} #f)"
|
|
686
|
+
vm "effekt::isUndefined[A](A)"
|
|
665
687
|
|
|
666
688
|
|
|
667
689
|
// Tuples
|
|
@@ -57,14 +57,17 @@ def server(host: String, port: Int, handler: () => Unit / Socket at {io, async,
|
|
|
57
57
|
|
|
58
58
|
namespace examples {
|
|
59
59
|
def helloWorldApp(): Unit / Socket = {
|
|
60
|
-
val request = do receive();
|
|
60
|
+
val request = do receive().toString;
|
|
61
61
|
|
|
62
|
-
println("Received a request: " ++ request
|
|
62
|
+
println("Received a request: " ++ request)
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
do send(
|
|
64
|
+
def respond(s: String): Unit / Socket =
|
|
65
|
+
do send(s.fromString)
|
|
66
|
+
|
|
67
|
+
if (request.startsWith("GET /")) {
|
|
68
|
+
respond("HTTP/1.1 200 OK\r\n\r\nHello from Effekt!")
|
|
66
69
|
} else {
|
|
67
|
-
|
|
70
|
+
respond("HTTP/1.1 400 Bad Request\r\n\r\n")
|
|
68
71
|
}
|
|
69
72
|
do end()
|
|
70
73
|
}
|
|
@@ -62,7 +62,7 @@ def encodeJsonObject[R]{ body: => R / JsonObjectBuilder }: R / emit[String] = {
|
|
|
62
62
|
do emit("{")
|
|
63
63
|
def c(k: String) = {
|
|
64
64
|
if (not(first)) { do emit(",") }
|
|
65
|
-
do emit(escape(k)); do emit(":")
|
|
65
|
+
do emit(escape(k)); do emit(":")
|
|
66
66
|
first = false
|
|
67
67
|
}
|
|
68
68
|
val r = encodeJson {
|
|
@@ -76,7 +76,7 @@ def encodeJsonObject[R]{ body: => R / JsonObjectBuilder }: R / emit[String] = {
|
|
|
76
76
|
/// Main entry point for encoding json.
|
|
77
77
|
/// Emits individual tokens of the resulting json.
|
|
78
78
|
def encodeJson[R]{ body: => R / JsonBuilder }: R / emit[String] = {
|
|
79
|
-
try body() with JsonBuilder {
|
|
79
|
+
try body() with JsonBuilder {
|
|
80
80
|
def null() = { resume(do emit("null")) }
|
|
81
81
|
def bool(v) = { resume(do emit( if(v){ "true" } else { "false" } )) }
|
|
82
82
|
def number(n) = { resume(do emit(show(n))) }
|
|
@@ -112,7 +112,7 @@ def readDouble(): Double / Scan[Char] = {
|
|
|
112
112
|
if (optionally { readIf('.') }) {
|
|
113
113
|
var b = 0.1
|
|
114
114
|
var r = pre.toDouble
|
|
115
|
-
while (optionally[Int] { readDigit() } is Some(d)) {
|
|
115
|
+
while (returning::optionally[Int] { readDigit() } is Some(d)) {
|
|
116
116
|
r = r + b * d.toDouble
|
|
117
117
|
b = b * 0.1
|
|
118
118
|
}
|
|
@@ -126,14 +126,14 @@ def readDouble(): Double / Scan[Char] = {
|
|
|
126
126
|
|
|
127
127
|
def expectString(string: String): Unit / { Scan[Char], Exception[WrongFormat] } =
|
|
128
128
|
for[Char] { string.each } { char =>
|
|
129
|
-
expect
|
|
129
|
+
expect("Expected " ++ string) { readIf(char) }
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
/// Read and unescape a string in ""
|
|
133
133
|
def readQuotedString(): Unit / { Scan[Char], emit[Char], Exception[WrongFormat] } = {
|
|
134
134
|
try {
|
|
135
135
|
skipWhitespace()
|
|
136
|
-
expect
|
|
136
|
+
expect("Expected \"") { readIf('"') }
|
|
137
137
|
while(read[Char]() is c and c != '"') {
|
|
138
138
|
c match {
|
|
139
139
|
case '\\' => read[Char]() match {
|
|
@@ -191,7 +191,7 @@ def decodeJsonObject(): Unit / {Scan[Char], JsonObjectBuilder, Exception[WrongFo
|
|
|
191
191
|
}
|
|
192
192
|
do skip[Char]()
|
|
193
193
|
}
|
|
194
|
-
def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
|
|
194
|
+
def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
|
|
195
195
|
var first = true
|
|
196
196
|
expectString("[")
|
|
197
197
|
with boundary
|
|
@@ -278,7 +278,7 @@ def build[R](){ body: => R / JsonBuilder }: (R, JsonValue) = {
|
|
|
278
278
|
}
|
|
279
279
|
(x, r)
|
|
280
280
|
}
|
|
281
|
-
def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = collectList[JsonValue, R] {
|
|
281
|
+
def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = returning::collectList[JsonValue, R] {
|
|
282
282
|
try body() with JsonBuilder {
|
|
283
283
|
def number(n) = { do emit(Number(n)); resume(()) }
|
|
284
284
|
def bool(b) = { do emit(Bool(b)); resume(()) }
|
|
@@ -296,7 +296,7 @@ def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = collectLi
|
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
|
-
def buildDict[R](){ body: => R / JsonObjectBuilder }: (R, List[(String, JsonValue)]) = collectList[(String, JsonValue), R] {
|
|
299
|
+
def buildDict[R](){ body: => R / JsonObjectBuilder }: (R, List[(String, JsonValue)]) = returning::collectList[(String, JsonValue), R] {
|
|
300
300
|
try body() with JsonObjectBuilder {
|
|
301
301
|
def field(k) = resume { {v} =>
|
|
302
302
|
val x = build{v}
|
|
@@ -315,10 +315,12 @@ namespace test {
|
|
|
315
315
|
|
|
316
316
|
// Read quoted string
|
|
317
317
|
feed("\"\ta\n\ra\"") {
|
|
318
|
-
with scanner[Char
|
|
318
|
+
with scanner[Char]
|
|
319
319
|
println(collectString { readQuotedString() })
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
+
println("")
|
|
323
|
+
|
|
322
324
|
// Parse example
|
|
323
325
|
feed("""{ "a": null, "b": [true,false,false,true], "f": 12.532 }"""){
|
|
324
326
|
with scanner[Char]
|
|
@@ -336,8 +338,10 @@ namespace test {
|
|
|
336
338
|
}
|
|
337
339
|
handleJsonBuilder{d}{ decodeJson() }
|
|
338
340
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
+
|
|
342
|
+
println("")
|
|
343
|
+
|
|
344
|
+
try {
|
|
341
345
|
// Encode example
|
|
342
346
|
encodeJson {
|
|
343
347
|
do dict{
|
|
@@ -352,13 +356,15 @@ namespace test {
|
|
|
352
356
|
}
|
|
353
357
|
}
|
|
354
358
|
}
|
|
355
|
-
|
|
359
|
+
|
|
360
|
+
println("")
|
|
361
|
+
|
|
356
362
|
// format with intermediate value
|
|
357
|
-
val j = feed("""{
|
|
358
|
-
"a": null,
|
|
359
|
-
"b": [true, false, false, true], "f": 12.532
|
|
363
|
+
val j = feed[JsonValue]("""{
|
|
364
|
+
"a": null,
|
|
365
|
+
"b": [true, false, false, true], "f": 12.532
|
|
360
366
|
} """){
|
|
361
|
-
with scanner[Char, JsonValue]
|
|
367
|
+
with returning::scanner[Char, JsonValue]
|
|
362
368
|
build {
|
|
363
369
|
decodeJson()
|
|
364
370
|
}.second
|
|
@@ -366,18 +372,20 @@ namespace test {
|
|
|
366
372
|
encodeJson{
|
|
367
373
|
unbuild(j)
|
|
368
374
|
}
|
|
369
|
-
|
|
375
|
+
|
|
376
|
+
println("")
|
|
377
|
+
|
|
370
378
|
// format (minify) example
|
|
371
379
|
encodeJson{
|
|
372
|
-
feed("""{
|
|
373
|
-
"a": null,
|
|
374
|
-
"b": [true, false, false, true], "f": 12.532
|
|
380
|
+
feed("""{
|
|
381
|
+
"a": null,
|
|
382
|
+
"b": [true, false, false, true], "f": 12.532
|
|
375
383
|
} """){
|
|
376
|
-
with scanner[Char
|
|
384
|
+
with scanner[Char]
|
|
377
385
|
decodeJson()
|
|
378
386
|
}
|
|
379
387
|
}
|
|
380
|
-
|
|
388
|
+
|
|
381
389
|
} with emit[String] { e =>
|
|
382
390
|
resume(println(e))
|
|
383
391
|
}
|
|
@@ -670,35 +670,113 @@ def partition[A](l: List[A]) { pred: A => Bool }: (List[A], List[A]) = {
|
|
|
670
670
|
(lefts.reverse, rights.reverse)
|
|
671
671
|
}
|
|
672
672
|
|
|
673
|
-
///
|
|
673
|
+
/// Utilities for sorting, see 'sortBy' for more details.
|
|
674
|
+
namespace sort {
|
|
675
|
+
/// Splits the given list into monotonic segments (so a list of lists).
|
|
676
|
+
///
|
|
677
|
+
/// Internally used in the mergesort 'sortBy' to prepare the to-be-merged partitions.
|
|
678
|
+
def sequences[A](list: List[A]) { compare: (A, A) => Bool }: List[List[A]] = list match {
|
|
679
|
+
case Cons(a, Cons(b, rest)) =>
|
|
680
|
+
if (compare(a, b)) {
|
|
681
|
+
ascending(b, rest) { diffRest => Cons(a, diffRest) } {compare}
|
|
682
|
+
} else {
|
|
683
|
+
descending(b, [a], rest) {compare}
|
|
684
|
+
}
|
|
685
|
+
case _ => [list]
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/// When in an ascending sequence, try to add `current` to `run` (if possible)
|
|
689
|
+
def ascending[A](current: A, rest: List[A]) { runDiff: List[A] => List[A] } { compare: (A, A) => Bool }: List[List[A]] = rest match {
|
|
690
|
+
case Cons(next, tail) and compare(current, next) =>
|
|
691
|
+
ascending(next, tail) { diffRest => runDiff(Cons(current, diffRest)) } {compare}
|
|
692
|
+
case _ => Cons(runDiff([current]), sequences(rest) {compare})
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/// When in an descending sequence, try to add `current` to `run` (if possible)
|
|
696
|
+
def descending[A](current: A, run: List[A], rest: List[A]) { compare: (A, A) => Bool }: List[List[A]] = rest match {
|
|
697
|
+
case Cons(next, tail) and not(compare(current, next)) =>
|
|
698
|
+
descending(next, Cons(current, run), tail) {compare}
|
|
699
|
+
case _ => Cons(Cons(current, run), sequences(rest) {compare})
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
def mergeAll[A](runs: List[List[A]]) { compare: (A, A) => Bool }: List[A] = runs match {
|
|
703
|
+
case Cons(single, Nil()) => single
|
|
704
|
+
case _ => {
|
|
705
|
+
// recursively merge in pairs until there's only a single list
|
|
706
|
+
val newRuns = mergePairs(runs) {compare}
|
|
707
|
+
mergeAll(newRuns) {compare}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
def mergePairs[A](runs: List[List[A]]) { compare: (A, A) => Bool }: List[List[A]] = runs match {
|
|
712
|
+
case Cons(a, Cons(b, rest)) =>
|
|
713
|
+
Cons(merge(a, b) {compare}, mergePairs(rest) {compare})
|
|
714
|
+
case _ => runs
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
def merge[A](l1: List[A], l2: List[A]) { compare: (A, A) => Bool }: List[A] =
|
|
718
|
+
(l1, l2) match {
|
|
719
|
+
case (Nil(), _) => l2
|
|
720
|
+
case (_, Nil()) => l1
|
|
721
|
+
case (Cons(h1, t1), Cons(h2, t2)) =>
|
|
722
|
+
if (compare(h1, h2)) {
|
|
723
|
+
Cons(h1, merge(t1, l2) {compare})
|
|
724
|
+
} else {
|
|
725
|
+
Cons(h2, merge(l1, t2) {compare})
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/// Sort a list given a comparison operator (like less-or-equal!)
|
|
731
|
+
/// The sorting algorithm is stable and should act reasonably well on partially sorted data.
|
|
732
|
+
///
|
|
733
|
+
/// Examples:
|
|
734
|
+
/// ```
|
|
735
|
+
/// > [1, 3, -1, 5].sortBy { (a, b) => a <= b }
|
|
736
|
+
/// [-1, 1, 3, 5]
|
|
737
|
+
///
|
|
738
|
+
/// > [1, 3, -1, 5].sortBy { (a, b) => a >= b }
|
|
739
|
+
/// [5, 3, 1, -1]
|
|
740
|
+
///
|
|
741
|
+
/// > [(1, 0), (0, 1), (-1, 1), (0, 0)].sortBy { (a, b) => a.first + a.second <= b.first + b.second }
|
|
742
|
+
/// [(1, -1), (0, 0), (1, 0), (0, 1)]
|
|
743
|
+
///
|
|
744
|
+
/// > Nil[Int]().sortBy { (a, b) => a <= b }
|
|
745
|
+
/// []
|
|
746
|
+
/// ```
|
|
674
747
|
///
|
|
675
748
|
/// Note: this implementation is not stacksafe!
|
|
749
|
+
/// (works for ~5M random elements just fine, but OOMs on ~10M random elements)
|
|
676
750
|
///
|
|
677
|
-
/// O(N log N)
|
|
678
|
-
def sortBy[A](
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
751
|
+
/// O(N log N) worstcase
|
|
752
|
+
def sortBy[A](list: List[A]) { lessOrEqual: (A, A) => Bool }: List[A] = {
|
|
753
|
+
val monotonicRuns = sort::sequences(list) {lessOrEqual}
|
|
754
|
+
sort::mergeAll(monotonicRuns) {lessOrEqual}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/// Sort a list of integers in an ascending order.
|
|
758
|
+
/// See 'sortBy' for more details.
|
|
759
|
+
///
|
|
760
|
+
/// O(N log N) worstcase
|
|
761
|
+
def sort(l: List[Int]): List[Int] = l.sortBy { (a, b) => a <= b }
|
|
762
|
+
|
|
763
|
+
/// Sort a list of doubles in an ascending order.
|
|
764
|
+
/// See 'sortBy' for more details.
|
|
765
|
+
///
|
|
766
|
+
/// O(N log N) worstcase
|
|
767
|
+
def sort(l: List[Double]): List[Double] = l.sortBy { (a, b) => a <= b }
|
|
687
768
|
|
|
688
|
-
def sort(l: List[Int]): List[Int] = l.sortBy { (a, b) => a < b }
|
|
689
|
-
def sort(l: List[Double]): List[Double] = l.sortBy { (a, b) => a < b }
|
|
690
769
|
|
|
691
|
-
/// Check if a list is sorted according to the given comparison function.
|
|
770
|
+
/// Check if a list is sorted according to the given comparison function (less-or-equal).
|
|
692
771
|
///
|
|
693
772
|
/// O(N)
|
|
694
|
-
def isSortedBy[A](list: List[A]) {
|
|
773
|
+
def isSortedBy[A](list: List[A]) { lessOrEqual: (A, A) => Bool }: Bool = {
|
|
695
774
|
def go(list: List[A]): Bool = {
|
|
696
775
|
list match {
|
|
697
|
-
case Nil() => true
|
|
698
|
-
case Cons(x, Nil()) => true
|
|
699
776
|
case Cons(x, Cons(y, rest)) =>
|
|
700
777
|
val next = Cons(y, rest) // Future work: Replace this by an @-pattern!
|
|
701
|
-
|
|
778
|
+
lessOrEqual(x, y) && go(next)
|
|
779
|
+
case _ => true
|
|
702
780
|
}
|
|
703
781
|
}
|
|
704
782
|
go(list)
|