@effekt-lang/effekt 0.2.2 → 0.4.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/README.md +6 -18
- package/bin/effekt +0 -0
- package/bin/effekt.sh +1 -1
- package/libraries/chez/common/effekt_primitives.ss +10 -2
- package/libraries/chez/common/immutable/cslist.effekt +13 -7
- package/libraries/chez/common/mutable/dict.effekt +8 -10
- package/libraries/chez/common/text/regex.effekt +7 -9
- package/libraries/common/args.effekt +68 -0
- package/libraries/common/array.effekt +220 -0
- package/libraries/common/bench.effekt +86 -0
- package/libraries/common/buffer.effekt +123 -0
- package/libraries/common/bytes.effekt +125 -0
- package/libraries/{js/immutable → common}/dequeue.effekt +18 -4
- package/libraries/common/effekt.effekt +725 -0
- package/libraries/common/exception.effekt +121 -0
- package/libraries/common/io/console.effekt +62 -0
- package/libraries/common/io/error.effekt +474 -0
- package/libraries/common/io/filesystem.effekt +274 -0
- package/libraries/common/io/network.effekt +81 -0
- package/libraries/common/io/time.effekt +12 -0
- package/libraries/common/io.effekt +88 -0
- package/libraries/{llvm/immutable → common}/list.effekt +101 -74
- package/libraries/common/option.effekt +72 -0
- package/libraries/common/process.effekt +9 -0
- package/libraries/common/queue.effekt +140 -0
- package/libraries/common/ref.effekt +60 -0
- package/libraries/common/result.effekt +45 -0
- package/libraries/{ml/immutable → common}/seq.effekt +30 -19
- package/libraries/common/string/tty.effekt +66 -0
- package/libraries/common/string.effekt +361 -0
- package/libraries/common/test.effekt +160 -0
- package/libraries/js/effekt_builtins.js +37 -6
- package/libraries/js/mutable/map.effekt +8 -9
- package/libraries/js/text/regex.effekt +7 -8
- package/libraries/js/unsafe/cont.effekt +2 -2
- package/libraries/js/web/dom.effekt +13 -13
- package/libraries/llvm/array.c +53 -0
- package/libraries/llvm/buffer.c +181 -76
- package/libraries/llvm/forward-declare-c.ll +27 -8
- package/libraries/llvm/io.c +423 -1
- package/libraries/llvm/main.c +6 -1
- package/libraries/llvm/ref.c +45 -0
- package/libraries/llvm/rts.ll +515 -354
- package/libraries/llvm/types.c +32 -0
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +4 -4
- package/licenses/mit license - mit-license.html +532 -62
- package/licenses/the bsd license - bsd-license.html +535 -60
- package/licenses/the mit license - mit.html +532 -62
- package/package.json +2 -2
- package/libraries/chez/callcc/effekt.effekt +0 -221
- package/libraries/chez/common/effekt_matching.ss +0 -100
- package/libraries/chez/common/immutable/dequeue.effekt +0 -78
- package/libraries/chez/common/immutable/list.effekt +0 -643
- package/libraries/chez/common/immutable/option.effekt +0 -39
- package/libraries/chez/common/immutable/result.effekt +0 -29
- package/libraries/chez/common/io/args.effekt +0 -20
- package/libraries/chez/common/mutable/array.effekt +0 -27
- package/libraries/chez/common/mutable/heap.effekt +0 -12
- package/libraries/chez/common/test.effekt +0 -59
- package/libraries/chez/common/text/string.effekt +0 -46
- package/libraries/chez/lift/effekt.effekt +0 -217
- package/libraries/chez/lift/effekt.ss +0 -118
- package/libraries/chez/monadic/effekt.effekt +0 -218
- package/libraries/js/effekt.effekt +0 -284
- package/libraries/js/effekt_matching.js +0 -79
- package/libraries/js/immutable/list.effekt +0 -643
- package/libraries/js/immutable/option.effekt +0 -49
- package/libraries/js/immutable/result.effekt +0 -29
- package/libraries/js/io/args.effekt +0 -9
- package/libraries/js/io/async.effekt +0 -28
- package/libraries/js/io/async.js +0 -2
- package/libraries/js/io/file.effekt +0 -50
- package/libraries/js/io/file_include.js +0 -13
- package/libraries/js/mutable/array.effekt +0 -98
- package/libraries/js/mutable/heap.effekt +0 -32
- package/libraries/js/test.effekt +0 -59
- package/libraries/js/text/string.effekt +0 -64
- package/libraries/llvm/effekt.effekt +0 -243
- package/libraries/llvm/immutable/option.effekt +0 -53
- package/libraries/llvm/immutable/result.effekt +0 -29
- package/libraries/llvm/io/args.effekt +0 -24
- package/libraries/llvm/text/string.effekt +0 -179
- package/libraries/ml/effekt.effekt +0 -267
- package/libraries/ml/effekt.sml +0 -63
- package/libraries/ml/immutable/dequeue.effekt +0 -81
- package/libraries/ml/immutable/list.effekt +0 -674
- package/libraries/ml/immutable/option.effekt +0 -53
- package/libraries/ml/immutable/result.effekt +0 -29
- package/libraries/ml/internal/option.effekt +0 -18
- package/libraries/ml/io/args.effekt +0 -41
- package/libraries/ml/mutable/array.effekt +0 -43
- package/libraries/ml/random.effekt +0 -85
- package/libraries/ml/test.effekt +0 -60
- package/libraries/ml/text/regex.effekt +0 -107
- package/libraries/ml/text/string.effekt +0 -81
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import immutable/option
|
|
2
|
-
|
|
3
|
-
// Results
|
|
4
|
-
// =======
|
|
5
|
-
// The "reification" of the Exception[E] effect.
|
|
6
|
-
type Result[A, E] {
|
|
7
|
-
Error(exception: E, msg: String);
|
|
8
|
-
Success(a: A)
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
def result[A, E] { f: () => A / Exception[E] }: Result[A, E] = try {
|
|
12
|
-
Success(f())
|
|
13
|
-
} with Exception[E] {
|
|
14
|
-
def raise(exc, msg) = Error(exc, msg)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Extracts the value of a result, if available
|
|
19
|
-
* Monadic "reflection"
|
|
20
|
-
*/
|
|
21
|
-
def value[A, E](r: Result[A, E]): A / Exception[E] = r match {
|
|
22
|
-
case Success(a) => a
|
|
23
|
-
case Error(exc, msg) => do raise(exc, msg)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
def toOption[A, E](r: Result[A, E]): Option[A] = r match {
|
|
27
|
-
case Success(a) => Some(a)
|
|
28
|
-
case Error(exc, msg) => None()
|
|
29
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
module internal/option
|
|
2
|
-
|
|
3
|
-
import effekt
|
|
4
|
-
import immutable/option
|
|
5
|
-
|
|
6
|
-
extern type MLOption[T]
|
|
7
|
-
|
|
8
|
-
extern pure def isDefined[T](opt: MLOption[T]): Boolean =
|
|
9
|
-
"Option.isSome ${opt}"
|
|
10
|
-
|
|
11
|
-
def isEmpty[T](opt: MLOption[T]): Boolean = not(opt.isDefined())
|
|
12
|
-
|
|
13
|
-
extern pure def force[T](opt: MLOption[T]): T =
|
|
14
|
-
"Option.valOf ${opt}"
|
|
15
|
-
|
|
16
|
-
def toOption[T](opt: MLOption[T]): Option[T] = {
|
|
17
|
-
if (isDefined(opt)) Some(force(opt)) else None()
|
|
18
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
module io/args
|
|
2
|
-
|
|
3
|
-
import effekt
|
|
4
|
-
import internal/option
|
|
5
|
-
import immutable/option
|
|
6
|
-
import immutable/list
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// ================================================
|
|
10
|
-
//
|
|
11
|
-
// Conceptual Public Functions:
|
|
12
|
-
// - commandLineArgs(): List[String]
|
|
13
|
-
//
|
|
14
|
-
// ================================================
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// ml Ref of ml list
|
|
18
|
-
extern type Args
|
|
19
|
-
|
|
20
|
-
// Works like an iterator
|
|
21
|
-
extern io def nativeArgs(): Args =
|
|
22
|
-
"ref (CommandLine.arguments ())"
|
|
23
|
-
|
|
24
|
-
extern """
|
|
25
|
-
fun nextArgument a =
|
|
26
|
-
case !a of
|
|
27
|
-
[] => NONE |
|
|
28
|
-
x :: xs => (a := xs; SOME x);
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
// consumes the next arg if present
|
|
32
|
-
extern io def nativeNext(a: Args): MLOption[String] =
|
|
33
|
-
"nextArgument ${a}"
|
|
34
|
-
|
|
35
|
-
def commandLineArgs(): List[String] = {
|
|
36
|
-
def argsToRevList(a: Args, acc: List[String]): List[String] = toOption(nativeNext(a)) match {
|
|
37
|
-
case None() => acc
|
|
38
|
-
case Some(v) => argsToRevList(a, Cons(v, acc))
|
|
39
|
-
}
|
|
40
|
-
reverse(argsToRevList(nativeArgs(), Nil()))
|
|
41
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
module mutable/array
|
|
2
|
-
|
|
3
|
-
import immutable/option
|
|
4
|
-
|
|
5
|
-
// A mutable array, backed by a sml array.
|
|
6
|
-
extern type Array[T]
|
|
7
|
-
|
|
8
|
-
extern pure def emptyArray[T](): Array[T] =
|
|
9
|
-
"Array.fromList []"
|
|
10
|
-
|
|
11
|
-
extern pure def array[T](initialSize: Int, init: T): Array[T] =
|
|
12
|
-
"Array.array (${initialSize}, ${init})"
|
|
13
|
-
|
|
14
|
-
def get[T](arr: Array[T], index: Int): Option[T] = {
|
|
15
|
-
val len = size(arr);
|
|
16
|
-
if (index < 0 || len <= index) None()
|
|
17
|
-
else Some(unsafeGet(arr, index))
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
extern pure def size[T](arr: Array[T]): Int =
|
|
21
|
-
"Array.length ${arr}"
|
|
22
|
-
|
|
23
|
-
extern pure def unsafeGet[T](arr: Array[T], index: Int): T =
|
|
24
|
-
"Array.sub (${arr}, ${index})"
|
|
25
|
-
|
|
26
|
-
def put[T](arr: Array[T], index: Int, value: T): Unit =
|
|
27
|
-
if (index < 0 || size(arr) <= index) ()
|
|
28
|
-
else unsafeput(arr, index, value)
|
|
29
|
-
|
|
30
|
-
extern io def unsafeput[T](arr: Array[T], index: Int, value: T): Unit =
|
|
31
|
-
"Array.update (${arr}, ${index}, ${value})"
|
|
32
|
-
|
|
33
|
-
def copy[T](arr: Array[T]): Array[T] =
|
|
34
|
-
get(arr, 0) match {
|
|
35
|
-
case None() => emptyArray()
|
|
36
|
-
case Some(first) =>
|
|
37
|
-
val dst = array(size(arr), first)
|
|
38
|
-
copyInto(arr, dst);
|
|
39
|
-
dst
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
extern pure def copyInto[T](src: Array[T], dst: Array[T]): Unit =
|
|
43
|
-
"Array.copy {src = ${src}, dst = ${dst}, di = 0}"
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
module random
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Imperative Random Numbers
|
|
5
|
-
* -------------------------
|
|
6
|
-
* The following are simple bindings for the following SML/NJ module:
|
|
7
|
-
* https://www.smlnj.org/doc/smlnj-lib/Util/str-Random.html
|
|
8
|
-
*
|
|
9
|
-
* They are not supposed to be used directly, but only via the
|
|
10
|
-
* effect [[Random]], below.
|
|
11
|
-
*/
|
|
12
|
-
extern type RNG // = int * int
|
|
13
|
-
extern io def seed(n: Int, m: Int): RNG = "Random.rand (${n}, ${m})"
|
|
14
|
-
extern pure def toString(s: RNG): String = "Random.toString ${s}"
|
|
15
|
-
|
|
16
|
-
extern io def int(s: RNG): Int = "Random.randInt ${s}"
|
|
17
|
-
extern io def int(s: RNG, from: Int, to: Int): Int = "Random.randRange (${from}, ${to}) ${s}"
|
|
18
|
-
extern io def double(s: RNG): Double = "Random.randReal ${s}"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Functional Random Numbers
|
|
23
|
-
* -------------------------
|
|
24
|
-
* The following are simple bindings for the following SML/NJ module:
|
|
25
|
-
* https://www.smlnj.org/doc/smlnj-lib/Util/str-Rand.html
|
|
26
|
-
*
|
|
27
|
-
* They are not supposed to be used directly, but only via the
|
|
28
|
-
* effect [[Random]], below.
|
|
29
|
-
*/
|
|
30
|
-
extern type PseudoRNG
|
|
31
|
-
extern pure def random(s: PseudoRNG): PseudoRNG =
|
|
32
|
-
"Rand.random ${s}"
|
|
33
|
-
extern pure def range(s: PseudoRNG, n: Int, m: Int): Int =
|
|
34
|
-
"Rand.range (${n}, ${m}) ${s}"
|
|
35
|
-
extern pure def norm(s: PseudoRNG): Double =
|
|
36
|
-
"Rand.norm ${s}"
|
|
37
|
-
|
|
38
|
-
// converting two integers into a word that is between randMin and randMax is completely made up
|
|
39
|
-
extern pure def pseudo(n: Int, m: Int): PseudoRNG =
|
|
40
|
-
"Word.fromInt ((Word.toInt Rand.randMin) + ((abs (${n} * ${m})) mod (Word.toInt Rand.randMax)))"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// "Public" API
|
|
45
|
-
// ------------
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* A random number generator.
|
|
49
|
-
*
|
|
50
|
-
* Supposed to be used as an effect: `do randomInt(0, 10)`.
|
|
51
|
-
* There are two canonical implementations as effect handlers (see below).
|
|
52
|
-
*/
|
|
53
|
-
interface Random {
|
|
54
|
-
// returns a random integer between `from` and `to`
|
|
55
|
-
def randomInt(from: Int, to: Int): Int
|
|
56
|
-
// returns a random double between 0.0 and 1.0
|
|
57
|
-
def randomDouble(): Double
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Imperative random numbers. Does not perform backtracking.
|
|
62
|
-
*/
|
|
63
|
-
def withRandom[R](n: Int, m: Int) { prog: => R / Random } = {
|
|
64
|
-
// should be second class, is mutated...
|
|
65
|
-
val rng = seed(n, m)
|
|
66
|
-
try { prog() }
|
|
67
|
-
with Random {
|
|
68
|
-
def randomInt(from, to) = resume(rng.int(from, to))
|
|
69
|
-
def randomDouble() = resume(rng.double())
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Functional, pseudo random numbers. According to SMLNJ docs it is faster than the imperative version
|
|
75
|
-
*/
|
|
76
|
-
def withPseudoRandom[R](n: Int, m: Int) { prog: => R / Random } = {
|
|
77
|
-
// should be second class, is mutated...
|
|
78
|
-
var rng = pseudo(n, m)
|
|
79
|
-
def next() = rng = random(rng);
|
|
80
|
-
try { prog() }
|
|
81
|
-
with Random {
|
|
82
|
-
def randomInt(from, to) = { next(); resume(rng.range(from, to)) }
|
|
83
|
-
def randomDouble() = { next(); resume(rng.norm()) }
|
|
84
|
-
}
|
|
85
|
-
}
|
package/libraries/ml/test.effekt
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import text/string
|
|
2
|
-
|
|
3
|
-
interface Assertion {
|
|
4
|
-
def assert(condition: Boolean, msg: String): Unit
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
def assertTrue(condition: Boolean, msg: String) =
|
|
8
|
-
do assert(condition, msg)
|
|
9
|
-
|
|
10
|
-
def assertTrue(condition: Boolean) =
|
|
11
|
-
do assert(condition, "Assertion failed")
|
|
12
|
-
|
|
13
|
-
def assertFalse(condition: Boolean, msg: String) =
|
|
14
|
-
do assert(not(condition), msg)
|
|
15
|
-
|
|
16
|
-
def assertFalse(condition: Boolean) =
|
|
17
|
-
do assert(not(condition), "Assertion failed")
|
|
18
|
-
|
|
19
|
-
def assertEqual[A](obtained: A, expected: A, msg: String) =
|
|
20
|
-
do assert(obtained == expected, msg)
|
|
21
|
-
|
|
22
|
-
// Note: every other backend uses a generic `show` instead
|
|
23
|
-
def assertEqual[A](obtained: A, expected: A) { show : A => String } =
|
|
24
|
-
do assert(obtained == expected, "Obtained: " ++ show(obtained) ++ "\n but expected:" ++ show(expected))
|
|
25
|
-
|
|
26
|
-
interface Test {
|
|
27
|
-
def success(name: String): Unit
|
|
28
|
-
def failure(name: String, msg: String): Unit
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
def test(name: String) { body: => Unit / Assertion } =
|
|
32
|
-
try { body(); do success(name) } with Assertion {
|
|
33
|
-
def assert(condition, msg) = if (condition) resume(()) else do failure(name, msg)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
def suite(name: String) { body: => Unit / Test }: Boolean = {
|
|
37
|
-
println("Running suite: " ++ name);
|
|
38
|
-
var failed = 0
|
|
39
|
-
var passed = 0
|
|
40
|
-
|
|
41
|
-
try { body() } with Test {
|
|
42
|
-
def success(name) = {
|
|
43
|
-
passed = passed + 1
|
|
44
|
-
println(ANSI_GREEN ++ "+ " ++ name ++ "" ++ ANSI_RESET);
|
|
45
|
-
resume(())
|
|
46
|
-
}
|
|
47
|
-
def failure(name, msg) = {
|
|
48
|
-
failed = failed + 1
|
|
49
|
-
println(ANSI_RED ++ "- (FAIL) " ++ name ++ "\n " ++ msg ++ ANSI_RESET);
|
|
50
|
-
resume(())
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
if (failed > 0) {
|
|
54
|
-
println(ANSI_RED ++ "Some tests failed (" ++ passed.show ++ " passed, " ++ failed.show ++ " failed)" ++ ANSI_RESET)
|
|
55
|
-
false
|
|
56
|
-
} else {
|
|
57
|
-
println(ANSI_GREEN ++ "All tests passed (" ++ passed.show ++ " passed)" ++ ANSI_RESET)
|
|
58
|
-
true
|
|
59
|
-
}
|
|
60
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
module text/regex
|
|
2
|
-
|
|
3
|
-
extern type MLList[Elem] // = 'elem list
|
|
4
|
-
extern pure def isEmpty[A](l: MLList[A]): Boolean = "null ${l}"
|
|
5
|
-
extern pure def head[A](l: MLList[A]): A = "hd ${l}"
|
|
6
|
-
extern pure def tail[A](l: MLList[A]): MLList[A] = "tl ${l}"
|
|
7
|
-
|
|
8
|
-
def toList[A](l: MLList[A]): List[A] =
|
|
9
|
-
if (l.isEmpty) Nil() else Cons(l.head, toList(l.tail))
|
|
10
|
-
|
|
11
|
-
extern type Char // TODO move to text/string
|
|
12
|
-
type Chars = MLList[Char]
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
extern pure def chars(str: String): Chars = "String.explode ${str}"
|
|
16
|
-
extern pure def toString(chars: Chars): String = "String.implode ${chars}"
|
|
17
|
-
|
|
18
|
-
extern """
|
|
19
|
-
|
|
20
|
-
(** The RegExp module is created by choosing a parser (P) and an engine (E)
|
|
21
|
-
* for regular expressions.
|
|
22
|
-
*
|
|
23
|
-
* There is no big choice for the parser, since only Awk syntax is supported.
|
|
24
|
-
* For the regular expression engine, we choose Ken Thompson's algorighm.
|
|
25
|
-
*
|
|
26
|
-
* Also see:
|
|
27
|
-
* <https://stackoverflow.com/questions/35462919/how-to-use-regex-in-smlnj>
|
|
28
|
-
* <https://www.smlnj.org/doc/smlnj-lib/RegExp/fun-RegExpFn.html>
|
|
29
|
-
*)
|
|
30
|
-
structure RegExp = RegExpFn(
|
|
31
|
-
structure P = AwkSyntax
|
|
32
|
-
structure E = ThompsonEngine
|
|
33
|
-
(* structure E = BackTrackEngine *)
|
|
34
|
-
(* structure E = DfaEngine *)
|
|
35
|
-
)
|
|
36
|
-
"""
|
|
37
|
-
|
|
38
|
-
// The type of regular expressions (ML representation)
|
|
39
|
-
extern type Regex
|
|
40
|
-
|
|
41
|
-
record Match(matched: String)
|
|
42
|
-
|
|
43
|
-
// TODO handle CannotParse and CannotCompile ml exceptions.
|
|
44
|
-
extern pure def regex(regexString: String): Regex =
|
|
45
|
-
"RegExp.compileString ${regexString}"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// A stream of elements of type Elem, read from a source of type Source
|
|
49
|
-
extern type MLStream[Elem, Source] // = ('elem, 'source) StringCvt.reader
|
|
50
|
-
|
|
51
|
-
// Internal ml type that represents matches
|
|
52
|
-
// <https://www.smlnj.org/doc/smlnj-lib/RegExp/str-MatchTree.html>
|
|
53
|
-
extern type MLMatchResult // = match * char list
|
|
54
|
-
|
|
55
|
-
extern """
|
|
56
|
-
type regex_match = {len: int, pos: char list} MatchTree.match_tree;
|
|
57
|
-
fun matchedString (match : regex_match * char list) =
|
|
58
|
-
let val res = MatchTree.root (#1 match)
|
|
59
|
-
in String.implode (List.take (#pos res, #len res)) end;
|
|
60
|
-
|
|
61
|
-
fun remainderChars (match : regex_match * char list) =
|
|
62
|
-
let val res = MatchTree.root (#1 match)
|
|
63
|
-
in List.drop (#pos res, #len res) end;
|
|
64
|
-
|
|
65
|
-
(** The remainder is just a draft and doesn't work yet...) *)
|
|
66
|
-
|
|
67
|
-
(** MatchTree -> string list **)
|
|
68
|
-
fun allMatched (matchtree: regex_match) =
|
|
69
|
-
MatchTree.foldl (fn (m, b) => (String.implode (List.take (#pos m, #len m))) :: b) nil matchtree
|
|
70
|
-
fun allBut str = RegExp.compileString ("[^" ^ str ^ "]+");
|
|
71
|
-
|
|
72
|
-
fun unsafeSplit str sep =
|
|
73
|
-
case (RegExp.find (allBut str) List.getItem (String.explode str)) of
|
|
74
|
-
NONE => nil
|
|
75
|
-
| SOME v => allMatched (#1 v);
|
|
76
|
-
"""
|
|
77
|
-
|
|
78
|
-
extern pure def getMatch(result: MLMatchResult): String =
|
|
79
|
-
"matchedString ${result}"
|
|
80
|
-
extern pure def getRemainder(result: MLMatchResult): Chars =
|
|
81
|
-
"remainderChars ${result}"
|
|
82
|
-
|
|
83
|
-
extern pure def unsafeSplit(str: String, ch: Char): MLList[String] =
|
|
84
|
-
"String.fields (fn c => c = ${ch}) ${str}"
|
|
85
|
-
|
|
86
|
-
extern pure def unsafeMatchPrefix(r: Regex, str: Chars): MLOption[MLMatchResult] =
|
|
87
|
-
"RegExp.prefix ${r} List.getItem ${str}"
|
|
88
|
-
|
|
89
|
-
extern pure def unsafeSplit(str: String, sep: String): MLList[String] =
|
|
90
|
-
"unsafeSplit ${str} ${sep}"
|
|
91
|
-
|
|
92
|
-
extern """
|
|
93
|
-
val NEWLINE_CHAR = #"\n";
|
|
94
|
-
"""
|
|
95
|
-
|
|
96
|
-
extern pure def unsafeLines(str: String): MLList[String] =
|
|
97
|
-
"String.fields (fn c => c = NEWLINE_CHAR) ${str}"
|
|
98
|
-
|
|
99
|
-
def lines(str: String): List[String] = unsafeLines(str).toList
|
|
100
|
-
|
|
101
|
-
def split(str: String, sep: String): List[String] = unsafeSplit(str, sep).toList
|
|
102
|
-
|
|
103
|
-
def exec(reg: Regex, str: String): Option[Match] = {
|
|
104
|
-
val matched = reg.unsafeMatchPrefix(str.chars)
|
|
105
|
-
if (matched.isDefined) { Some(Match(matched.force.getMatch())) }
|
|
106
|
-
else { None() }
|
|
107
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
module text/string
|
|
2
|
-
|
|
3
|
-
import effekt
|
|
4
|
-
import immutable/option
|
|
5
|
-
import internal/option
|
|
6
|
-
|
|
7
|
-
// For more information on the sml string type:
|
|
8
|
-
// https://smlfamily.github.io/Basis/string.html#String:STR:SPEC
|
|
9
|
-
|
|
10
|
-
def charAt(str: String, index: Int): Option[String] =
|
|
11
|
-
if (index < 0 || length(str) <= index)
|
|
12
|
-
Some(unsafeCharAt(str, index))
|
|
13
|
-
else None()
|
|
14
|
-
|
|
15
|
-
extern pure def length(str: String): Int =
|
|
16
|
-
"String.size ${str}"
|
|
17
|
-
|
|
18
|
-
extern pure def startsWith(str: String, prefix: String): Boolean =
|
|
19
|
-
"String.isPrefix ${prefix} ${str}"
|
|
20
|
-
|
|
21
|
-
extern pure def endsWith(str: String, suffix: String): Boolean =
|
|
22
|
-
"String.isSuffix ${suffix} ${str}"
|
|
23
|
-
|
|
24
|
-
// extern pure def repeat(str: String, n: Int): String =
|
|
25
|
-
// "???"
|
|
26
|
-
|
|
27
|
-
def substring(str: String, from: Int, to: Int): String = {
|
|
28
|
-
def clamp(lower: Int, x: Int, upper: Int) = max(lower, min(x, upper))
|
|
29
|
-
|
|
30
|
-
val len = str.length
|
|
31
|
-
val clampedTo = clamp(0, to, len)
|
|
32
|
-
val clampedFrom = clamp(0, from, to)
|
|
33
|
-
|
|
34
|
-
val substringLength = max(0, clampedTo - clampedFrom)
|
|
35
|
-
str.unsafeSubstring(clampedFrom, substringLength)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
def substring(str: String, from: Int): String =
|
|
39
|
-
str.substring(from, str.length)
|
|
40
|
-
|
|
41
|
-
extern pure def unsafeSubstring(str: String, from: Int, len: Int): String =
|
|
42
|
-
"String.extract (${str}, ${from}, SOME ${len})"
|
|
43
|
-
|
|
44
|
-
// extern pure def split(str: String, sep: String): Array[String] =
|
|
45
|
-
// "Array.fromList (map (String.str) (String.explode str))"
|
|
46
|
-
|
|
47
|
-
// extern pure def trim(str: String): String =
|
|
48
|
-
// "???"
|
|
49
|
-
|
|
50
|
-
// also accepts sml style `~2` for unary minus along with `+2` and `-2`
|
|
51
|
-
extern pure def internalStringToInt(str: String): MLOption[Int] =
|
|
52
|
-
"Int.fromString ${str}"
|
|
53
|
-
|
|
54
|
-
def toInt(str: String): Option[Int] = toOption(internalStringToInt(str))
|
|
55
|
-
|
|
56
|
-
extern pure def unsafeCharAt(str: String, n: Int): String =
|
|
57
|
-
"String.sub (${str}, ${n})"
|
|
58
|
-
|
|
59
|
-
def indexOf(str: String, sub: String): Option[Int] = {
|
|
60
|
-
def loop(str: String, sub: String, index: Int): Int = {
|
|
61
|
-
if (index >= str.length) { -1 }
|
|
62
|
-
else if (str.substring(index).startsWith(sub)) { index }
|
|
63
|
-
else { loop(str, sub, index + 1) }
|
|
64
|
-
}
|
|
65
|
-
val index = loop(str, sub, 0)
|
|
66
|
-
if (index == -1) { None() } else { Some(index) }
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
def lastIndexOf(str: String, sub: String): Option[Int] = {
|
|
70
|
-
def loop(str: String, sub: String, index: Int): Int = {
|
|
71
|
-
if (index < 0) { -1 }
|
|
72
|
-
else if (str.substring(index).startsWith(sub)) { index }
|
|
73
|
-
else { loop(str, sub, index - 1) }
|
|
74
|
-
}
|
|
75
|
-
val index = loop(str, sub, str.length)
|
|
76
|
-
if (index == -1) { None() } else { Some(index) }
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
val ANSI_GREEN = "\u001b[32m"
|
|
80
|
-
val ANSI_RED = "\u001b[31m"
|
|
81
|
-
val ANSI_RESET = "\u001b[0m"
|