Skip to content
Draft
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
206 changes: 206 additions & 0 deletions effekt/shared/src/main/scala/effekt/cps/PrettyPrinter.scala
Original file line number Diff line number Diff line change
@@ -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))
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Comment on lines -253 to +256
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh this already fixed #842...

case other => (e, stmts :+ js.Break())
}
},
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions effekt/shared/src/main/scala/effekt/util/Debug.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down