Skip to content

Commit b43d0f2

Browse files
chengluyuFlandiaYingman
authored andcommitted
Move UCS helper functions to the runtime module (hkust-taco#299)
1 parent 3c41858 commit b43d0f2

File tree

10 files changed

+139
-87
lines changed

10 files changed

+139
-87
lines changed

hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ trait JSBuilderArgNumSanityChecks(using Config, Elaborator.State)
621621
doc"\nlet ${nme} = ${paramsStr}[$i];"}.mkDocument("")
622622
val restAssign = paramRest match
623623
case N => doc""
624-
case S(p) => doc"\nlet $p = globalThis.Predef.tupleSlice($paramsStr, ${params.paramCountLB}, 0);"
624+
case S(p) => doc"\nlet $p = runtime.Tuple.slice($paramsStr, ${params.paramCountLB}, 0);"
625625
(doc"...$paramsStr", doc"$checkArgsNum$paramsAssign$restAssign${this.body(body, endSemi = false)}")
626626
else
627627
super.setupFunction(name, params, body)

hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/DesugaringBase.scala

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import syntax.Tree.*, Elaborator.{Ctxl, ctx}, Elaborator.State
99
trait DesugaringBase(using state: State):
1010
val elaborator: Elaborator
1111

12-
import elaborator.tl.*, state.globalThisSymbol
12+
import elaborator.tl.*
1313

1414
protected final def sel(p: Term, k: Ident): Term.SynthSel = Term.SynthSel(p, k)(N)
1515
protected final def sel(p: Term, k: Ident, s: FieldSymbol): Term.SynthSel = Term.SynthSel(p, k)(S(s))
@@ -22,54 +22,50 @@ trait DesugaringBase(using state: State):
2222
protected final def app(l: Term, r: Term, label: Str): Term.App = app(l, r, FlowSymbol(label))
2323
protected final def app(l: Term, r: Term, s: FlowSymbol): Term.App = Term.App(l, r)(App(Empty(), Empty()), N, s)
2424

25-
/** Make a term looks like `globalThis.Predef.MatchResult` with its symbol. */
25+
/** Make a term that looks like `runtime.MatchResult` with its symbol. */
2626
protected lazy val matchResultClass: Ctxl[(Term.Sel | Term.SynthSel, ClassSymbol)] =
2727
(State.runtimeSymbol.ref().selNoSym("MatchResult", synth=true), State.matchResultClsSymbol)
2828

29-
/** Make a pattern looks like `globalThis.Predef.MatchResult.class`. */
29+
/** Make a pattern that looks like `runtime.MatchResult.class`. */
3030
protected def matchResultPattern(parameters: Opt[List[BlockLocalSymbol]]): Ctxl[Pattern.ClassLike] =
3131
val (classRef, classSym) = matchResultClass
3232
val classSel = Term.SynthSel(classRef, Ident("class"))(S(classSym))
3333
Pattern.ClassLike(classSym, classSel, parameters, false)(Empty())
3434

35-
/** Make a term looks like `globalThis.Predef.MatchFailure` with its symbol. */
35+
/** Make a term that looks like `runtime.MatchFailure` with its symbol. */
3636
protected lazy val matchFailureClass: Ctxl[(Term.Sel | Term.SynthSel, ClassSymbol)] =
3737
(State.runtimeSymbol.ref().selNoSym("MatchFailure", synth=true), State.matchFailureClsSymbol)
3838

39-
/** Make a pattern looks like `globalThis.Predef.MatchFailure.class`. */
39+
/** Make a pattern that looks like `runtime.MatchFailure.class`. */
4040
protected def matchFailurePattern(parameters: Opt[List[BlockLocalSymbol]]): Ctxl[Pattern.ClassLike] =
4141
val (classRef, classSym) = matchResultClass
4242
val classSel = Term.SynthSel(classRef, Ident("class"))(S(classSym))
4343
Pattern.ClassLike(classSym, classSel, parameters, false)(Empty())
4444

45-
/** Create a term that selects a method in the `Predef` module. */
46-
protected final def selectPredefMethod =
47-
sel(sel(globalThisSymbol.ref(), "Predef"), _: Str)
45+
protected lazy val tupleSlice = sel(sel(state.runtimeSymbol.ref(), "Tuple"), "slice")
46+
protected lazy val tupleGet = sel(sel(state.runtimeSymbol.ref(), "Tuple"), "get")
47+
protected lazy val stringStartsWith = sel(sel(state.runtimeSymbol.ref(), "Str"), "startsWith")
48+
protected lazy val stringGet = sel(sel(state.runtimeSymbol.ref(), "Str"), "get")
49+
protected lazy val stringDrop = sel(sel(state.runtimeSymbol.ref(), "Str"), "drop")
4850

49-
protected lazy val tupleSlice = selectPredefMethod("tupleSlice")
50-
protected lazy val tupleGet = selectPredefMethod("tupleGet")
51-
protected lazy val stringStartsWith = selectPredefMethod("stringStartsWith")
52-
protected lazy val stringGet = selectPredefMethod("stringGet")
53-
protected lazy val stringDrop = selectPredefMethod("stringDrop")
54-
55-
/** Make a term that looks like `tupleGet(t, i)`. */
51+
/** Make a term that looks like `runtime.Tuple.get(t, i)`. */
5652
protected final def callTupleGet(t: Term, i: Int, label: Str): Ctxl[Term] =
5753
callTupleGet(t, i, FlowSymbol(label))
5854

59-
/** Make a term that looks like `tupleGet(t, i)`. */
55+
/** Make a term that looks like `runtime.Tuple.slice(t, i)`. */
6056
protected final def callTupleGet(t: Term, i: Int, s: FlowSymbol): Ctxl[Term] =
6157
app(tupleGet, tup(fld(t), fld(int(i))), s)
6258

63-
/** Make a term that looks like `stringStartsWith(t, p)`. */
64-
protected final def callStringStartsWith(t: Term, p: Term, label: Str): Ctxl[Term] =
59+
/** Make a term that looks like `runtime.Str.startsWith(t, p)`. */
60+
protected final def callStringStartsWith(t: Term.Ref, p: Term, label: Str): Ctxl[Term] =
6561
app(stringStartsWith, tup(fld(t), fld(p)), FlowSymbol(label))
6662

67-
/** Make a term that looks like `stringStartsWith(t, i)`. */
68-
protected final def callStringGet(t: Term, i: Int, label: Str): Ctxl[Term] =
63+
/** Make a term that looks like `runtime.Str.get(t, i)`. */
64+
protected final def callStringGet(t: Term.Ref, i: Int, label: Str): Ctxl[Term] =
6965
app(stringGet, tup(fld(t), fld(int(i))), FlowSymbol(label))
7066

71-
/** Make a term that looks like `stringStartsWith(t, n)`. */
72-
protected final def callStringDrop(t: Term, n: Int, label: Str): Ctxl[Term] =
67+
/** Make a term that looks like `runtime.Str.drop(t, n)`. */
68+
protected final def callStringDrop(t: Term.Ref, n: Int, label: Str): Ctxl[Term] =
7369
app(stringDrop, tup(fld(t), fld(int(n))), FlowSymbol(label))
7470

7571
protected final def tempLet(dbgName: Str, term: Term)(inner: TempSymbol => Split): Split =

hkmc2/shared/src/test/mlscript-compile/Predef.mjs

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -150,32 +150,24 @@ let Predef1;
150150
static tuple(...xs1) {
151151
return xs1
152152
}
153-
static tupleSlice(xs2, i, j) {
154-
let tmp;
155-
tmp = xs2.length - j;
156-
return runtime.safeCall(globalThis.Array.prototype.slice.call(xs2, i, tmp))
157-
}
158-
static tupleGet(xs3, i1) {
159-
return globalThis.Array.prototype.at.call(xs3, i1)
160-
}
161153
static foldr(f9) {
162154
return (first, ...rest) => {
163-
let len, i2, init, scrut, scrut1, tmp, tmp1, tmp2, tmp3, tmp4, tmp5;
155+
let len, i, init, scrut, scrut1, tmp, tmp1, tmp2, tmp3, tmp4, tmp5;
164156
len = rest.length;
165157
scrut1 = len == 0;
166158
if (scrut1 === true) {
167159
return first
168160
} else {
169161
tmp = len - 1;
170-
i2 = tmp;
171-
tmp1 = runtime.safeCall(rest.at(i2));
162+
i = tmp;
163+
tmp1 = runtime.safeCall(rest.at(i));
172164
init = tmp1;
173165
tmp6: while (true) {
174-
scrut = i2 > 0;
166+
scrut = i > 0;
175167
if (scrut === true) {
176-
tmp2 = i2 - 1;
177-
i2 = tmp2;
178-
tmp3 = runtime.safeCall(rest.at(i2));
168+
tmp2 = i - 1;
169+
i = tmp2;
170+
tmp3 = runtime.safeCall(rest.at(i));
179171
tmp4 = runtime.safeCall(f9(tmp3, init));
180172
init = tmp4;
181173
tmp5 = runtime.Unit;
@@ -189,7 +181,7 @@ let Predef1;
189181
}
190182
}
191183
}
192-
static mkStr(...xs4) {
184+
static mkStr(...xs2) {
193185
let tmp, tmp1, lambda;
194186
lambda = (undefined, function (acc, x7) {
195187
let tmp2, tmp3, tmp4;
@@ -204,16 +196,7 @@ let Predef1;
204196
});
205197
tmp = lambda;
206198
tmp1 = runtime.safeCall(Predef.fold(tmp));
207-
return runtime.safeCall(tmp1(...xs4))
208-
}
209-
static stringStartsWith(string, prefix) {
210-
return runtime.safeCall(string.startsWith(prefix))
211-
}
212-
static stringGet(string1, i2) {
213-
return runtime.safeCall(string1.at(i2))
214-
}
215-
static stringDrop(string2, n) {
216-
return runtime.safeCall(string2.slice(n))
199+
return runtime.safeCall(tmp1(...xs2))
217200
}
218201
static get unreachable() {
219202
throw globalThis.Error("unreachable");

hkmc2/shared/src/test/mlscript-compile/Predef.mls

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,6 @@ fun (???) notImplementedError = throw Error("Not implemented")
5050

5151
fun tuple(...xs) = xs
5252

53-
fun tupleSlice(xs, i, j) =
54-
// * This is more robust than `xs.slice(i, xs.length - j)`
55-
// * as it is not affected by users redefining `slice`
56-
globalThis.Array.prototype.slice.call(xs, i, xs.length - j)
57-
58-
fun tupleGet(xs, i) =
59-
// * Contrary to `xs.[i]`, this supports negative indices (Python-style)
60-
globalThis.Array.prototype.at.call(xs, i)
61-
6253
val foldl = fold
6354

6455
// fun foldr(f)(...rest, init) = // TODO allow this syntax
@@ -78,13 +69,6 @@ fun mkStr(...xs) =
7869
fold((acc, x) => assert(x is Str); acc + x) of ...xs
7970

8071

81-
fun stringStartsWith(string, prefix) = string.startsWith(prefix)
82-
83-
fun stringGet(string, i) = string.at(i)
84-
85-
fun stringDrop(string, n) = string.slice(n)
86-
87-
8872
fun unreachable = throw Error("unreachable")
8973

9074
fun checkArgs(functionName, expected, isUB, got) =

hkmc2/shared/src/test/mlscript-compile/Runtime.mjs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,47 @@ let Runtime1;
5656
}
5757
toString() { return "MatchFailure(" + globalThis.Predef.render(this.errors) + ")"; }
5858
};
59+
(class Tuple {
60+
static {
61+
Runtime.Tuple = Tuple;
62+
}
63+
static slice(xs, i, j) {
64+
let tmp;
65+
tmp = xs.length - j;
66+
return runtime.safeCall(globalThis.Array.prototype.slice.call(xs, i, tmp))
67+
}
68+
static get(xs1, i1) {
69+
let scrut;
70+
scrut = i1 >= xs1.length;
71+
if (scrut === true) {
72+
throw globalThis.RangeError("Tuple.get: index out of bounds");
73+
} else {
74+
return globalThis.Array.prototype.at.call(xs1, i1)
75+
}
76+
}
77+
static toString() { return "Tuple"; }
78+
});
79+
(class Str {
80+
static {
81+
Runtime.Str = Str;
82+
}
83+
static startsWith(string, prefix) {
84+
return runtime.safeCall(string.startsWith(prefix))
85+
}
86+
static get(string1, i) {
87+
let scrut;
88+
scrut = i >= string1.length;
89+
if (scrut === true) {
90+
throw globalThis.RangeError("Str.get: index out of bounds");
91+
} else {
92+
return runtime.safeCall(string1.at(i))
93+
}
94+
}
95+
static drop(string2, n) {
96+
return runtime.safeCall(string2.slice(n))
97+
}
98+
static toString() { return "Str"; }
99+
});
59100
const FatalEffect$class = class FatalEffect {
60101
constructor() {}
61102
toString() { return "FatalEffect"; }

hkmc2/shared/src/test/mlscript-compile/Runtime.mls

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,28 @@ fun try(f) =
4040
data class MatchResult(captures)
4141
data class MatchFailure(errors)
4242

43+
// For pattern matching on tuples
44+
module Tuple with
45+
fun slice(xs, i, j) =
46+
// * This is more robust than `xs.slice(i, xs.length - j)`
47+
// * as it is not affected by users redefining `slice`
48+
globalThis.Array.prototype.slice.call(xs, i, xs.length - j)
49+
50+
fun get(xs, i) =
51+
// * Contrary to `xs.[i]`, this supports negative indices (Python-style)
52+
if i >= xs.length then
53+
throw RangeError("Tuple.get: index out of bounds")
54+
else globalThis.Array.prototype.at.call(xs, i)
55+
56+
module Str with
57+
fun startsWith(string, prefix) = string.startsWith(prefix)
58+
59+
fun get(string, i) =
60+
if i >= string.length then
61+
throw RangeError("Str.get: index out of bounds")
62+
else string.at(i)
63+
64+
fun drop(string, n) = string.slice(n)
4365

4466
// Private definitions for algebraic effects
4567

hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
//│ lambda1 = (undefined, function (...args1) {
3131
//│ globalThis.Predef.checkArgs("", 1, false, args1.length);
3232
//│ let self = args1[0];
33-
//│ let args = globalThis.Predef.tupleSlice(args1, 1, 0);
33+
//│ let args = runtime.Tuple.slice(args1, 1, 0);
3434
//│ return runtime.safeCall(self.x(...args))
3535
//│ });
3636
//│ block$res2 = runtime.checkCall(lambda1());

hkmc2/shared/src/test/mlscript/codegen/RuntimeUsage.mls

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,40 @@ print of checkCall(undefined)
4646
//│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value.
4747

4848

49+
print of Tuple.slice([1, 2, 3, 4], 1, -1)
50+
//│ > [2, 3, 4]
51+
52+
print of Tuple.get([1, 2, 3, 4], 0)
53+
//│ > 1
54+
55+
print of Tuple.get([1, 2, 3, 4], -1)
56+
//│ > 4
57+
58+
:re
59+
print of Tuple.get([1, 2, 3, 4], 5)
60+
//│ ═══[RUNTIME ERROR] RangeError: Tuple.get: index out of bounds
61+
62+
print of Str.drop("hello, world", 2)
63+
//│ > llo, world
64+
65+
print of Str.drop("hello, world", -2)
66+
//│ > ld
67+
68+
print of Str.get("hello, world", 2)
69+
//│ > l
70+
71+
:re
72+
print of Str.get("hello, world", 22)
73+
//│ ═══[RUNTIME ERROR] RangeError: Str.get: index out of bounds
74+
75+
print of Str.get("hello, world", -1)
76+
//│ > d
77+
78+
print of Str.startsWith("hello, world", "hello")
79+
//│ > true
80+
81+
print of Str.startsWith("hello, world", "")
82+
//│ > true
83+
84+
print of Str.startsWith("hello, world", "farewell")
85+
//│ > false

hkmc2/shared/src/test/mlscript/decls/Prelude.mls

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ declare class Set
1717
declare class Map
1818
declare class Error
1919
declare class TypeError
20+
declare class RangeError
2021
declare class Symbol
2122

2223
// MLscript-specific types

hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,5 @@
11
:js
22

3-
// Drop the first and last elements.
4-
tupleSlice([1, 2, 3, 4], 1, -1)
5-
//│ = [2, 3, 4]
6-
7-
// Get the first element.
8-
tupleGet([1, 2, 3, 4], 0)
9-
//│ = 1
10-
11-
// Get the last element.
12-
tupleGet([1, 2, 3, 4], -1)
13-
//│ = 4
14-
153
:sjs
164
fun nonsense(xs) = if xs is
175
[..ys] then ys
@@ -21,7 +9,7 @@ fun nonsense(xs) = if xs is
219
//│ nonsense = function nonsense(xs) {
2210
//│ let rest, ys;
2311
//│ if (globalThis.Array.isArray(xs) && xs.length >= 0) {
24-
//│ rest = runtime.safeCall(globalThis.Predef.tupleSlice(xs, 0, 0));
12+
//│ rest = runtime.safeCall(runtime.Tuple.slice(xs, 0, 0));
2513
//│ ys = rest;
2614
//│ return ys
2715
//│ } else {
@@ -45,8 +33,8 @@ fun lead_and_last(xs) = if xs is
4533
//│ let last0, rest, first0, x, ys, y;
4634
//│ if (globalThis.Array.isArray(xs) && xs.length >= 2) {
4735
//│ first0 = xs[0];
48-
//│ rest = runtime.safeCall(globalThis.Predef.tupleSlice(xs, 1, 1));
49-
//│ last0 = globalThis.Predef.tupleGet(xs, -1);
36+
//│ rest = runtime.safeCall(runtime.Tuple.slice(xs, 1, 1));
37+
//│ last0 = runtime.Tuple.get(xs, -1);
5038
//│ x = first0;
5139
//│ ys = rest;
5240
//│ y = last0;
@@ -78,21 +66,21 @@ fun nested_tuple_patterns(xs) = if xs is
7866
//│ JS (unsanitized):
7967
//│ let nested_tuple_patterns;
8068
//│ nested_tuple_patterns = function nested_tuple_patterns(xs) {
81-
//│ let last0, rest, first0, x, first1, first01, y, z, w, tmp2, tmp3;
69+
//│ let last0, rest, first0, x, first1, first01, y, z, w, tmp, tmp1;
8270
//│ if (globalThis.Array.isArray(xs) && xs.length >= 2) {
8371
//│ first0 = xs[0];
84-
//│ rest = runtime.safeCall(globalThis.Predef.tupleSlice(xs, 1, 1));
85-
//│ last0 = globalThis.Predef.tupleGet(xs, -1);
72+
//│ rest = runtime.safeCall(runtime.Tuple.slice(xs, 1, 1));
73+
//│ last0 = runtime.Tuple.get(xs, -1);
8674
//│ x = first0;
8775
//│ if (globalThis.Array.isArray(rest) && rest.length === 2) {
8876
//│ first01 = rest[0];
8977
//│ first1 = rest[1];
9078
//│ y = first01;
9179
//│ z = first1;
9280
//│ w = last0;
93-
//│ tmp2 = x + y;
94-
//│ tmp3 = tmp2 + z;
95-
//│ return tmp3 + w
81+
//│ tmp = x + y;
82+
//│ tmp1 = tmp + z;
83+
//│ return tmp1 + w
9684
//│ } else {
9785
//│ throw new globalThis.Error("match error");
9886
//│ }

0 commit comments

Comments
 (0)