diff --git a/morphir-interpreter/shared/src/main/scala/zio/morphir/value/Interpreter.scala b/morphir-interpreter/shared/src/main/scala/zio/morphir/value/Interpreter.scala index 68f90116..03582023 100644 --- a/morphir-interpreter/shared/src/main/scala/zio/morphir/value/Interpreter.scala +++ b/morphir-interpreter/shared/src/main/scala/zio/morphir/value/Interpreter.scala @@ -5,20 +5,20 @@ import zio.morphir.ir.ValueModule.RawValue import zio.morphir.ir.TypeModule import zio.morphir.IR import zio.morphir.ir.LiteralValue -import zio.morphir.ir.ValueModule.ValueCase.* +import zio.morphir.ir.ValueModule.ValueCase._ import zio.morphir.ir.NativeFunction import zio.morphir.ir.FQName import zio.morphir.ir.Pattern -import zio.morphir.ir.NativeFunction.* - +import zio.morphir.ir.NativeFunction._ import zio.Chunk import zio.prelude._ import scala.collection.immutable.ListMap import zio.morphir.ir.ValueModule.Value import zio.morphir.ir.TypeModule.Specification.TypeAliasSpecification +import IR._ -import IR.* +import java.math.BigInteger object Interpreter { sealed trait Result @@ -74,7 +74,7 @@ object Interpreter { val dealiased = ir.resolveAliases(fqName) def getRecordConstructor(name: FQName): Option[Any] = ir.typeSpecifications.get(name).collect { - case TypeAliasSpecification(_, TypeModule.Type(TypeModule.TypeCase.RecordCase(fields), _)) => + case TypeAliasSpecification(_, TypeModule.Type.Record(_, fields)) => constructFunction(fqName, fields) } @@ -89,7 +89,7 @@ object Interpreter { getRecordConstructor(dealiased) orElse getTypeConstructor(dealiased) match { case Some(fn) => fn case None => - throw new InterpretationError.TypeNotFound(dealiased.toString) + throw InterpretationError.TypeNotFound(dealiased.toString) } // case class Constructor(name FQName) @@ -115,7 +115,7 @@ object Interpreter { record.get(name) match { case Some(value) => value case None => - throw new InterpretationError.FieldNotFound(name, s"Field $name not found in $record") + throw InterpretationError.FieldNotFound(name, s"Field $name not found in $record") } case FieldFunctionCase(name) => @@ -126,7 +126,7 @@ object Interpreter { case Some(fieldValue) => fieldValue case None => InterpretationError.FieldNotFound(name, s"Field $name not found in $input") } - case _ => throw new InterpretationError.RecordExpected(s"Record expected but got $input") + case _ => throw InterpretationError.RecordExpected(s"Record expected but got $input") } case IfThenElseCase(condition, thenBranch, elseBranch) => @@ -160,7 +160,7 @@ object Interpreter { } } - if (rightHandSide eq null) throw new InterpretationError.MatchError(s"could not match $evaluatedBody") + if (rightHandSide eq null) throw InterpretationError.MatchError(s"could not match $evaluatedBody") else loop(rightHandSide, variables ++ newVariables, references) case RecordCase(fields) => @@ -173,7 +173,7 @@ object Interpreter { references.get(name) match { case Some(value) => value - case None => throw new InterpretationError.ReferenceNotFound(name, s"Reference $name not found") + case None => throw InterpretationError.ReferenceNotFound(name, s"Reference $name not found") } case TupleCase(elements) => @@ -185,13 +185,12 @@ object Interpreter { case VariableCase(name) => variables.get(name) match { case Some(Result.Strict(value)) => value - case Some(Result.Lazy(value, variables, references, definitions)) => { + case Some(Result.Lazy(value, variables, references, definitions)) => def shallow = definitions.map { case (key, value) => key -> Result.Lazy(value, variables, references, definitions) } loop(value, variables ++ shallow, references) - } - case None => throw new InterpretationError.VariableNotFound(name, s"Variable $name not found") + case None => throw InterpretationError.VariableNotFound(name, s"Variable $name not found") } case LetDefinitionCase(name, value, body) => @@ -227,7 +226,7 @@ object Interpreter { val newRecord = record.asInstanceOf[ListMap[Name, Any]] ++ evaluatedFieldsToUpdate.toMap newRecord case _ => - throw new InterpretationError.RecordExpected( + throw InterpretationError.RecordExpected( s"Record expected but got $evaluatedValueToUpdate" ) } @@ -242,7 +241,7 @@ object Interpreter { references ) case Left(MatchFailure(pattern, input)) => - throw new InterpretationError.MatchError( + throw InterpretationError.MatchError( s"Pattern $pattern didn't match input $input" ) } @@ -257,7 +256,7 @@ object Interpreter { references ) case Left(MatchFailure(pattern, input)) => - throw new InterpretationError.MatchError( + throw InterpretationError.MatchError( s"Pattern $pattern didn't match input $input" ) } @@ -353,8 +352,8 @@ object Interpreter { } private def evalAddition(args: Chunk[Any]): Any = - if (args.length == 0) - throw new InterpretationError.InvalidArguments(args, s"Addition expected at least two argument but got none.") + if (args.isEmpty) + throw InterpretationError.InvalidArguments(args, s"Addition expected at least two argument but got none.") else if (args(0).isInstanceOf[java.math.BigInteger]) args.asInstanceOf[Chunk[java.math.BigInteger]].reduce(_ add _) else @@ -362,11 +361,12 @@ object Interpreter { private def evalSubtraction(args: Chunk[Any]): Any = if (args.length != 2) - throw new InterpretationError.InvalidArguments(args, s"Subtraction expected exactly two arguments.") - else if (args(0).isInstanceOf[java.math.BigInteger]) - args(0).asInstanceOf[java.math.BigInteger] subtract args(1).asInstanceOf[java.math.BigInteger] + throw InterpretationError.InvalidArguments(args, s"Subtraction expected exactly two arguments.") else - args(0).asInstanceOf[java.math.BigDecimal] subtract args(1).asInstanceOf[java.math.BigDecimal] + args(0) match { + case integer: BigInteger => integer subtract args(1).asInstanceOf[BigInteger] + case _ => args(0).asInstanceOf[java.math.BigDecimal] subtract args(1).asInstanceOf[java.math.BigDecimal] + } // format: off private def evalTuple(value: Chunk[Any]): Any = @@ -393,7 +393,7 @@ object Interpreter { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: q :: r :: s :: t :: Nil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: q :: r :: s :: t :: u :: Nil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u) case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: q :: r :: s :: t :: u :: v :: Nil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v) - case _ => throw new InterpretationError.TupleTooLong(value.length) + case _ => throw InterpretationError.TupleTooLong(value.length) } // format: on @@ -427,7 +427,7 @@ object Interpreter { Chunk(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u) case (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v) => Chunk(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v) - case _ => throw new InterpretationError.MatchError("value was not a tuple") + case _ => throw InterpretationError.MatchError("value was not a tuple") } def applyFunction(function: Any, arguments: Chunk[Any]): Any = @@ -540,7 +540,7 @@ object GenericCaseClass { def nameToFieldName(name: Name): String = name.toString - def named(name: FQName) = ??? + // def named(name: FQName) = ??? } // To Do List: diff --git a/morphir-interpreter/shared/src/test/scala/zio/morphir/value/InterpreterSpec.scala b/morphir-interpreter/shared/src/test/scala/zio/morphir/value/InterpreterSpec.scala index d0cdba15..83137401 100644 --- a/morphir-interpreter/shared/src/test/scala/zio/morphir/value/InterpreterSpec.scala +++ b/morphir-interpreter/shared/src/test/scala/zio/morphir/value/InterpreterSpec.scala @@ -1,9 +1,9 @@ package zio.morphir.value import java.math.BigInteger -import zio.test.* +import zio.test._ import zio.morphir.ir.Name -import zio.morphir.ir.testing.CaseExample.* +import zio.morphir.ir.testing.CaseExample._ import zio.morphir.ir.ValueModule.RawValue import zio.morphir.IR import zio.morphir.testing.MorphirBaseSpec diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/IR.scala b/morphir-ir/shared/src/main/scala/zio/morphir/IR.scala index 65c2a9f8..d1ddadd1 100644 --- a/morphir-ir/shared/src/main/scala/zio/morphir/IR.scala +++ b/morphir-ir/shared/src/main/scala/zio/morphir/IR.scala @@ -23,8 +23,8 @@ final case class IR( case Some(typeSpecification) => typeSpecification match { case TypeModule.Specification.TypeAliasSpecification(_, underlyingType) => - underlyingType.caseValue match { - case TypeModule.TypeCase.ReferenceCase(fqName, _) => + underlyingType match { + case TypeModule.Type.Reference(_, fqName, _) => fqName case _ => fqName } diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/Pattern.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/Pattern.scala index 0ceb1334..666d9d2d 100644 --- a/morphir-ir/shared/src/main/scala/zio/morphir/ir/Pattern.scala +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/Pattern.scala @@ -5,30 +5,30 @@ import zio.Chunk sealed trait Pattern[+Attributes] { self => import Pattern._ - def annotations: Attributes + def attributes: Attributes final def mapAttributes[B](f: Attributes => B): Pattern[B] = self match { - case AsPattern(pattern, name, annotations) => AsPattern(pattern.mapAttributes(f), name, f(annotations)) - case ConstructorPattern(constructorName, argumentPatterns, annotations) => - ConstructorPattern(constructorName, argumentPatterns.map(_.mapAttributes(f)), f(annotations)) - case EmptyListPattern(annotations) => EmptyListPattern(f(annotations)) - case HeadTailPattern(headPattern, tailPattern, annotations) => - HeadTailPattern(headPattern.mapAttributes(f), tailPattern.mapAttributes(f), f(annotations)) - case LiteralPattern(literal, annotations) => LiteralPattern(literal, f(annotations)) - case TuplePattern(elementPatterns, annotations) => - TuplePattern(elementPatterns.map(_.mapAttributes(f)), f(annotations)) - case UnitPattern(annotations) => UnitPattern(f(annotations)) - case WildcardPattern(annotations) => WildcardPattern(f(annotations)) + case AsPattern(pattern, name, attributes) => AsPattern(pattern.mapAttributes(f), name, f(attributes)) + case ConstructorPattern(constructorName, argumentPatterns, attributes) => + ConstructorPattern(constructorName, argumentPatterns.map(_.mapAttributes(f)), f(attributes)) + case EmptyListPattern(attributes) => EmptyListPattern(f(attributes)) + case HeadTailPattern(headPattern, tailPattern, attributes) => + HeadTailPattern(headPattern.mapAttributes(f), tailPattern.mapAttributes(f), f(attributes)) + case LiteralPattern(literal, attributes) => LiteralPattern(literal, f(attributes)) + case TuplePattern(elementPatterns, attributes) => + TuplePattern(elementPatterns.map(_.mapAttributes(f)), f(attributes)) + case UnitPattern(attributes) => UnitPattern(f(attributes)) + case WildcardPattern(attributes) => WildcardPattern(f(attributes)) } } object Pattern { def asPattern[Attributes]( - annotations: Attributes, + attributes: Attributes, pattern: Pattern[Attributes], name: Name ): AsPattern[Attributes] = - AsPattern(pattern, name, annotations) + AsPattern(pattern, name, attributes) def asPattern(pattern: Pattern[Any], name: Name): AsPattern[Any] = AsPattern(pattern, name, ()) @@ -38,49 +38,49 @@ object Pattern { lazy val wildcardPattern: WildcardPattern[Any] = WildcardPattern[Any](()) - def wildcardPattern[Attributes](annotations: Attributes): WildcardPattern[Attributes] = - WildcardPattern(annotations) + def wildcardPattern[Attributes](attributes: Attributes): WildcardPattern[Attributes] = + WildcardPattern(attributes) // val unit: UnitPattern[Any] = UnitPattern(ZEnvironment.empty) - // def unit[Attributes](annotations: ZEnvironment[Attributes]): UnitPattern[Attributes] = UnitPattern(annotations) + // def unit[Attributes](attributes: ZEnvironment[Attributes]): UnitPattern[Attributes] = UnitPattern(attributes) // val wildcard: Wildcard[Any] = Wildcard(ZEnvironment.empty) - // def wildcard[Attributes](annotations: ZEnvironment[Attributes]): Wildcard[Attributes] = Wildcard(annotations) + // def wildcard[Attributes](attributes: ZEnvironment[Attributes]): Wildcard[Attributes] = Wildcard(attributes) - // final case class LiteralPattern[+Attributes, +Value](value: Lit[Value], annotations: ZEnvironment[Attributes]) + // final case class LiteralPattern[+Attributes, +Value](value: Lit[Value], attributes: ZEnvironment[Attributes]) // extends Pattern[Attributes] final case class AsPattern[+Attributes]( pattern: Pattern[Attributes], name: Name, - annotations: Attributes + attributes: Attributes ) extends Pattern[Attributes] final case class ConstructorPattern[+Attributes]( constructorName: FQName, argumentPatterns: Chunk[Pattern[Attributes]], - annotations: Attributes + attributes: Attributes ) extends Pattern[Attributes] - final case class EmptyListPattern[+Attributes](annotations: Attributes) extends Pattern[Attributes] + final case class EmptyListPattern[+Attributes](attributes: Attributes) extends Pattern[Attributes] final case class HeadTailPattern[+Attributes]( headPattern: Pattern[Attributes], tailPattern: Pattern[Attributes], - annotations: Attributes + attributes: Attributes ) extends Pattern[Attributes] final case class LiteralPattern[+A, +Attributes]( literal: Literal[A], - annotations: Attributes + attributes: Attributes ) extends Pattern[Attributes] final case class TuplePattern[+Attributes]( elementPatterns: Chunk[Pattern[Attributes]], - annotations: Attributes + attributes: Attributes ) extends Pattern[Attributes] - final case class UnitPattern[+Attributes](annotations: Attributes) extends Pattern[Attributes] + final case class UnitPattern[+Attributes](attributes: Attributes) extends Pattern[Attributes] - final case class WildcardPattern[+Attributes](annotations: Attributes) extends Pattern[Attributes] + final case class WildcardPattern[+Attributes](attributes: Attributes) extends Pattern[Attributes] } diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/TypeModule.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/TypeModule.scala index 6cec0715..44754656 100644 --- a/morphir-ir/shared/src/main/scala/zio/morphir/ir/TypeModule.scala +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/TypeModule.scala @@ -1,368 +1,38 @@ package zio.morphir.ir -import zio.{Chunk, ZIO} -import zio.prelude._ import zio.morphir.syntax.TypeModuleSyntax -import zio.prelude.fx.ZPure -object TypeModule extends TypeModuleSyntax { - - final case class Field[+T](name: Name, fieldType: T) { self => - - /** - * An alias for `attributeTypeWith`. - */ - def @@[Attributes0, Attributes](f: Attributes0 => Attributes)(implicit - ev: T <:< Type[Attributes0] - ): Field[Type[Attributes]] = - attributeTypeWith(f) - - /** - * Attributes the field with the given `attributes`. - */ - def attributeTypeAs[Attributes](attributes: => Attributes)(implicit ev: T <:< Type[_]): Field[Type[Attributes]] = - Field(name, fieldType.mapAttributes(_ => attributes)) - - /** - * Attributes the field's type using the given function. - */ - def attributeTypeWith[Attributes0, Attributes](f: Attributes0 => Attributes)(implicit - ev: T <:< Type[Attributes0] - ): Field[Type[Attributes]] = - Field(name, fieldType.mapAttributes(f)) - - def forEach[G[+_]: IdentityBoth: Covariant, U](f: T => G[U]): G[Field[U]] = - f(self.fieldType).map(newType => self.copy(fieldType = newType)) - - def map[U](f: T => U): Field[U] = Field(name, f(fieldType)) - - } - - final case class Constructors[+Attributes](toMap: Map[Name, Chunk[(Name, Type[Attributes])]]) extends AnyVal { self => - def eraseAttributes: Constructors[Any] = Constructors(toMap.map { case (ctor, args) => - (ctor, args.map { case (paramName, paramType) => (paramName, paramType.eraseAttributes) }) - }) - - def collectReferences: Set[FQName] = { - toMap.values.flatMap { - case Chunk((_, tpe)) => - tpe.collectReferences - case _ => Nil - }.toSet - } - - def ctorNames: Set[Name] = toMap.keySet - } - - object Constructors { - - def forEnum(case1: String, otherCases: String*): Constructors[Any] = { - val allCases = (Chunk(case1) ++ otherCases).map(Name.fromString) - val emptyArgs = Chunk[(Name, UType)]() - Constructors(allCases.map(name => (name, emptyArgs)).toMap) - } - - } - - sealed trait Definition[+Attributes] { self => - import Definition._ - import Specification._ - - def toSpecification: Specification[Attributes] = self match { - case TypeAlias(typeParams, typeExp) => - TypeAliasSpecification[Attributes](typeParams, typeExp) - case CustomType(typeParams: Chunk[Name], ctors) if ctors.withPublicAccess.isDefined => - val constructors: Constructors[Attributes] = ctors.withPublicAccess.get - // val annotations = constructors.items.values.map(_.tpe.annotations).reduce(_ ++ _) // ??? - CustomTypeSpecification[Attributes](typeParams, constructors) - case CustomType(typeParams, _) => - OpaqueTypeSpecification(typeParams) // TODO fix annotations - } - - // def eraseAttributes: Definition[Nothing] = self match { - // case TypeAlias(typeParams, typeExp) => - // TypeAlias(typeParams, typeExp.eraseAttributes) - // case CustomType(typeParams, ctors) => - // CustomType(typeParams, ctors.eraseAttributes) - // } - } - - object Definition { - final case class TypeAlias[+Attributes](typeParams: Chunk[Name], typeExp: Type[Attributes]) - extends Definition[Attributes] - - final case class CustomType[+Attributes]( - typeParams: Chunk[Name], - ctors: AccessControlled[Constructors[Attributes]] - ) extends Definition[Attributes] - } - - type USpecification = Specification[Any] - val USpecification = Specification - sealed trait Specification[+Attributes] { self => - import Specification._ - - def ??(doc: String): Documented[Specification[Attributes]] = - Documented(doc, self) - - // def map[Annotations0 >: Annotations](f: Annotations => Annotations0): Specification[Annotations0] = self match { - // case c @ TypeAliasSpecification(_, _, _) => - // TypeAliasSpecification[Annotations0](c.typeParams, c.expr.map(f), c.annotations.map(f)) - // case c @ OpaqueTypeSpecification(_, _) => - // OpaqueTypeSpecification[Annotations0](c.typeParams, c.annotations.map(f)) - // case c @ CustomTypeSpecification(_, _, _) => - // CustomTypeSpecification[Annotations0](c.typeParams, c.ctors.map(f), c.annotations.map(f)) - // } - def eraseAttributes: Specification[Any] = self match { - case c @ TypeAliasSpecification(_, _) => - TypeAliasSpecification(c.typeParams, c.expr.eraseAttributes) - case c @ OpaqueTypeSpecification(_) => - OpaqueTypeSpecification(c.typeParams) - case c @ CustomTypeSpecification(_, _) => - CustomTypeSpecification(c.typeParams, c.ctors.eraseAttributes) - } - } - - object Specification { - final case class TypeAliasSpecification[+Attributes]( - typeParams: Chunk[Name], - expr: Type[Attributes] - ) extends Specification[Attributes] - - final case class OpaqueTypeSpecification(typeParams: Chunk[Name]) extends Specification[Nothing] - - object OpaqueTypeSpecification { - def apply(typeParams: String*): OpaqueTypeSpecification = - OpaqueTypeSpecification(Chunk.fromIterable(typeParams.map(Name.fromString))) - } - - final case class CustomTypeSpecification[+Attributes]( - typeParams: Chunk[Name], - ctors: Constructors[Attributes] - ) extends Specification[Attributes] - object CustomTypeSpecification { - def fromCtors[Attributes]( - ctor: (String, Iterable[(String, Type[Attributes])]), - ctors: (String, Iterable[(String, Type[Attributes])])* - ): CustomTypeSpecification[Attributes] = { - val allCtors = (ctor +: ctors).map { case (name, args) => - ( - Name.fromString(name), - Chunk.fromIterable(args.map { case (name, tpe) => - (Name.fromString(name), tpe) - }) - ) - }.toMap - CustomTypeSpecification(Chunk.empty, Constructors(allCtors)) - } - - def mkEnum(case1: String, otherCases: String*): CustomTypeSpecification[Any] = - CustomTypeSpecification(Chunk.empty, Constructors.forEnum(case1, otherCases: _*)) - } - - type UCustomTypeSpecification = CustomTypeSpecification[Any] - val UCustomTypeSpecification: CustomTypeSpecification.type = CustomTypeSpecification - } - - final case class Type[+Attributes] private[morphir] ( - caseValue: TypeCase[Type[Attributes]], - attributes: Attributes - ) { - self => - - def @@[Attributes2](f: Attributes => Attributes2): Type[Attributes2] = - mapAttributes(f) - - def ??(doc: String): Documented[Type[Attributes]] = Documented(doc, self) - - def fold[Z](f: TypeCase[Z] => Z): Z = - foldAttributed((typeCase, _) => f(typeCase)) - - def foldAttributed[Z](f: (TypeCase[Z], Attributes) => Z): Z = - self.caseValue match { - case c @ TypeCase.ExtensibleRecordCase(_, _) => - f(TypeCase.ExtensibleRecordCase(c.name, c.fields.map(field => field.map(_.foldAttributed(f)))), attributes) - case c @ TypeCase.FunctionCase(_, _) => - f(TypeCase.FunctionCase(c.paramTypes.map(_.foldAttributed(f)), c.returnType.foldAttributed(f)), attributes) - case c @ TypeCase.RecordCase(_) => - f(TypeCase.RecordCase(c.fields.map(field => field.map(_.foldAttributed(f)))), attributes) - case c @ TypeCase.ReferenceCase(_, _) => - f(TypeCase.ReferenceCase(c.typeName, c.typeParams.map(_.foldAttributed(f))), attributes) - case c @ TypeCase.TupleCase(_) => f(TypeCase.TupleCase(c.elementTypes.map(_.foldAttributed(f))), attributes) - case _ @TypeCase.UnitCase => f(TypeCase.UnitCase, attributes) - case c @ TypeCase.VariableCase(_) => f(c, attributes) - } - - def foldDown[Z](z: Z)(f: (Z, Type[Attributes]) => Z): Z = - caseValue.foldLeft(f(z, self))((z, recursive) => recursive.foldDown(z)(f)) - - def foldDownSome[Z](z: Z)(pf: PartialFunction[(Z, Type[Attributes]), Z]): Z = - foldDown(z)((z, recursive) => pf.lift(z -> recursive).getOrElse(z)) - - def foldM[F[+_]: AssociativeFlatten: Covariant: IdentityBoth, Z](f: TypeCase[Z] => F[Z]): F[Z] = - fold[F[Z]](_.flip.flatMap(f)) - - def foldPure[W, S, R, E, Z](f: TypeCase[Z] => ZPure[W, S, S, R, E, Z]): ZPure[W, S, S, R, E, Z] = - foldM(f) - - def transformDown[Annotations0 >: Attributes]( - f: Type[Annotations0] => Type[Annotations0] - ): Type[Annotations0] = { - def loop(recursive: Type[Annotations0]): Type[Attributes] = - Type(f(recursive).caseValue.map(loop), attributes) - loop(self) - } - - def foldZIO[R, E, Z](f: TypeCase[Z] => ZIO[R, E, Z]): ZIO[R, E, Z] = - foldM(f) - - def foldRecursive[Z](f: TypeCase[(Type[Attributes], Z)] => Z): Z = - f(caseValue.map(recursive => recursive -> recursive.foldRecursive(f))) - - def foldUp[Z](z: Z)(f: (Z, Type[Attributes]) => Z): Z = - f(caseValue.foldLeft(z)((z, recursive) => recursive.foldUp(z)(f)), self) - - def foldUpSome[Z](z: Z)(pf: PartialFunction[(Z, Type[Attributes]), Z]): Z = - foldUp(z)((z, recursive) => pf.lift(z -> recursive).getOrElse(z)) - - def mapAttributes[Attributes2](f: Attributes => Attributes2): Type[Attributes2] = - self.foldAttributed[Type[Attributes2]] { case (typeCase, attributes) => - Type(typeCase, f(attributes)) - } - - def collectVariables: Set[Name] = fold[Set[Name]] { - case c @ TypeCase.ExtensibleRecordCase(_, _) => c.fields.map(_.fieldType).flatten.toSet + c.name - case TypeCase.FunctionCase(paramTypes, returnType) => paramTypes.flatten.toSet ++ returnType - case TypeCase.RecordCase(fields) => fields.map(_.fieldType).flatten.toSet - case TypeCase.ReferenceCase(_, typeParams) => typeParams.flatten.toSet - case TypeCase.TupleCase(elementTypes) => elementTypes.flatten.toSet - case TypeCase.UnitCase => Set.empty - case TypeCase.VariableCase(name) => Set(name) - } - - def collectReferences: Set[FQName] = fold[Set[FQName]] { - case c @ TypeCase.ExtensibleRecordCase(_, _) => c.fields.map(_.fieldType).flatten.toSet - case TypeCase.FunctionCase(paramTypes, returnType) => - paramTypes.flatten.toSet ++ returnType - case TypeCase.RecordCase(fields) => - fields.map(_.fieldType).flatten.toSet - case TypeCase.ReferenceCase(name, typeParams) => - typeParams.flatten.toSet + name - case TypeCase.TupleCase(elementTypes) => elementTypes.flatten.toSet - case TypeCase.UnitCase => Set.empty - case TypeCase.VariableCase(_) => Set.empty - } - - /** - * Erase the attributes from this type. - */ - def eraseAttributes: UType = self.mapAttributes(_ => Type.emptyAttributes) - - // TO DO - // def substituteTypeVariables(mapping: Map[Name, Type[Annotations]]): Type[Annotations] = self.caseValue match { - // case TypeCase.VariableCase(name) => - // mapping.getOrElse(name, self) - // case TypeCase.ExtensibleRecordCase(name, fields) => - // TypeCase.ExtensibleRecordCase(name, fields.map(_.substituteTypeVariables(mapping))) - // case TypeCase.FieldCase(name, fieldType) => - // TypeCase.FieldCase(name, fieldType.substituteTypeVariables(mapping)) - // case TypeCase.FunctionCase(paramTypes, returnType) => - // TypeCase.FunctionCase(paramTypes.map(_.substituteTypeVariables(mapping)), returnType.substituteTypeVariables(mapping)) - // case TypeCase.RecordCase(fields) => - // TypeCase.RecordCase(fields.map(_.substituteTypeVariables(mapping))) - // case TypeCase.ReferenceCase(fqName, typeParams) => - // TypeCase.ReferenceCase(fqName, typeParams.map(_.substituteTypeVariables(mapping))) - // case TypeCase.TupleCase(elementTypes) => - // TypeCase.TupleCase(elementTypes.map(_.substituteTypeVariables(mapping))) - // case TypeCase.UnitCase => - // TypeCase.UnitCase - // } - - def satisfiesCaseOf(check: PartialFunction[TypeCase[Type[Attributes]], Boolean]): Boolean = - check.lift(self.caseValue).getOrElse(false) +object TypeModule extends TypeModuleSyntax { - override def toString: String = fold[String] { - case c @ TypeCase.ExtensibleRecordCase(_, _) => - s"{ ${c.name.toCamelCase} | ${c.fields.mkString(", ")} }" - case TypeCase.FunctionCase(paramTypes, returnType) => - paramTypes - .map(_.toString) - .mkString("(", ",", ")") - .concat(" -> " + returnType.toString) - case TypeCase.RecordCase(fields) => fields.mkString("{ ", ", ", " }") - case TypeCase.ReferenceCase(name, typeParams) => s"${name.toString} ${typeParams.mkString(" ")}" - case TypeCase.TupleCase(elementTypes) => elementTypes.mkString("(", ", ", ")") - case TypeCase.UnitCase => "()" - case TypeCase.VariableCase(name) => name.toCamelCase - } - } + final type Constructors[+Attributes] = types.Constructors[Attributes] + final val Constructors: types.Constructors.type = types.Constructors - object Type extends TypeModuleSyntax { - import TypeCase._ + final type UConstructors = types.UConstructors + final val UConstructors: types.Constructors.type = types.UConstructors - lazy val emptyAttributes: Unit = () + final type Definition[+Attributes] = types.Definition[Attributes] + final val Definition: types.Definition.type = types.Definition - def apply(caseValue: TypeCase[UType]): UType = Type(caseValue, emptyAttributes) + final type UDefinition = types.UDefinition + final val UDefinition: types.Definition.type = types.UDefinition - object Variable { - def unapply(tpe: Type[Any]): Option[Name] = tpe.caseValue match { - case VariableCase(name) => Some(name) - case _ => None - } - } - } + final type Field[+Attributes] = types.Field[Attributes] + final val Field: types.Field.type = types.Field - sealed trait TypeCase[+Self] { self => - import TypeCase._ + final type UField = types.UField + final val UField: types.Field.type = types.UField - def map[B](f: Self => B): TypeCase[B] = self match { - case c @ ExtensibleRecordCase(_, _) => ExtensibleRecordCase(c.name, c.fields.map(_.map(f))) - case c @ FunctionCase(_, _) => FunctionCase(c.paramTypes.map(f), f(c.returnType)) - case c @ ReferenceCase(_, _) => ReferenceCase(c.typeName, c.typeParams.map(f)) - case c @ TupleCase(_) => TupleCase(c.elementTypes.map(f)) - case UnitCase => UnitCase - case c @ VariableCase(_) => VariableCase(c.name) - case c @ RecordCase(_) => RecordCase(c.fields.map(_.map(f))) - } - } + final type Type[+Attributes] = types.Type[Attributes] + final val Type = types.Type - object TypeCase { - final case class ExtensibleRecordCase[+Self](name: Name, fields: Chunk[Field[Self]]) extends TypeCase[Self] - final case class FunctionCase[+Self](paramTypes: Chunk[Self], returnType: Self) extends TypeCase[Self] - final case class RecordCase[+Self](fields: Chunk[Field[Self]]) extends TypeCase[Self] - final case class ReferenceCase[+Self](typeName: FQName, typeParams: Chunk[Self]) extends TypeCase[Self] - final case class TupleCase[+Self](elementTypes: Chunk[Self]) extends TypeCase[Self] - case object UnitCase extends TypeCase[Nothing] - final case class VariableCase(name: Name) extends TypeCase[Nothing] + /** Represents an un-annotated type. */ + final type UType = types.UType + final val UType: types.Type.type = types.UType - implicit val TypeCaseForEach: ForEach[TypeCase] = - new ForEach[TypeCase] { - def forEach[G[+_]: IdentityBoth: Covariant, A, B](fa: TypeCase[A])(f: A => G[B]): G[TypeCase[B]] = - fa match { - case ExtensibleRecordCase(name, fields) => - fields.forEach(_.forEach(f)).map(fields => ExtensibleRecordCase(name, fields)) - case FunctionCase(paramTypes, returnType) => - paramTypes - .forEach(f) - .zipWith(f(returnType))((paramTypes, returnType) => FunctionCase(paramTypes, returnType)) - case RecordCase(fields) => - fields.forEach(_.forEach(f)).map(fields => RecordCase(fields)) - case ReferenceCase(typeName, typeParams) => - typeParams.forEach(f).map(typeParams => ReferenceCase(typeName, typeParams)) - case TupleCase(elementTypes) => - elementTypes.forEach(f).map(elementTypes => TupleCase(elementTypes)) - case UnitCase => - UnitCase.succeed - case VariableCase(name) => - VariableCase(name).succeed - } - } - } + final type Specification[+Attributes] = types.Specification[Attributes] + final val Specification: types.Specification.type = types.Specification - type UConstructors = Constructors[Any] - val UConstructors: Constructors.type = Constructors + final type USpecification = types.USpecification + final val USpecification: types.Specification.type = types.USpecification - /** Represents an un-annotated type. */ - type UType = Type[Unit] - val UType = Type } diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/sdk/Basics.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/sdk/Basics.scala index 43c67809..668248d4 100644 --- a/morphir-ir/shared/src/main/scala/zio/morphir/ir/sdk/Basics.scala +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/sdk/Basics.scala @@ -98,6 +98,6 @@ object Basics { lazy val floatType: UType = reference((toFQName(moduleName, "Float"))) lazy val intType: UType = reference((toFQName(moduleName, "Int"))) lazy val neverType: UType = reference((toFQName(moduleName, "Never"))) - lazy val orderType: UType = orderType(UType.emptyAttributes) + lazy val orderType: UType = orderType(()) def orderType[A](attributes: A): Type[A] = reference(attributes)((toFQName(moduleName, "Order"))) } diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Constructors.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Constructors.scala new file mode 100644 index 00000000..2e1e7ef0 --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Constructors.scala @@ -0,0 +1,30 @@ +package zio.morphir.ir.types + +import zio.Chunk +import zio.morphir.ir.{FQName, Name} + +final case class Constructors[+Attributes](toMap: Map[Name, Chunk[(Name, Type[Attributes])]]) extends AnyVal { self => + def eraseAttributes: Constructors[Any] = Constructors(toMap.map { case (ctor, args) => + (ctor, args.map { case (paramName, paramType) => (paramName, paramType.eraseAttributes) }) + }) + + def collectReferences: Set[FQName] = { + toMap.values.flatMap { + case Chunk((_, tpe)) => + tpe.collectReferences + case _ => Nil + }.toSet + } + + def ctorNames: Set[Name] = toMap.keySet +} + +object Constructors { + + def forEnum(case1: String, otherCases: String*): Constructors[Any] = { + val allCases = (Chunk(case1) ++ otherCases).map(Name.fromString) + val emptyArgs = Chunk[(Name, UType)]() + Constructors(allCases.map(name => (name, emptyArgs)).toMap) + } + +} diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Definition.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Definition.scala new file mode 100644 index 00000000..76f1e48a --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Definition.scala @@ -0,0 +1,37 @@ +package zio.morphir.ir.types + +import zio.Chunk +import zio.morphir.ir.{AccessControlled, Name} + +sealed trait Definition[+Attributes] { self => + import Definition._ + import Specification._ + + def toSpecification: Specification[Attributes] = self match { + case TypeAlias(typeParams, typeExp) => + TypeAliasSpecification[Attributes](typeParams, typeExp) + case CustomType(typeParams: Chunk[Name], ctors) if ctors.withPublicAccess.isDefined => + val constructors: Constructors[Attributes] = ctors.withPublicAccess.get + // val annotations = constructors.items.values.map(_.tpe.annotations).reduce(_ ++ _) // ??? + CustomTypeSpecification[Attributes](typeParams, constructors) + case CustomType(typeParams, _) => + OpaqueTypeSpecification(typeParams) // TODO fix annotations + } + + // def eraseAttributes: Definition[Nothing] = self match { + // case TypeAlias(typeParams, typeExp) => + // TypeAlias(typeParams, typeExp.eraseAttributes) + // case CustomType(typeParams, ctors) => + // CustomType(typeParams, ctors.eraseAttributes) + // } +} + +object Definition { + final case class TypeAlias[+Attributes](typeParams: Chunk[Name], typeExp: Type[Attributes]) + extends Definition[Attributes] + + final case class CustomType[+Attributes]( + typeParams: Chunk[Name], + ctors: AccessControlled[Constructors[Attributes]] + ) extends Definition[Attributes] +} diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Field.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Field.scala new file mode 100644 index 00000000..55863f40 --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Field.scala @@ -0,0 +1,40 @@ +package zio.morphir.ir.types + +import zio.morphir.ir.Name +import zio.prelude._ + +final case class Field[+T](name: Name, fieldType: T) { self => + + /** + * An alias for `attributeTypeWith`. + */ + def @@[Attributes0, Attributes](f: Attributes0 => Attributes)(implicit + ev: T <:< Type[Attributes0] + ): Field[Type[Attributes]] = + attributeTypeWith(f) + + /** + * Attributes the field with the given `attributes`. + */ + def attributeTypeAs[Attributes](attributes: => Attributes)(implicit ev: T <:< Type[_]): Field[Type[Attributes]] = + Field(name, fieldType.mapAttributes(_ => attributes)) + + /** + * Attributes the field's type using the given function. + */ + def attributeTypeWith[Attributes0, Attributes](f: Attributes0 => Attributes)(implicit + ev: T <:< Type[Attributes0] + ): Field[Type[Attributes]] = + Field(name, fieldType.mapAttributes(f)) + + def forEach[G[+_]: IdentityBoth: Covariant, U](f: T => G[U]): G[Field[U]] = + f(self.fieldType).map(newType => self.copy(fieldType = newType)) + + def map[U](f: T => U): Field[U] = Field(name, f(fieldType)) + + def mapAttributes[Attributes0, Attributes1](f: Attributes0 => Attributes1)(implicit + ev: T <:< Type[Attributes0] + ): Field[Type[Attributes1]] = + Field(name, fieldType.mapAttributes(f)) + +} diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Specification.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Specification.scala new file mode 100644 index 00000000..6cc5b7f3 --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Specification.scala @@ -0,0 +1,70 @@ +package zio.morphir.ir.types + +import zio.Chunk +import zio.morphir.ir.{Documented, Name} + +sealed trait Specification[+Attributes] { self => + import Specification._ + + def ??(doc: String): Documented[Specification[Attributes]] = + Documented(doc, self) + + // def map[Annotations0 >: Annotations](f: Annotations => Annotations0): Specification[Annotations0] = self match { + // case c @ TypeAliasSpecification(_, _, _) => + // TypeAliasSpecification[Annotations0](c.typeParams, c.expr.map(f), c.annotations.map(f)) + // case c @ OpaqueTypeSpecification(_, _) => + // OpaqueTypeSpecification[Annotations0](c.typeParams, c.annotations.map(f)) + // case c @ CustomTypeSpecification(_, _, _) => + // CustomTypeSpecification[Annotations0](c.typeParams, c.ctors.map(f), c.annotations.map(f)) + // } + def eraseAttributes: Specification[Any] = self match { + case c @ TypeAliasSpecification(_, _) => + TypeAliasSpecification(c.typeParams, c.expr.eraseAttributes) + case c @ OpaqueTypeSpecification(_) => + OpaqueTypeSpecification(c.typeParams) + case c @ CustomTypeSpecification(_, _) => + CustomTypeSpecification(c.typeParams, c.ctors.eraseAttributes) + } +} + +object Specification { + final case class TypeAliasSpecification[+Attributes]( + typeParams: Chunk[Name], + expr: Type[Attributes] + ) extends Specification[Attributes] + + final case class OpaqueTypeSpecification(typeParams: Chunk[Name]) extends Specification[Nothing] + + object OpaqueTypeSpecification { + def apply(typeParams: String*): OpaqueTypeSpecification = + OpaqueTypeSpecification(Chunk.fromIterable(typeParams.map(Name.fromString))) + } + + final case class CustomTypeSpecification[+Attributes]( + typeParams: Chunk[Name], + ctors: Constructors[Attributes] + ) extends Specification[Attributes] + + object CustomTypeSpecification { + def fromCtors[Attributes]( + ctor: (String, Iterable[(String, Type[Attributes])]), + ctors: (String, Iterable[(String, Type[Attributes])])* + ): CustomTypeSpecification[Attributes] = { + val allCtors = (ctor +: ctors).map { case (name, args) => + ( + Name.fromString(name), + Chunk.fromIterable(args.map { case (name, tpe) => + (Name.fromString(name), tpe) + }) + ) + }.toMap + CustomTypeSpecification(Chunk.empty, Constructors(allCtors)) + } + + def mkEnum(case1: String, otherCases: String*): CustomTypeSpecification[Any] = + CustomTypeSpecification(Chunk.empty, Constructors.forEnum(case1, otherCases: _*)) + } + + type UCustomTypeSpecification = CustomTypeSpecification[Any] + val UCustomTypeSpecification: CustomTypeSpecification.type = CustomTypeSpecification +} diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Type.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Type.scala new file mode 100644 index 00000000..f62477a4 --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/Type.scala @@ -0,0 +1,194 @@ +package zio.morphir.ir.types + +import zio.Chunk +import zio.morphir.ir.{Documented, FQName, Name} +import zio.morphir.syntax.TypeModuleSyntax +import zio.prelude._ + +import scala.annotation.tailrec + +sealed trait Type[+Attributes] { self => + def @@[Attributes2](f: Attributes => Attributes2): Type[Attributes2] = + mapAttributes(f) + + def ??(doc: String): Documented[Type[Attributes]] = Documented(doc, self) + + def attributes: Attributes + + def collect[Z](pf: PartialFunction[Type[Attributes], Z]): Chunk[Z] = + foldLeft[Chunk[Z]](Chunk.empty) { case (acc, tpe) => + if (pf.isDefinedAt(tpe)) acc :+ pf(tpe) + else acc + } + + def fold[Z: Associative](f: Type[Attributes] => Z): Z = { + @tailrec + def loop(types: List[Type[Attributes]], acc: Z): Z = types match { + case (tpe @ Type.ExtensibleRecord(_, _, _)) :: tail => + loop(tpe.fields.map(_.fieldType).toList ++ tail, f(tpe) <> acc) + case (tpe @ Type.Function(_, _, _)) :: tail => + loop(tpe.paramTypes.toList ++ (tpe.returnType :: tail), f(tpe) <> acc) + case (tpe @ Type.Record(_, _)) :: tail => loop(tpe.fields.map(_.fieldType).toList ++ tail, f(tpe) <> acc) + case (tpe @ Type.Reference(_, _, _)) :: tail => loop(tpe.typeParams.toList ++ tail, f(tpe) <> acc) + case (tpe @ Type.Tuple(_, elements)) :: tail => loop(elements.toList ++ tail, f(tpe) <> acc) + case Type.Variable(_, _) :: tail => loop(tail, acc) + case Type.Unit(_) :: tail => loop(tail, acc) + case Nil => acc + } + loop(List(self), f(self)) + } + + def foldLeft[Z](zero: Z)(f: (Z, Type[Attributes]) => Z): Z = { + @tailrec + def loop(types: List[Type[Attributes]], acc: Z): Z = types match { + case (tpe @ Type.ExtensibleRecord(_, _, _)) :: tail => + loop(tpe.fields.map(_.fieldType).toList ++ tail, f(acc, tpe)) + case (tpe @ Type.Function(_, _, _)) :: tail => + loop(tpe.paramTypes.toList ++ (tpe.returnType :: tail), f(acc, tpe)) + case (tpe @ Type.Record(_, _)) :: tail => + loop(tpe.fields.map(_.fieldType).toList ++ tail, f(acc, tpe)) + case (tpe @ Type.Reference(_, _, _)) :: tail => + loop(tpe.typeParams.toList ++ tail, f(acc, tpe)) + case (tpe @ Type.Tuple(_, elements)) :: tail => + loop(elements.toList ++ tail, f(acc, tpe)) + case (tpe @ Type.Variable(_, _)) :: tail => loop(tail, f(acc, tpe)) + case (tpe @ Type.Unit(_)) :: tail => loop(tail, f(acc, tpe)) + case Nil => acc + } + loop(List(self), zero) + } + + def foldDown[Z](z: Z)(f: (Z, Type[Attributes]) => Z): Z = + foldLeft(f(z, self))((z, recursive) => recursive.foldDown(z)(f)) + + def foldDownSome[Z](z: Z)(pf: PartialFunction[(Z, Type[Attributes]), Z]): Z = + foldDown(z)((z, recursive) => pf.lift(z -> recursive).getOrElse(z)) + // + // def foldM[F[+_]: AssociativeFlatten: Covariant: IdentityBoth, Z](f: TypeCase[Z] => F[Z]): F[Z] = + // fold[F[Z]](_.flip.flatMap(f)) + // + // def foldPure[W, S, R, E, Z](f: TypeCase[Z] => ZPure[W, S, S, R, E, Z]): ZPure[W, S, S, R, E, Z] = + // foldM(f) + // + // def transformDown[Annotations0 >: Attributes]( + // f: Type[Annotations0] => Type[Annotations0] + // ): Type[Annotations0] = { + // def loop(recursive: Type[Annotations0]): Type[Attributes] = + // Type(f(recursive).caseValue.map(loop), attributes) + // loop(self) + // } + // + // def foldZIO[R, E, Z](f: TypeCase[Z] => ZIO[R, E, Z]): ZIO[R, E, Z] = + // foldM(f) + // + // def foldRecursive[Z](f: TypeCase[(Type[Attributes], Z)] => Z): Z = + // f(caseValue.map(recursive => recursive -> recursive.foldRecursive(f))) + // + def foldUp[Z](z: Z)(f: (Z, Type[Attributes]) => Z): Z = + f(self.foldLeft(z)((z, recursive) => recursive.foldUp(z)(f)), self) + + def foldUpSome[Z](z: Z)(pf: PartialFunction[(Z, Type[Attributes]), Z]): Z = + foldUp(z)((z, recursive) => pf.lift(z -> recursive).getOrElse(z)) + + // TODO: See if we can refactor to be stack safe/ tail recursive + def mapAttributes[Attributes2](f: Attributes => Attributes2): Type[Attributes2] = self match { + case Type.ExtensibleRecord(attributes, name, fields) => + Type.ExtensibleRecord(f(attributes), name, fields.map(_.mapAttributes(f))) + case Type.Function(attributes, paramTypes, returnType) => + Type.Function(f(attributes), paramTypes.map(_.mapAttributes(f)), returnType.mapAttributes(f)) + case Type.Record(attributes, fields) => Type.Record(f(attributes), fields.map(_.mapAttributes(f))) + case Type.Reference(attributes, typeName, typeParams) => + Type.Reference(f(attributes), typeName, typeParams.map(_.mapAttributes(f))) + case Type.Tuple(attributes, elementTypes) => Type.Tuple(f(attributes), elementTypes.map(_.mapAttributes(f))) + case Type.Unit(attributes) => Type.Unit(f(attributes)) + case Type.Variable(attributes, name) => Type.Variable(f(attributes), name) + } + + def collectReferences: Set[FQName] = foldLeft(Set.empty[FQName]) { case (acc, tpe) => + tpe match { + case Type.Reference(_, typeName, _) => acc + typeName + case _ => acc + } + } + + def collectVariables: Set[Name] = foldLeft(Set.empty[Name]) { case (acc, tpe) => + tpe match { + case tpe @ Type.ExtensibleRecord(_, _, _) => acc + tpe.name + case Type.Variable(_, name) => acc + name + case _ => acc + } + } + + /** + * Erase the attributes from this type. + */ + def eraseAttributes: UType = self.mapAttributes(_ => ()) + + // TO DO + // def substituteTypeVariables(mapping: Map[Name, Type[Annotations]]): Type[Annotations] = self.caseValue match { + // case TypeCase.VariableCase(name) => + // mapping.getOrElse(name, self) + // case TypeCase.ExtensibleRecordCase(name, fields) => + // TypeCase.ExtensibleRecordCase(name, fields.map(_.substituteTypeVariables(mapping))) + // case TypeCase.FieldCase(name, fieldType) => + // TypeCase.FieldCase(name, fieldType.substituteTypeVariables(mapping)) + // case TypeCase.FunctionCase(paramTypes, returnType) => + // TypeCase.FunctionCase(paramTypes.map(_.substituteTypeVariables(mapping)), returnType.substituteTypeVariables(mapping)) + // case TypeCase.RecordCase(fields) => + // TypeCase.RecordCase(fields.map(_.substituteTypeVariables(mapping))) + // case TypeCase.ReferenceCase(fqName, typeParams) => + // TypeCase.ReferenceCase(fqName, typeParams.map(_.substituteTypeVariables(mapping))) + // case TypeCase.TupleCase(elementTypes) => + // TypeCase.TupleCase(elementTypes.map(_.substituteTypeVariables(mapping))) + // case TypeCase.UnitCase => + // TypeCase.UnitCase + // } + + def satisfiesCaseOf(check: PartialFunction[Type[Attributes], Boolean]): Boolean = + check.lift(self).getOrElse(false) + // + // override def toString: String = fold[String] { + // case c @ Type.ExtensibleRecord(_, _) => + // s"{ ${c.name.toCamel} | ${c.fields.mkString(", ")} }" + // Type.Function(paramTypes, returnType) => + // paramTypes + // .map(_.toString) + // .mkString("(", ",", ")") + // .concat(" -> " + returnType.toString) + // Type.Record(fields) => fields.mkString("{ ", ", ", " }") + // Type.Reference(name, typeParams) => s"${name.toString} ${typeParams.mkString(" ")}" + // Type.Tuple(elementTypes) => elementTypes.mkString("(", ", ", ")") + // Type.Unit => "()" + // Type.Variable(name) => name.toCamel + // } +} + +object Type extends TypeModuleSyntax { + + final case class ExtensibleRecord[+Attributes]( + attributes: Attributes, + name: Name, + fields: Chunk[Field[Type[Attributes]]] + ) extends Type[Attributes] + final case class Function[+Attributes]( + attributes: Attributes, + paramTypes: Chunk[Type[Attributes]], + returnType: Type[Attributes] + ) extends Type[Attributes] + final case class Record[+Attributes](attributes: Attributes, fields: Chunk[Field[Type[Attributes]]]) + extends Type[Attributes] + final case class Reference[+Attributes]( + attributes: Attributes, + typeName: FQName, + typeParams: Chunk[Type[Attributes]] + ) extends Type[Attributes] + final case class Tuple[+Attributes](attributes: Attributes, elementTypes: Chunk[Type[Attributes]]) + extends Type[Attributes] + final case class Unit[+Attributes](attributes: Attributes) extends Type[Attributes] + + final case class Variable[+Attributes](attributes: Attributes, name: Name) extends Type[Attributes] + object Variable { + def apply(name: Name): Variable[scala.Unit] = Variable((), name) + } + +} diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/package.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/package.scala new file mode 100644 index 00000000..6030f611 --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/types/package.scala @@ -0,0 +1,21 @@ +package zio.morphir.ir + +package object types { + + final type UConstructors = Constructors[Unit] + final val UConstructors: Constructors.type = types.Constructors + + final type UDefinition = Definition[Unit] + final val UDefinition: Definition.type = types.Definition + + final type UField = Field[Unit] + final val UField: Field.type = types.Field + + final type USpecification = Specification[Unit] + final val USpecification: Specification.type = types.Specification + + /** Represents an un-annotated/un-attributed type. */ + type UType = Type[Unit] + val UType: Type.type = Type + +} diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/syntax/TypeSyntax.scala b/morphir-ir/shared/src/main/scala/zio/morphir/syntax/TypeSyntax.scala index d18a7803..65ceda0e 100644 --- a/morphir-ir/shared/src/main/scala/zio/morphir/syntax/TypeSyntax.scala +++ b/morphir-ir/shared/src/main/scala/zio/morphir/syntax/TypeSyntax.scala @@ -1,9 +1,9 @@ package zio.morphir.syntax import zio.Chunk -import zio.morphir.ir.TypeModule.Specification.{CustomTypeSpecification, UCustomTypeSpecification} -import zio.morphir.ir.TypeModule.TypeCase._ -import zio.morphir.ir.TypeModule.{Field, Type} +import zio.morphir.ir.types.Specification.{CustomTypeSpecification, UCustomTypeSpecification} +import zio.morphir.ir.types.Type._ +import zio.morphir.ir.types.{Field, Type} import zio.morphir.ir.{FQName, Name, TypeConstructors, UType} trait TypeSyntax { @@ -22,52 +22,49 @@ trait TypeSyntax { ): CustomTypeSpecification[Attributes] = CustomTypeSpecification(Chunk.empty, TypeConstructors(Map(ctors))) - def defineVariable(name: String): UType = Type(VariableCase(Name.fromString(name)), Type.emptyAttributes) - def defineVariable(name: Name): UType = Type(VariableCase(name), Type.emptyAttributes) + def defineVariable(name: String): UType = Variable((), Name.fromString(name)) + def defineVariable(name: Name): UType = Variable((), name) def defineField(name: Name, fieldType: UType): Field[UType] = Field(name, fieldType) def defineField(name: String, fieldType: UType): Field[UType] = Field(Name.fromString(name), fieldType) def defineRecord(fields: Chunk[Field[UType]]): UType = - Type(RecordCase(fields), Type.emptyAttributes) + Record((), fields) def defineRecord(fields: Field[UType]*): UType = - Type(RecordCase(Chunk.fromIterable(fields)), Type.emptyAttributes) + Record((), Chunk.fromIterable(fields)) def defineTuple(elementTypes: Chunk[UType]): UType = - Type(TupleCase(elementTypes), Type.emptyAttributes) + Tuple((), elementTypes) def defineTuple(first: UType, second: UType, rest: UType*): UType = - Type(TupleCase(Chunk(first, second) ++ Chunk.fromIterable(rest)), Type.emptyAttributes) + Tuple((), Chunk(first, second) ++ Chunk.fromIterable(rest)) def defineFunction(paramTypes: Chunk[UType], returnType: UType): UType = - Type(FunctionCase(paramTypes, returnType), Type.emptyAttributes) + Function((), paramTypes, returnType) def defineFunction[Annotations](paramTypes: Type[Annotations]*): SyntaxHelper.DefineFunction[Annotations] = new SyntaxHelper.DefineFunction(() => Chunk.fromIterable(paramTypes)) def defineExtensibleRecord(name: Name, fields: Chunk[Field[UType]]): UType = - Type(ExtensibleRecordCase(name, fields), Type.emptyAttributes) + ExtensibleRecord((), name, fields) def defineExtensibleRecord(name: Name, fields: Field[UType]*): UType = - Type(ExtensibleRecordCase(name, Chunk.fromIterable(fields)), Type.emptyAttributes) + ExtensibleRecord((), name, Chunk.fromIterable(fields)) def defineExtensibleRecord(name: String, fields: Chunk[Field[UType]]): UType = - Type(ExtensibleRecordCase(Name.fromString(name), fields), Type.emptyAttributes) + ExtensibleRecord((), Name.fromString(name), fields) def defineExtensibleRecord(name: String, fields: Field[UType]*): UType = - Type(ExtensibleRecordCase(Name.fromString(name), Chunk.fromIterable(fields)), Type.emptyAttributes) + ExtensibleRecord((), Name.fromString(name), Chunk.fromIterable(fields)) def defineReference(name: FQName, typeParams: Chunk[UType]): UType = - Type(ReferenceCase(name, typeParams), Type.emptyAttributes) + Reference((), name, typeParams) def defineReference(name: FQName, typeParams: UType*): UType = - Type(ReferenceCase(name, Chunk.fromIterable(typeParams)), Type.emptyAttributes) + Reference((), name, Chunk.fromIterable(typeParams)) def defineReference( packageName: String, moduleName: String, localName: String, typeParams: Chunk[UType] ): UType = - Type(ReferenceCase(FQName.fqn(packageName, moduleName, localName), typeParams), Type.emptyAttributes) + Reference((), FQName.fqn(packageName, moduleName, localName), typeParams) def defineReference(packageName: String, moduleName: String, localName: String, typeParams: UType*): UType = - Type( - ReferenceCase(FQName.fqn(packageName, moduleName, localName), Chunk.fromIterable(typeParams)), - Type.emptyAttributes - ) + Reference((), FQName.fqn(packageName, moduleName, localName), Chunk.fromIterable(typeParams)) def enumType(case1: String, otherCases: String*): UCustomTypeSpecification = UCustomTypeSpecification.mkEnum(case1, otherCases: _*) @@ -86,44 +83,44 @@ trait TypeSyntax { } trait TypeModuleSyntax { - val unit: UType = Type(UnitCase, ()) - final def unit[Annotations](annotations: Annotations): Type[Annotations] = Type(UnitCase, annotations) + val unit: UType = Unit(()) + final def unit[Attributes](attributes: Attributes): Type[Attributes] = Unit(attributes) /** * Creates a type variable with the given `name`. */ - final def variable[Attributes](name: String, attributes: Attributes): Type[Attributes] = - Type(VariableCase(Name.fromString(name)), attributes) - final def variable[Attributes](name: Name, attributes: Attributes): Type[Attributes] = - Type(VariableCase(name), attributes) - final def variable(name: String): UType = Type(VariableCase(Name.fromString(name))) - final def variable(name: Name): UType = Type(VariableCase(name)) + final def variable[Attributes](name: String, attributes: Attributes): Variable[Attributes] = + Variable(attributes, Name.fromString(name)) + final def variable[Attributes](name: Name, attributes: Attributes): Variable[Attributes] = + Variable(attributes, name) + final def variable(name: String): Variable[scala.Unit] = Variable(Name.fromString(name)) + final def variable(name: Name): Variable[scala.Unit] = Variable(name) final def field(name: Name, fieldType: UType): Field[UType] = Field(name, fieldType) final def field(name: String, fieldType: UType): Field[UType] = Field(Name.fromString(name), fieldType) final def record(fields: Chunk[Field[UType]]): UType = - Type(RecordCase(fields), Type.emptyAttributes) + Record((), fields) final def record(fields: Field[UType]*): UType = - Type(RecordCase(Chunk.fromIterable(fields)), Type.emptyAttributes) + Record((), Chunk.fromIterable(fields)) final def record[Attributes](attributes: Attributes, fields: Chunk[Field[Type[Attributes]]]): Type[Attributes] = - Type(RecordCase(fields), attributes) + Record(attributes, fields) final def record[Attributes](attributes: Attributes, fields: Field[Type[Attributes]]*): Type[Attributes] = - Type(RecordCase(Chunk.fromIterable(fields)), attributes) + Record(attributes, Chunk.fromIterable(fields)) final def tuple(elementTypes: Chunk[UType]): UType = - Type(TupleCase(elementTypes), Type.emptyAttributes) + Tuple((), elementTypes) final def tuple(first: UType, second: UType, rest: UType*): UType = - Type(TupleCase(Chunk(first, second) ++ Chunk.fromIterable(rest)), Type.emptyAttributes) + Tuple((), Chunk(first, second) ++ Chunk.fromIterable(rest)) final def tuple[Attributes](attributes: Attributes, elementTypes: Chunk[Type[Attributes]]): Type[Attributes] = - Type(TupleCase(elementTypes), attributes) + Tuple(attributes, elementTypes) final def tuple[Attributes]( attributes: Attributes, first: Type[Attributes], second: Type[Attributes], rest: Type[Attributes]* ): Type[Attributes] = - Type(TupleCase(Chunk(first, second) ++ Chunk.fromIterable(rest)), attributes) + Tuple(attributes, Chunk(first, second) ++ Chunk.fromIterable(rest)) def curriedFunction(paramTypes: List[UType], returnType: UType): UType = { def curry(args: List[UType]): UType = args match { @@ -134,33 +131,33 @@ trait TypeModuleSyntax { } final def function1(paramType: UType, returnType: UType): UType = - Type(FunctionCase(Chunk.single(paramType), returnType), Type.emptyAttributes) + Function((), Chunk.single(paramType), returnType) final def function(paramTypes: Chunk[UType], returnType: UType): UType = - Type(FunctionCase(paramTypes, returnType), Type.emptyAttributes) + Function((), paramTypes, returnType) final def function[Annotations](paramTypes: Type[Annotations]*): SyntaxHelper.DefineFunction[Annotations] = new SyntaxHelper.DefineFunction(() => Chunk.fromIterable(paramTypes)) final def extensibleRecord(name: Name, fields: Chunk[Field[UType]]): UType = - Type(ExtensibleRecordCase(name, fields), Type.emptyAttributes) + ExtensibleRecord((), name, fields) final def extensibleRecord(name: Name, fields: Field[UType]*): UType = - Type(ExtensibleRecordCase(name, Chunk.fromIterable(fields)), Type.emptyAttributes) + ExtensibleRecord((), name, Chunk.fromIterable(fields)) final def extensibleRecord(name: String, fields: Chunk[Field[UType]]): UType = - Type(ExtensibleRecordCase(Name.fromString(name), fields), Type.emptyAttributes) + ExtensibleRecord((), Name.fromString(name), fields) final def extensibleRecord(name: String, fields: Field[UType]*): UType = - Type(ExtensibleRecordCase(Name.fromString(name), Chunk.fromIterable(fields)), Type.emptyAttributes) + ExtensibleRecord((), Name.fromString(name), Chunk.fromIterable(fields)) final def reference[Attributes]( attributes: Attributes )(fqName: FQName, typeParams: Type[Attributes]*): Type[Attributes] = - Type(ReferenceCase(fqName, Chunk.fromIterable(typeParams)), attributes) + Reference(attributes, fqName, Chunk.fromIterable(typeParams)) final def reference(name: FQName, typeParams: Chunk[UType]): UType = - Type(ReferenceCase(name, typeParams), Type.emptyAttributes) + Reference((), name, typeParams) final def reference(name: FQName, typeParams: UType*): UType = - Type(ReferenceCase(name, Chunk.fromIterable(typeParams)), Type.emptyAttributes) + Reference((), name, Chunk.fromIterable(typeParams)) final def reference( packageName: String, @@ -168,21 +165,18 @@ trait TypeModuleSyntax { localName: String, typeParams: Chunk[UType] ): UType = - Type(ReferenceCase(FQName.fqn(packageName, moduleName, localName), typeParams), Type.emptyAttributes) + Reference((), FQName.fqn(packageName, moduleName, localName), typeParams) def reference(packageName: String, moduleName: String, localName: String, typeParams: UType*): UType = - Type( - ReferenceCase(FQName.fqn(packageName, moduleName, localName), Chunk.fromIterable(typeParams)), - Type.emptyAttributes - ) + Reference((), FQName.fqn(packageName, moduleName, localName), Chunk.fromIterable(typeParams)) @inline final def ref(name: FQName): UType = reference(name, Chunk.empty) } object SyntaxHelper { - final class DefineFunction[Annotations](val paramTypes: () => Chunk[Type[Annotations]]) extends AnyVal { - def apply(returnType: Type[Annotations], annotations: Annotations): Type[Annotations] = - Type(FunctionCase(paramTypes(), returnType), annotations) + final class DefineFunction[Attributes](val paramTypes: () => Chunk[Type[Attributes]]) extends AnyVal { + def apply(returnType: Type[Attributes], attributes: Attributes): Type[Attributes] = + Function(attributes, paramTypes(), returnType) } } diff --git a/morphir-ir/shared/src/test/scala/zio/morphir/ir/TypeModuleSpec.scala b/morphir-ir/shared/src/test/scala/zio/morphir/ir/TypeModuleSpec.scala index d0d124a7..edce2601 100644 --- a/morphir-ir/shared/src/test/scala/zio/morphir/ir/TypeModuleSpec.scala +++ b/morphir-ir/shared/src/test/scala/zio/morphir/ir/TypeModuleSpec.scala @@ -1,10 +1,10 @@ package zio.morphir.ir import zio.morphir.testing.MorphirBaseSpec -import zio.morphir.ir.TypeModule.{Type, TypeCase} +import zio.morphir.ir.TypeModule.Type import zio.test._ import zio.morphir.syntax.TypeModuleSyntax -import TypeCase._ +import Type._ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { def spec = suite("Type")( @@ -17,12 +17,12 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { suite("Variable")( test("testing first variable constructor") { val actual = variable("FizzBuzz") - assertTrue(actual.satisfiesCaseOf { case VariableCase(name) => name.toString == "[fizz,buzz]" }) && + assertTrue(actual.satisfiesCaseOf { case Variable(_, name) => name.toString == "[fizz,buzz]" }) && assertTrue(actual.collectVariables == Set(Name.fromString("FizzBuzz"))) }, test("testing second variable constructor") { val actual = variable(Name("FizzBuzz")) - assertTrue(actual.satisfiesCaseOf { case VariableCase(name) => name.toString == "[fizz,buzz]" }) && + assertTrue(actual.satisfiesCaseOf { case Variable(_, name) => name.toString == "[fizz,buzz]" }) && assertTrue(actual.collectVariables == Set(Name.fromString("FizzBuzz"))) }, test("eraseAttributes should clear out the Attributes") { @@ -30,9 +30,9 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val expected = variable("foo") assertTrue( actual != expected, - actual.attributes == ((0, 0)) && expected.attributes == Type.emptyAttributes, + actual.attributes == ((0, 0)) && expected.attributes == (()), actual.eraseAttributes == variable("foo"), - actual.eraseAttributes == actual.mapAttributes(_ => Type.emptyAttributes) + actual.eraseAttributes == actual.mapAttributes(_ => (())) ) } ), @@ -41,7 +41,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val actual = field(Name("field1"), variable("FizzBuzz")) assertTrue( actual.name == Name("field1"), - actual.fieldType.satisfiesCaseOf { case VariableCase(name) => name.toString == "[fizz,buzz]" }, + actual.fieldType.satisfiesCaseOf { case Variable(_, name) => name.toString == "[fizz,buzz]" }, actual.fieldType.collectVariables == Set(Name.fromString("FizzBuzz")) ) }, @@ -49,7 +49,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val actual = field("field1", variable("FizzBuzz")) assertTrue( actual.name == Name("field1"), - actual.fieldType.satisfiesCaseOf { case VariableCase(name) => name.toString == "[fizz,buzz]" }, + actual.fieldType.satisfiesCaseOf { case Variable(_, name) => name.toString == "[fizz,buzz]" }, actual.fieldType.collectVariables == Set(Name.fromString("FizzBuzz")) ) } @@ -61,7 +61,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val chunk = zio.Chunk(var1, var2) val actual = record(chunk) assertTrue( - actual.satisfiesCaseOf { case RecordCase(fields) => fields.contains(var1) && fields.contains(var2) } + actual.satisfiesCaseOf { case Record(_, fields) => fields.contains(var1) && fields.contains(var2) } ) }, test("testing second record constructor") { @@ -69,7 +69,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val var2 = field("second", variable("there")) val actual = record(var1, var2) assertTrue( - actual.satisfiesCaseOf { case RecordCase(fields) => fields.contains(var1) && fields.contains(var2) } + actual.satisfiesCaseOf { case Record(_, fields) => fields.contains(var1) && fields.contains(var2) } ) } ), @@ -80,7 +80,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val chunk = zio.Chunk(var1, var2) val actual = tuple(chunk) assertTrue( - actual.satisfiesCaseOf { case TupleCase(elements) => elements.contains(var1) && elements.contains(var2) } + actual.satisfiesCaseOf { case Tuple(_, elements) => elements.contains(var1) && elements.contains(var2) } ) }, test("testing second tuple constructor") { @@ -89,7 +89,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val var3 = variable("notThere") val actual = tuple(var1, var2) assertTrue( - actual.satisfiesCaseOf { case TupleCase(elements) => + actual.satisfiesCaseOf { case Tuple(_, elements) => elements.contains(var1) && elements.contains(var2) && !elements.contains(var3) } ) @@ -102,7 +102,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val retType = tuple(variable("v3"), variable("v4")) val actual = function(zio.Chunk(param1, param2), retType) assertTrue( - actual.satisfiesCaseOf { case FunctionCase(params, returnType) => + actual.satisfiesCaseOf { case Function(_, params, returnType) => params.contains(param1) && params.contains(param2) && returnType == retType } ) @@ -111,9 +111,9 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val param1 = variable("v1") val param2 = variable("v2") val retType = tuple(variable("v3"), variable("v4")) - val actual = function(param1, param2)(retType, Type.emptyAttributes) + val actual = function(param1, param2)(retType, ()) assertTrue( - actual.satisfiesCaseOf { case FunctionCase(params, returnType) => + actual.satisfiesCaseOf { case Function(_, params, returnType) => params.contains(param1) && params.contains(param2) && returnType == retType } ) @@ -127,7 +127,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val n1 = Name("SomeName") val actual = extensibleRecord(n1, zio.Chunk(f1, f2, f3)) assertTrue( - actual.satisfiesCaseOf { case ExtensibleRecordCase(name, fields) => + actual.satisfiesCaseOf { case ExtensibleRecord(_, name, fields) => name == n1 && fields.contains(f1) && fields.contains(f2) && fields.contains(f3) } ) @@ -139,7 +139,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val n1 = Name("SomeName") val actual = extensibleRecord(n1, f1, f2, f3) assertTrue( - actual.satisfiesCaseOf { case ExtensibleRecordCase(name, fields) => + actual.satisfiesCaseOf { case ExtensibleRecord(_, name, fields) => name == n1 && fields.contains(f1) && fields.contains(f2) && fields.contains(f3) } ) @@ -150,7 +150,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val f3 = field("third", tuple(variable("v3"), variable("v4"))) val actual = extensibleRecord("SomeName", zio.Chunk(f1, f2, f3)) assertTrue( - actual.satisfiesCaseOf { case ExtensibleRecordCase(name, fields) => + actual.satisfiesCaseOf { case ExtensibleRecord(_, name, fields) => name.toString == "[some,name]" && fields.contains(f1) && fields.contains(f2) && fields.contains(f3) } ) @@ -161,7 +161,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val f3 = field("third", tuple(variable("v3"), variable("v4"))) val actual = extensibleRecord("SomeName", f1, f2, f3) assertTrue( - actual.satisfiesCaseOf { case ExtensibleRecordCase(name, fields) => + actual.satisfiesCaseOf { case ExtensibleRecord(_, name, fields) => name.toString == "[some,name]" && fields.contains(f1) && fields.contains(f2) && fields.contains(f3) } ) @@ -175,7 +175,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val fqn1 = FQName.fqn("packageName", "moduleName", "localName") val actual = reference(fqn1, zio.Chunk(v1, v2, v3)) assertTrue( - actual.satisfiesCaseOf { case ReferenceCase(fqName, typeParams) => + actual.satisfiesCaseOf { case Reference(_, fqName, typeParams) => fqName == fqn1 && typeParams.contains(v1) && typeParams.contains(v2) && typeParams.contains(v3) } ) @@ -187,7 +187,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val fqn1 = FQName.fqn("packageName", "moduleName", "localName") val actual = reference(fqn1, v1, v2, v3) assertTrue( - actual.satisfiesCaseOf { case ReferenceCase(fqName, typeParams) => + actual.satisfiesCaseOf { case Reference(_, fqName, typeParams) => fqName == fqn1 && typeParams.contains(v1) && typeParams.contains(v2) && typeParams.contains(v3) } ) @@ -199,7 +199,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val fqn1 = FQName.fqn("packageName", "moduleName", "localName") val actual = reference("packageName", "moduleName", "localName", zio.Chunk(v1, v2, v3)) assertTrue( - actual.satisfiesCaseOf { case ReferenceCase(fqName, typeParams) => + actual.satisfiesCaseOf { case Reference(_, fqName, typeParams) => fqName == fqn1 && typeParams.contains(v1) && typeParams.contains(v2) && typeParams.contains(v3) } ) @@ -211,7 +211,7 @@ object TypeModuleSpec extends MorphirBaseSpec with TypeModuleSyntax { val fqn1 = FQName.fqn("packageName", "moduleName", "localName") val actual = reference("packageName", "moduleName", "localName", v1, v2, v3) assertTrue( - actual.satisfiesCaseOf { case ReferenceCase(fqName, typeParams) => + actual.satisfiesCaseOf { case Reference(_, fqName, typeParams) => fqName == fqn1 && typeParams.contains(v1) && typeParams.contains(v2) && typeParams.contains(v3) } ) diff --git a/morphir-ir/shared/src/test/scala/zio/morphir/ir/sdk/CommonSpec.scala b/morphir-ir/shared/src/test/scala/zio/morphir/ir/sdk/CommonSpec.scala index c86500aa..6bab999f 100644 --- a/morphir-ir/shared/src/test/scala/zio/morphir/ir/sdk/CommonSpec.scala +++ b/morphir-ir/shared/src/test/scala/zio/morphir/ir/sdk/CommonSpec.scala @@ -1,7 +1,7 @@ package zio.morphir.ir.sdk import zio.morphir.ir.{Gens, Name, Path} -import zio.morphir.ir.TypeModule.TypeCase +import zio.morphir.ir.TypeModule.Type import zio.morphir.testing.MorphirBaseSpec import zio.test._ @@ -17,7 +17,7 @@ object CommonSpec extends MorphirBaseSpec { test("should work as expected") { check(Gens.words) { s => val actual = Common.tVar(s) - assertTrue(actual.satisfiesCaseOf { case TypeCase.VariableCase(name) => + assertTrue(actual.satisfiesCaseOf { case Type.Variable(_, name) => name == Name.fromString(s) }) } diff --git a/morphir-json/shared/src/main/scala/zio/morphir/json/Decoders.scala b/morphir-json/shared/src/main/scala/zio/morphir/json/Decoders.scala deleted file mode 100644 index 4184c4f8..00000000 --- a/morphir-json/shared/src/main/scala/zio/morphir/json/Decoders.scala +++ /dev/null @@ -1,61 +0,0 @@ -package zio.morphir.json - -import zio.json._ -import zio.morphir.ir._ -import zio.morphir.ir.AccessControlled.Access._ -import zio.morphir.ir.Literal -import zio.morphir.ir.TypeModule._ - -object Decoders { - trait MorphirJsonCodecV1 { - implicit val unitDecoder: JsonDecoder[Unit] = JsonDecoder.list[String].mapOrFail { - case a if a.isEmpty => Right(()) - case a => Left(s"Expected empty list, got [${a.mkString(", ")}]") - } - implicit val nameDecoder: JsonDecoder[Name] = JsonDecoder.list[String].map(Name.fromList) - implicit val pathDecoder: JsonDecoder[Path] = JsonDecoder.list[Name].map(Path.fromList) - implicit val modulePathDecoder: JsonDecoder[ModulePath] = pathDecoder.map(ModulePath(_)) - implicit val packageNameDecoder: JsonDecoder[PackageName] = pathDecoder.map(PackageName(_)) - implicit val qNameDecoder: JsonDecoder[QName] = JsonDecoder.tuple2[Path, Name].map(QName.fromTuple) - implicit val fqNameDecoder: JsonDecoder[FQName] = - JsonDecoder.tuple3[PackageName, ModulePath, Name].map(x => FQName(x._1, x._2, x._3)) - - implicit val moduleNameDecoder: JsonDecoder[ModuleModule.ModuleName] = - JsonDecoder.tuple2[Path, Name].map(x => ModuleModule.ModuleName(x._1, x._2)) - - implicit def literalBoolDecoder: JsonDecoder[Literal.Bool] = - JsonDecoder.tuple2[String, Boolean].map(x => Literal.Bool(x._2)) - - implicit def literalCharDecoder: JsonDecoder[Literal.Char] = - JsonDecoder.tuple2[String, Char].map(x => Literal.Char(x._2)) - - implicit def literalFloatDecoder: JsonDecoder[Literal.Float] = - JsonDecoder.tuple2[String, java.math.BigDecimal].map(x => Literal.Float(x._2)) - - implicit def literalStringDecoder: JsonDecoder[Literal.String] = - JsonDecoder.tuple2[String, String].map(x => Literal.String(x._2)) - - implicit def literalWholeNumberDecoder: JsonDecoder[Literal.WholeNumber] = - JsonDecoder.tuple2[String, java.math.BigInteger].map(x => Literal.WholeNumber(x._2)) - - implicit def fieldDecoder[A](implicit decoder: JsonDecoder[A]): JsonDecoder[Field[A]] = - JsonDecoder.tuple2[Name, A].map(x => Field(x._1, x._2)) - - implicit def documentedDecoder[A](implicit valueDecoder: JsonDecoder[A]): JsonDecoder[Documented[A]] = - JsonDecoder.tuple2[String, A].map(x => Documented(x._1, x._2)) - - implicit def accessControlledDecoder[A](implicit encoder: JsonDecoder[A]): JsonDecoder[AccessControlled[A]] = { - JsonDecoder.tuple2[String, A].map { x => - AccessControlled( - x._1 match { - case "public" => Public - case "private" => Private - }, - x._2 - ) - } - } - } - - object MorphirJsonCodecV1 extends MorphirJsonCodecV1 -} diff --git a/morphir-json/shared/src/main/scala/zio/morphir/json/Encoders.scala b/morphir-json/shared/src/main/scala/zio/morphir/json/Encoders.scala deleted file mode 100644 index 7806fc82..00000000 --- a/morphir-json/shared/src/main/scala/zio/morphir/json/Encoders.scala +++ /dev/null @@ -1,393 +0,0 @@ -package zio.morphir.json - -import zio._ -import zio.json._ -import zio.json.ast.Json -import zio.morphir.ir._ -import zio.morphir.ir.AccessControlled.Access._ -import zio.morphir.ir.Literal -import zio.morphir.ir.ValueModule.{Value, ValueCase} -import zio.morphir.ir.TypeModule._ -import zio.morphir.ir.TypeModule.Definition._ -import zio.morphir.ir.TypeModule.Specification._ - -object Encoders { - trait MorphirJsonCodecV1 { - // NOTE: We will want to create JSON encoders which follow the format in the morphir-elm project - implicit val unitEncoder: JsonEncoder[Unit] = JsonEncoder.list[String].contramap(_ => List.empty[String]) - implicit val nameEncoder: JsonEncoder[Name] = JsonEncoder.list[String].contramap(name => name.toList) - implicit val pathEncoder: JsonEncoder[Path] = JsonEncoder.list[Name].contramap(path => path.segments.toList) - implicit val modulePathEncoder: JsonEncoder[ModulePath] = pathEncoder.contramap(_.toPath) - implicit val packageNameEncoder: JsonEncoder[PackageName] = pathEncoder.contramap(_.toPath) - implicit val qNameEncoder: JsonEncoder[QName] = - Json.encoder.contramap[QName](qName => - Json.Arr(toJsonAstOrThrow(qName.modulePath), toJsonAstOrThrow(qName.localName)) - ) - - implicit val fqNameEncoder: JsonEncoder[FQName] = - Json.encoder.contramap[FQName](fqName => - Json.Arr( - toJsonAstOrThrow(fqName.packagePath), - toJsonAstOrThrow(fqName.modulePath), - toJsonAstOrThrow(fqName.localName) - ) - ) - - implicit val moduleNameEncoder: JsonEncoder[ModuleModule.ModuleName] = - Json.encoder.contramap[ModuleModule.ModuleName](moduleName => - Json.Arr(toJsonAstOrThrow(moduleName.namespace), toJsonAstOrThrow(moduleName.localName)) - ) - - implicit def fieldEncoder[A](implicit encoder: JsonEncoder[A]): JsonEncoder[Field[A]] = - Json.encoder.contramap[Field[A]](field => - Json.Arr(toJsonAstOrThrow(field.name), toJsonAstOrThrow(field.fieldType)) - ) - - implicit def literalBoolEncoder: JsonEncoder[Literal.Bool] = Json.encoder.contramap[Literal.Bool] { literal => - Json.Arr(Json.Str("bool_literal"), Json.Bool(literal.value)) - } - implicit def literalCharEncoder: JsonEncoder[Literal.Char] = Json.encoder.contramap[Literal.Char] { literal => - Json.Arr(Json.Str("char_literal"), Json.Str(literal.value.toString)) - } - implicit def literalFloatEncoder: JsonEncoder[Literal.Float] = Json.encoder.contramap[Literal.Float] { literal => - Json.Arr(Json.Str("float_literal"), Json.Num(literal.value)) - } - implicit def literalStringEncoder: JsonEncoder[Literal.String] = Json.encoder.contramap[Literal.String] { literal => - Json.Arr(Json.Str("string_literal"), Json.Str(literal.value)) - } - implicit def literalWholeNumberEncoder: JsonEncoder[Literal.WholeNumber] = - Json.encoder.contramap[Literal.WholeNumber] { literal => - Json.Arr(Json.Str("int_literal"), Json.Num(new java.math.BigDecimal(literal.value))) - } - - implicit def literalEncoder[A]: JsonEncoder[Literal[A]] = Json.encoder.contramap[Literal[A]] { ??? } - - implicit def patternEncoder[Annotations](implicit - annotationsEncoder: JsonEncoder[Annotations] - ): JsonEncoder[Pattern[Annotations]] = Json.encoder.contramap[Pattern[Annotations]] { pattern => - pattern match { - case Pattern.AsPattern(pattern @ _, name, annotations) => - Json.Arr( - Json.Str("as_pattern"), - toJsonAstOrThrow(annotations), - ???, // toJsonAstOrThrow(pattern), - toJsonAstOrThrow(name) - ) - case Pattern.ConstructorPattern(constructorName, argumentPatterns @ _, annotations) => - Json.Arr( - Json.Str("constructor_pattern"), - toJsonAstOrThrow(annotations), - toJsonAstOrThrow(constructorName), - ??? // Json.Arr(argumentPatterns.map(toJsonAstOrThrow(_))) - ) - case Pattern.EmptyListPattern(annotations) => - Json.Arr(Json.Str("empty_list_pattern"), toJsonAstOrThrow(annotations)) - case Pattern.HeadTailPattern(headPattern @ _, tailPattern @ _, annotations) => - Json.Arr( - Json.Str("head_tail_pattern"), - toJsonAstOrThrow(annotations), - ???, // toJsonAstOrThrow(headPattern), - ??? // toJsonAstOrThrow(tailPattern) - ) - case Pattern.LiteralPattern(literal, annotations) => - Json.Arr(Json.Str("literal_pattern"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(literal)) - case Pattern.TuplePattern(patterns @ _, annotations) => - Json.Arr( - Json.Str("tuple_pattern"), - toJsonAstOrThrow(annotations), - ??? // Json.Arr(patterns.map(toJsonAstOrThrow(_))) - ) - case Pattern.UnitPattern(annotations) => - Json.Arr(Json.Str("unit_pattern"), toJsonAstOrThrow(annotations)) - case Pattern.WildcardPattern(annotations) => - Json.Arr(Json.Str("wildcard_pattern"), toJsonAstOrThrow(annotations)) - } - } - - implicit def constructorsEncoder[Annotations](implicit - annotationsEncoder: JsonEncoder[Annotations] - ): JsonEncoder[Constructors[Annotations]] = { - Json.encoder.contramap[Constructors[Annotations]] { ctors => - Json.Arr( - ( - toJsonAstOrThrow( - ctors.toMap.toList.map { case (ctorName: Name, ctorArgs: Chunk[(Name, Type[Annotations])]) => - ( - toJsonAstOrThrow(ctorName), - toJsonAstOrThrow( - ctorArgs.map { case (argName: Name, argType: Type[Annotations]) => - Json.Arr(toJsonAstOrThrow(argName), toJsonAstOrThrow(argType)) - } - ) - ) - } - ) - ) - ) - } - } - - implicit def accessControlledEncoder[A](implicit encoder: JsonEncoder[A]): JsonEncoder[AccessControlled[A]] = - Json.encoder.contramap[AccessControlled[A]] { accessControlled => - accessControlled.access match { - case Public => Json.Arr(Json.Str("public"), toJsonAstOrThrow(accessControlled.value)) - case Private => Json.Arr(Json.Str("private"), toJsonAstOrThrow(accessControlled.value)) - } - } - - implicit def typeDefinitionEncoder[Annotations](implicit - annotationsEncoder: JsonEncoder[Annotations] - ): JsonEncoder[TypeModule.Definition[Annotations]] = { - Json.encoder.contramap[TypeModule.Definition[Annotations]] { definition => - definition match { - case TypeAlias(typeParams, typeExp) => - Json.Arr(Json.Str("type_alias_definition"), toJsonAstOrThrow(typeParams), toJsonAstOrThrow(typeExp)) - case CustomType(typeParams, ctors) => { - Json.Arr(Json.Str("custom_type_definition"), toJsonAstOrThrow(typeParams), toJsonAstOrThrow(ctors)) - } - } - } - } - - implicit def typeSpecificationEncoder[Annotations](implicit - annotationsEncoder: JsonEncoder[Annotations] - ): JsonEncoder[TypeModule.Specification[Annotations]] = { - Json.encoder.contramap[TypeModule.Specification[Annotations]] { specification => - specification match { - case TypeAliasSpecification(typeParams, expr) => { - Json.Arr( - Json.Str("type_alias_specification"), - toJsonAstOrThrow(typeParams), - toJsonAstOrThrow(expr) - ) - } - case CustomTypeSpecification(typeParams, ctors) => { - Json.Arr( - Json.Str("custom_type_specification"), - toJsonAstOrThrow(typeParams), - toJsonAstOrThrow(ctors) - ) - } - case OpaqueTypeSpecification(typeParams) => { - Json.Arr( - Json.Str("opaque_type_specification"), - toJsonAstOrThrow(typeParams) - ) - } - } - } - } - - implicit def inputParameterEncoder[Annotations](implicit - annotationsEncoder: JsonEncoder[Annotations] - ): JsonEncoder[ValueModule.InputParameter[Annotations]] = - Json.encoder.contramap[ValueModule.InputParameter[Annotations]](ip => - Json.Arr(toJsonAstOrThrow(ip.name), toJsonAstOrThrow(ip.annotations), toJsonAstOrThrow(ip.tpe)) - ) - - implicit def valueDefinitionEncoder[Self, Annotations](implicit - annotationsEncoder: JsonEncoder[Annotations], - bodyEncoder: JsonEncoder[Self] - ): JsonEncoder[ValueModule.Definition[Self, Annotations]] = { - Json.encoder.contramap[ValueModule.Definition[Self, Annotations]] { definition => - Json.Obj( - "inputTypes" -> toJsonAstOrThrow(definition.inputTypes), - "outputType" -> toJsonAstOrThrow(definition.outputType), - "body" -> toJsonAstOrThrow(definition.body) - ) - } - } - - implicit def valueSpecificationEncoder[Annotations](implicit - annotationsEncoder: JsonEncoder[Annotations] - ): JsonEncoder[ValueModule.Specification[Annotations]] = { - Json.encoder.contramap[ValueModule.Specification[Annotations]] { specification => - Json.Obj( - "inputs" -> toJsonAstOrThrow(specification.inputs), - "outputs" -> toJsonAstOrThrow(specification.output) - ) - } - } - - implicit def valueEncoder[Annotations](implicit - annotationsEncoder: JsonEncoder[Annotations] - ): JsonEncoder[Value[Annotations]] = { - Json.encoder.contramap[Value[Annotations]] { value => - value.foldAnnotated[Json] { - case (ValueCase.UnitCase, annotations) => - Json.Arr(Json.Str("unit"), toJsonAstOrThrow(annotations)) - case (ValueCase.RecordCase(fields), annotations) => - Json.Arr(Json.Str("record"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(fields)) - case (ValueCase.LiteralCase(literal), annotations) => - Json.Arr(Json.Str("literal"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(literal)) - case (ValueCase.ConstructorCase(name), annotations) => - Json.Arr(Json.Str("constructor"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(name)) - case (ValueCase.ReferenceCase(name), annotations) => - Json.Arr(Json.Str("reference"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(name)) - case (ValueCase.VariableCase(name), annotations) => - Json.Arr(Json.Str("variable"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(name)) - case (ValueCase.TupleCase(elements), annotations) => - Json.Arr(Json.Str("tuple"), toJsonAstOrThrow(annotations), Json.Arr(elements)) - case (ValueCase.ListCase(elements), annotations) => - Json.Arr(Json.Str("list"), toJsonAstOrThrow(annotations), Json.Arr(elements)) - case (ValueCase.FieldCase(target, name), annotations) => - Json.Arr(Json.Str("field"), toJsonAstOrThrow(annotations), target, toJsonAstOrThrow(name)) - case (ValueCase.FieldFunctionCase(name), annotations) => - Json.Arr(Json.Str("field_function"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(name)) - case (ValueCase.ApplyCase(function, arguments), annotations) => - Json.Arr(Json.Str("apply"), toJsonAstOrThrow(annotations), function, Json.Arr(arguments)) - case (ValueCase.LambdaCase(argumentPattern @ _, body), annotations) => - Json.Arr(Json.Str("lambda"), toJsonAstOrThrow(annotations), ???, body) - case (ValueCase.LetDefinitionCase(valueName, valueDefinition @ _, inValue), annotations) => - Json.Arr( - Json.Str("let_definition"), - toJsonAstOrThrow(annotations), - toJsonAstOrThrow(valueName), - ???, - inValue - ) - case (ValueCase.LetRecursionCase(valueDefinitions @ _, inValue), annotations) => - Json.Arr(Json.Str("let_recursion"), toJsonAstOrThrow(annotations), ???, inValue) - case (ValueCase.DestructureCase(pattern @ _, valueToDestruct, inValue), annotations) => - Json.Arr(Json.Str("destructure"), toJsonAstOrThrow(annotations), ???, valueToDestruct, inValue) - case (ValueCase.IfThenElseCase(condition, thenBranch, elseBranch), annotations) => - Json.Arr(Json.Str("if_then_else"), toJsonAstOrThrow(annotations), condition, thenBranch, elseBranch) - case (ValueCase.PatternMatchCase(branchOutOn, cases @ _), annotations) => - Json.Arr(Json.Str("pattern_match"), toJsonAstOrThrow(annotations), branchOutOn, ???) - case (ValueCase.UpdateRecordCase(valueToUpdate, fieldsToUpdate), annotations) => - Json.Arr( - Json.Str("update_record"), - toJsonAstOrThrow(annotations), - valueToUpdate, - toJsonAstOrThrow(fieldsToUpdate) - ) - case (ValueCase.NativeApplyCase(nativeFunction @ _, arguments), annotations) => - Json.Arr(Json.Str("apply"), toJsonAstOrThrow(annotations), ???, Json.Arr(arguments)) - } - } - } - - // sealed trait Annotation - // } - // object Annotation { - // XYZ extends Annotation - // } - // } - - // A type level map where all entries in the map are guaranteed to have an - // instance of the type class TypeClass - - // Map[TypeTag, (Implementation, TypeClass)] - sealed trait ZEnvironmentSubset[+R, TypeClass[_]] { - def unsafeMap: Map[Tag[_], (Any, Any)] - def get[A >: R]: A - def instance[A >: R]: TypeClass[A] - } - - // the typeclass with no capabilities that exists for every data type - // trait AnyF[_] - - // type ZEnvironmentUnconstrained[+R] = ZEnvironmentSubset[R, AnyF] - - // def encodeEnvironment[R](environment: ZEnvironmentSubset[R, JsonEncoder]): Json = { - // environment.unsafeMap.map { case (tag, (value, encoder)) => - // ??? // Json.Arr(tag.toJsonAST.right.get, encoder.encode(value)) - // } // Iterable[JSON] --> Json.Arr - // ??? - // } - - implicit def typeEncoder[Annotations](implicit - annotationsEncoder: JsonEncoder[Annotations] - ): JsonEncoder[Type[Annotations]] = - Json.encoder.contramap[Type[Annotations]] { tpe => - tpe.foldAttributed[Json] { - case (TypeCase.ExtensibleRecordCase(name, fields), annotations) => - Json.Arr( - Json.Str("extensible_record"), - toJsonAstOrThrow(annotations), - toJsonAstOrThrow(name), - Json.Arr(fields.map(toJsonAstOrThrow(_))) - ) - case (TypeCase.FunctionCase(paramTypes, returnType), annotations) => - Json.Arr(Json.Str("function"), toJsonAstOrThrow(annotations), Json.Arr(paramTypes), returnType) - case (TypeCase.RecordCase(fields), annotations) => - Json.Arr(Json.Str("record"), toJsonAstOrThrow(annotations), Json.Arr(fields.map(toJsonAstOrThrow(_)))) - case (TypeCase.ReferenceCase(typeName, typeParameters), annotations) => - Json.Arr( - Json.Str("reference"), - toJsonAstOrThrow(annotations), - toJsonAstOrThrow(typeName), - Json.Arr(typeParameters) - ) - case (TypeCase.TupleCase(items), annotations) => - Json.Arr(Json.Str("tuple"), toJsonAstOrThrow(annotations), Json.Arr(items)) - case (TypeCase.VariableCase(name), annotations) => - Json.Arr(Json.Str("variable"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(name)) - case (TypeCase.UnitCase, annotations) => Json.Arr(Json.Str("unit"), toJsonAstOrThrow(annotations)) - } - } - - implicit def documentedEncoder[A](implicit valueEncoder: JsonEncoder[A]): JsonEncoder[Documented[A]] = { - Json.encoder.contramap[Documented[A]] { documented => - Json.Arr(Json.Str(documented.doc), toJsonAstOrThrow(documented.value)) - } - } - - implicit def moduleSpecificationEncoder[Annotations](implicit - annotationsEncoder: JsonEncoder[Annotations] - ): JsonEncoder[ModuleModule.Specification[Annotations]] = { - Json.encoder.contramap[ModuleModule.Specification[Annotations]] { specification => - Json.Obj( - "types" -> toJsonAstOrThrow(specification.types.toList), - "values" -> toJsonAstOrThrow(specification.values.toList) - ) - } - } - - implicit def moduleDefinitionEncoder[Annotations](implicit - annotationsEncoder: JsonEncoder[Annotations] - ): JsonEncoder[ModuleModule.Definition[Annotations]] = { - Json.encoder.contramap[ModuleModule.Definition[Annotations]] { definition => - Json.Obj( - "types" -> toJsonAstOrThrow(definition.types.toList), - "values" -> toJsonAstOrThrow(definition.values.toList) - ) - } - } - - implicit def packageSpecificationEncoder[Annotations](implicit - annotationsEncoder: JsonEncoder[Annotations] - ): JsonEncoder[PackageModule.Specification[Annotations]] = { - Json.encoder.contramap[PackageModule.Specification[Annotations]] { specification => - Json.Obj( - "modules" -> toJsonAstOrThrow(specification.modules.toList.map { case (name, moduleSpecification) => - Json.Obj( - "name" -> toJsonAstOrThrow(name), - "spec" -> toJsonAstOrThrow(moduleSpecification) - ) - }) - ) - } - } - - implicit def packageDefinitionEncoder[Annotations](implicit - annotationsEncoder: JsonEncoder[Annotations] - ): JsonEncoder[PackageModule.Definition[Annotations]] = { - Json.encoder.contramap[PackageModule.Definition[Annotations]] { definition => - Json.Obj( - "modules" -> toJsonAstOrThrow(definition.modules.toList.map { case (name, moduleSpecification) => - Json.Obj( - "name" -> toJsonAstOrThrow(name), - "def" -> toJsonAstOrThrow(moduleSpecification) - ) - }) - ) - } - } - - private def toJsonAstOrThrow[A](a: A)(implicit encoder: JsonEncoder[A]): Json = - a.toJsonAST.toOption.get - } - - object MorphirJsonCodecV1 extends MorphirJsonCodecV1 -} diff --git a/morphir-json/shared/src/main/scala/zio/morphir/json/MorphirJsonDecodingSupportV1.scala b/morphir-json/shared/src/main/scala/zio/morphir/json/MorphirJsonDecodingSupportV1.scala new file mode 100644 index 00000000..16684ae7 --- /dev/null +++ b/morphir-json/shared/src/main/scala/zio/morphir/json/MorphirJsonDecodingSupportV1.scala @@ -0,0 +1,59 @@ +package zio.morphir.json + +import zio.json._ +import zio.morphir.ir._ +import zio.morphir.ir.AccessControlled.Access._ +import zio.morphir.ir.Literal +import zio.morphir.ir.TypeModule.Field + +trait MorphirJsonDecodingSupportV1 { + implicit val unitDecoder: JsonDecoder[Unit] = JsonDecoder.list[String].mapOrFail { + case a if a.isEmpty => Right(()) + case a => Left(s"Expected empty list, got [${a.mkString(", ")}]") + } + implicit val nameDecoder: JsonDecoder[Name] = JsonDecoder.list[String].map(Name.fromList) + implicit val pathDecoder: JsonDecoder[Path] = JsonDecoder.list[Name].map(Path.fromList) + implicit val modulePathDecoder: JsonDecoder[ModulePath] = pathDecoder.map(ModulePath(_)) + implicit val packageNameDecoder: JsonDecoder[PackageName] = pathDecoder.map(PackageName(_)) + implicit val qNameDecoder: JsonDecoder[QName] = JsonDecoder.tuple2[Path, Name].map(QName.fromTuple) + implicit val fqNameDecoder: JsonDecoder[FQName] = + JsonDecoder.tuple3[PackageName, ModulePath, Name].map(x => FQName(x._1, x._2, x._3)) + + implicit val moduleNameDecoder: JsonDecoder[ModuleModule.ModuleName] = + JsonDecoder.tuple2[Path, Name].map(x => ModuleModule.ModuleName(x._1, x._2)) + + implicit def literalBoolDecoder: JsonDecoder[Literal.Bool] = + JsonDecoder.tuple2[String, Boolean].map(x => Literal.Bool(x._2)) + + implicit def literalCharDecoder: JsonDecoder[Literal.Char] = + JsonDecoder.tuple2[String, Char].map(x => Literal.Char(x._2)) + + implicit def literalFloatDecoder: JsonDecoder[Literal.Float] = + JsonDecoder.tuple2[String, java.math.BigDecimal].map(x => Literal.Float(x._2)) + + implicit def literalStringDecoder: JsonDecoder[Literal.String] = + JsonDecoder.tuple2[String, String].map(x => Literal.String(x._2)) + + implicit def literalWholeNumberDecoder: JsonDecoder[Literal.WholeNumber] = + JsonDecoder.tuple2[String, java.math.BigInteger].map(x => Literal.WholeNumber(x._2)) + + implicit def fieldDecoder[A](implicit decoder: JsonDecoder[A]): JsonDecoder[Field[A]] = + JsonDecoder.tuple2[Name, A].map(x => Field(x._1, x._2)) + + implicit def documentedDecoder[A](implicit valueDecoder: JsonDecoder[A]): JsonDecoder[Documented[A]] = + JsonDecoder.tuple2[String, A].map(x => Documented(x._1, x._2)) + + implicit def accessControlledDecoder[A](implicit encoder: JsonDecoder[A]): JsonDecoder[AccessControlled[A]] = { + JsonDecoder.tuple2[String, A].map { x => + AccessControlled( + x._1 match { + case "public" => Public + case "private" => Private + }, + x._2 + ) + } + } +} + +object MorphirJsonDecodingSupportV1 extends MorphirJsonDecodingSupportV1 diff --git a/morphir-json/shared/src/main/scala/zio/morphir/json/MorphirJsonEncodingSupportV1.scala b/morphir-json/shared/src/main/scala/zio/morphir/json/MorphirJsonEncodingSupportV1.scala new file mode 100644 index 00000000..7e1099a8 --- /dev/null +++ b/morphir-json/shared/src/main/scala/zio/morphir/json/MorphirJsonEncodingSupportV1.scala @@ -0,0 +1,385 @@ +package zio.morphir.json + +import zio._ +import zio.json._ +import zio.json.ast.Json +import zio.morphir.ir._ +import zio.morphir.ir.AccessControlled.Access._ +import zio.morphir.ir.Literal +import zio.morphir.ir.ValueModule.{Value, ValueCase} +import zio.morphir.ir.TypeModule._ +import zio.morphir.ir.TypeModule.Definition._ +import zio.morphir.ir.TypeModule.Specification._ +import zio.json.internal.Write +import zio.morphir.ir.TypeModule.Type.{Record, Reference, ExtensibleRecord, Variable, Tuple} + +trait MorphirJsonEncodingSupportV1 { + // NOTE: We will want to create JSON encoders which follow the format in the morphir-elm project + implicit val unitEncoder: JsonEncoder[Unit] = JsonEncoder.list[String].contramap(_ => List.empty[String]) + implicit val nameEncoder: JsonEncoder[Name] = JsonEncoder.list[String].contramap(name => name.toList) + implicit val pathEncoder: JsonEncoder[Path] = JsonEncoder.list[Name].contramap(path => path.segments.toList) + implicit val modulePathEncoder: JsonEncoder[ModulePath] = pathEncoder.contramap(_.toPath) + implicit val packageNameEncoder: JsonEncoder[PackageName] = pathEncoder.contramap(_.toPath) + implicit val qNameEncoder: JsonEncoder[QName] = + Json.encoder.contramap[QName](qName => + Json.Arr(toJsonAstOrThrow(qName.modulePath), toJsonAstOrThrow(qName.localName)) + ) + + implicit val fqNameEncoder: JsonEncoder[FQName] = + Json.encoder.contramap[FQName](fqName => + Json.Arr( + toJsonAstOrThrow(fqName.packagePath), + toJsonAstOrThrow(fqName.modulePath), + toJsonAstOrThrow(fqName.localName) + ) + ) + + implicit val moduleNameEncoder: JsonEncoder[ModuleModule.ModuleName] = + Json.encoder.contramap[ModuleModule.ModuleName](moduleName => + Json.Arr(toJsonAstOrThrow(moduleName.namespace), toJsonAstOrThrow(moduleName.localName)) + ) + + implicit def fieldEncoder[A](implicit encoder: JsonEncoder[A]): JsonEncoder[Field[A]] = + Json.encoder.contramap[Field[A]](field => Json.Arr(toJsonAstOrThrow(field.name), toJsonAstOrThrow(field.fieldType))) + + implicit def literalBoolEncoder: JsonEncoder[Literal.Bool] = Json.encoder.contramap[Literal.Bool] { literal => + Json.Arr(Json.Str("bool_literal"), Json.Bool(literal.value)) + } + implicit def literalCharEncoder: JsonEncoder[Literal.Char] = Json.encoder.contramap[Literal.Char] { literal => + Json.Arr(Json.Str("char_literal"), Json.Str(literal.value.toString)) + } + implicit def literalFloatEncoder: JsonEncoder[Literal.Float] = Json.encoder.contramap[Literal.Float] { literal => + Json.Arr(Json.Str("float_literal"), Json.Num(literal.value)) + } + implicit def literalStringEncoder: JsonEncoder[Literal.String] = Json.encoder.contramap[Literal.String] { literal => + Json.Arr(Json.Str("string_literal"), Json.Str(literal.value)) + } + implicit def literalWholeNumberEncoder: JsonEncoder[Literal.WholeNumber] = + Json.encoder.contramap[Literal.WholeNumber] { literal => + Json.Arr(Json.Str("int_literal"), Json.Num(new java.math.BigDecimal(literal.value))) + } + + implicit def literalEncoder[A]: JsonEncoder[Literal[A]] = Json.encoder.contramap[Literal[A]] { ??? } + + implicit def patternEncoder[Annotations](implicit + annotationsEncoder: JsonEncoder[Annotations] + ): JsonEncoder[Pattern[Annotations]] = Json.encoder.contramap[Pattern[Annotations]] { pattern => + pattern match { + case Pattern.AsPattern(pattern @ _, name, annotations) => + Json.Arr( + Json.Str("as_pattern"), + toJsonAstOrThrow(annotations), + ???, // toJsonAstOrThrow(pattern), + toJsonAstOrThrow(name) + ) + case Pattern.ConstructorPattern(constructorName, argumentPatterns @ _, annotations) => + Json.Arr( + Json.Str("constructor_pattern"), + toJsonAstOrThrow(annotations), + toJsonAstOrThrow(constructorName), + ??? // Json.Arr(argumentPatterns.map(toJsonAstOrThrow(_))) + ) + case Pattern.EmptyListPattern(annotations) => + Json.Arr(Json.Str("empty_list_pattern"), toJsonAstOrThrow(annotations)) + case Pattern.HeadTailPattern(headPattern @ _, tailPattern @ _, annotations) => + Json.Arr( + Json.Str("head_tail_pattern"), + toJsonAstOrThrow(annotations), + ???, // toJsonAstOrThrow(headPattern), + ??? // toJsonAstOrThrow(tailPattern) + ) + case Pattern.LiteralPattern(literal, annotations) => + Json.Arr(Json.Str("literal_pattern"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(literal)) + case Pattern.TuplePattern(patterns @ _, annotations) => + Json.Arr( + Json.Str("tuple_pattern"), + toJsonAstOrThrow(annotations), + ??? // Json.Arr(patterns.map(toJsonAstOrThrow(_))) + ) + case Pattern.UnitPattern(annotations) => + Json.Arr(Json.Str("unit_pattern"), toJsonAstOrThrow(annotations)) + case Pattern.WildcardPattern(annotations) => + Json.Arr(Json.Str("wildcard_pattern"), toJsonAstOrThrow(annotations)) + } + } + + implicit def constructorsEncoder[Annotations](implicit + annotationsEncoder: JsonEncoder[Annotations] + ): JsonEncoder[Constructors[Annotations]] = { + Json.encoder.contramap[Constructors[Annotations]] { ctors => + Json.Arr( + ( + toJsonAstOrThrow( + ctors.toMap.toList.map { case (ctorName: Name, ctorArgs: Chunk[(Name, Type[Annotations])]) => + ( + toJsonAstOrThrow(ctorName), + toJsonAstOrThrow( + ctorArgs.map { case (argName: Name, argType: Type[Annotations]) => + Json.Arr(toJsonAstOrThrow(argName), toJsonAstOrThrow(argType)) + } + ) + ) + } + ) + ) + ) + } + } + + implicit def accessControlledEncoder[A](implicit encoder: JsonEncoder[A]): JsonEncoder[AccessControlled[A]] = + Json.encoder.contramap[AccessControlled[A]] { accessControlled => + accessControlled.access match { + case Public => Json.Arr(Json.Str("public"), toJsonAstOrThrow(accessControlled.value)) + case Private => Json.Arr(Json.Str("private"), toJsonAstOrThrow(accessControlled.value)) + } + } + + implicit def typeDefinitionEncoder[Annotations](implicit + annotationsEncoder: JsonEncoder[Annotations] + ): JsonEncoder[TypeModule.Definition[Annotations]] = { + Json.encoder.contramap[TypeModule.Definition[Annotations]] { definition => + definition match { + case TypeAlias(typeParams, typeExp) => + Json.Arr(Json.Str("type_alias_definition"), toJsonAstOrThrow(typeParams), toJsonAstOrThrow(typeExp)) + case CustomType(typeParams, ctors) => { + Json.Arr(Json.Str("custom_type_definition"), toJsonAstOrThrow(typeParams), toJsonAstOrThrow(ctors)) + } + } + } + } + + implicit def typeSpecificationEncoder[Annotations](implicit + annotationsEncoder: JsonEncoder[Annotations] + ): JsonEncoder[TypeModule.Specification[Annotations]] = { + Json.encoder.contramap[TypeModule.Specification[Annotations]] { specification => + specification match { + case TypeAliasSpecification(typeParams, expr) => { + Json.Arr( + Json.Str("type_alias_specification"), + toJsonAstOrThrow(typeParams), + toJsonAstOrThrow(expr) + ) + } + case CustomTypeSpecification(typeParams, ctors) => { + Json.Arr( + Json.Str("custom_type_specification"), + toJsonAstOrThrow(typeParams), + toJsonAstOrThrow(ctors) + ) + } + case OpaqueTypeSpecification(typeParams) => { + Json.Arr( + Json.Str("opaque_type_specification"), + toJsonAstOrThrow(typeParams) + ) + } + } + } + } + + implicit def inputParameterEncoder[Annotations](implicit + annotationsEncoder: JsonEncoder[Annotations] + ): JsonEncoder[ValueModule.InputParameter[Annotations]] = + Json.encoder.contramap[ValueModule.InputParameter[Annotations]](ip => + Json.Arr(toJsonAstOrThrow(ip.name), toJsonAstOrThrow(ip.annotations), toJsonAstOrThrow(ip.tpe)) + ) + + implicit def valueDefinitionEncoder[Self, Annotations](implicit + annotationsEncoder: JsonEncoder[Annotations], + bodyEncoder: JsonEncoder[Self] + ): JsonEncoder[ValueModule.Definition[Self, Annotations]] = { + Json.encoder.contramap[ValueModule.Definition[Self, Annotations]] { definition => + Json.Obj( + "inputTypes" -> toJsonAstOrThrow(definition.inputTypes), + "outputType" -> toJsonAstOrThrow(definition.outputType), + "body" -> toJsonAstOrThrow(definition.body) + ) + } + } + + implicit def valueSpecificationEncoder[Annotations](implicit + annotationsEncoder: JsonEncoder[Annotations] + ): JsonEncoder[ValueModule.Specification[Annotations]] = { + Json.encoder.contramap[ValueModule.Specification[Annotations]] { specification => + Json.Obj( + "inputs" -> toJsonAstOrThrow(specification.inputs), + "outputs" -> toJsonAstOrThrow(specification.output) + ) + } + } + + implicit def valueEncoder[Annotations](implicit + annotationsEncoder: JsonEncoder[Annotations] + ): JsonEncoder[Value[Annotations]] = { + Json.encoder.contramap[Value[Annotations]] { value => + value.foldAnnotated[Json] { + case (ValueCase.UnitCase, annotations) => + Json.Arr(Json.Str("unit"), toJsonAstOrThrow(annotations)) + case (ValueCase.RecordCase(fields), annotations) => + Json.Arr(Json.Str("record"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(fields)) + case (ValueCase.LiteralCase(literal), annotations) => + Json.Arr(Json.Str("literal"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(literal)) + case (ValueCase.ConstructorCase(name), annotations) => + Json.Arr(Json.Str("constructor"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(name)) + case (ValueCase.ReferenceCase(name), annotations) => + Json.Arr(Json.Str("reference"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(name)) + case (ValueCase.VariableCase(name), annotations) => + Json.Arr(Json.Str("variable"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(name)) + case (ValueCase.TupleCase(elements), annotations) => + Json.Arr(Json.Str("tuple"), toJsonAstOrThrow(annotations), Json.Arr(elements)) + case (ValueCase.ListCase(elements), annotations) => + Json.Arr(Json.Str("list"), toJsonAstOrThrow(annotations), Json.Arr(elements)) + case (ValueCase.FieldCase(target, name), annotations) => + Json.Arr(Json.Str("field"), toJsonAstOrThrow(annotations), target, toJsonAstOrThrow(name)) + case (ValueCase.FieldFunctionCase(name), annotations) => + Json.Arr(Json.Str("field_function"), toJsonAstOrThrow(annotations), toJsonAstOrThrow(name)) + case (ValueCase.ApplyCase(function, arguments), annotations) => + Json.Arr(Json.Str("apply"), toJsonAstOrThrow(annotations), function, Json.Arr(arguments)) + case (ValueCase.LambdaCase(argumentPattern @ _, body), annotations) => + Json.Arr(Json.Str("lambda"), toJsonAstOrThrow(annotations), ???, body) + case (ValueCase.LetDefinitionCase(valueName, valueDefinition @ _, inValue), annotations) => + Json.Arr( + Json.Str("let_definition"), + toJsonAstOrThrow(annotations), + toJsonAstOrThrow(valueName), + ???, + inValue + ) + case (ValueCase.LetRecursionCase(valueDefinitions @ _, inValue), annotations) => + Json.Arr(Json.Str("let_recursion"), toJsonAstOrThrow(annotations), ???, inValue) + case (ValueCase.DestructureCase(pattern @ _, valueToDestruct, inValue), annotations) => + Json.Arr(Json.Str("destructure"), toJsonAstOrThrow(annotations), ???, valueToDestruct, inValue) + case (ValueCase.IfThenElseCase(condition, thenBranch, elseBranch), annotations) => + Json.Arr(Json.Str("if_then_else"), toJsonAstOrThrow(annotations), condition, thenBranch, elseBranch) + case (ValueCase.PatternMatchCase(branchOutOn, cases @ _), annotations) => + Json.Arr(Json.Str("pattern_match"), toJsonAstOrThrow(annotations), branchOutOn, ???) + case (ValueCase.UpdateRecordCase(valueToUpdate, fieldsToUpdate), annotations) => + Json.Arr( + Json.Str("update_record"), + toJsonAstOrThrow(annotations), + valueToUpdate, + toJsonAstOrThrow(fieldsToUpdate) + ) + case (ValueCase.NativeApplyCase(nativeFunction @ _, arguments), annotations) => + Json.Arr(Json.Str("apply"), toJsonAstOrThrow(annotations), ???, Json.Arr(arguments)) + } + } + } + + implicit def ExtensibleRecordTypeJsonEncoder[Attributes: JsonEncoder] + : JsonEncoder[Type.ExtensibleRecord[Attributes]] = + JsonEncoder.tuple4[String, Attributes, Name, Chunk[Field[Type[Attributes]]]].contramap { + case Type.ExtensibleRecord(attributes, name, fields) => ("extensible_record", attributes, name, fields) + } + + implicit def FunctionTypeJsonEncoder[Attributes: JsonEncoder]: JsonEncoder[Type.Function[Attributes]] = ??? + + implicit def RecordTypeJsonEncoder[Attributes: JsonEncoder]: JsonEncoder[Type.Record[Attributes]] = + JsonEncoder.tuple3[String, Attributes, Chunk[Field[Type[Attributes]]]].contramap { + case Type.Record(attributes, fields) => + ("record", attributes, fields) + } + + implicit def ReferenceTypeJsonEncoder[Attributes: JsonEncoder]: JsonEncoder[Type.Reference[Attributes]] = + JsonEncoder.tuple4[String, Attributes, FQName, Chunk[Type[Attributes]]].contramap { + case Type.Reference(attributes, name, typeParams) => + ("reference", attributes, name, typeParams) + } + + implicit def TupleTypeJsonEncoder[Attributes](implicit + attributesEncoder: JsonEncoder[Attributes], + typeEncoder: JsonEncoder[Type[Attributes]] + ): JsonEncoder[Type.Tuple[Attributes]] = + JsonEncoder.tuple3[String, Attributes, Chunk[Type[Attributes]]].contramap[Type.Tuple[Attributes]] { + case Type.Tuple(attributes, elements) => ("tuple", attributes, elements) + } + + implicit def UnitTypeJsonEncoder[Attributes: JsonEncoder]: JsonEncoder[Type.Unit[Attributes]] = + JsonEncoder.tuple2[String, Attributes].contramap[Type.Unit[Attributes]] { case Type.Unit(attributes) => + ("unit", attributes) + } + + implicit def VariableTypeJsonEncoder[Attributes: JsonEncoder]: JsonEncoder[Type.Variable[Attributes]] = + JsonEncoder.tuple3[String, Attributes, Name].contramap[Type.Variable[Attributes]] { + case Type.Variable(attributes, name) => ("variable", attributes, name) + } + implicit def typeEncoder[Attributes: JsonEncoder]: JsonEncoder[Type[Attributes]] = + new JsonEncoder[Type[Attributes]] { + def unsafeEncode(tpe: Type[Attributes], indent: Option[Int], out: Write): Unit = tpe match { + case t @ Record(_, _) => JsonEncoder[Type.Record[Attributes]].unsafeEncode(t, indent, out) + case t @ Reference(_, _, _) => + JsonEncoder[Type.Reference[Attributes]].unsafeEncode(t, indent, out) + case t @ Type.Unit(_) => + JsonEncoder[Type.Unit[Attributes]].unsafeEncode(t, indent, out) + case t @ Type.Function(_, _, _) => + JsonEncoder[Type.Function[Attributes]].unsafeEncode(t, indent, out) + case t @ ExtensibleRecord(_, _, _) => + JsonEncoder[Type.ExtensibleRecord[Attributes]].unsafeEncode(t, indent, out) + case t @ Variable(_, _) => JsonEncoder[Type.Variable[Attributes]].unsafeEncode(t, indent, out) + case t @ Tuple(_, _) => JsonEncoder[Type.Tuple[Attributes]].unsafeEncode(t, indent, out) + } + } + + implicit def documentedEncoder[A](implicit valueEncoder: JsonEncoder[A]): JsonEncoder[Documented[A]] = { + Json.encoder.contramap[Documented[A]] { documented => + Json.Arr(Json.Str(documented.doc), toJsonAstOrThrow(documented.value)) + } + } + + implicit def moduleSpecificationEncoder[Annotations](implicit + annotationsEncoder: JsonEncoder[Annotations] + ): JsonEncoder[ModuleModule.Specification[Annotations]] = { + Json.encoder.contramap[ModuleModule.Specification[Annotations]] { specification => + Json.Obj( + "types" -> toJsonAstOrThrow(specification.types.toList), + "values" -> toJsonAstOrThrow(specification.values.toList) + ) + } + } + + implicit def moduleDefinitionEncoder[Annotations](implicit + annotationsEncoder: JsonEncoder[Annotations] + ): JsonEncoder[ModuleModule.Definition[Annotations]] = { + Json.encoder.contramap[ModuleModule.Definition[Annotations]] { definition => + Json.Obj( + "types" -> toJsonAstOrThrow(definition.types.toList), + "values" -> toJsonAstOrThrow(definition.values.toList) + ) + } + } + + implicit def packageSpecificationEncoder[Annotations](implicit + annotationsEncoder: JsonEncoder[Annotations] + ): JsonEncoder[PackageModule.Specification[Annotations]] = { + Json.encoder.contramap[PackageModule.Specification[Annotations]] { specification => + Json.Obj( + "modules" -> toJsonAstOrThrow(specification.modules.toList.map { case (name, moduleSpecification) => + Json.Obj( + "name" -> toJsonAstOrThrow(name), + "spec" -> toJsonAstOrThrow(moduleSpecification) + ) + }) + ) + } + } + + implicit def packageDefinitionEncoder[Annotations](implicit + annotationsEncoder: JsonEncoder[Annotations] + ): JsonEncoder[PackageModule.Definition[Annotations]] = { + Json.encoder.contramap[PackageModule.Definition[Annotations]] { definition => + Json.Obj( + "modules" -> toJsonAstOrThrow(definition.modules.toList.map { case (name, moduleSpecification) => + Json.Obj( + "name" -> toJsonAstOrThrow(name), + "def" -> toJsonAstOrThrow(moduleSpecification) + ) + }) + ) + } + } + + private def toJsonAstOrThrow[A](a: A)(implicit encoder: JsonEncoder[A]): Json = + a.toJsonAST.toOption.get +} + +object MorphirJsonEncodingSupportV1 extends MorphirJsonEncodingSupportV1 diff --git a/morphir-json/shared/src/main/scala/zio/morphir/json/MorphirJsonSupportV1.scala b/morphir-json/shared/src/main/scala/zio/morphir/json/MorphirJsonSupportV1.scala new file mode 100644 index 00000000..26a2001d --- /dev/null +++ b/morphir-json/shared/src/main/scala/zio/morphir/json/MorphirJsonSupportV1.scala @@ -0,0 +1,4 @@ +package zio.morphir.json + +trait MorphirJsonSupportV1 extends MorphirJsonEncodingSupportV1 with MorphirJsonDecodingSupportV1 +object MorphirJsonSupportV1 extends MorphirJsonSupportV1 diff --git a/morphir-json/shared/src/test/scala/zio/morphir/json/DecodingSpec.scala b/morphir-json/shared/src/test/scala/zio/morphir/json/DecodingSpec.scala index 739795e2..8b4a7935 100644 --- a/morphir-json/shared/src/test/scala/zio/morphir/json/DecodingSpec.scala +++ b/morphir-json/shared/src/test/scala/zio/morphir/json/DecodingSpec.scala @@ -3,7 +3,7 @@ package zio.morphir.json import zio.json._ import zio.morphir.ir._ import zio.morphir.ir.TypeModule._ -import zio.morphir.json.Decoders.MorphirJsonCodecV1._ +import zio.morphir.json.MorphirJsonDecodingSupportV1._ import zio.test._ import zio.test.DefaultRunnableSpec diff --git a/morphir-json/shared/src/test/scala/zio/morphir/json/EncodingSpec.scala b/morphir-json/shared/src/test/scala/zio/morphir/json/EncodingSpec.scala index 5c35a7e9..64981cab 100644 --- a/morphir-json/shared/src/test/scala/zio/morphir/json/EncodingSpec.scala +++ b/morphir-json/shared/src/test/scala/zio/morphir/json/EncodingSpec.scala @@ -3,7 +3,7 @@ package zio.morphir.json import zio.json._ import zio.morphir.ir._ import zio.morphir.ir.TypeModule._ -import zio.morphir.json.Encoders.MorphirJsonCodecV1._ +import zio.morphir.json.MorphirJsonEncodingSupportV1._ import zio.test._ import zio.test.DefaultRunnableSpec import zio.Chunk