Skip to content

Commit a1de9b6

Browse files
Fix statement desugar in Unquote (#221)
1 parent 4923fb8 commit a1de9b6

File tree

2 files changed

+163
-4
lines changed

2 files changed

+163
-4
lines changed

shared/src/main/scala/mlscript/JSBackend.scala

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,13 +298,16 @@ abstract class JSBackend {
298298
else Let(rec, Var(name), desugarQuote(value), desugarQuote(body)(letScope, isQuoted, freeVars))
299299
case Blk(stmts) =>
300300
val blkScope = scope.derive("blk")
301-
val res = stmts.map {
301+
if (isQuoted) createASTCall("Blk", stmts.map {
302302
case t: Term =>
303303
desugarQuote(t)(blkScope, isQuoted, freeVars)
304304
case s => throw CodeGenError(s"statement $s is not supported in quasiquotes")
305-
}
306-
if (isQuoted) createASTCall("Blk", res)
307-
else Blk(res)
305+
})
306+
else Blk(stmts.map {
307+
case t: Term =>
308+
desugarQuote(t)(blkScope, isQuoted, freeVars)
309+
case s => desugarStatementInUnquote(s)(blkScope, freeVars)
310+
})
308311
case Tup(eles) =>
309312
def toVar(b: Bool) = if (b) Var("true") else Var("false")
310313
def toVars(flg: FldFlags) = toVar(flg.mut) :: toVar(flg.spec) :: toVar(flg.genGetter) :: Nil
@@ -345,6 +348,30 @@ abstract class JSBackend {
345348
throw CodeGenError("this quote syntax is not supported yet.")
346349
}
347350

351+
// * Statements inside **Unquote** can refer to quoted code fragments.
352+
// * Desugar them recursively.
353+
private def desugarStatementInUnquote(s: Statement)(implicit scope: Scope, freeVars: FreeVars): Statement = {
354+
implicit val isQuoted: Bool = false
355+
s match {
356+
case nd @ NuFunDef(isLetRec, nme, symbol, tparams, rhs) =>
357+
NuFunDef(isLetRec, nme, symbol, tparams, rhs match {
358+
case L(t) => L(desugarQuote(t))
359+
case R(t) => R(t)
360+
})(nd.declareLoc, nd.virtualLoc, nd.mutLoc, nd.signature, nd.outer, nd.genField, nd.annotations)
361+
case nt @ NuTypeDef(kind, nme, tparams, params, ctor, sig, parents, superAnnot, thisAnnot, TypingUnit(body)) =>
362+
NuTypeDef(kind, nme, tparams, params, ctor.map(c => desugarStatementInUnquote(c) match {
363+
case c: Constructor => c
364+
case _ => die
365+
}), sig, parents.map(p => desugarQuote(p)), superAnnot, thisAnnot, TypingUnit(body.map(s => desugarStatementInUnquote(s))))(nt.declareLoc, nt.abstractLoc, nt.annotations)
366+
case Constructor(ps, body) => Constructor(ps, desugarQuote(body) match {
367+
case b: Blk => b
368+
case _ => die
369+
})
370+
case t: Term => desugarQuote(t)
371+
case _: LetS | _: DataDefn | _: DatatypeDefn | _: TypeDef | _: Def => die // * Impossible. newDef is true
372+
}
373+
}
374+
348375
/**
349376
* Translate MLscript terms into JavaScript expressions.
350377
*/

shared/src/test/diff/qq/Codegen.mls

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,3 +358,135 @@ fun foo(dbg) =
358358
:ne
359359
code"x => let c = 42 in ${code"x"}"
360360
//│ Code[forall 'a. 'a -> 'a, nothing]
361+
362+
class Ref[A](init: A) { mut val value: A = init }
363+
//│ class Ref[A](init: A) {
364+
//│ mut val value: A
365+
//│ }
366+
367+
:ne
368+
:js
369+
x `=>
370+
let v = Ref(x)
371+
let _ = y `=>
372+
set v.value = y
373+
`0
374+
v.value
375+
//│ Code[forall 'a. 'a -> 'a, ??y & ~??x]
376+
//│ // Prelude
377+
//│ class TypingUnit30 {}
378+
//│ const typing_unit30 = new TypingUnit30;
379+
//│ // Query 1
380+
//│ res = ((x1) => Lam(Var(x1), (() => {
381+
//│ let v = Ref(Var(x1));
382+
//│ let _ = ((y1) => Lam(Var(y1), (() => {
383+
//│ void(v.value = Var(y1));
384+
//│ return IntLit(0);
385+
//│ })()))(freshName("y"));
386+
//│ return v.value;
387+
//│ })()))(freshName("x"));
388+
//│ // End of generated code
389+
390+
:ne
391+
:js
392+
x `=>
393+
class A(y: Code[Int, nothing]) {
394+
val z = x `+ y
395+
}
396+
A(`0).z
397+
//│ Code[Int -> Int, nothing]
398+
//│ // Prelude
399+
//│ class TypingUnit31 {}
400+
//│ const typing_unit31 = new TypingUnit31;
401+
//│ // Query 1
402+
//│ res = ((x1) => Lam(Var(x1), (() => {
403+
//│ const A = (() => {
404+
//│ class A {
405+
//│ #y;
406+
//│ #z;
407+
//│ get z() { return this.#z; }
408+
//│ constructor(y) {
409+
//│ this.#y = y;
410+
//│ this.#z = App(Var("+"), Var(x1), y);
411+
//│ const z = this.#z;
412+
//│ }
413+
//│ static
414+
//│ unapply(x) {
415+
//│ return [x.#y];
416+
//│ }
417+
//│ }
418+
//│ let ctor;
419+
//│ ctor = ((y) => new A(y));
420+
//│ ctor.class = A;
421+
//│ return ctor;
422+
//│ })();
423+
//│ return A(IntLit(0)).z;
424+
//│ })()))(freshName("x"));
425+
//│ // End of generated code
426+
427+
:ne
428+
:js
429+
x `=>
430+
class A() {
431+
constructor() {
432+
log(x)
433+
}
434+
}
435+
new A(), x
436+
//│ Code[forall 'a. 'a -> 'a, nothing]
437+
//│ // Prelude
438+
//│ class TypingUnit32 {}
439+
//│ const typing_unit32 = new TypingUnit32;
440+
//│ // Query 1
441+
//│ res = ((x1) => Lam(Var(x1), (() => {
442+
//│ const A = (() => {
443+
//│ class A {
444+
//│ constructor() {
445+
//│ log(Var(x1));
446+
//│ }
447+
//│ static
448+
//│ unapply(x) {
449+
//│ return [];
450+
//│ }
451+
//│ }
452+
//│ let ctor;
453+
//│ ctor = (() => new A());
454+
//│ ctor.class = A;
455+
//│ return ctor;
456+
//│ })();
457+
//│ return new A.class() , Var(x1);
458+
//│ })()))(freshName("x"));
459+
//│ // End of generated code
460+
461+
class Foo(x: Code[Int, anything])
462+
//│ class Foo(x: Code[Int, anything])
463+
464+
:ne
465+
:js
466+
x `=>
467+
class B() extends Foo(x)
468+
`x
469+
//│ Code[forall 'a. (Int & 'a) -> 'a, nothing]
470+
//│ // Prelude
471+
//│ class TypingUnit34 {}
472+
//│ const typing_unit34 = new TypingUnit34;
473+
//│ // Query 1
474+
//│ res = ((x1) => Lam(Var(x1), (() => {
475+
//│ const B = (() => {
476+
//│ class B extends Foo.class {
477+
//│ constructor() {
478+
//│ super(Var(x1));
479+
//│ }
480+
//│ static
481+
//│ unapply(x) {
482+
//│ return [];
483+
//│ }
484+
//│ }
485+
//│ let ctor;
486+
//│ ctor = (() => new B());
487+
//│ ctor.class = B;
488+
//│ return ctor;
489+
//│ })();
490+
//│ return Var(x1);
491+
//│ })()))(freshName("x"));
492+
//│ // End of generated code

0 commit comments

Comments
 (0)