diff --git a/effekt/jvm/src/test/scala/effekt/MLTests.scala b/effekt/jvm/src/test/scala/effekt/MLTests.scala index 795da8ca3..7f4d0870b 100644 --- a/effekt/jvm/src/test/scala/effekt/MLTests.scala +++ b/effekt/jvm/src/test/scala/effekt/MLTests.scala @@ -84,6 +84,7 @@ class MLTests extends EffektTests { examplesDir / "casestudies" / "lexer.effekt.md", examplesDir / "casestudies" / "parser.effekt.md", examplesDir / "casestudies" / "prettyprinter.effekt.md", + examplesDir / "benchmarks" / "pretty.effekt", examplesDir / "pos" / "simpleparser.effekt", // cont diff --git a/effekt/jvm/src/test/scala/effekt/core/PolymorphismBoxingTests.scala b/effekt/jvm/src/test/scala/effekt/core/PolymorphismBoxingTests.scala index 814af3a7c..d99845a6e 100644 --- a/effekt/jvm/src/test/scala/effekt/core/PolymorphismBoxingTests.scala +++ b/effekt/jvm/src/test/scala/effekt/core/PolymorphismBoxingTests.scala @@ -42,7 +42,6 @@ abstract class AbstractPolymorphismBoxingTests extends CorePhaseTests(Polymorphi ) } class PolymorphismBoxingTests extends AbstractPolymorphismBoxingTests { - test("simple non-polymorphic code should stay the same"){ val code = """module main @@ -87,7 +86,7 @@ class PolymorphismBoxingTests extends AbstractPolymorphismBoxingTests { """module main | |def id = { ['A](a: 'A) => return a: 'A } - |def idInt = { (x: Int) => return (id: ['A]('A) => 'A @ {})[BoxedInt]((MkBoxedInt: (Int) => BoxedInt @ {})(x: Int)).unboxInt: Int } + |def idInt = { (x: Int) => return (id: ['A]('A) => 'A @ {})[BoxedInt](make BoxedInt MkBoxedInt(x: Int)).unboxInt: Int } |""".stripMargin assertTransformsTo(from, to) } @@ -103,7 +102,7 @@ class PolymorphismBoxingTests extends AbstractPolymorphismBoxingTests { """module main | |def id = { ['A](a: 'A) => return a: 'A } - |def idInt = { (x: Int) => val tmp = (id: ['A]('A) => 'A @ {})[BoxedInt]((MkBoxedInt: (Int) => BoxedInt @ {})(x: Int)) ; return tmp:BoxedInt.unboxInt: Int } + |def idInt = { (x: Int) => val tmp = (id: ['A]('A) => 'A @ {})[BoxedInt](make BoxedInt MkBoxedInt(x: Int)) ; return tmp:BoxedInt.unboxInt: Int } |""".stripMargin assertTransformsTo(from, to) } @@ -127,7 +126,7 @@ class PolymorphismBoxingTests extends AbstractPolymorphismBoxingTests { |def idInt = { (x: Int) => | { | let res = run { - | let boxedRes = !(id: ['A]('A) => 'A @ {})[BoxedInt]((MkBoxedInt: (Int) => BoxedInt @ {})(x: Int)) + | let boxedRes = !(id: ['A]('A) => 'A @ {})[BoxedInt](make BoxedInt MkBoxedInt(x: Int)) | return boxedRes:BoxedInt.unboxInt: Int | } | return res: Int @@ -151,7 +150,7 @@ class PolymorphismBoxingTests extends AbstractPolymorphismBoxingTests { | { | def originalFn = { (x: Int) => return x: Int } | val result = (originalFn: (Int) => Int @ {})(boxedX: BoxedInt.unboxInt: Int); - | return (MkBoxedInt: (Int) => BoxedInt @ {})(result: Int) + | return make BoxedInt MkBoxedInt(result: Int) | } | }; | return r:BoxedInt.unboxInt: Int @@ -182,11 +181,11 @@ class PolymorphismBoxingTests extends AbstractPolymorphismBoxingTests { | (hhofargarg: Int) => | { | def tmp = hhofargB: ('A) => 'A @ {} - | val rres = (tmp: ('A) => 'A @ {})((MkBoxedInt: (Int) => BoxedInt @ {})(hhofargarg: Int)); + | val rres = (tmp: ('A) => 'A @ {})(make BoxedInt MkBoxedInt(hhofargarg: Int)); | return rres:BoxedInt.unboxInt: Int | } | }; - | return (MkBoxedInt: (Int) => BoxedInt @ {})(res:Int) + | return make BoxedInt MkBoxedInt(res:Int) | } | }; | return result:BoxedInt.unboxInt: Int diff --git a/effekt/shared/src/main/scala/effekt/core/Parser.scala b/effekt/shared/src/main/scala/effekt/core/Parser.scala index 76e62e367..eed2eed3c 100644 --- a/effekt/shared/src/main/scala/effekt/core/Parser.scala +++ b/effekt/shared/src/main/scala/effekt/core/Parser.scala @@ -31,8 +31,9 @@ class CoreParsers(positions: Positions, names: Names) extends EffektLexers(posit None } - lazy val `run` = keyword("run") - lazy val `;` = super.literal(";") + lazy val `run` = keyword("run") + lazy val `;` = super.literal(";") + lazy val `make` = keyword("make") /** * Literals @@ -154,6 +155,7 @@ class CoreParsers(positions: Positions, names: Names) extends EffektLexers(posit ( literal | id ~ (`:` ~> valueType) ^^ Pure.ValueVar.apply | `box` ~> captures ~ block ^^ { case capt ~ block => Pure.Box(block, capt) } + | `make` ~> dataType ~ id ~ valueArgs ^^ Pure.Make.apply | block ~ maybeTypeArgs ~ valueArgs ^^ Pure.PureApp.apply | failure("Expected a pure expression.") ) @@ -261,11 +263,14 @@ class CoreParsers(positions: Positions, names: Names) extends EffektLexers(posit lazy val primValueType: P[ValueType] = ( typeParam ^^ ValueType.Var.apply - | id ~ maybeTypeArgs ^^ ValueType.Data.apply + | dataType | `(` ~> valueType <~ `)` | failure("Expected a value type") ) + lazy val dataType: P[ValueType.Data] = + id ~ maybeTypeArgs ^^ { case id ~ targs => ValueType.Data(id, targs) : ValueType.Data } + lazy val blockType: P[BlockType] = ( maybeTypeParams ~ maybeValueTypes ~ many(blockTypeParam) ~ (`=>` ~/> primValueType) ^^ { case tparams ~ vparams ~ bcparams ~ result => diff --git a/effekt/shared/src/main/scala/effekt/core/PolymorphismBoxing.scala b/effekt/shared/src/main/scala/effekt/core/PolymorphismBoxing.scala index a5b69415f..5f66cb3f6 100644 --- a/effekt/shared/src/main/scala/effekt/core/PolymorphismBoxing.scala +++ b/effekt/shared/src/main/scala/effekt/core/PolymorphismBoxing.scala @@ -1,4 +1,5 @@ -package effekt.core +package effekt +package core import effekt.PhaseResult.CoreTransformed import effekt.context.Context @@ -28,7 +29,7 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] { * @param constructor The constructor to box the values with * @param field The field to access for unboxing */ - case class Boxer(tpe: ValueType, constructor: Id, field: Id) + case class Boxer(tpe: ValueType.Data, constructor: Id, field: Id) /** * Partial function to describe which values to box and how. @@ -205,7 +206,20 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] { case Pure.ValueVar(id, annotatedType) => Pure.ValueVar(id, transform(annotatedType)) case Pure.Literal(value, annotatedType) => Pure.Literal(value, transform(annotatedType)) case Pure.PureApp(b, targs, vargs) => instantiate(b, targs).callPure(b, vargs map transform) - case m : Pure.Make => ??? + case Pure.Make(data, tag, vargs) => + val dataDecl = PContext.getDataLikeDeclaration(data.name) + val ctorDecl = dataDecl.constructors.find(_.id == tag).getOrElse { + Context.panic(s"No constructor found for tag ${tag} in data type: ${data}") + } + + val argTypes = vargs.map(_.tpe) + val paramTypes = ctorDecl.fields.map(_.tpe) + + val coercedArgs = (paramTypes zip (argTypes zip vargs)).map { case (param, (targ, arg)) => + coercer(targ, param)(transform(arg)) + } + Pure.Make(transform(data), tag, coercedArgs) + case Pure.Select(target, field, annotatedType) => { val (symbol, targs) = target.tpe match { case ValueType.Data(symbol, targs) => (symbol, targs) @@ -230,6 +244,10 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] { case ValueType.Boxed(tpe, capt) => ValueType.Boxed(transform(tpe), capt) } + def transform(valueType: ValueType.Data)(using PContext): ValueType.Data = valueType match { + case ValueType.Data(symbol, targs) => ValueType.Data(symbol, targs map transformArg) + } + def transform(blockType: BlockType)(using PContext): BlockType = blockType match { case BlockType.Function(tparams, cparams, vparams, bparams, result) => BlockType.Function(tparams, cparams, vparams map transform, bparams map transform, transform(result)) @@ -274,8 +292,7 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] { override def apply(t: Pure): Pure = { val boxer = box(valueType) - val blockTpe = BlockType.Function(List(), List(), List(from), List(), to) - Pure.PureApp(Block.BlockVar(boxer.constructor, blockTpe, Set()), List(), List(t)) + Pure.Make(boxer.tpe, boxer.constructor, List(t)) } } case class UnboxCoercer(valueType: ValueType)(using PContext) extends Coercer[ValueType, Pure] { @@ -352,7 +369,7 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] { } override def callPure(block: B, vargs: List[Pure])(using PContext): Pure = { - rcoercer(Pure.PureApp(block, targs map transformArg, (vcoercers zip vargs).map {case (c,v)=> c(v)})) + rcoercer(Pure.PureApp(block, targs map transformArg, (vcoercers zip vargs).map { case (c,v) => c(v) })) } override def callDirect(block: B, vargs: List[Pure], bargs: List[Block])(using PContext): Expr = { diff --git a/effekt/shared/src/main/scala/effekt/lifted/PrettyPrinter.scala b/effekt/shared/src/main/scala/effekt/lifted/PrettyPrinter.scala index 3cc6a7253..b7acc1fc6 100644 --- a/effekt/shared/src/main/scala/effekt/lifted/PrettyPrinter.scala +++ b/effekt/shared/src/main/scala/effekt/lifted/PrettyPrinter.scala @@ -21,6 +21,13 @@ object PrettyPrinter extends ParenPrettyPrinter { def format(defs: List[Definition]): String = pretty(toDoc(defs), 60).layout + val show: PartialFunction[Any, String] = { + case m: ModuleDecl => format(m).layout + case d: Definition => format(List(d)) + case s: Stmt => format(s) + case x: Id => x.show + } + val emptyline: Doc = line <> line def toDoc(m: ModuleDecl): Doc = { diff --git a/effekt/shared/src/main/scala/effekt/machine/Transformer.scala b/effekt/shared/src/main/scala/effekt/machine/Transformer.scala index e43f44d89..64b3ab80d 100644 --- a/effekt/shared/src/main/scala/effekt/machine/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/machine/Transformer.scala @@ -18,7 +18,6 @@ object Transformer { def transform(main: CoreTransformed, mainSymbol: TermSymbol)(using C: Context): Program = { val Some(CoreLifted(_, _, _, liftedMain)) = LiftInference(main) : @unchecked - C.using(module = main.mod) { transform(mainSymbol, liftedMain); } @@ -394,7 +393,7 @@ object Transformer { } case lifted.PureApp(lifted.BlockVar(blockName: symbols.ExternFunction, tpe: lifted.BlockType.Function), targs, args) => - if(targs.exists(requiresBoxing)){ ErrorReporter.abort(s"Types ${targs} are used as type parameters but would require boxing.") } + if (targs.exists(requiresBoxing)) { ErrorReporter.abort(s"Types ${targs} are used as type parameters but would require boxing.") } val variable = Variable(freshName("x"), transform(tpe.result)) transform(args).flatMap { values => @@ -403,12 +402,10 @@ object Transformer { } } - case lifted.PureApp(lifted.BlockVar(blockName, tpe: lifted.BlockType.Function), targs, args) - if DeclarationContext.findConstructor(blockName).isDefined => - if(targs.exists(requiresBoxing)){ ErrorReporter.abort(s"Types ${targs} are used as type parameters but would require boxing.") } - val variable = Variable(freshName("x"), transform(tpe.result)); - val tag = DeclarationContext.getConstructorTag(blockName) + case lifted.Make(data, constructor, args) => + val variable = Variable(freshName("x"), transform(data)); + val tag = DeclarationContext.getConstructorTag(constructor) transform(args).flatMap { values => Binding { k => @@ -416,8 +413,7 @@ object Transformer { } } - case lifted.Select(target, field, tpe) - if DeclarationContext.findField(field).isDefined => + case lifted.Select(target, field, tpe) if DeclarationContext.findField(field).isDefined => // TODO all of this can go away, if we desugar records in the translation to core! val fields = DeclarationContext.getField(field).constructor.fields val fieldIndex = fields.indexWhere(_.id == field) diff --git a/effekt/shared/src/main/scala/effekt/util/Debug.scala b/effekt/shared/src/main/scala/effekt/util/Debug.scala index 3bba1df5f..2c601e0f4 100644 --- a/effekt/shared/src/main/scala/effekt/util/Debug.scala +++ b/effekt/shared/src/main/scala/effekt/util/Debug.scala @@ -9,7 +9,7 @@ val showGeneric: PartialFunction[Any, String] = { } val show: PartialFunction[Any, String] = - TypePrinter.show orElse core.PrettyPrinter.show orElse showGeneric + TypePrinter.show orElse core.PrettyPrinter.show orElse lifted.PrettyPrinter.show orElse showGeneric inline def debug[A](inline value: A): A = ${ debugMacros.debugCode('value) }