@effekt-lang/effekt 0.14.0 → 0.16.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/char.effekt +47 -23
- package/libraries/common/effekt.effekt +6 -2
- package/libraries/common/io/filesystem.effekt +2 -0
- package/libraries/common/io.effekt +2 -1
- package/libraries/common/json.effekt +385 -0
- package/libraries/common/scanner.effekt +171 -0
- package/libraries/common/stream.effekt +161 -134
- package/libraries/llvm/array.c +5 -1
- package/libraries/llvm/bytearray.c +24 -18
- package/libraries/llvm/io.c +5 -5
- package/libraries/llvm/main.c +0 -1
- package/libraries/llvm/ref.c +2 -0
- package/libraries/llvm/rts.ll +20 -0
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +2 -2
- package/licenses/mit license - mit-license.html +1263 -0
- package/licenses/the bsd license - bsd-license.html +1261 -0
- package/licenses/the mit license - mit.html +1263 -0
- package/package.json +1 -1
- package/libraries/llvm/sanity.c +0 -16
package/bin/effekt
CHANGED
|
Binary file
|
|
@@ -15,33 +15,57 @@ def isWhitespace(c: Char): Bool = c match {
|
|
|
15
15
|
case _ => false
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
/// Checks if the given character is an ASCII digit in base 10
|
|
19
|
-
/// Use `digitValue(c: Char)` to get the numeric value out.
|
|
20
|
-
def isDigit(c: Char): Bool = { c >= '0' && c <= '9' }
|
|
21
|
-
|
|
22
|
-
/// Checks if the given character is an ASCII digit in the given base
|
|
23
|
-
/// Use `digitValue(c: Char, base: Int)` to get the numeric value out.
|
|
24
|
-
def isDigit(c: Char, base: Int): Bool = {
|
|
25
|
-
with on[WrongFormat].default { false }
|
|
26
|
-
val _ = digitValue(c, base)
|
|
27
|
-
true
|
|
28
|
-
}
|
|
29
|
-
|
|
30
18
|
/// Gets the value of a given ASCII digit in base 10
|
|
31
|
-
def digitValue(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
19
|
+
def digitValue(char: Char): Option[Int] =
|
|
20
|
+
// TODO use fail
|
|
21
|
+
if (char >= '0' && char <= '9') {
|
|
22
|
+
Some(char.toInt - '0'.toInt)
|
|
23
|
+
} else {
|
|
24
|
+
None()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Gets the value of a given ASCII digit in base 16
|
|
28
|
+
def hexDigitValue(char: Char): Option[Int] =
|
|
29
|
+
// TODO use fail
|
|
30
|
+
char match {
|
|
31
|
+
case char and char >= '0' && char <= '9' => Some(char.toInt - '0'.toInt)
|
|
32
|
+
case char and char >= 'A' && char <= 'F' => Some((char.toInt - 'A'.toInt) + 10)
|
|
33
|
+
case char and char >= 'a' && char <= 'f' => Some((char.toInt - 'a'.toInt) + 10)
|
|
34
|
+
case _ => None()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Gets the value of a given ASCII digit in the given base up to 36
|
|
38
|
+
def digitValue(char: Char, base: Int): Option[Int] = {
|
|
39
|
+
// TODO use fail
|
|
40
|
+
val perhapsDigit = char match {
|
|
41
|
+
case char and char >= '0' && char <= '9' => Some(char.toInt - '0'.toInt)
|
|
42
|
+
case char and char >= 'A' && char <= 'Z' => Some((char.toInt - 'A'.toInt) + 10)
|
|
43
|
+
case char and char >= 'a' && char <= 'z' => Some((char.toInt - 'a'.toInt) + 10)
|
|
44
|
+
case _ => None()
|
|
45
|
+
}
|
|
46
|
+
perhapsDigit match {
|
|
47
|
+
case Some(digit) =>
|
|
48
|
+
if (digit < base) {
|
|
49
|
+
Some(digit)
|
|
50
|
+
} else {
|
|
51
|
+
None()
|
|
52
|
+
}
|
|
53
|
+
case None() => None()
|
|
41
54
|
}
|
|
42
|
-
if (v >= base) { wrongFormat(c.toString ++ " is not a valid digit in base " ++ base.show) } else { v }
|
|
43
55
|
}
|
|
44
56
|
|
|
57
|
+
/// Checks if the given character is an ASCII digit in base 10
|
|
58
|
+
/// Use `digitValue(c: Char)` to get the numeric value out.
|
|
59
|
+
def isDigit(char: Char): Bool = digitValue(char).isDefined
|
|
60
|
+
|
|
61
|
+
/// Checks if the given character is an ASCII digit in base 16
|
|
62
|
+
/// Use `hexDigitValue(c: Char)` to get the numeric value out.
|
|
63
|
+
def isHexDigit(char: Char): Bool = hexDigitValue(char).isDefined
|
|
64
|
+
|
|
65
|
+
/// Checks if the given character is an ASCII digit in base 10
|
|
66
|
+
/// Use `digitValue(c: Char)` to get the numeric value out.
|
|
67
|
+
def isDigit(char: Char, base: Int): Bool = digitValue(char, base).isDefined
|
|
68
|
+
|
|
45
69
|
/// Checks if a given character is a 7-bit ASCII character
|
|
46
70
|
def isASCII(c: Char): Bool = { c.toInt < 128 }
|
|
47
71
|
|
|
@@ -85,6 +85,7 @@ extern pure def show(value: Char): String =
|
|
|
85
85
|
"""
|
|
86
86
|
|
|
87
87
|
extern pure def show(value: Byte): String =
|
|
88
|
+
js "'' + ${value}"
|
|
88
89
|
llvm """
|
|
89
90
|
%z = call %Pos @c_bytearray_show_Byte(i8 ${value})
|
|
90
91
|
ret %Pos %z
|
|
@@ -114,7 +115,6 @@ extern pure def length(str: String): Int =
|
|
|
114
115
|
chez "(string-length ${str})"
|
|
115
116
|
llvm """
|
|
116
117
|
%x = call %Int @c_bytearray_size(%Pos ${str})
|
|
117
|
-
call void @erasePositive(%Pos ${str})
|
|
118
118
|
ret %Int %x
|
|
119
119
|
"""
|
|
120
120
|
|
|
@@ -367,7 +367,11 @@ def pow(base: Double, exponent: Int): Double = {
|
|
|
367
367
|
else if (exponent.mod(2) == 0) loop(base * base, exponent / 2, acc) // even exponent
|
|
368
368
|
else loop(base, exponent - 1, acc * base) // odd exponent
|
|
369
369
|
}
|
|
370
|
-
|
|
370
|
+
if (exponent < 0) {
|
|
371
|
+
loop(1.0 / base, 0 - exponent, 1.0)
|
|
372
|
+
} else {
|
|
373
|
+
loop(base, exponent, 1.0)
|
|
374
|
+
}
|
|
371
375
|
}
|
|
372
376
|
|
|
373
377
|
extern pure def pow(base: Double, exponent: Double): Double =
|
|
@@ -198,6 +198,7 @@ namespace internal {
|
|
|
198
198
|
ret void
|
|
199
199
|
"""
|
|
200
200
|
|
|
201
|
+
/// The buffer must be kept alive by using it after the call
|
|
201
202
|
extern async def read(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int): Int =
|
|
202
203
|
jsNode "$effekt.capture(callback => read(${file}, ${buffer}, ${offset}, ${size}, ${position}, callback))"
|
|
203
204
|
llvm """
|
|
@@ -205,6 +206,7 @@ namespace internal {
|
|
|
205
206
|
ret void
|
|
206
207
|
"""
|
|
207
208
|
|
|
209
|
+
/// The buffer must be kept alive by using it after the call
|
|
208
210
|
extern async def write(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int): Int =
|
|
209
211
|
jsNode "$effekt.capture(callback => write(${file}, ${buffer}, ${offset}, ${size}, ${position}, callback))"
|
|
210
212
|
llvm """
|
|
@@ -16,7 +16,8 @@ extern async def spawn(task: Task[Unit]): Unit =
|
|
|
16
16
|
js "$effekt.capture(k => { setTimeout(() => k($effekt.unit), 0); return $effekt.run(${task}) })"
|
|
17
17
|
llvm """
|
|
18
18
|
call void @c_yield(%Stack %stack)
|
|
19
|
-
call
|
|
19
|
+
%unboxed = call ccc %Neg @unbox(%Pos ${task})
|
|
20
|
+
call void @run(%Neg %unboxed)
|
|
20
21
|
ret void
|
|
21
22
|
"""
|
|
22
23
|
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
module json
|
|
2
|
+
|
|
3
|
+
import char
|
|
4
|
+
import scanner
|
|
5
|
+
import stream
|
|
6
|
+
|
|
7
|
+
/// for constructing a json value
|
|
8
|
+
interface JsonBuilder {
|
|
9
|
+
def null(): Unit
|
|
10
|
+
def bool(b: Bool): Unit
|
|
11
|
+
def number(n: Double): Unit
|
|
12
|
+
def string(s: String): Unit
|
|
13
|
+
def list[R](){ elements: => R / JsonBuilder }: R
|
|
14
|
+
def dict[R](){ elements: => R / JsonObjectBuilder }: R
|
|
15
|
+
}
|
|
16
|
+
/// for constructing a json object
|
|
17
|
+
interface JsonObjectBuilder {
|
|
18
|
+
def field[R](k: String){ contents: => R / JsonBuilder }: R
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
def escape(s: String): String = "\"" ++ s ++ "\"" // TODO handle escapes
|
|
22
|
+
|
|
23
|
+
/// Make explicitly bound JsonBuilder instance implicit
|
|
24
|
+
def handleJsonBuilder[R]{b: JsonBuilder}{ body: => R / JsonBuilder }: R = try body() with JsonBuilder {
|
|
25
|
+
def null() = resume(b.null())
|
|
26
|
+
def bool(v) = resume(b.bool(v))
|
|
27
|
+
def number(n) = resume(b.number(n))
|
|
28
|
+
def string(s) = resume(b.string(s))
|
|
29
|
+
def list() = resume { {c} => b.list{c} }
|
|
30
|
+
def dict() = resume { {c} => b.dict{c} }
|
|
31
|
+
}
|
|
32
|
+
/// Make explicitly bound JsonObjectBuilder instance implicit
|
|
33
|
+
def handleJsonObjectBuilder[R]{b: JsonObjectBuilder}{ body: => R / JsonObjectBuilder }: R = try body() with JsonObjectBuilder {
|
|
34
|
+
def field(k) = resume { {c} => b.field(k){c} }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// --------------------------------------------------------------------------------
|
|
38
|
+
// Encoding
|
|
39
|
+
// --------------------------------------------------------------------------------
|
|
40
|
+
def encodeJsonList[R]{ body: => R / JsonBuilder }: R / emit[String] = {
|
|
41
|
+
var first = true
|
|
42
|
+
do emit("[")
|
|
43
|
+
def c() = {
|
|
44
|
+
if (not(first)) { do emit(",") }
|
|
45
|
+
first = false
|
|
46
|
+
}
|
|
47
|
+
val r = encodeJson {
|
|
48
|
+
try { body() } with JsonBuilder {
|
|
49
|
+
def null() = { c(); resume(do null()) }
|
|
50
|
+
def bool(v) = { c(); resume(do bool(v)) }
|
|
51
|
+
def number(n) = { c(); resume(do number(n)) }
|
|
52
|
+
def string(s) = { c(); resume(do string(s)) }
|
|
53
|
+
def list() = resume { { b } => c(); do list{b} }
|
|
54
|
+
def dict() = resume { { b } => c(); do dict{b} }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
do emit("]")
|
|
58
|
+
r
|
|
59
|
+
}
|
|
60
|
+
def encodeJsonObject[R]{ body: => R / JsonObjectBuilder }: R / emit[String] = {
|
|
61
|
+
var first = true
|
|
62
|
+
do emit("{")
|
|
63
|
+
def c(k: String) = {
|
|
64
|
+
if (not(first)) { do emit(",") }
|
|
65
|
+
do emit(escape(k)); do emit(":")
|
|
66
|
+
first = false
|
|
67
|
+
}
|
|
68
|
+
val r = encodeJson {
|
|
69
|
+
try body() with JsonObjectBuilder {
|
|
70
|
+
def field(k) = resume { {b} => c(k); b() }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
do emit("}")
|
|
74
|
+
r
|
|
75
|
+
}
|
|
76
|
+
/// Main entry point for encoding json.
|
|
77
|
+
/// Emits individual tokens of the resulting json.
|
|
78
|
+
def encodeJson[R]{ body: => R / JsonBuilder }: R / emit[String] = {
|
|
79
|
+
try body() with JsonBuilder {
|
|
80
|
+
def null() = { resume(do emit("null")) }
|
|
81
|
+
def bool(v) = { resume(do emit( if(v){ "true" } else { "false" } )) }
|
|
82
|
+
def number(n) = { resume(do emit(show(n))) }
|
|
83
|
+
def string(s) = { resume(do emit(escape(s))) }
|
|
84
|
+
def list() = resume { {b} => encodeJsonList { b() } }
|
|
85
|
+
def dict() = resume { {b} => encodeJsonObject { b() } }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// --------------------------------------------------------------------------------
|
|
90
|
+
// Decoding
|
|
91
|
+
// --------------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
/// Read a double value, in the common format (TODO document)
|
|
95
|
+
def readDouble(): Double / Scan[Char] = {
|
|
96
|
+
def exponent(pre: Double) = {
|
|
97
|
+
try {
|
|
98
|
+
if(do peek() == 'e' || do peek() == 'E') {
|
|
99
|
+
do skip[Char]()
|
|
100
|
+
val e = do peek[Char]() match {
|
|
101
|
+
case '+' => do skip[Char](); readDecimal()
|
|
102
|
+
case '-' => do skip[Char](); neg(readDecimal())
|
|
103
|
+
case _ => readDecimal()
|
|
104
|
+
}
|
|
105
|
+
pre * pow(10.0, e)
|
|
106
|
+
} else { pre }
|
|
107
|
+
} with stop {
|
|
108
|
+
pre
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
def fraction(pre: Int) = {
|
|
112
|
+
if (optionally { readIf('.') }) {
|
|
113
|
+
var b = 0.1
|
|
114
|
+
var r = pre.toDouble
|
|
115
|
+
while (optionally[Int] { readDigit() } is Some(d)) {
|
|
116
|
+
r = r + b * d.toDouble
|
|
117
|
+
b = b * 0.1
|
|
118
|
+
}
|
|
119
|
+
exponent(r)
|
|
120
|
+
} else {
|
|
121
|
+
pre.toDouble
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
fraction(readInteger())
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
def expectString(string: String): Unit / { Scan[Char], Exception[WrongFormat] } =
|
|
128
|
+
for[Char] { string.each } { char =>
|
|
129
|
+
expect[Unit]("Expected " ++ string) { readIf(char) }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// Read and unescape a string in ""
|
|
133
|
+
def readQuotedString(): Unit / { Scan[Char], emit[Char], Exception[WrongFormat] } = {
|
|
134
|
+
try {
|
|
135
|
+
skipWhitespace()
|
|
136
|
+
expect[Unit]("Expected \"") { readIf('"') }
|
|
137
|
+
while(read[Char]() is c and c != '"') {
|
|
138
|
+
c match {
|
|
139
|
+
case '\\' => read[Char]() match {
|
|
140
|
+
case '"' => do emit('\"')
|
|
141
|
+
case '\\' => do emit('\\')
|
|
142
|
+
case '/' => do emit('/')
|
|
143
|
+
case 'b' => <>
|
|
144
|
+
case 'f' => <>
|
|
145
|
+
case 'n' => do emit('\n')
|
|
146
|
+
case 'r' => do emit('\r')
|
|
147
|
+
case 't' => do emit('\t')
|
|
148
|
+
case c => wrongFormat("Invalid escape sequence '\\" ++ c.toString ++ "'")
|
|
149
|
+
}
|
|
150
|
+
case o => do emit(o)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
} with stop {
|
|
154
|
+
wrongFormat("Unexpected end of input while reading a string")
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// Decode a json string and do the appropriate calls to an implicitly bound JsonBuilder
|
|
159
|
+
def decodeJson(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
|
|
160
|
+
with boundary
|
|
161
|
+
skipWhitespace()
|
|
162
|
+
if(do peek[Char]() is c) {
|
|
163
|
+
c match {
|
|
164
|
+
case 'n' => expectString("null"); do null()
|
|
165
|
+
case 't' => expectString("true"); do bool(true)
|
|
166
|
+
case 'f' => expectString("false"); do bool(false)
|
|
167
|
+
case '-' => do number(readDouble())
|
|
168
|
+
case d and d.isDigit => do number(readDouble())
|
|
169
|
+
case '"' => do string(collectString { readQuotedString() })
|
|
170
|
+
case '{' => do dict{ decodeJsonObject() }
|
|
171
|
+
case '[' => do list{ decodeJsonList() }
|
|
172
|
+
case _ => println("Unexpected " ++ c.toString); <>
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
def decodeJsonObject(): Unit / {Scan[Char], JsonObjectBuilder, Exception[WrongFormat]} = {
|
|
177
|
+
var first = true
|
|
178
|
+
expectString("{")
|
|
179
|
+
with boundary
|
|
180
|
+
skipWhitespace()
|
|
181
|
+
while(do peek[Char]() is c and (c != '}')) {
|
|
182
|
+
if (not(first)) { expectString(",") }
|
|
183
|
+
val k: String = collectString { readQuotedString() }
|
|
184
|
+
skipWhitespace()
|
|
185
|
+
expectString(":")
|
|
186
|
+
do field(k){
|
|
187
|
+
decodeJson()
|
|
188
|
+
}
|
|
189
|
+
skipWhitespace()
|
|
190
|
+
first = false
|
|
191
|
+
}
|
|
192
|
+
do skip[Char]()
|
|
193
|
+
}
|
|
194
|
+
def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
|
|
195
|
+
var first = true
|
|
196
|
+
expectString("[")
|
|
197
|
+
with boundary
|
|
198
|
+
skipWhitespace()
|
|
199
|
+
while(do peek[Char]() is c and (c != ']')) {
|
|
200
|
+
if (not(first)) { expectString(",") }
|
|
201
|
+
decodeJson()
|
|
202
|
+
skipWhitespace()
|
|
203
|
+
first = false
|
|
204
|
+
}
|
|
205
|
+
do skip[Char]()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// --------------------------------------------------------------------------------
|
|
209
|
+
// Ignoring
|
|
210
|
+
// --------------------------------------------------------------------------------
|
|
211
|
+
/// Ignore the generated calls to JsonObjectBuilder
|
|
212
|
+
def ignoreDict[R](){ body: => R / JsonObjectBuilder }: R = try body() with JsonObjectBuilder {
|
|
213
|
+
def field(k) = resume { {v} => ignore{v} }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/// Ignore the generated calls to JsonBuilder
|
|
217
|
+
def ignore[R](){ body: => R / JsonBuilder }: R = try body() with JsonBuilder {
|
|
218
|
+
def number(n) = resume(())
|
|
219
|
+
def bool(b) = resume(())
|
|
220
|
+
def null() = resume(())
|
|
221
|
+
def string(s) = resume(())
|
|
222
|
+
def list() = resume { {e} => ignore{e} }
|
|
223
|
+
def dict() = resume { {e} => ignoreDict{e} }
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// --------------------------------------------------------------------------------
|
|
227
|
+
// Residualized
|
|
228
|
+
// --------------------------------------------------------------------------------
|
|
229
|
+
/// Residualized Json value
|
|
230
|
+
type JsonValue {
|
|
231
|
+
Number(n: Double);
|
|
232
|
+
Bool(b: Bool);
|
|
233
|
+
Null();
|
|
234
|
+
String(s: String);
|
|
235
|
+
List(l: List[JsonValue]);
|
|
236
|
+
Dict(els: List[(String, JsonValue)])
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// --------------------------------------------------------------------------------
|
|
240
|
+
// Residualized --> Effect
|
|
241
|
+
// --------------------------------------------------------------------------------
|
|
242
|
+
/// Emit the appropriate calls to JsonBuilder to recreate the given JsonValue
|
|
243
|
+
def unbuild(v: JsonValue): Unit / JsonBuilder = v match {
|
|
244
|
+
case Number(n) => do number(n)
|
|
245
|
+
case Bool(b) => do bool(b)
|
|
246
|
+
case Null() => do null()
|
|
247
|
+
case String(s) => do string(s)
|
|
248
|
+
case List(l) => do list {
|
|
249
|
+
l.foreach { e => unbuild(e) }
|
|
250
|
+
}
|
|
251
|
+
case Dict(els) => do dict {
|
|
252
|
+
els.foreach {
|
|
253
|
+
case (k, v) => do field(k){ unbuild(v) }
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// --------------------------------------------------------------------------------
|
|
259
|
+
// Effect --> Residualized
|
|
260
|
+
// --------------------------------------------------------------------------------
|
|
261
|
+
def build[R](){ body: => R / JsonBuilder }: (R, JsonValue) = {
|
|
262
|
+
var r = Null()
|
|
263
|
+
val x = try body() with JsonBuilder {
|
|
264
|
+
def number(n) = { r = Number(n); resume(()) }
|
|
265
|
+
def bool(b) = { r = Bool(b); resume(()) }
|
|
266
|
+
def null() = { r = Null(); resume(()) }
|
|
267
|
+
def string(s) = { r = String(s); resume(()) }
|
|
268
|
+
def list() = resume { {els} =>
|
|
269
|
+
val x = buildList {els}
|
|
270
|
+
r = List(x.second)
|
|
271
|
+
x.first
|
|
272
|
+
}
|
|
273
|
+
def dict() = resume { {fs} =>
|
|
274
|
+
val x = buildDict {fs}
|
|
275
|
+
r = Dict(x.second)
|
|
276
|
+
x.first
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
(x, r)
|
|
280
|
+
}
|
|
281
|
+
def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = collectList[JsonValue, R] {
|
|
282
|
+
try body() with JsonBuilder {
|
|
283
|
+
def number(n) = { do emit(Number(n)); resume(()) }
|
|
284
|
+
def bool(b) = { do emit(Bool(b)); resume(()) }
|
|
285
|
+
def null() = { do emit(Null()); resume(()) }
|
|
286
|
+
def string(s) = { do emit(String(s)); resume(()) }
|
|
287
|
+
def list() = resume { {els} =>
|
|
288
|
+
val x = buildList {els}
|
|
289
|
+
do emit(List(x.second))
|
|
290
|
+
x.first
|
|
291
|
+
}
|
|
292
|
+
def dict() = resume { {fs} =>
|
|
293
|
+
val x = buildDict {fs}
|
|
294
|
+
do emit(Dict(x.second))
|
|
295
|
+
x.first
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
def buildDict[R](){ body: => R / JsonObjectBuilder }: (R, List[(String, JsonValue)]) = collectList[(String, JsonValue), R] {
|
|
300
|
+
try body() with JsonObjectBuilder {
|
|
301
|
+
def field(k) = resume { {v} =>
|
|
302
|
+
val x = build{v}
|
|
303
|
+
do emit((k, x.second))
|
|
304
|
+
x.first
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// --------------------------------------------------------------------------------
|
|
310
|
+
// Examples
|
|
311
|
+
// --------------------------------------------------------------------------------
|
|
312
|
+
namespace test {
|
|
313
|
+
def main() = {
|
|
314
|
+
with on[WrongFormat].panic
|
|
315
|
+
|
|
316
|
+
// Read quoted string
|
|
317
|
+
feed("\"\ta\n\ra\"") {
|
|
318
|
+
with scanner[Char, Unit]
|
|
319
|
+
println(collectString { readQuotedString() })
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Parse example
|
|
323
|
+
feed("""{ "a": null, "b": [true,false,false,true], "f": 12.532 }"""){
|
|
324
|
+
with scanner[Char]
|
|
325
|
+
|
|
326
|
+
def k = new JsonObjectBuilder {
|
|
327
|
+
def field(k){v} = { println(k); ignore{v} }
|
|
328
|
+
}
|
|
329
|
+
def d = new JsonBuilder {
|
|
330
|
+
def number(n) = println(n)
|
|
331
|
+
def bool(b) = ()
|
|
332
|
+
def null() = println("NULL")
|
|
333
|
+
def string(s) = ()
|
|
334
|
+
def list(){e} = handleJsonBuilder{d}{e}
|
|
335
|
+
def dict(){e} = handleJsonObjectBuilder{k}{e}
|
|
336
|
+
}
|
|
337
|
+
handleJsonBuilder{d}{ decodeJson() }
|
|
338
|
+
}
|
|
339
|
+
try{
|
|
340
|
+
|
|
341
|
+
// Encode example
|
|
342
|
+
encodeJson {
|
|
343
|
+
do dict{
|
|
344
|
+
do field("x"){
|
|
345
|
+
do string("Hallo")
|
|
346
|
+
}
|
|
347
|
+
do field("y"){
|
|
348
|
+
do null()
|
|
349
|
+
}
|
|
350
|
+
do field("z"){
|
|
351
|
+
do number(12.3783)
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// format with intermediate value
|
|
357
|
+
val j = feed("""{
|
|
358
|
+
"a": null,
|
|
359
|
+
"b": [true, false, false, true], "f": 12.532
|
|
360
|
+
} """){
|
|
361
|
+
with scanner[Char, JsonValue]
|
|
362
|
+
build {
|
|
363
|
+
decodeJson()
|
|
364
|
+
}.second
|
|
365
|
+
}
|
|
366
|
+
encodeJson{
|
|
367
|
+
unbuild(j)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// format (minify) example
|
|
371
|
+
encodeJson{
|
|
372
|
+
feed("""{
|
|
373
|
+
"a": null,
|
|
374
|
+
"b": [true, false, false, true], "f": 12.532
|
|
375
|
+
} """){
|
|
376
|
+
with scanner[Char, Unit]
|
|
377
|
+
decodeJson()
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
} with emit[String] { e =>
|
|
382
|
+
resume(println(e))
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
module scanner
|
|
2
|
+
|
|
3
|
+
import char
|
|
4
|
+
import stream
|
|
5
|
+
|
|
6
|
+
interface Scan[A] {
|
|
7
|
+
/// Return the next character, not advancing.
|
|
8
|
+
/// That is, this does not change what any future calls on the Scanner return.
|
|
9
|
+
def peek(): A / stop
|
|
10
|
+
/// Advance the Scanner to the next character.
|
|
11
|
+
def skip(): Unit / stop
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/// Advance the Scanner to the next character, returning it.
|
|
15
|
+
def read[A](): A / { Scan[A], stop } = {
|
|
16
|
+
val t = do peek[A]()
|
|
17
|
+
do skip[A]()
|
|
18
|
+
return t
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Run a scanner by reading from an input stream, discarding its output.
|
|
22
|
+
def scanner[A] { scanner: () => Unit / Scan[A] }: Unit / read[A] =
|
|
23
|
+
returning::scanner[A, Unit]{scanner}
|
|
24
|
+
|
|
25
|
+
def expect(message: String) { scanner: () => Unit / stop }: Unit / Exception[WrongFormat] =
|
|
26
|
+
returning::expect[Unit](message){scanner}
|
|
27
|
+
|
|
28
|
+
/// Check that the next token satisfies the predicate and skip and return it when it does.
|
|
29
|
+
def readIf[A] { predicate: A => Bool }: A / { Scan[A], stop } = {
|
|
30
|
+
val t = do peek[A]()
|
|
31
|
+
if (predicate(t)) {
|
|
32
|
+
do skip[A]()
|
|
33
|
+
return t
|
|
34
|
+
} else {
|
|
35
|
+
do stop()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Check that the next token satisfies the predicate and skip it when it does.
|
|
40
|
+
def skipIf[A] { predicate: A => Bool }: Unit / { Scan[A], stop } = {
|
|
41
|
+
readIf{predicate}; ()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
def readSome[A, B] { convert: A => Option[B] }: B / { Scan[A], stop } =
|
|
45
|
+
if (convert(do peek()) is Some(t)) {
|
|
46
|
+
do skip[A]()
|
|
47
|
+
return t
|
|
48
|
+
} else {
|
|
49
|
+
do stop()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// Reads until the predicate does not hold for the next token.
|
|
53
|
+
/// Emits the tokens read.
|
|
54
|
+
def readWhile[A] { predicate: A => Bool }: Unit / { Scan[A], emit[A] } =
|
|
55
|
+
many { readIf{predicate} }
|
|
56
|
+
|
|
57
|
+
/// Skips until the predicate does not hold for the next token.
|
|
58
|
+
def skipWhile[A] { predicate: A => Bool }: Unit / { Scan[A] } =
|
|
59
|
+
for[A] { readWhile{predicate} } { _ => () }
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/// Read the next character if it is the given one and return true in this case.
|
|
63
|
+
def readIf(e: Char): Unit / { Scan[Char], stop } = {
|
|
64
|
+
readIf[Char] { c => c == e }; ()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Check that the next token satisfies the predicate and skip it when it does.
|
|
68
|
+
def skipIf(e: Char): Unit / { Scan[Char], stop } =
|
|
69
|
+
skipIf { c => c == e }
|
|
70
|
+
|
|
71
|
+
/// Skip until the next character is not a whitespace character
|
|
72
|
+
def skipWhitespace(): Unit / Scan[Char] =
|
|
73
|
+
skipWhile { c => c.isWhitespace }
|
|
74
|
+
|
|
75
|
+
/// Read as many characters corresponding to the given string as possible.
|
|
76
|
+
def readString(string: String): Unit / { Scan[Char], stop } =
|
|
77
|
+
for[Char] { string.each } { char => readIf(char) }
|
|
78
|
+
|
|
79
|
+
/// Check that the next character is a digit in base 10, and if so read and return it.
|
|
80
|
+
def readDigit(): Int / { Scan[Char], stop } =
|
|
81
|
+
readSome[Char, Int]{ char => digitValue(char) }
|
|
82
|
+
|
|
83
|
+
/// Read a positive decimal number.
|
|
84
|
+
def readDecimal(): Int / Scan[Char] = {
|
|
85
|
+
var result = 0
|
|
86
|
+
for[Int] { many { readDigit() } } { digit =>
|
|
87
|
+
result = result * 10 + digit
|
|
88
|
+
}
|
|
89
|
+
result
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// Check that the next character is a digit in base 16, and if so read and return it.
|
|
93
|
+
def readHexDigit(): Int / { Scan[Char], stop } =
|
|
94
|
+
readSome[Char, Int]{ char => hexDigitValue(char) }
|
|
95
|
+
|
|
96
|
+
/// Read a hexadecimal number.
|
|
97
|
+
def readHexadecimal(): Int / Scan[Char] = {
|
|
98
|
+
var result = 0
|
|
99
|
+
for[Int] { many { readHexDigit() } } { digit =>
|
|
100
|
+
result = result * 16 + digit
|
|
101
|
+
}
|
|
102
|
+
result
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// Read a decimal integer.
|
|
106
|
+
def readInteger(): Int / Scan[Char] =
|
|
107
|
+
if (optionally { readIf('-') }) {
|
|
108
|
+
neg(readDecimal())
|
|
109
|
+
} else {
|
|
110
|
+
readDecimal()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
namespace returning {
|
|
115
|
+
|
|
116
|
+
/// Run a scanner by reading from an input stream.
|
|
117
|
+
def scanner[A, R] { scanner: () => R / Scan[A] }: R / read[A] = {
|
|
118
|
+
var last = None()
|
|
119
|
+
try {
|
|
120
|
+
scanner()
|
|
121
|
+
} with Scan[A] {
|
|
122
|
+
def peek() = resume {
|
|
123
|
+
last match {
|
|
124
|
+
case None() =>
|
|
125
|
+
val t = do read[A]()
|
|
126
|
+
last = Some(t)
|
|
127
|
+
return t
|
|
128
|
+
case Some(t) =>
|
|
129
|
+
return t
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
def skip() = resume {
|
|
133
|
+
last match {
|
|
134
|
+
case None() =>
|
|
135
|
+
val _ = do read[A]()
|
|
136
|
+
return ()
|
|
137
|
+
case Some(t) =>
|
|
138
|
+
last = None()
|
|
139
|
+
return ()
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
def expect[R](message: String) { scanner: () => R / stop }: R / Exception[WrongFormat] =
|
|
146
|
+
attempt { scanner() } { wrongFormat(message) }
|
|
147
|
+
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
namespace test {
|
|
152
|
+
def main(): Unit = {
|
|
153
|
+
|
|
154
|
+
val input =
|
|
155
|
+
"""
|
|
156
|
+
12
|
|
157
|
+
-13
|
|
158
|
+
A1B2
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
with feed(input)
|
|
162
|
+
with scanner[Char]
|
|
163
|
+
|
|
164
|
+
skipWhitespace()
|
|
165
|
+
println(readInteger().show)
|
|
166
|
+
skipWhitespace()
|
|
167
|
+
println(readInteger().show)
|
|
168
|
+
skipWhitespace()
|
|
169
|
+
println(readHexadecimal().show)
|
|
170
|
+
}
|
|
171
|
+
}
|