@effekt-lang/effekt 0.65.0 → 0.67.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.
Files changed (33) hide show
  1. package/bin/effekt +0 -0
  2. package/libraries/chez/cps/effekt.ss +5 -11
  3. package/libraries/common/args.effekt +2 -2
  4. package/libraries/common/array.effekt +26 -8
  5. package/libraries/common/bytearray.effekt +18 -10
  6. package/libraries/common/bytestream.effekt +4 -4
  7. package/libraries/common/char.effekt +10 -10
  8. package/libraries/common/dictionary.effekt +44 -43
  9. package/libraries/common/effekt.effekt +16 -5
  10. package/libraries/common/io/channel.effekt +29 -22
  11. package/libraries/common/io/console.effekt +2 -1
  12. package/libraries/common/io/filesystem.effekt +5 -5
  13. package/libraries/common/io/network.effekt +2 -2
  14. package/libraries/common/io/promise.effekt +2 -2
  15. package/libraries/common/io/signal.effekt +86 -22
  16. package/libraries/common/json.effekt +16 -42
  17. package/libraries/common/list.effekt +4 -4
  18. package/libraries/common/process.effekt +6 -2
  19. package/libraries/common/random.effekt +29 -7
  20. package/libraries/common/ref.effekt +4 -1
  21. package/libraries/common/regex.effekt +6 -4
  22. package/libraries/common/resizable_array.effekt +4 -4
  23. package/libraries/common/scanner.effekt +36 -41
  24. package/libraries/common/stream.effekt +76 -36
  25. package/libraries/common/string.effekt +14 -10
  26. package/libraries/common/test.effekt +2 -2
  27. package/libraries/js/effekt_runtime.js +55 -43
  28. package/libraries/llvm/io.c +0 -55
  29. package/licenses/eclipse public license, version 2.0 - epl-2.0.html +4 -4
  30. package/licenses/mit license - mit-license.html +33 -31
  31. package/licenses/the bsd license - bsd-license.html +33 -31
  32. package/licenses/the mit license - mit.html +34 -30
  33. package/package.json +1 -1
@@ -5,32 +5,29 @@ module io/signal
5
5
 
6
6
  /// Must be fired exactly once with an A receiving a B,
7
7
  /// and must be waited for exactly once with a B receiving an A.
8
- extern type Signal[A, B]
8
+ extern type Signal[A, B] =
9
+ js "{ state: int, value, stack }"
10
+ llvm "%Pos"
9
11
 
10
- extern def signal[A, B]() at global: Signal[A, B] =
11
- js "signal$make()"
12
- llvm """
13
- %signal = call %Pos @c_signal_make()
14
- ret %Pos %signal
15
- """
12
+ extern llvm """
13
+ %Signal = type { %Header, i64, ptr, %Pos }
16
14
 
17
- extern def unsafeFire[A, B](signal: Signal[A, B], value: A) at async: B =
18
- js "$effekt.capture(callback => signal$notify(${signal}, ${value}, callback))"
19
- llvm """
20
- call void @c_signal_notify(%Pos ${signal}, %Pos ${value}, %Stack %stack)
15
+ define void @signal_erase(ptr %envPtr) {
16
+ %base = getelementptr %Header, ptr %envPtr, i64 -1
17
+ %statePtr = getelementptr %Signal, ptr %base, i64 0, i32 1
18
+ %stackPtr = getelementptr %Signal, ptr %base, i64 0, i32 2
19
+ %valuePtr = getelementptr %Signal, ptr %base, i64 0, i32 3
20
+ %state = load i64, ptr %statePtr
21
+ switch i64 %state, label %none [i64 0, label %free i64 1, label %free i64 2, label %free]
22
+ free:
23
+ %stack = load %Stack, ptr %stackPtr
24
+ %value = load %Pos, ptr %valuePtr
25
+ call void @erasePositive(%Pos %value)
26
+ call void @eraseStack(%Stack %stack)
21
27
  ret void
22
- """
23
-
24
- extern def unsafeWait[A, B](signal: Signal[A, B], value: B) at async: A =
25
- js "$effekt.capture(callback => signal$notify(${signal}, ${value}, callback))"
26
- llvm """
27
- call void @c_signal_notify(%Pos ${signal}, %Pos ${value}, %Stack %stack)
28
+ none:
28
29
  ret void
29
- """
30
-
31
- extern llvm """
32
- declare %Pos @c_signal_make()
33
- declare void @c_signal_notify(%Pos, %Pos, %Stack)
30
+ }
34
31
  """
35
32
 
36
33
  extern js """
@@ -53,3 +50,70 @@ extern js """
53
50
  }
54
51
  }
55
52
  """
53
+
54
+ extern def signal[A, B]() at global: Signal[A, B] =
55
+ js "signal$make()"
56
+ llvm """
57
+ %base = call ptr @calloc(i64 1, i64 48)
58
+ %eraserPtr = getelementptr %Signal, ptr %base, i64 0, i32 0, i32 1
59
+ store ptr @signal_erase, ptr %eraserPtr
60
+ %result = insertvalue %Pos { i64 0, ptr poison }, ptr %base, 1
61
+ ret %Pos %result
62
+ """
63
+
64
+ extern def fire[A, B](signal: Signal[A, B], value: A) at async: B =
65
+ js "$effekt.capture(callback => signal$notify(${signal}, ${value}, callback))"
66
+ llvm """
67
+ %base = extractvalue %Pos ${signal}, 1
68
+ %statePtr = getelementptr %Signal, ptr %base, i64 0, i32 1
69
+ %stackPtr = getelementptr %Signal, ptr %base, i64 0, i32 2
70
+ %valuePtr = getelementptr %Signal, ptr %base, i64 0, i32 3
71
+ %state = load i64, ptr %statePtr
72
+ switch i64 %state, label %panic [i64 0, label %init i64 1, label %fire]
73
+ init:
74
+ store i64 2, ptr %statePtr
75
+ store %Stack %stack, ptr %stackPtr
76
+ store %Pos ${value}, ptr %valuePtr
77
+ call void @erasePositive(%Pos ${signal})
78
+ ret void
79
+ fire:
80
+ %otherStack = load %Stack, ptr %stackPtr
81
+ %otherValue = load %Pos, ptr %valuePtr
82
+ store i64 3, ptr %statePtr
83
+ call void @erasePositive(%Pos ${signal})
84
+ call void @resume_Pos(%Stack %otherStack, %Pos ${value})
85
+ call void @resume_Pos(%Stack %stack, %Pos %otherValue)
86
+ ret void
87
+ panic:
88
+ call void @exit(i64 1)
89
+ ret void
90
+ """
91
+
92
+ extern def wait[A, B](signal: Signal[A, B], value: B) at async: A =
93
+ js "$effekt.capture(callback => signal$notify(${signal}, ${value}, callback))"
94
+ llvm """
95
+ %base = extractvalue %Pos ${signal}, 1
96
+ %statePtr = getelementptr %Signal, ptr %base, i64 0, i32 1
97
+ %stackPtr = getelementptr %Signal, ptr %base, i64 0, i32 2
98
+ %valuePtr = getelementptr %Signal, ptr %base, i64 0, i32 3
99
+ %state = load i64, ptr %statePtr
100
+ switch i64 %state, label %panic [i64 0, label %init i64 2, label %wait]
101
+ init:
102
+ store i64 1, ptr %statePtr
103
+ store %Stack %stack, ptr %stackPtr
104
+ store %Pos ${value}, ptr %valuePtr
105
+ call void @erasePositive(%Pos ${signal})
106
+ ret void
107
+ wait:
108
+ %otherStack = load %Stack, ptr %stackPtr
109
+ %otherValue = load %Pos, ptr %valuePtr
110
+ store i64 3, ptr %statePtr
111
+ call void @erasePositive(%Pos ${signal})
112
+ call void @resume_Pos(%Stack %otherStack, %Pos ${value})
113
+ call void @resume_Pos(%Stack %stack, %Pos %otherValue)
114
+ ret void
115
+ panic:
116
+ call void @exit(i64 1)
117
+ ret void
118
+ """
119
+
@@ -90,40 +90,6 @@ def encodeJson[R]{ body: => R / JsonBuilder }: R / emit[String] = {
90
90
  // --------------------------------------------------------------------------------
91
91
 
92
92
 
93
- /// Read a double value, in the common format (TODO document)
94
- def readDouble(): Double / { Scan[Char], Exception[WrongFormat]} = {
95
- def exponent(pre: Double): Double / fail = {
96
- try {
97
- if(do peek() == 'e' || do peek() == 'E') {
98
- skip[Char]()
99
- val e = do peek[Char]() match {
100
- case '+' => skip[Char](); readDecimal()
101
- case '-' => skip[Char](); neg(readDecimal())
102
- case _ => readDecimal()
103
- }
104
- pre * pow(10.0, e)
105
- } else { pre }
106
- } with stop {
107
- pre
108
- }
109
- }
110
- def fraction(pre: Int): Double / fail = {
111
- if (succeeds { must { readIf('.') } }) {
112
- var b = 0.1
113
- var r = pre.toDouble
114
- while (optionally[Int] { must { readDigit() } } is Some(d)) {
115
- r = r + b * d.toDouble
116
- b = b * 0.1
117
- }
118
- exponent(r)
119
- } else {
120
- pre.toDouble
121
- }
122
- }
123
- with returning::expect("Expected double")
124
- fraction(readInteger())
125
- }
126
-
127
93
  def expectString(string: String): Unit / { Scan[Char], Exception[WrongFormat] } =
128
94
  expect("Expected " ++ string) { readString(string) }
129
95
 
@@ -133,9 +99,9 @@ def readQuotedString(): Unit / { Scan[Char], emit[Char], Exception[WrongFormat]
133
99
  with expect("Unexpected end of input while reading a string")
134
100
  skipWhitespace()
135
101
  expect("Expected \"") { must { readIf('"') } }
136
- while(must { do read[Char]() } is c and c != '"') {
102
+ while(must { do next[Char]() } is c and c != '"') {
137
103
  c match {
138
- case '\\' => must { do read[Char]() } match {
104
+ case '\\' => must { do next[Char]() } match {
139
105
  case '"' => do emit('\"')
140
106
  case '\\' => do emit('\\')
141
107
  case '/' => do emit('/')
@@ -159,8 +125,8 @@ def decodeJson(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
159
125
  case 'n' => expectString("null"); do null()
160
126
  case 't' => expectString("true"); do bool(true)
161
127
  case 'f' => expectString("false"); do bool(false)
162
- case '-' => do number(readDouble())
163
- case d and d.isDigit => do number(readDouble())
128
+ case '-' => do number(returning::expect("Expected double") { readDouble() })
129
+ case d and d.isDigit => do number(returning::expect("Expected double") { readDouble() })
164
130
  case '"' => do string(collect { readQuotedString() })
165
131
  case '{' => do dict{ decodeJsonObject() }
166
132
  case '[' => do list{ decodeJsonList() }
@@ -310,15 +276,23 @@ namespace test {
310
276
 
311
277
  // Read quoted string
312
278
  feed("\"\ta\n\ra\"") {
313
- with scanner[Char]
279
+ with peeking[Char]
314
280
  println(string::collect { readQuotedString() })
315
281
  }
316
282
 
317
283
  println("")
318
284
 
285
+ feed("-1.5") {
286
+ with peeking[Char]
287
+ with on[WrongFormat].report
288
+ println(returning::expect("Expected double") { readDouble() })
289
+ }
290
+
291
+ println("")
292
+
319
293
  // Parse example
320
294
  feed("""{ "a": null, "b": [true,false,false,true], "f": 12.532 }"""){
321
- with scanner[Char]
295
+ with peeking[Char]
322
296
 
323
297
  def k = new JsonObjectBuilder {
324
298
  def field[T](k){v} = { println(k); ignore{v} }
@@ -362,7 +336,7 @@ namespace test {
362
336
  "a": null,
363
337
  "b": [true, false, false, true], "f": 12.532
364
338
  } """){
365
- with returning::scanner[Char, JsonValue]
339
+ with returning::peeking[Char, JsonValue]
366
340
  build {
367
341
  decodeJson()
368
342
  }.second
@@ -379,7 +353,7 @@ namespace test {
379
353
  "a": null,
380
354
  "b": [true, false, false, true], "f": 12.532
381
355
  } """){
382
- with scanner[Char]
356
+ with peeking[Char]
383
357
  decodeJson()
384
358
  }
385
359
  }
@@ -974,12 +974,12 @@ def tails[A](list: List[A]): Unit / emit[List[A]] =
974
974
  case Cons(head, tail) => do emit(list); tails(tail)
975
975
  }
976
976
 
977
- /// Connects `list` as a producer to a given pull stream `reader` acting as a consumer.
978
- def feed[T, R](list: List[T]) { reader: () => R / read[T] }: R = {
977
+ /// Connects `list` as a producer to a given pull stream `driver` acting as a consumer.
978
+ def feed[T, R](list: List[T]) { driver: () => R / next[T] }: R = {
979
979
  var l = list
980
980
  try {
981
- reader()
982
- } with read[T] {
981
+ driver()
982
+ } with next[T] {
983
983
  resume {
984
984
  l match {
985
985
  case Nil() => do stop()
@@ -25,7 +25,9 @@ extern llvm """
25
25
  declare %Pos @c_spawn(%Pos, %Pos, %Pos)
26
26
  """
27
27
 
28
- extern type SpawnOptions
28
+ extern type SpawnOptions =
29
+ jsNode "{ stdio: list[int], onSpawn: fn => {} }"
30
+ llvm "%Pos"
29
31
  extern def default() at {}: SpawnOptions =
30
32
  jsNode """{ stdio: [0,1,2], onSpawn: (p) => {} }"""
31
33
  llvm """
@@ -64,7 +66,9 @@ extern def onStderr(opts: SpawnOptions, callback: String => Unit at {io, global}
64
66
  %opts = call %Pos @c_spawn_options_on_stderr(%Pos ${opts}, %Pos ${callback})
65
67
  ret %Pos %opts
66
68
  """
67
- extern type WritableStream
69
+ extern type WritableStream =
70
+ jsNode "stream.Writable"
71
+ llvm "%Pos"
68
72
  extern def withStdin(opts: SpawnOptions, callback: WritableStream => Unit at {io, global}) at {}: SpawnOptions =
69
73
  jsNode """(function () {
70
74
  let old = ${opts};
@@ -1,6 +1,9 @@
1
1
  module random
2
2
 
3
3
  import stream
4
+ import array
5
+ import option
6
+ import exception
4
7
  import io/filesystem
5
8
  import io/error
6
9
 
@@ -38,7 +41,7 @@ def minstd(seed: Int): Unit / emit[Byte] = {
38
41
  if (result < 0) result + m else result
39
42
  }
40
43
 
41
- while (true) {
44
+ forever {
42
45
  state = nextInt()
43
46
  val b = state.mod(256).toByte
44
47
  do emit(b)
@@ -73,7 +76,7 @@ def devurandom { randomnessReader: () => Unit / random }: Unit / Exception[IOErr
73
76
  try {
74
77
  with readFile("/dev/urandom")
75
78
  try randomnessReader() with random {
76
- resume(do read())
79
+ resume(do next())
77
80
  }
78
81
  } with stop {
79
82
  do raise(io::error::EOF(), "Unexpected EOF when reading /dev/urandom!")
@@ -88,14 +91,14 @@ def devurandom { randomnessReader: () => Unit / random }: Unit / Exception[IOErr
88
91
 
89
92
  def randomByte(): Byte / random = do random()
90
93
  def randomBytes(): Unit / {emit[Byte], random} =
91
- while (true) do emit(do random())
94
+ forever { do emit(do random()) }
92
95
 
93
96
  def randomBool(): Bool / random = {
94
97
  val b = do random()
95
98
  b.toInt.mod(2) == 1
96
99
  }
97
100
  def randomBools(): Unit / {emit[Bool], random} =
98
- while (true) do emit(randomBool())
101
+ forever { do emit(randomBool()) }
99
102
 
100
103
  def randomInt32(): Int / random = {
101
104
  var result = 0
@@ -107,7 +110,7 @@ def randomInt32(): Int / random = {
107
110
  result.mod(1.bitwiseShl(31)).abs * if (signBit) 1 else -1
108
111
  }
109
112
  def randomInt32s(): Unit / {emit[Int], random} =
110
- while (true) do emit(randomInt32())
113
+ forever { do emit(randomInt32()) }
111
114
 
112
115
  /// `max` is _inclusive_!
113
116
  def randomInt(min: Int, max: Int): Int / random = {
@@ -126,9 +129,10 @@ def randomInt(min: Int, max: Int): Int / random = {
126
129
  min + (abs(result).mod(range))
127
130
  }
128
131
  }
132
+
129
133
  /// `max` is _inclusive_!
130
134
  def randomInts(min: Int, max: Int): Unit / {emit[Int], random} =
131
- while (true) do emit(randomInt(min, max))
135
+ forever { do emit(randomInt(min, max)) }
132
136
 
133
137
 
134
138
  /// Random double between 0.0 and 1.0
@@ -136,7 +140,25 @@ def randomDouble(): Double / random =
136
140
  (randomInt32().toDouble / 1.bitwiseShl(31).toDouble).abs
137
141
  // This is not perfect, but it will do for now.
138
142
  def randomDoubles(): Unit / {emit[Double], random} =
139
- while (true) do emit(randomDouble())
143
+ forever { do emit(randomDouble()) }
144
+
145
+ /// Shuffle an array in-place using the Fisher-Yates algorithm.
146
+ def shuffle[A](arr: Array[A]): Unit / random = {
147
+ val n = arr.size
148
+ each(0, n - 1) { i =>
149
+ arr.swap(i, randomInt(i, n - 1))
150
+ }
151
+ }
152
+
153
+ /// Pick a random element from an array.
154
+ ///
155
+ /// Panics when called on an empty array.
156
+ def pick[A](arr: Array[A]): A / random =
157
+ if (arr.size == 0) {
158
+ panic("Pick from an empty array")
159
+ } else {
160
+ arr.get(randomInt(0, arr.size - 1))
161
+ }
140
162
 
141
163
 
142
164
  namespace examples {
@@ -18,7 +18,10 @@ define void @c_ref_erase_field(ptr %0) {
18
18
  """
19
19
 
20
20
  /// Global, mutable references
21
- extern type Ref[T]
21
+ extern type Ref[T] =
22
+ js "{ value: T }"
23
+ chez "box"
24
+ llvm "%Pos"
22
25
 
23
26
  /** We represent references like positive types in LLVM
24
27
  * The tag is 0 and the obj points to memory with the following layout:
@@ -4,7 +4,9 @@ import string
4
4
 
5
5
  extern include chez "text/pregexp.scm"
6
6
 
7
- extern type Regex
7
+ extern type Regex =
8
+ js "RegExp"
9
+ chez "pregexp"
8
10
 
9
11
  record Match(matched: String, index: Int)
10
12
 
@@ -25,9 +27,9 @@ namespace internal {
25
27
 
26
28
  /// The type RegexMatch is used internally to represent platform
27
29
  /// dependent results of matching a regex.
28
- extern type RegexMatch
29
- // js: { matched: String, index: Int } | undefined
30
- // vm: scala.util.matching.Regex.Match | null
30
+ extern type RegexMatch =
31
+ js "{ matched: String, index: Int } | undefined"
32
+ vm "scala.util.matching.Regex.Match | null"
31
33
 
32
34
  extern def matched(r: RegexMatch) at {}: String =
33
35
  js "${r}.matched"
@@ -224,12 +224,12 @@ def each[T](arr: ResizableArray[T]): Unit / emit[T] =
224
224
  do emit(arr.rawContentPtr.get.get(i))
225
225
  }
226
226
 
227
- /// Connects `arr` as a producer to a given pull stream `reader` acting as a consumer.
228
- def feed[R, T](arr: ResizableArray[T]) { reader: () => R / read[T] }: R = {
227
+ /// Connects `arr` as a producer to a given pull stream `driver` acting as a consumer.
228
+ def feed[R, T](arr: ResizableArray[T]) { driver: () => R / next[T] }: R = {
229
229
  var i = 0
230
230
  try {
231
- reader()
232
- } with read[T] {
231
+ driver()
232
+ } with next[T] {
233
233
  resume {
234
234
  if (i < arr.size) {
235
235
  val v = arr.rawContentPtr.get.get(i)
@@ -3,19 +3,10 @@ module scanner
3
3
  import stream
4
4
 
5
5
 
6
- /// Return the next character, not advancing.
7
- /// That is, this does not change what any future calls on the Scanner return.
8
- effect peek[A](): A / stop
9
-
10
- effect Scan[A] = {read[A], peek[A]}
6
+ effect Scan[A] = {next[A], peek[A]}
11
7
 
12
8
  /// Advance the pull stream to the next element, discarding the current element.
13
- def skip[A](): Unit / {read[A], stop} = { do read(); () }
14
-
15
-
16
- /// Run a scanner by reading from an input stream, discarding its output.
17
- def scanner[A] { scanner: () => Unit / Scan[A] }: Unit / read[A] =
18
- returning::scanner[A, Unit]{scanner}
9
+ def skip[A](): Unit / {next[A], stop} = { do next(); () }
19
10
 
20
11
  def expect(message: String) { scanner: () => Unit / fail }: Unit / Exception[WrongFormat] =
21
12
  returning::expect[Unit](message){scanner}
@@ -23,7 +14,7 @@ def expect(message: String) { scanner: () => Unit / fail }: Unit / Exception[Wro
23
14
  /// If conversion is successful read the next token, otherwise stop.
24
15
  def readWhen[A, B] { convert: A => B / fail }: B / { Scan[A], stop } = {
25
16
  val t = attempt { convert(do peek()) } { do stop() };
26
- do read()
17
+ do next()
27
18
  return t
28
19
  }
29
20
 
@@ -31,7 +22,7 @@ def readWhen[A, B] { convert: A => B / fail }: B / { Scan[A], stop } = {
31
22
  def readIf[A] { predicate: A => Bool }: A / { Scan[A], stop } = {
32
23
  val t = do peek()
33
24
  if (predicate(t)) {
34
- do read()
25
+ do next()
35
26
  return t
36
27
  } else {
37
28
  do stop()
@@ -92,10 +83,10 @@ def until[A, R] { predicate: A => Bool } { scanner: () => R / Scan[A] }: R / { S
92
83
  resume {
93
84
  if (stopped) { do stop() } else { do peek() }
94
85
  }
95
- } with read[A] {
86
+ } with next[A] {
96
87
  resume {
97
88
  if (stopped) { do stop() } else {
98
- val t = do read()
89
+ val t = do next()
99
90
  boundary {
100
91
  if (predicate(do peek())) { stopped = true; skip() }
101
92
  }
@@ -166,38 +157,42 @@ def readInteger(): Int / { Scan[Char], fail } =
166
157
  case _ => do fail()
167
158
  }
168
159
 
169
- namespace returning {
170
160
 
171
- /// Run a scanner by reading from an input stream.
172
- def scanner[A, R] { scanner: () => R / Scan[A] }: R / read[A] = {
173
- def upstream(): A / stop = do read()
174
- var last = None()
175
- try {
176
- scanner()
177
- } with peek[A] { () =>
178
- resume {
179
- last match {
180
- case None() =>
181
- val t = upstream()
182
- last = Some(t)
183
- return t
184
- case Some(t) =>
185
- return t
186
- }
161
+ /// Read a decimal floating-point number with optional fraction and exponent.
162
+ def readDouble(): Double / { Scan[Char], fail} = {
163
+ def exponent(pre: Double): Double / fail = {
164
+ try {
165
+ if(do peek() == 'e' || do peek() == 'E') {
166
+ skip[Char]()
167
+ val e = do peek[Char]() match {
168
+ case '+' => skip[Char](); readDecimal()
169
+ case '-' => skip[Char](); neg(readDecimal())
170
+ case _ => readDecimal()
171
+ }
172
+ pre * pow(10.0, e)
173
+ } else { pre }
174
+ } with stop {
175
+ pre
187
176
  }
188
- } with read[A] {
189
- resume {
190
- last match {
191
- case None() =>
192
- upstream()
193
- case Some(t) =>
194
- last = None()
195
- return t
177
+ }
178
+ def fraction(pre: Int): Double / fail = {
179
+ if (succeeds { must { readIf('.') } }) {
180
+ var b = if (pre >= 0) 0.1 else -0.1
181
+ var r = pre.toDouble
182
+ while (optionally[Int] { must { readDigit() } } is Some(d)) {
183
+ r = r + b * d.toDouble
184
+ b = b * 0.1
196
185
  }
186
+ exponent(r)
187
+ } else {
188
+ exponent(pre.toDouble)
197
189
  }
198
190
  }
191
+ fraction(readInteger())
199
192
  }
200
193
 
194
+ namespace returning {
195
+
201
196
  def expect[R](message: String) { scanner: () => R / fail }: R / Exception[WrongFormat] =
202
197
  try { scanner() } with fail { wrongFormat(message) }
203
198
 
@@ -217,7 +212,7 @@ namespace test {
217
212
  with on[WrongFormat].panic
218
213
  with expect("this grammar")
219
214
  with feed(input)
220
- with scanner[Char]
215
+ with peeking[Char]
221
216
 
222
217
  skipWhitespace()
223
218
  println(readInteger().show)