@effekt-lang/effekt 0.57.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 +0 -0
- package/libraries/common/char.effekt +13 -46
- package/libraries/common/effekt.effekt +10 -0
- package/libraries/common/exception.effekt +0 -13
- package/libraries/common/json.effekt +32 -36
- package/libraries/common/partial.effekt +48 -0
- package/libraries/common/scanner.effekt +94 -41
- package/libraries/common/stream.effekt +7 -24
- package/libraries/common/string.effekt +8 -7
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +2 -2
- package/licenses/mit license - mit-license.html +18 -18
- package/licenses/the bsd license - bsd-license.html +18 -16
- package/licenses/the mit license - mit.html +17 -17
- package/package.json +2 -3
- package/bin/effekt.sh +0 -2
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
|
|
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
|
-
|
|
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
|
-
|
|
26
|
+
do fail()
|
|
29
27
|
}
|
|
30
28
|
|
|
31
|
-
/// Gets the value of a given ASCII digit in base 16
|
|
32
|
-
|
|
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 _ =>
|
|
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 /
|
|
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 _ =>
|
|
45
|
+
case _ => do fail()
|
|
49
46
|
}
|
|
50
47
|
if (digit >= base) {
|
|
51
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
128
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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 /
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
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
|
|
67
|
-
for[A] {
|
|
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
|
|
80
|
+
/// Read the next character if it is the given one.
|
|
71
81
|
def readIf(e: Char): Unit / { Scan[Char], stop } = {
|
|
72
|
-
readIf
|
|
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
|
-
///
|
|
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
|
-
|
|
128
|
+
skipMany { c => c.isWhitespace }
|
|
82
129
|
|
|
83
|
-
/// Read
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
137
|
+
readWhen { char => digitValue(char) }
|
|
90
138
|
|
|
91
139
|
/// Read a positive decimal number.
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
151
|
+
readWhen { char => hexDigitValue(char) }
|
|
103
152
|
|
|
104
153
|
/// Read a hexadecimal number.
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
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 /
|
|
154
|
-
|
|
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
|
|
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],
|
|
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
|
-
/// <
|
|
323
|
+
/// <fail>
|
|
334
324
|
/// ```
|
|
335
|
-
def first[A] {
|
|
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 /
|
|
122
|
+
def toBool(s: String): Bool / fail = s match {
|
|
122
123
|
case "true" => true
|
|
123
124
|
case "false" => false
|
|
124
|
-
case _ =>
|
|
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 /
|
|
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
|
-
|
|
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 /
|
|
150
|
+
def toInt(str: String, base: Int): Int / fail = {
|
|
150
151
|
if (base > 36 || base < 1) {
|
|
151
|
-
|
|
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
|
-
|
|
165
|
+
do fail()
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
str.charAt(0) match {
|