@effekt-lang/effekt 0.58.0 → 0.59.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
@@ -2,9 +2,8 @@
2
2
  module char
3
3
 
4
4
  import effekt
5
- import exception
6
5
  import option
7
- import result
6
+ import partial
8
7
  import stream
9
8
 
10
9
 
@@ -19,51 +18,49 @@ def isWhitespace(c: Char): Bool = c match {
19
18
  case _ => false
20
19
  }
21
20
 
22
- /// Gets the value of a given ASCII digit in base 10,
23
- /// throwing an exception on wrong format
24
- def digitValue(char: Char): Int / Exception[WrongFormat] =
21
+ /// Gets the value of a given ASCII digit in base 10 or fails
22
+ def digitValue(char: Char): Int / fail =
25
23
  if (char >= '0' && char <= '9') {
26
24
  char.toInt - '0'.toInt
27
25
  } else {
28
- wrongFormat("Not a valid digit: '" ++ char.toString ++ "' in base 10")
26
+ do fail()
29
27
  }
30
28
 
31
- /// Gets the value of a given ASCII digit in base 16,
32
- /// throwing an exception on wrong format
33
- def hexDigitValue(char: Char): Int / Exception[WrongFormat] =
29
+ /// Gets the value of a given ASCII digit in base 16 or fails
30
+ def hexDigitValue(char: Char): Int / fail =
34
31
  char match {
35
32
  case char and char >= '0' && char <= '9' => char.toInt - '0'.toInt
36
33
  case char and char >= 'A' && char <= 'F' => (char.toInt - 'A'.toInt) + 10
37
34
  case char and char >= 'a' && char <= 'f' => (char.toInt - 'a'.toInt) + 10
38
- case _ => wrongFormat("Not a valid digit: '" ++ char.toString ++ "' in base 16")
35
+ case _ => do fail()
39
36
  }
40
37
 
41
38
  /// Gets the value of a given ASCII digit in the given base up to 36,
42
39
  /// throwing an exception on wrong format
43
- def digitValue(char: Char, base: Int): Int / Exception[WrongFormat] = {
40
+ def digitValue(char: Char, base: Int): Int / fail = {
44
41
  val digit = char match {
45
42
  case char and char >= '0' && char <= '9' => char.toInt - '0'.toInt
46
43
  case char and char >= 'A' && char <= 'Z' => (char.toInt - 'A'.toInt) + 10
47
44
  case char and char >= 'a' && char <= 'z' => (char.toInt - 'a'.toInt) + 10
48
- case _ => wrongFormat("Not a valid digit: '" ++ char.toString ++ "'")
45
+ case _ => do fail()
49
46
  }
50
47
  if (digit >= base) {
51
- wrongFormat("Digit '" ++ digit.show ++ "' is too big for base " ++ base.show)
48
+ do fail()
52
49
  }
53
50
  digit
54
51
  }
55
52
 
56
53
  /// Checks if the given character is an ASCII digit in base 10
57
54
  /// Prefer using `digitValue(c: Char)` to get the numeric value out.
58
- def isDigit(char: Char): Bool = result[Int, WrongFormat] { digitValue(char) }.isSuccess
55
+ def isDigit(char: Char): Bool = optionally[Int] { digitValue(char) }.isDefined
59
56
 
60
57
  /// Checks if the given character is an ASCII digit in base 16
61
58
  /// Prefer using `hexDigitValue(c: Char)` to get the numeric value out.
62
- def isHexDigit(char: Char): Bool = result[Int, WrongFormat] { hexDigitValue(char)}.isSuccess
59
+ def isHexDigit(char: Char): Bool = optionally[Int] { hexDigitValue(char)}.isDefined
63
60
 
64
61
  /// Checks if the given character is an ASCII digit in base 10
65
62
  /// Prefer using `digitValue(c: Char, base: Int)` to get the numeric value out.
66
- def isDigit(char: Char, base: Int): Bool = result[Int, WrongFormat] { digitValue(char, base) }.isSuccess
63
+ def isDigit(char: Char, base: Int): Bool = optionally[Int] { digitValue(char, base) }.isDefined
67
64
 
68
65
  /// Checks if a given character is a 7-bit ASCII character
69
66
  def isASCII(c: Char): Bool = { c.toInt < 128 }
@@ -178,36 +175,6 @@ extern def charWidth(c: Char) at {}: Int =
178
175
  js "(${c} > 0xffff) ? 2 : 1"
179
176
 
180
177
 
181
- def writeLine { body: () => Unit / emit[Char] }: Unit / emit[Char] =
182
- returning::writeLine[Unit]{body}
183
-
184
- def readLine { body: () => Unit / read[Char] }: Unit / {read[Char], stop} =
185
- returning::readLine[Unit]{body}
186
-
187
- namespace returning {
188
- def writeLine[R] { body: () => R / emit[Char] }: R / emit[Char] = {
189
- val result = body()
190
- do emit('\n')
191
- return result
192
- }
193
-
194
- def readLine[R] { body: () => R / read[Char] }: R / {read[Char], stop} = {
195
- var stopped = false
196
- try {
197
- body()
198
- } with read[Char] {
199
- if(stopped){
200
- resume { do stop() }
201
- } else {
202
- do read() match {
203
- case '\n' => stopped = true; resume { do stop() }
204
- case char => resume { return char }
205
- }
206
- }
207
- }
208
- }
209
- }
210
-
211
178
  def decodeChar(): Char / {read[Byte], stop} = {
212
179
  val b = do read().toInt
213
180
  if (b < 128) {
@@ -127,6 +127,16 @@ extern def inspect[R](value: R) at io: Unit =
127
127
  chez { println(genericShow(value)) }
128
128
  vm "effekt::inspect(Any)"
129
129
 
130
+ /// A fatal runtime error that cannot be caught
131
+ extern def panic(msg: String) at io: Nothing =
132
+ js "(function() { throw ${msg} })()"
133
+ chez "(raise ${msg})"
134
+ llvm """
135
+ call void @c_io_println(%Pos ${msg})
136
+ call void @exit(i32 1)
137
+ ret %Pos zeroinitializer ; Unit
138
+ """
139
+
130
140
 
131
141
  // Strings
132
142
  // =======
@@ -47,19 +47,6 @@ def wrongFormat(msg: String): Nothing / Exception[WrongFormat] =
47
47
  do raise(WrongFormat(), msg)
48
48
 
49
49
 
50
- /**
51
- * A fatal runtime error that cannot be caught
52
- */
53
- extern def panic(msg: String) at io: Nothing =
54
- js "(function() { throw ${msg} })()"
55
- chez "(raise ${msg})"
56
- llvm """
57
- call void @c_io_println(%Pos ${msg})
58
- call void @exit(i32 1)
59
- ret %Pos zeroinitializer ; Unit
60
- """
61
-
62
-
63
50
  // Exception Handlers
64
51
  // ==================
65
52
 
@@ -91,8 +91,8 @@ def encodeJson[R]{ body: => R / JsonBuilder }: R / emit[String] = {
91
91
 
92
92
 
93
93
  /// Read a double value, in the common format (TODO document)
94
- def readDouble(): Double / Scan[Char] = {
95
- def exponent(pre: Double) = {
94
+ def readDouble(): Double / { Scan[Char], Exception[WrongFormat]} = {
95
+ def exponent(pre: Double): Double / fail = {
96
96
  try {
97
97
  if(do peek() == 'e' || do peek() == 'E') {
98
98
  do skip[Char]()
@@ -107,11 +107,11 @@ def readDouble(): Double / Scan[Char] = {
107
107
  pre
108
108
  }
109
109
  }
110
- def fraction(pre: Int) = {
111
- if (optionally { readIf('.') }) {
110
+ def fraction(pre: Int): Double / fail = {
111
+ if (succeeds { must { readIf('.') } }) {
112
112
  var b = 0.1
113
113
  var r = pre.toDouble
114
- while (returning::optionally[Int] { readDigit() } is Some(d)) {
114
+ while (optionally[Int] { must { readDigit() } } is Some(d)) {
115
115
  r = r + b * d.toDouble
116
116
  b = b * 0.1
117
117
  }
@@ -120,45 +120,41 @@ def readDouble(): Double / Scan[Char] = {
120
120
  pre.toDouble
121
121
  }
122
122
  }
123
+ with returning::expect("Expected double")
123
124
  fraction(readInteger())
124
125
  }
125
126
 
126
127
  def expectString(string: String): Unit / { Scan[Char], Exception[WrongFormat] } =
127
- for[Char] { string.each } { char =>
128
- expect("Expected " ++ string) { readIf(char) }
129
- }
128
+ expect("Expected " ++ string) { readString(string) }
129
+
130
130
 
131
131
  /// Read and unescape a string in ""
132
132
  def readQuotedString(): Unit / { Scan[Char], emit[Char], Exception[WrongFormat] } = {
133
- try {
134
- skipWhitespace()
135
- expect("Expected \"") { readIf('"') }
136
- while(read[Char]() is c and c != '"') {
137
- c match {
138
- case '\\' => read[Char]() match {
139
- case '"' => do emit('\"')
140
- case '\\' => do emit('\\')
141
- case '/' => do emit('/')
142
- case 'b' => <>
143
- case 'f' => <>
144
- case 'n' => do emit('\n')
145
- case 'r' => do emit('\r')
146
- case 't' => do emit('\t')
147
- case c => wrongFormat("Invalid escape sequence '\\" ++ c.toString ++ "'")
148
- }
149
- case o => do emit(o)
133
+ with expect("Unexpected end of input while reading a string")
134
+ skipWhitespace()
135
+ expect("Expected \"") { must { readIf('"') } }
136
+ while(must { read[Char]() } is c and c != '"') {
137
+ c match {
138
+ case '\\' => must { read[Char]() } match {
139
+ case '"' => do emit('\"')
140
+ case '\\' => do emit('\\')
141
+ case '/' => do emit('/')
142
+ case 'b' => <>
143
+ case 'f' => <>
144
+ case 'n' => do emit('\n')
145
+ case 'r' => do emit('\r')
146
+ case 't' => do emit('\t')
147
+ case c => wrongFormat("Invalid escape sequence '\\" ++ c.toString ++ "'")
150
148
  }
149
+ case o => do emit(o)
150
+ }
151
151
  }
152
- } with stop {
153
- wrongFormat("Unexpected end of input while reading a string")
154
- }
155
152
  }
156
153
 
157
154
  /// Decode a json string and do the appropriate calls to an implicitly bound JsonBuilder
158
155
  def decodeJson(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
159
- with boundary
160
156
  skipWhitespace()
161
- if(do peek() is c) {
157
+ if(optionally { must { do peek() } } is Some(c)) {
162
158
  c match {
163
159
  case 'n' => expectString("null"); do null()
164
160
  case 't' => expectString("true"); do bool(true)
@@ -175,9 +171,9 @@ def decodeJson(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
175
171
  def decodeJsonObject(): Unit / {Scan[Char], JsonObjectBuilder, Exception[WrongFormat]} = {
176
172
  var first = true
177
173
  expectString("{")
178
- with boundary
174
+ with expect("Expected whitespace")
179
175
  skipWhitespace()
180
- while(do peek() is c and (c != '}')) {
176
+ while(must { do peek() } is c and (c != '}')) {
181
177
  if (not(first)) { expectString(",") }
182
178
  val k = string::collect { readQuotedString() }
183
179
  skipWhitespace()
@@ -188,20 +184,20 @@ def decodeJsonObject(): Unit / {Scan[Char], JsonObjectBuilder, Exception[WrongFo
188
184
  skipWhitespace()
189
185
  first = false
190
186
  }
191
- do skip()
187
+ must { do skip() }
192
188
  }
193
189
  def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
194
190
  var first = true
195
191
  expectString("[")
196
- with boundary
192
+ with expect("Expected whitespace")
197
193
  skipWhitespace()
198
- while(do peek() is c and (c != ']')) {
194
+ while(must { do peek() } is c and (c != ']')) {
199
195
  if (not(first)) { expectString(",") }
200
196
  decodeJson()
201
197
  skipWhitespace()
202
198
  first = false
203
199
  }
204
- do skip()
200
+ must { do skip() }
205
201
  }
206
202
 
207
203
  // --------------------------------------------------------------------------------
@@ -0,0 +1,48 @@
1
+ module partial
2
+
3
+ import effekt
4
+ import option
5
+
6
+
7
+ effect fail(): Nothing
8
+
9
+ def attempt[R] { program: () => R / fail } { fallback: () => R }: R =
10
+ try {
11
+ program()
12
+ } with fail {
13
+ fallback()
14
+ }
15
+
16
+ def succeeds { program: () => Unit / fail }: Bool =
17
+ try { program(); true } with fail { false }
18
+
19
+ def optionally[R] { program: () => R / fail }: Option[R] =
20
+ try {
21
+ Some(program())
22
+ } with fail {
23
+ None()
24
+ }
25
+
26
+ def must[R] { program: () => R / stop }: R / fail =
27
+ try { program() } with stop { resume(do fail()) }
28
+
29
+ /// Run `program` forever until `fail` is thrown.
30
+ def exhaustively { program: () => Unit / fail }: Unit =
31
+ try {
32
+ def go(): Unit = {
33
+ program()
34
+ go()
35
+ }
36
+ go()
37
+ } with fail {
38
+ ()
39
+ }
40
+
41
+ def crash[R](message: String) { program: () => R / fail }: R =
42
+ attempt { program() } { panic(message) }
43
+
44
+ def suchThat[A](value: A){ predicate: A => Bool }: Option[A] =
45
+ if (predicate(value)) { Some(value) } else { None() }
46
+
47
+ def getOrFail[A](option: Option[A]): A / fail =
48
+ option.getOrElse { do fail() }
@@ -10,20 +10,29 @@ interface Scan[A] {
10
10
  def skip(): Unit / stop
11
11
  }
12
12
 
13
- /// Advance the Scanner to the next character, returning it.
13
+
14
+ /// Advance the Scanner to the next token, returning it.
14
15
  def read[A](): A / { Scan[A], stop } = {
15
16
  val t = do peek()
16
17
  do skip()
17
18
  return t
18
19
  }
19
20
 
21
+
20
22
  /// Run a scanner by reading from an input stream, discarding its output.
21
23
  def scanner[A] { scanner: () => Unit / Scan[A] }: Unit / read[A] =
22
24
  returning::scanner[A, Unit]{scanner}
23
25
 
24
- def expect(message: String) { scanner: () => Unit / stop }: Unit / Exception[WrongFormat] =
26
+ def expect(message: String) { scanner: () => Unit / fail }: Unit / Exception[WrongFormat] =
25
27
  returning::expect[Unit](message){scanner}
26
28
 
29
+ /// If conversion is successful read the next token, otherwise stop.
30
+ def readWhen[A, B] { convert: A => B / fail }: B / { Scan[A], stop } = {
31
+ val t = attempt { convert(do peek()) } { do stop() };
32
+ do skip();
33
+ return t
34
+ }
35
+
27
36
  /// Check that the next token satisfies the predicate and skip and return it when it does.
28
37
  def readIf[A] { predicate: A => Bool }: A / { Scan[A], stop } = {
29
38
  val t = do peek()
@@ -40,57 +49,97 @@ def skipIf[A] { predicate: A => Bool }: Unit / { Scan[A], stop } = {
40
49
  readIf{predicate}; ()
41
50
  }
42
51
 
43
- def readSome[A, B] { convert: A => Option[B] }: B / { Scan[A], stop } =
44
- if (convert(do peek()) is Some(t)) {
45
- do skip()
46
- return t
47
- } else {
48
- do stop()
49
- }
50
-
51
- /// like `readSome`, but for functions `A => B / Exception[WrongFormat]` such as `char::digitValue`
52
- def tryRead[A, B] { convert: A => B / Exception[WrongFormat] }: B / { Scan[A], stop } = {
53
- with on[WrongFormat].default { do stop() }
52
+ /// Skips the next token but fails instead of stopping.
53
+ def mustSkip[A](): Unit / { Scan[A], fail } =
54
+ must { do skip() }
54
55
 
55
- val t = convert(do peek())
56
- do skip()
57
- return t
58
- }
56
+ /// Skips the next character but fails if it is not the given one.
57
+ def mustSkip(token: Char): Unit / { Scan[Char], fail } =
58
+ must { skipIf(token) }
59
59
 
60
60
  /// Reads until the predicate does not hold for the next token.
61
61
  /// Emits the tokens read.
62
- def readWhile[A] { predicate: A => Bool }: Unit / { Scan[A], emit[A] } =
62
+ def readMany[A] { predicate: A => Bool }: Unit / { Scan[A], emit[A] } =
63
63
  many { readIf{predicate} }
64
64
 
65
+ /// Reads until the predicate does not hold for the next token.
66
+ /// Must read at least one token, else fails.
67
+ /// Emits the tokens read.
68
+ def readSome[A] { predicate: A => Bool }: Unit / { Scan[A], emit[A], fail } =
69
+ some { readIf{predicate} }
70
+
65
71
  /// Skips until the predicate does not hold for the next token.
66
- def skipWhile[A] { predicate: A => Bool }: Unit / { Scan[A] } =
67
- for[A] { readWhile{predicate} } { _ => () }
72
+ def skipMany[A] { predicate: A => Bool }: Unit / Scan[A] =
73
+ for[A] { readMany{predicate} } { _ => () }
68
74
 
75
+ /// Skips until the predicate does not hold for the next token.
76
+ /// Must skip at least one token, else fail.
77
+ def skipSome[A] { predicate: A => Bool }: Unit / { Scan[A], fail } =
78
+ for[A] { readSome{predicate} } { _ => () }
69
79
 
70
- /// Read the next character if it is the given one and return true in this case.
80
+ /// Read the next character if it is the given one.
71
81
  def readIf(e: Char): Unit / { Scan[Char], stop } = {
72
- readIf[Char] { c => c == e }; ()
82
+ readIf { c => c == e }; ()
73
83
  }
74
84
 
75
85
  /// Check that the next token satisfies the predicate and skip it when it does.
76
86
  def skipIf(e: Char): Unit / { Scan[Char], stop } =
77
87
  skipIf { c => c == e }
78
88
 
79
- /// Skip until the next character is not a whitespace character
89
+ /// Read tokens and supply them to the given scanner.
90
+ /// Stop and skip whenn the predicate is true.
91
+ /// Immediately stops when already at the end.
92
+ def until[A, R] { predicate: A => Bool } { scanner: () => R / Scan[A] }: R / { Scan[A], stop } = {
93
+ var stopped = false
94
+ if (predicate(do peek())) { stopped = true; do skip() }
95
+ try {
96
+ scanner()
97
+ } with Scan[A] {
98
+ def peek() = resume {
99
+ if (stopped) { do stop() } else { do peek() }
100
+ }
101
+ def skip() = resume {
102
+ if (stopped) { do stop() } else {
103
+ do skip()
104
+ boundary {
105
+ if (predicate(do peek())) { stopped = true; do skip() }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ }
111
+
112
+ /// Read the next line and supply each character to the given scanner.
113
+ def line[R] { scanner: () => R / Scan[Char] }: R / { Scan[Char], stop } =
114
+ until { c => c == '\n' } { scanner() }
115
+
116
+ /// Read values produced by `action` followed by `separator` until one of them stops.
117
+ /// Will consume a trailing separator.
118
+ def list[A] { seperator: () => Unit / stop } { action: () => A / stop }: Unit / emit[A] =
119
+ loop { do emit(action()); seperator() }
120
+
121
+ /// Read a sequence separated by `separator`.
122
+ /// Will consume a trailing separator.
123
+ def list[A](seperator: Char) { action: () => A / stop }: Unit / { Scan[Char], emit[A] } =
124
+ list { skipIf(seperator) } {action}
125
+
126
+ /// Skip until the next character is not a whitespace character.
80
127
  def skipWhitespace(): Unit / Scan[Char] =
81
- skipWhile { c => c.isWhitespace }
128
+ skipMany { c => c.isWhitespace }
82
129
 
83
- /// Read as many characters corresponding to the given string as possible.
84
- def readString(string: String): Unit / { Scan[Char], stop } =
85
- for[Char] { string.each } { char => readIf(char) }
130
+ /// Read a prefix of the characters of the given string.
131
+ /// Fails when it cannot read all of them.
132
+ def readString(string: String): Unit / { Scan[Char], fail } =
133
+ for[Char] { string.each } { char => must { readIf(char) } }
86
134
 
87
135
  /// Check that the next character is a digit in base 10, and if so read and return it.
88
136
  def readDigit(): Int / { Scan[Char], stop } =
89
- tryRead[Char, Int]{ char => digitValue(char) }
137
+ readWhen { char => digitValue(char) }
90
138
 
91
139
  /// Read a positive decimal number.
92
- def readDecimal(): Int / Scan[Char] = {
93
- var result = 0
140
+ /// Fails when there isn't at least one digit.
141
+ def readDecimal(): Int / { Scan[Char], fail } = {
142
+ var result = must { readDigit() }
94
143
  for[Int] { many { readDigit() } } { digit =>
95
144
  result = result * 10 + digit
96
145
  }
@@ -99,11 +148,12 @@ def readDecimal(): Int / Scan[Char] = {
99
148
 
100
149
  /// Check that the next character is a digit in base 16, and if so read and return it.
101
150
  def readHexDigit(): Int / { Scan[Char], stop } =
102
- tryRead[Char, Int]{ char => hexDigitValue(char) }
151
+ readWhen { char => hexDigitValue(char) }
103
152
 
104
153
  /// Read a hexadecimal number.
105
- def readHexadecimal(): Int / Scan[Char] = {
106
- var result = 0
154
+ /// Fails when there isn't at least one digit.
155
+ def readHexadecimal(): Int / { Scan[Char], fail } = {
156
+ var result = must { readHexDigit() }
107
157
  for[Int] { many { readHexDigit() } } { digit =>
108
158
  result = result * 16 + digit
109
159
  }
@@ -111,14 +161,15 @@ def readHexadecimal(): Int / Scan[Char] = {
111
161
  }
112
162
 
113
163
  /// Read a decimal integer.
114
- def readInteger(): Int / Scan[Char] =
115
- if (optionally { readIf('-') }) {
116
- neg(readDecimal())
117
- } else {
118
- readDecimal()
164
+ /// Will consume a minus sign if it is not followed by a digit.
165
+ /// Fails whenn there isn't at least one digit.
166
+ def readInteger(): Int / { Scan[Char], fail } =
167
+ must { do peek() } match {
168
+ case '-' => must { do skip() }; neg(readDecimal())
169
+ case c and isDigit(c) => readDecimal()
170
+ case _ => do fail()
119
171
  }
120
172
 
121
-
122
173
  namespace returning {
123
174
 
124
175
  /// Run a scanner by reading from an input stream.
@@ -150,8 +201,8 @@ def scanner[A, R] { scanner: () => R / Scan[A] }: R / read[A] = {
150
201
  }
151
202
  }
152
203
 
153
- def expect[R](message: String) { scanner: () => R / stop }: R / Exception[WrongFormat] =
154
- attempt { scanner() } { wrongFormat(message) }
204
+ def expect[R](message: String) { scanner: () => R / fail }: R / Exception[WrongFormat] =
205
+ try { scanner() } with fail { wrongFormat(message) }
155
206
 
156
207
  }
157
208
 
@@ -166,6 +217,8 @@ namespace test {
166
217
  A1B2
167
218
  """
168
219
 
220
+ with on[WrongFormat].panic
221
+ with expect("this grammar")
169
222
  with feed(input)
170
223
  with scanner[Char]
171
224
 
@@ -2,7 +2,7 @@ module stream
2
2
 
3
3
  import effekt
4
4
  import option
5
- import exception
5
+ import partial
6
6
 
7
7
  // Effects
8
8
  // -------
@@ -51,21 +51,11 @@ def fix[T] { one: T => Unit / emit[T] } { stream: => Unit / emit[T] }: Unit =
51
51
  def many[A] { action: () => A / stop }: Unit / emit[A] =
52
52
  loop { do emit(action()) }
53
53
 
54
- def some[A] { action: () => A / stop }: Unit / { emit[A], stop } = {
55
- do emit(action())
54
+ def some[A] { action: () => A / stop }: Unit / { emit[A], fail } = {
55
+ do emit(must { action() })
56
56
  many { action() }
57
57
  }
58
58
 
59
- def optionally { program: () => Unit / stop }: Bool =
60
- returning::optionally[Unit]{program}.isDefined
61
-
62
- def attempt[R] { program: () => R / stop } { fallback: () => R }: R =
63
- try {
64
- program()
65
- } with stop {
66
- fallback()
67
- }
68
-
69
59
 
70
60
  /// In Effekt lower bounds are inclusive and upper bounds are exclusive
71
61
  record Indexed[A](index: Int, value: A)
@@ -330,14 +320,14 @@ def tee[A]{ cons1: { => Unit / emit[A] } => Unit }{ cons2: { => Unit / emit[A] }
330
320
  /// > first[Bool] { do emit(true) }
331
321
  /// true
332
322
  /// > first[String] { () } // empty stream
333
- /// <Exception MissingValue: code in first did not emit any values>
323
+ /// <fail>
334
324
  /// ```
335
- def first[A] { body: => Unit / emit[A] }: A / Exception[MissingValue] =
325
+ def first[A] { stream: () => Unit / emit[A] }: A / fail = {
336
326
  try {
327
+ def body(): A = { stream(); do fail() }
337
328
  body()
338
- val r: A = do raise[MissingValue](MissingValue(), "code in first did not emit any values")
339
- r
340
329
  } with emit[A] { a => a }
330
+ }
341
331
 
342
332
  namespace returning {
343
333
 
@@ -371,13 +361,6 @@ def boundary[R] { program: () => R / stop }: Option[R] =
371
361
  None()
372
362
  }
373
363
 
374
- def optionally[R] { program: () => R / stop }: Option[R] =
375
- try {
376
- Some(program())
377
- } with stop {
378
- None()
379
- }
380
-
381
364
  def index[A, R] { stream: () => R / emit[A] }: R / emit[Indexed[A]] = {
382
365
  var i = 0;
383
366
  try {
@@ -5,6 +5,7 @@ import option
5
5
  import stream
6
6
  import list
7
7
  import exception
8
+ import partial
8
9
  import result
9
10
  import char
10
11
  import bytearray
@@ -118,14 +119,14 @@ extern def trim(str: String) at {}: String =
118
119
 
119
120
  // Parsing
120
121
  // -------
121
- def toBool(s: String): Bool / Exception[WrongFormat] = s match {
122
+ def toBool(s: String): Bool / fail = s match {
122
123
  case "true" => true
123
124
  case "false" => false
124
- case _ => wrongFormat("Not a boolean value: '" ++ s ++ "'")
125
+ case _ => do fail()
125
126
  }
126
127
 
127
128
  // TODO optimize (right now this will be horribly slow (compared to the native JS version)
128
- def toInt(str: String): Int / Exception[WrongFormat] = {
129
+ def toInt(str: String): Int / fail = {
129
130
  def go(index: Int, acc: Int): Int = {
130
131
  // wrong index means we're done parsing
131
132
  with on[OutOfBounds].default { () => acc };
@@ -136,7 +137,7 @@ def toInt(str: String): Int / Exception[WrongFormat] = {
136
137
  }
137
138
 
138
139
  with on[OutOfBounds].default {
139
- wrongFormat("Empty string is not a valid number")
140
+ do fail()
140
141
  }
141
142
 
142
143
  str.charAt(0) match {
@@ -146,9 +147,9 @@ def toInt(str: String): Int / Exception[WrongFormat] = {
146
147
  }
147
148
 
148
149
  // TODO optimize (right now this will be horribly slow (compared to the native JS version)
149
- def toInt(str: String, base: Int): Int / Exception[WrongFormat] = {
150
+ def toInt(str: String, base: Int): Int / fail = {
150
151
  if (base > 36 || base < 1) {
151
- wrongFormat("Invalid base: " ++ base.show)
152
+ do fail()
152
153
  }
153
154
 
154
155
  def go(index: Int, acc: Int): Int = {
@@ -161,7 +162,7 @@ def toInt(str: String, base: Int): Int / Exception[WrongFormat] = {
161
162
  }
162
163
 
163
164
  with on[OutOfBounds].default {
164
- wrongFormat("Empty string is not a valid number")
165
+ do fail()
165
166
  }
166
167
 
167
168
  str.charAt(0) match {