@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 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(c: Char): Int / Exception[WrongFormat] =
32
- digitValue(c, 10)
33
-
34
- /// Gets the value of a given ASCII digit in the given base
35
- def digitValue(c: Char, base: Int): Int / Exception[WrongFormat] = {
36
- val v = c match {
37
- case c and c >= '0' && c <= '9' => (c.toInt - '0'.toInt)
38
- case c and c >= 'a' && c <= 'z' => (c.toInt - 'a'.toInt) + 10
39
- case c and c >= 'A' && c <= 'Z' => (c.toInt - 'A'.toInt) + 10
40
- case _ => wrongFormat("'" ++ c.toString ++ "' is not a digit")
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
- loop(base, exponent, 1.0)
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 void @run(%Neg ${task})
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
+ }