diff --git a/effekt/shared/src/main/scala/effekt/cps/PrettyPrinter.scala b/effekt/shared/src/main/scala/effekt/cps/PrettyPrinter.scala new file mode 100644 index 000000000..6d33f21f6 --- /dev/null +++ b/effekt/shared/src/main/scala/effekt/cps/PrettyPrinter.scala @@ -0,0 +1,206 @@ +package effekt +package cps + +import core.Id + +import kiama.output.ParenPrettyPrinter +import effekt.source.FeatureFlag + +object PrettyPrinter extends ParenPrettyPrinter { + + import kiama.output.PrettyPrinterTypes.Document + + override val defaultIndent = 2 + + def format(t: ModuleDecl): Document = + pretty(toDoc(t), 4) + + def format(defs: List[ToplevelDefinition]): String = + pretty(toDoc(defs), 60).layout + + def format(s: Stmt): String = + pretty(toDoc(s), 60).layout + + def format(t: Block): String = + pretty(toDoc(t), 60).layout + + def format(e: Expr): String = + pretty(toDoc(e), 60).layout + + val show: PartialFunction[Any, String] = { + case m: ModuleDecl => format(m).layout + case d: ToplevelDefinition => format(List(d)) + case s: Stmt => format(s) + case b: Block => format(b) + case e: Expr => format(e) + case x: Id => x.show + } + + val emptyline: Doc = line <> line + + def toDoc(m: ModuleDecl): Doc = { + "module" <+> m.path <> emptyline <> + toDoc(m.definitions) + } + + def toDoc(definitions: List[ToplevelDefinition]): Doc = + vsep(definitions map toDoc, semi) + + def toDoc(d: ToplevelDefinition): Doc = d match { + case ToplevelDefinition.Def(id, block) => + "def" <+> toDoc(id) <+> "=" <+> toDoc(block) + case ToplevelDefinition.Val(id, ks, k, binding) => + "let" <+> toDoc(id) <+> "|" <+> toDoc(ks) <> "," <+> toDoc(k) <+> "=" <+> toDoc(binding) + case ToplevelDefinition.Let(id, binding) => + "let" <+> toDoc(id) <+> "=" <+> toDoc(binding) + } + + def toDoc(e: Expr): Doc = e match { + case DirectApp(id, vargs, bargs) => + toDoc(id) <> argsToDoc(vargs, bargs) + case Pure.ValueVar(id) => toDoc(id) + case Pure.Literal(()) => "()" + case Pure.Literal(s: String) => "\"" + s + "\"" + case Pure.Literal(value) => value.toString + case Pure.PureApp(id, vargs) => toDoc(id) <> argsToDoc(vargs, Nil) + case Pure.Make(data, tag, vargs) => "make" <+> toDoc(data.name) <+> toDoc(tag) <> argsToDoc(vargs, Nil) + case Pure.Box(b) => parens("box" <+> toDoc(b)) + } + + def toDoc(b: Block): Doc = b match { + case Block.BlockVar(id) => toDoc(id) + case b: Block.BlockLit => toDoc(b) + case Block.Unbox(e) => parens("unbox" <+> toDoc(e)) + case Block.New(handler) => "new" <+> toDoc(handler) + } + + def toDoc(b: Block.BlockLit): Doc = b match { + case Block.BlockLit(vps, bps, ks, k, body) => + val params = if (vps.isEmpty && bps.isEmpty) emptyDoc else + parens(hsep(vps.map(toDoc), comma)) <+> hsep(bps.map(toDoc)) + braces { + space <> params <+> "|" <+> toDoc(ks) <> "," <+> toDoc(k) <+> "=>" <+> + nest(line <> toDoc(body)) <> line + } + } + + def toDoc(s: Stmt): Doc = s match { + case Stmt.Jump(k, vargs, ks) => + "jump" <+> toDoc(k) <> argsToDoc(vargs, Nil) <+> "@" <+> toDoc(ks) + + case Stmt.App(callee, vargs, bargs, ks, k) => + toDoc(callee) <> argsToDoc(vargs, bargs) <+> "@" <+> toDoc(ks) <> "," <+> toDoc(k) + + case Stmt.Invoke(callee, method, vargs, bargs, ks, k) => + toDoc(callee) <> "." <> method.name.toString <> argsToDoc(vargs, bargs) <+> "@" <+> toDoc(ks) <> "," <+> toDoc(k) + + case Stmt.If(cond, thn, els) => + "if" <+> parens(toDoc(cond)) <+> block(toDoc(thn)) <+> "else" <+> block(toDoc(els)) + + case Stmt.Match(scrutinee, clauses, default) => + val cs = braces(nest(line <> vsep(clauses map { case (p, b) => + "case" <+> toDoc(p) <+> toDoc(b) + })) <> line) + val d = default.map { body => + space <> "else" <+> block(toDoc(body)) + }.getOrElse(emptyDoc) + toDoc(scrutinee) <+> "match" <+> cs <> d + + case Stmt.LetDef(id, binding, body) => + "def" <+> toDoc(id) <+> "=" <+> toDoc(binding) <> line <> + toDoc(body) + + case Stmt.LetExpr(id, binding, body) => + "let" <+> toDoc(id) <+> "=" <+> toDoc(binding) <> line <> + toDoc(body) + + case Stmt.LetCont(id, binding, body) => + "let" <+> toDoc(id) <+> "=" <+> toDoc(binding) <> line <> + toDoc(body) + + case Stmt.Region(id, ks, body) => + "region" <+> toDoc(id) <+> "@" <+> toDoc(ks) <+> block(toDoc(body)) + + case Stmt.Alloc(id, init, region, body) => + "var" <+> toDoc(id) <+> "in" <+> toDoc(region) <+> "=" <+> toDoc(init) <> ";" <> line <> + toDoc(body) + + case Stmt.Dealloc(ref, body) => + "dealloc" <> parens(toDoc(ref)) <> ";" <> line <> + toDoc(body) + + case Stmt.Var(id, init, ks, body) => + "var" <+> toDoc(id) <+> "=" <+> toDoc(init) <+> "@" <+> toDoc(ks) <> ";" <> line <> + toDoc(body) + + case Stmt.Get(ref, id, body) => + "let" <+> toDoc(id) <+> "=" <+> "!" <> toDoc(ref) <> ";" <> line <> + toDoc(body) + + case Stmt.Put(ref, value, body) => + toDoc(ref) <+> ":=" <+> toDoc(value) <> ";" <> line <> + toDoc(body) + + case Stmt.Reset(prog, ks, k) => + "reset" <+> toDoc(prog) <+> "@" <+> toDoc(ks) <> "," <+> toDoc(k) + + case Stmt.Shift(prompt, body, ks, k) => + "shift" <> parens(toDoc(prompt)) <+> toDoc(body) <+> "@" <+> toDoc(ks) <> "," <+> toDoc(k) + + case Stmt.Resume(r, body, ks, k) => + "resume" <> parens(toDoc(r)) <+> toDoc(body) <+> "@" <+> toDoc(ks) <> "," <+> toDoc(k) + + case Stmt.Hole() => + "<>" + } + + def toDoc(clause: Clause): Doc = clause match { + case Clause(vparams, body) => + parens(hsep(vparams.map(toDoc), comma)) <+> "=>" <+> block(toDoc(body)) + } + + def toDoc(impl: Implementation): Doc = { + val handlerName = toDoc(impl.interface.name) + val clauses = impl.operations.map { op => + "def" <+> toDoc(op.name) <> paramsToDoc(op.vparams, op.bparams, op.ks, op.k) <+> "=" <+> + nested(toDoc(op.body)) + } + handlerName <+> block(vsep(clauses)) + } + + def toDoc(k: Cont): Doc = k match { + case Cont.ContVar(id) => toDoc(id) + case Cont.ContLam(results, ks, body) => + braces { + space <> parens(hsep(results.map(toDoc), comma)) <+> "|" <+> toDoc(ks) <+> "=>" <+> + nest(line <> toDoc(body)) <> line + } + case Cont.Abort => "abort" + } + + def toDoc(ks: MetaCont): Doc = toDoc(ks.id) + + def toDoc(s: symbols.Symbol): Doc = s.show + + def argsToDoc(vargs: List[Pure], bargs: List[Block]): Doc = { + val vargsDoc = if (vargs.isEmpty && !bargs.isEmpty) emptyDoc else parens(vargs.map(toDoc)) + val bargsDoc = if (bargs.isEmpty) emptyDoc else hcat(bargs.map { b => braces(toDoc(b)) }) + vargsDoc <> bargsDoc + } + + def paramsToDoc(vps: List[Id], bps: List[Id], ks: Id, k: Id): Doc = { + val vpsDoc = if (vps.isEmpty && !bps.isEmpty) emptyDoc else parens(vps.map(toDoc)) + val bpsDoc = if (bps.isEmpty) emptyDoc else hcat(bps.map(toDoc)) + vpsDoc <> bpsDoc <+> "|" <+> toDoc(ks) <> "," <+> toDoc(k) + } + + def nested(content: Doc): Doc = group(nest(line <> content)) + + def parens(docs: List[Doc]): Doc = parens(hsep(docs, comma)) + + def braces(docs: List[Doc]): Doc = braces(hsep(docs, semi)) + + def block(content: Doc): Doc = braces(nest(line <> content) <> line) + + def block(docs: List[Doc]): Doc = block(vsep(docs, line)) +} diff --git a/effekt/shared/src/main/scala/effekt/generator/js/TransformerCps.scala b/effekt/shared/src/main/scala/effekt/generator/js/TransformerCps.scala index 7b8687813..f8548bb26 100644 --- a/effekt/shared/src/main/scala/effekt/generator/js/TransformerCps.scala +++ b/effekt/shared/src/main/scala/effekt/generator/js/TransformerCps.scala @@ -232,9 +232,11 @@ object TransformerCps extends Transformer { js.Const(nameDef(id), toJS(binding)(using nonrecursive(ks2))) :: requiringThunk { toJS(body) }.run(k) } + // no clauses case cps.Stmt.Match(sc, Nil, None) => pure(js.Return($effekt.call("emptyMatch")) :: Nil) + // only one match clause case cps.Stmt.Match(sc, List((tag, clause)), None) => val scrutinee = toJS(sc) val (_, stmts) = toJS(scrutinee, tag, clause) @@ -250,8 +252,8 @@ object TransformerCps extends Transformer { val stmts = binding.stmts - stmts.last match { - case terminator : (js.Stmt.Return | js.Stmt.Break | js.Stmt.Continue) => (e, stmts) + stmts.lastOption match { + case Some(terminator : (js.Stmt.Return | js.Stmt.Break | js.Stmt.Continue)) => (e, stmts) case other => (e, stmts :+ js.Break()) } }, @@ -521,8 +523,12 @@ object TransformerCps extends Transformer { } && default.forall(body => canBeDirect(k, body)) case Stmt.LetDef(id, binding, body) => notIn(binding) && canBeDirect(k, body) case Stmt.LetExpr(id, binding, body) => notIn(binding) && canBeDirect(k, body) - case Stmt.LetCont(id, Cont.ContLam(result, ks2, body), body2) => - def willBeDirectItself = canBeDirect(id, body2) && canBeDirect(k, maintainDirectStyle(ks2, body)) + + // letcont k1 x = ... k; ... k1 + // or + // letcont k1 x = ...; ... k ... and k not free in body of k1 + case Stmt.LetCont(k1, Cont.ContLam(result, ks2, body), body2) => + def willBeDirectItself = canBeDirect(k, maintainDirectStyle(ks2, body)) && canBeDirect(k1, body2) def notFreeinContinuation = notIn(body) && canBeDirect(k, body2) willBeDirectItself || notFreeinContinuation case Stmt.Region(id, ks, body) => notIn(body) diff --git a/effekt/shared/src/main/scala/effekt/util/Debug.scala b/effekt/shared/src/main/scala/effekt/util/Debug.scala index f58d5d6ca..8bf699601 100644 --- a/effekt/shared/src/main/scala/effekt/util/Debug.scala +++ b/effekt/shared/src/main/scala/effekt/util/Debug.scala @@ -16,6 +16,7 @@ val show: PartialFunction[Any, String] = TypePrinter.show orElse core.PrettyPrinter.show orElse generator.js.PrettyPrinter.show orElse + cps.PrettyPrinter.show orElse showGeneric inline def debug[A](inline value: A): A =