Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ sealed abstract class Block extends Product:
case _: End => true
case _ => false

lazy val isAbortive: Bool = this match
case _: End => false
case _: Throw | _: Break | _: Continue => true
case ret: Return => !ret.implct
case Begin(sub, rst) => sub.isAbortive || rst.isAbortive
case Assign(_, _, rst) => rst.isAbortive
case AssignField(_, _, _, rst) => rst.isAbortive
case AssignDynField(_, _, _, _, rst) => rst.isAbortive
case Match(_, arms, dflt, rst) => rst.isAbortive
case Define(_, rst) => rst.isAbortive
case TryBlock(sub, fin, rst) => rst.isAbortive || sub.isAbortive || fin.isAbortive
case Label(_, _, bod, rst) => rst.isAbortive
case HandleBlock(_, _, _, _, _, handlers, body, rst) => rst.isAbortive

lazy val definedVars: Set[Local] = this match
case _: Return | _: Throw => Set.empty
Expand Down Expand Up @@ -287,6 +300,23 @@ case class AssignDynField(lhs: Path, fld: Path, arrayIdx: Bool, rhs: Result, res
case class Define(defn: Defn, rest: Block) extends Block with ProductWithTail


object Match:
def apply(scrut: Path, arms: Ls[Case -> Block], dflt: Opt[Block], rest: Block): Block = dflt match
case S(Match(`scrut`, arms2, dflt2, _: End)) => // TODO: also handle non-End rest (may require a join point)
// * Currently, this branch does not seem used, because the UCS already does a good job at merging matches
Match(scrut, arms ::: arms2, dflt2, rest)
case _ =>
if !rest.isEmpty && arms.forall(_._2.isAbortive) && dflt.exists(_.isAbortive)
then new Match(scrut, arms, dflt, End("unreachable"))
else new Match(scrut, arms, dflt, rest)

object Begin:
def apply(sub: Block, rest: Block): Block =
if sub.isEmpty then rest
else if sub.isAbortive then sub
else new Begin(sub, rest)


case class HandleBlock(
lhs: Local,
res: Local,
Expand Down
11 changes: 11 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,17 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
case S(el) => returningTerm(el, endSemi = true)
case N => doc""
e :: returningTerm(rest, endSemi)
case Match(scrut, arms, els, rest)
if arms.sizeCompare(1) > 0 && arms.forall(_._1.isInstanceOf[Case.Lit]) =>
val l = arms.foldLeft(doc""): (acc, arm) =>
acc :: doc" # case ${arm._1.asInstanceOf[Case.Lit].lit.idStr}: #{ ${
returningTerm(arm._2, endSemi = true)
} # break; #} "
val e = els match
case S(el) =>
doc" # default: #{ ${ returningTerm(el, endSemi = true) } # break; #} "
case N => doc""
doc" # switch (${result(scrut)}) { #{ ${l :: e} #} # }" :: returningTerm(rest, endSemi)
case Match(scrut, hd :: tl, els, rest) =>
val sd = result(scrut)
def cond(cse: Case) = cse match
Expand Down
16 changes: 10 additions & 6 deletions hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,16 @@ fun nott = case
//│ nott = function nott() {
//│ let lambda1;
//│ lambda1 = (undefined, function (caseScrut) {
//│ if (caseScrut === true) {
//│ return false
//│ } else if (caseScrut === false) {
//│ return true
//│ } else {
//│ throw globalThis.Object.freeze(new globalThis.Error("match error"))
//│ switch (caseScrut) {
//│ case true:
//│ return false;
//│ break;
//│ case false:
//│ return true;
//│ break;
//│ default:
//│ throw globalThis.Object.freeze(new globalThis.Error("match error"));
//│ break;
//│ }
//│ });
//│ return lambda1
Expand Down
22 changes: 11 additions & 11 deletions hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ if true then 1 else 0
:sjs
let f = x => if x then print("ok") else print("ko")
//│ JS (unsanitized):
//│ let f, lambda;
//│ lambda = (undefined, function (x) {
//│ let f, f1;
//│ f1 = function f(x) {
//│ if (x === true) { return Predef.print("ok") } else { return Predef.print("ko") }
//│ });
//│ f = lambda;
//│ f = fun
//│ };
//│ f = f1;
//│ f = fun f

f(true)
//│ > ok
Expand All @@ -26,31 +26,31 @@ f(false)
:sjs
let f = x => print((if x then "ok" else "ko") + "!")
//│ JS (unsanitized):
//│ let f1, lambda1;
//│ lambda1 = (undefined, function (x) {
//│ let f2, lambda;
//│ lambda = (undefined, function (x) {
//│ let tmp, tmp1;
//│ if (x === true) {
//│ tmp = "ok";
//│ } else { tmp = "ko"; }
//│ tmp1 = tmp + "!";
//│ return Predef.print(tmp1)
//│ });
//│ f1 = lambda1;
//│ f2 = lambda;
//│ f = fun

:sjs
let f = x => print((if x and x then "ok" else "ko") + "!")
//│ JS (unsanitized):
//│ let f2, lambda2;
//│ lambda2 = (undefined, function (x) {
//│ let f3, lambda1;
//│ lambda1 = (undefined, function (x) {
//│ let tmp, tmp1;
//│ if (x === true) {
//│ tmp = "ok";
//│ } else { tmp = "ko"; }
//│ tmp1 = tmp + "!";
//│ return Predef.print(tmp1)
//│ });
//│ f2 = lambda2;
//│ f3 = lambda1;
//│ f = fun
// --- TODO: What we want ---
// this.f = (x) => {
Expand Down
8 changes: 4 additions & 4 deletions hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls
Original file line number Diff line number Diff line change
Expand Up @@ -225,17 +225,17 @@ _ - _ of 1, 2
:sjs
1 \ (_ - 2)
//│ JS (unsanitized):
//│ let lambda38; lambda38 = (undefined, function (_0) { return _0 - 2 }); Predef.passTo(1, lambda38)
//│ let lambda37; lambda37 = (undefined, function (_0) { return _0 - 2 }); Predef.passTo(1, lambda37)
//│ = fun

:sjs
1 \ (_ - 2)()
//│ JS (unsanitized):
//│ let lambda39, tmp19;
//│ lambda39 = (undefined, function (_0) {
//│ let lambda38, tmp19;
//│ lambda38 = (undefined, function (_0) {
//│ return _0 - 2
//│ });
//│ tmp19 = Predef.passTo(1, lambda39);
//│ tmp19 = Predef.passTo(1, lambda38);
//│ runtime.safeCall(tmp19())
//│ = -1

Expand Down
16 changes: 16 additions & 0 deletions hkmc2/shared/src/test/mlscript/codegen/Throw.mls
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,19 @@ f(false)
//│ };
//│ f3(false)
//│ ═══[RUNTIME ERROR] Error: y


:re
:sjs
if false then throw 0
throw 1
//│ JS (unsanitized):
//│ let scrut;
//│ scrut = false;
//│ if (scrut === true) {
//│ throw 0
//│ } else { throw globalThis.Object.freeze(new globalThis.Error("match error")) }
//│ /* unreachable */
//│ ═══[RUNTIME ERROR] Error: match error


2 changes: 1 addition & 1 deletion hkmc2/shared/src/test/mlscript/codegen/While.mls
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
//│ } else {
//│ throw globalThis.Object.freeze(new globalThis.Error("match error"))
//│ }
//│ return runtime.LoopEnd
//│ /* unreachable */
//│ });
//│ tmp1 = while1();
//│ tmp2 = tmp1 !== runtime.LoopEnd;
Expand Down
103 changes: 58 additions & 45 deletions hkmc2/shared/src/test/mlscript/handlers/Debugging.mls
Original file line number Diff line number Diff line change
Expand Up @@ -53,44 +53,53 @@ fun f() =
//│ this.pc = pc;
//│ }
//│ resume(value$) {
//│ if (this.pc === 1) {
//│ scrut = value$;
//│ } else if (this.pc === 2) {
//│ tmp = value$;
//│ } else if (this.pc === 3) {
//│ tmp1 = value$;
//│ switch (this.pc) {
//│ case 1:
//│ scrut = value$;
//│ break;
//│ case 2:
//│ tmp = value$;
//│ break;
//│ case 3:
//│ tmp1 = value$;
//│ break;
//│ }
//│ contLoop: while (true) {
//│ if (this.pc === 4) {
//│ return j / i
//│ } else if (this.pc === 1) {
//│ if (scrut === true) {
//│ tmp = Predef.raiseUnhandledEffect();
//│ if (tmp instanceof runtime.EffectSig.class) {
//│ return this.doUnwind(tmp, 2)
//│ switch (this.pc) {
//│ case 4:
//│ return j / i;
//│ break;
//│ case 1:
//│ if (scrut === true) {
//│ tmp = Predef.raiseUnhandledEffect();
//│ if (tmp instanceof runtime.EffectSig.class) {
//│ return this.doUnwind(tmp, 2)
//│ }
//│ this.pc = 2;
//│ continue contLoop
//│ } else {
//│ tmp1 = runtime.Unit;
//│ this.pc = 4;
//│ continue contLoop
//│ }
//│ this.pc = 2;
//│ continue contLoop
//│ } else {
//│ tmp1 = runtime.Unit;
//│ /* unreachable */
//│ break;
//│ case 5:
//│ tmp1 = Predef.print(tmp);
//│ if (tmp1 instanceof runtime.EffectSig.class) {
//│ return this.doUnwind(tmp1, 3)
//│ }
//│ this.pc = 3;
//│ continue contLoop;
//│ break;
//│ case 2:
//│ this.pc = 5;
//│ continue contLoop;
//│ break;
//│ case 3:
//│ this.pc = 4;
//│ continue contLoop
//│ }
//│ this.pc = 4;
//│ continue contLoop
//│ } else if (this.pc === 5) {
//│ tmp1 = Predef.print(tmp);
//│ if (tmp1 instanceof runtime.EffectSig.class) {
//│ return this.doUnwind(tmp1, 3)
//│ }
//│ this.pc = 3;
//│ continue contLoop
//│ } else if (this.pc === 2) {
//│ this.pc = 5;
//│ continue contLoop
//│ } else if (this.pc === 3) {
//│ this.pc = 4;
//│ continue contLoop
//│ continue contLoop;
//│ break;
//│ }
//│ break;
//│ }
Expand All @@ -99,12 +108,16 @@ fun f() =
//│ return getLocals3();
//│ }
//│ get getLoc() {
//│ if (this.pc === 1) {
//│ return "Debugging.mls:16:6"
//│ } else if (this.pc === 2) {
//│ return "Debugging.mls:17:14"
//│ } else if (this.pc === 3) {
//│ return "Debugging.mls:17:5"
//│ switch (this.pc) {
//│ case 1:
//│ return "Debugging.mls:16:6";
//│ break;
//│ case 2:
//│ return "Debugging.mls:17:14";
//│ break;
//│ case 3:
//│ return "Debugging.mls:17:5";
//│ break;
//│ }
//│ }
//│ toString() { return runtime.render(this); }
Expand Down Expand Up @@ -148,8 +161,8 @@ lambda_test(() =>
raiseUnhandledEffect()
100)
//│ ═══[RUNTIME ERROR] Error: Unhandled effect FatalEffect
//│ at lambda (Debugging.mls:148:3)
//│ at lambda_test (Debugging.mls:145:3)
//│ at lambda (Debugging.mls:161:3)
//│ at lambda_test (Debugging.mls:158:3)


import "../../mlscript-compile/Runtime.mls"
Expand Down Expand Up @@ -228,9 +241,9 @@ fun f() =
f()
//│ > [FnLocalsInfo("‹top level›", [LocalVarInfo("i", 100)]), FnLocalsInfo("f", [LocalVarInfo("j", 200)])]
//│ > Stack Trace:
//│ > at f (Debugging.mls:221:3) with locals: j=200
//│ > at f (Debugging.mls:234:3) with locals: j=200
//│ > Stack Trace:
//│ > at f (Debugging.mls:223:3)
//│ > at f (Debugging.mls:236:3)
//│ > Stack Trace:
//│ > at f (Debugging.mls:224:3) with locals: j=300
//│ > at f (Debugging.mls:237:3) with locals: j=300
//│ > [FnLocalsInfo("‹top level›", [LocalVarInfo("i", 100)]), FnLocalsInfo("f", [LocalVarInfo("j", 300)])]
46 changes: 27 additions & 19 deletions hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,35 @@ data class Lol(h) with
//│ this.pc = pc;
//│ }
//│ resume(value$) {
//│ if (this.pc === 1) {
//│ tmp = value$;
//│ } else if (this.pc === 2) {
//│ res = value$;
//│ switch (this.pc) {
//│ case 1:
//│ tmp = value$;
//│ break;
//│ case 2:
//│ res = value$;
//│ break;
//│ }
//│ contLoop: while (true) {
//│ if (this.pc === 3) {
//│ return this$Lol
//│ } else if (this.pc === 4) {
//│ res = Predef.print(tmp);
//│ if (res instanceof runtime.EffectSig.class) {
//│ return this.doUnwind(res, 2)
//│ }
//│ this.pc = 2;
//│ continue contLoop
//│ } else if (this.pc === 1) {
//│ this.pc = 4;
//│ continue contLoop
//│ } else if (this.pc === 2) {
//│ this.pc = 3;
//│ continue contLoop
//│ switch (this.pc) {
//│ case 3:
//│ return this$Lol;
//│ break;
//│ case 4:
//│ res = Predef.print(tmp);
//│ if (res instanceof runtime.EffectSig.class) {
//│ return this.doUnwind(res, 2)
//│ }
//│ this.pc = 2;
//│ continue contLoop;
//│ break;
//│ case 1:
//│ this.pc = 4;
//│ continue contLoop;
//│ break;
//│ case 2:
//│ this.pc = 3;
//│ continue contLoop;
//│ break;
//│ }
//│ break;
//│ }
Expand Down
Loading
Loading