diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/dsl.scala b/morphir-ir/shared/src/main/scala/zio/morphir/dsl.scala new file mode 100644 index 00000000..e2e34746 --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/dsl.scala @@ -0,0 +1,3 @@ +package zio.morphir + +object Dsl extends syntax.ValueSyntax diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/Literal.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/Literal.scala index fbec2f30..b983ae3b 100644 --- a/morphir-ir/shared/src/main/scala/zio/morphir/ir/Literal.scala +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/Literal.scala @@ -4,6 +4,12 @@ sealed trait Literal[+A] { def value: A } object Literal { + def boolean(value: Boolean): Bool = Bool(value) + def int(value: Int): WholeNumber = WholeNumber(java.math.BigInteger.valueOf(value.toLong)) + def long(value: Long): WholeNumber = WholeNumber(java.math.BigInteger.valueOf(value)) + def string(value: java.lang.String): String = Literal.String(value) + def wholeNumber(value: java.math.BigInteger): WholeNumber = WholeNumber(value) + final case class Bool(value: scala.Boolean) extends Literal[scala.Boolean] final case class Char(value: scala.Char) extends Literal[scala.Char] final case class String(value: java.lang.String) extends Literal[java.lang.String] diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/Module.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/ModuleModule.scala similarity index 94% rename from morphir-ir/shared/src/main/scala/zio/morphir/ir/Module.scala rename to morphir-ir/shared/src/main/scala/zio/morphir/ir/ModuleModule.scala index 6aa372cd..f35a81b9 100644 --- a/morphir-ir/shared/src/main/scala/zio/morphir/ir/Module.scala +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/ModuleModule.scala @@ -1,6 +1,6 @@ package zio.morphir.ir -object Module { +object ModuleModule { type Definition[+Annotations] = MorphirIR.ModuleDefinition[Annotations] val Definition = MorphirIR.ModuleDefinition @@ -25,14 +25,14 @@ object Module { } trait ModuleSpecFor[A] { - import Module.* + import ModuleModule.* def module: ModuleName def spec: Specification[Any] } object ModuleSpecFor { - import Module.* + import ModuleModule.* /** Summon the module specification for the given module/type. */ def apply[A](implicit specFor: ModuleSpecFor[A]): ModuleSpecFor[A] = specFor diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/MorphirIR.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/MorphirIR.scala index 20b8a2dc..ff5c3455 100644 --- a/morphir-ir/shared/src/main/scala/zio/morphir/ir/MorphirIR.scala +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/MorphirIR.scala @@ -1,5 +1,6 @@ package zio.morphir.ir +import zio.morphir.ir.{Literal => Lit} import zio.morphir.ir.recursive.* import zio.prelude._ import zio.prelude.fx._ @@ -68,8 +69,9 @@ sealed trait MorphirIR[+Annotations] { self => c.inValue.fold(f) ) ) - case c @ ValueCase.ListCase(_) => f(ValueCase.ListCase(c.elements.map(_.fold(f)))) - case c @ ValueCase.LiteralCase(_) => f(c) + case c @ ValueCase.ListCase(_) => f(ValueCase.ListCase(c.elements.map(_.fold(f)))) + case c @ ValueCase.LiteralCase(_) => f(c) + case c @ ValueCase.NativeApplyCase(_, _) => f(ValueCase.NativeApplyCase(c.function, c.arguments.map(_.fold(f)))) case c @ ValueCase.PatternMatchCase(_, _) => f( ValueCase.PatternMatchCase( @@ -193,6 +195,14 @@ sealed trait MorphirIR[+Annotations] { self => object MorphirIR { + def apply( + caseValue0: MorphirIRCase[MorphirIR[Any]] + ): MorphirIR[Any] = + new MorphirIR[Any] { + def caseValue = caseValue0 + def annotations = ZEnvironment.empty + } + def apply[Annotations]( caseValue0: MorphirIRCase[MorphirIR[Annotations]], annotations0: ZEnvironment[Annotations] @@ -229,6 +239,7 @@ object MorphirIR { object Definition { object WithTypeParams { + def unapply[Annotations]( ir: MorphirIR[Annotations] ): Option[(List[Name], DefinitionCase[TypeTree[Annotations]])] = @@ -413,11 +424,84 @@ object MorphirIR { } object Value { + def apply( + caseValue0: ValueCase[Value[Any]] + ): Value[Any] = + new Value[Any] { + override def caseValue: ValueCase[Value[Any]] = caseValue0 + override def annotations: ZEnvironment[Any] = ZEnvironment.empty + } + + def apply[Annotations]( + caseValue0: ValueCase[Value[Annotations]], + annotations0: ZEnvironment[Annotations] + ): Value[Annotations] = + new Value[Annotations] { + override def caseValue: ValueCase[Value[Annotations]] = caseValue0 + override def annotations: ZEnvironment[Annotations] = annotations0 + } + + def field(name: Name)(record: Record[Any]): Field[Any] = Field(record, name, ZEnvironment.empty) + def patternMatch(scrutinee: Value[Any], cases: (Value[Any], Value[Any])*): PatternMatch[Any] = + PatternMatch(scrutinee, Chunk.fromIterable(cases), ZEnvironment.empty) + // def patternMatch[Annotations]( + // scrutinee: Value[Annotations], + // annotations: ZEnvironment[Annotations], + // cases: (Value[Annotations], Value[Annotations])* + // ): PatternMatch[Annotations] = + // PatternMatch(scrutinee, Chunk.fromIterable(cases), annotations) + + def record(fields: (Name, Value[Any])*): Record[Any] = + Record(Chunk.fromIterable(fields), ZEnvironment.empty) + + def wholeNumber(value: java.math.BigInteger): Literal[Any, java.math.BigInteger] = { + println("In Value.wholeNumber") + Literal(Lit.wholeNumber(value), ZEnvironment.empty) + } + + def string(value: String): Literal[Any, String] = { + println(s"In Value.string: $value") + val l = Literal(Lit.string(value), ZEnvironment.empty) + println(s"In Value.string: Literal: $l") + l + } + import ValueCase.* + + final case class Field[+Annotations]( + target: Value[Annotations], + name: Name, + annotations: ZEnvironment[Annotations] + ) extends Value[Annotations] { + override lazy val caseValue: ValueCase[Value[Annotations]] = FieldCase(target, name) + } + + final case class PatternMatch[+Annotations]( + scrutinee: Value[Annotations], + cases: Chunk[(Value[Annotations], Value[Annotations])], + annotations: ZEnvironment[Annotations] + ) extends Value[Annotations] { + override lazy val caseValue: ValueCase[Value[Annotations]] = PatternMatchCase(scrutinee, cases) + } + final case class Variable[+Annotations](name: Name, annotations: ZEnvironment[Annotations]) extends Value[Annotations] { override lazy val caseValue: VariableCase = VariableCase(name) } + + final case class Record[+Annotations]( + fields: Chunk[(Name, Value[Annotations])], + annotations: ZEnvironment[Annotations] + ) extends Value[Annotations] { + override lazy val caseValue: ValueCase[Value[Annotations]] = RecordCase(fields) + } + + final case class Literal[+Annotations, +V]( + value: zio.morphir.ir.Literal[V], + annotations: ZEnvironment[Annotations] + ) extends Value[Annotations] { + override lazy val caseValue: ValueCase[Value[Annotations]] = LiteralCase(value) + } } sealed trait Distribution[+Annotations] extends MorphirIR[Annotations] { self => diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/Name.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/Name.scala index 0541bf3c..b0e4d9dc 100644 --- a/morphir-ir/shared/src/main/scala/zio/morphir/ir/Name.scala +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/Name.scala @@ -93,7 +93,8 @@ object Name { /** * Creates a new name from a chunk of strings without checking. */ - private[ir] def unsafeMake(value: List[String]): Name = Name(value) + private[ir] def unsafeMake(value: List[String]): Name = Name(value) + private[ir] def unsafeMake(exactSegments: String*): Name = Name(exactSegments.toList) def toList(name: Name): List[String] = name.toList diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/NativeFunction.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/NativeFunction.scala new file mode 100644 index 00000000..ef50c93b --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/NativeFunction.scala @@ -0,0 +1,6 @@ +package zio.morphir.ir + +sealed trait NativeFunction {} +object NativeFunction { + case object Addition extends NativeFunction +} diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/Type.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/Type.scala index 37d1e8b8..13b247ed 100644 --- a/morphir-ir/shared/src/main/scala/zio/morphir/ir/Type.scala +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/Type.scala @@ -1,6 +1,5 @@ package zio.morphir.ir import zio.{Chunk, ZEnvironment} - object Type { type Type[+Annotations] = MorphirIR.Type[Annotations] val Type = MorphirIR.Type 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 new file mode 100644 index 00000000..d876d184 --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/TypeModule.scala @@ -0,0 +1,183 @@ +package zio.morphir.ir + +import zio.{Chunk, ZEnvironment} +import zio.prelude.* + +object TypeModule { + + sealed trait Type[+Annotations] { self => + // import TypeCase.* + + final def asType: Type[Annotations] = self + + def annotations: ZEnvironment[Annotations] + def caseValue: TypeCase[Type[Annotations]] + + def fold[Z](f: TypeCase[Z] => Z): Z = self.caseValue match { + case c @ TypeCase.ExtensibleRecordCase(_, _) => f(TypeCase.ExtensibleRecordCase(c.name, c.fields.map(_.fold(f)))) + case c @ TypeCase.FieldCase(_, _) => f(TypeCase.FieldCase(c.name, c.fieldType.fold(f))) + case c @ TypeCase.FunctionCase(_, _) => + f(TypeCase.FunctionCase(c.paramTypes.map(_.fold(f)), c.returnType.fold(f))) + case c @ TypeCase.RecordCase(_) => f(TypeCase.RecordCase(c.fields.map(_.fold(f)))) + case c @ TypeCase.ReferenceCase(_, _) => f(TypeCase.ReferenceCase(c.typeName, c.typeParams.map(_.fold(f)))) + case c @ TypeCase.TupleCase(_) => f(TypeCase.TupleCase(c.elementTypes.map(_.fold(f)))) + case _ @TypeCase.UnitCase => f(TypeCase.UnitCase) + case c @ TypeCase.VariableCase(_) => f(c) + } + + def transformDown[Annotations0 >: Annotations]( + f: Type[Annotations0] => Type[Annotations0] + ): Type[Annotations0] = { + def loop(recursive: Type[Annotations0]): Type[Annotations] = + Type(f(recursive).caseValue.map(loop), annotations) + loop(self) + } + } + + object Type { + import TypeCase.* + + def apply[Annotations]( + caseValue0: TypeCase[Type[Annotations]], + annotations0: ZEnvironment[Annotations] + ): Type[Annotations] = + new Type[Annotations] { + override def caseValue: TypeCase[Type[Annotations]] = caseValue0 + override def annotations: ZEnvironment[Annotations] = annotations0 + } + + def ref(name: FQName): Reference[Any] = Reference(name, Chunk.empty, ZEnvironment.empty) + + /** + * Creates a type variable with the given `name`. + */ + def variable(name: Name): Variable[Any] = Variable(name, ZEnvironment.empty) + def variable(name: String): Variable[Any] = variable(Name(name)) + val unit: Type[Any] = Unit(ZEnvironment.empty) + + final case class Unit[+Annotations](annotations: ZEnvironment[Annotations]) extends Type[Annotations] { + override val caseValue: TypeCase[Type[Annotations]] = UnitCase + } + + final case class Field[+Annotations]( + name: Name, + fieldType: Type[Annotations], + annotations: ZEnvironment[Annotations] + ) extends Type[Annotations] { + override lazy val caseValue: FieldCase[Type[Annotations]] = FieldCase(name, fieldType) + } + object Field { + + object Case { + def unapply[Annotations](field: Field[Annotations]): Option[FieldCase[Type[Annotations]]] = + Some(field.caseValue) + } + } + + final case class Reference[+Annotations]( + name: FQName, + typeParams: Chunk[Type[Annotations]], + annotations: ZEnvironment[Annotations] + ) extends Type[Annotations] { + override lazy val caseValue: ReferenceCase[Type[Annotations]] = ReferenceCase(name, typeParams) + } + + object Reference { + object Case { + def unapply[Annotations](reference: Reference[Annotations]): Option[ReferenceCase[Type[Annotations]]] = + Some(reference.caseValue) + } + } + + final case class Variable[+Annotations](name: Name, annotations: ZEnvironment[Annotations]) + extends Type[Annotations] { + override lazy val caseValue: VariableCase = VariableCase(name) + } + object Variable { + object Case { + def unapply[Annotations](variable: Variable[Annotations]): Option[VariableCase] = + Some(variable.caseValue) + } + } + } + + sealed trait TypeCase[+Self] { self => + import TypeCase.* + def map[B](f: Self => B): TypeCase[B] = self match { + case c @ ExtensibleRecordCase(_, _) => ExtensibleRecordCase(c.name, c.fields.map(f)) + case c @ FieldCase(_, _) => FieldCase(c.name, f(c.fieldType)) + 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(f)) + } + } + + object TypeCase { + final case class ExtensibleRecordCase[+Self](name: Name, fields: Chunk[Self]) extends TypeCase[Self] + final case class FunctionCase[+Self](paramTypes: List[Self], returnType: Self) extends TypeCase[Self] + final case class RecordCase[+Self](fields: Chunk[Self]) extends TypeCase[Self] + final case class ReferenceCase[+Self](typeName: FQName, typeParams: Chunk[Self]) extends TypeCase[Self] + final case class TupleCase[+Self](elementTypes: List[Self]) extends TypeCase[Self] + case object UnitCase extends TypeCase[Nothing] + final case class VariableCase(name: Name) extends TypeCase[Nothing] + final case class FieldCase[+Self](name: Name, fieldType: Self) extends TypeCase[Self] + + 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(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(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 + case FieldCase(name, fieldType) => + f(fieldType).map(fieldType => FieldCase(name, fieldType)) + } + } + } + + final case class TypeArg[+Annotations]( + name: Name, + tpe: Type[Annotations] + ) + + final case class Constructors[+Annotations](items: Map[Name, TypeArg[Annotations]]) + + sealed trait Specification[+Annotations] { + def annotations: ZEnvironment[Annotations] + } + + object Specification { + final case class TypeAliasSpecification[+Annotations]( + typeParams: Chunk[Name], + expr: Type[Annotations], + annotations: ZEnvironment[Annotations] + ) extends Specification[Annotations] + + final case class OpaqueTypeSpecification[+Annotations]( + typeParams: Chunk[Name], + annotations: ZEnvironment[Annotations] + ) extends Specification[Annotations] + + final case class CustomTypeSpecification[+Annotations]( + typeParams: Chunk[Name], + expr: Type[Annotations], + annotations: ZEnvironment[Annotations] + ) extends Specification[Annotations] + } +} diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/ValueModule.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/ValueModule.scala new file mode 100644 index 00000000..9d9ae7a3 --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/ValueModule.scala @@ -0,0 +1,251 @@ +package zio.morphir.ir + +import zio.morphir.ir.{Literal => Lit} +import zio.{Chunk, ZEnvironment} +import zio.prelude.* + +object ValueModule { + + sealed trait Value[+Annotations] { self => + import ValueCase.* + + def annotations: ZEnvironment[Annotations] + def caseValue: ValueCase[Value[Annotations]] + + def fold[Z](f: ValueCase[Z] => Z): Z = self.caseValue match { + + case c @ PatternCase.AsPatternCase(_, _) => f(PatternCase.AsPatternCase(c.pattern.fold(f), c.name)) + case c @ PatternCase.ConstructorPatternCase(_, _) => + f(PatternCase.ConstructorPatternCase(c.constructorName, c.argumentPatterns.map(_.fold(f)))) + case _ @PatternCase.EmptyListPatternCase => f(PatternCase.EmptyListPatternCase) + case c @ PatternCase.HeadTailPatternCase(_, _) => + f(PatternCase.HeadTailPatternCase(c.head.fold(f), c.tail.fold(f))) + case c @ PatternCase.LiteralPatternCase(_) => f(PatternCase.LiteralPatternCase(c.value)) + case c @ PatternCase.TuplePatternCase(_) => f(PatternCase.TuplePatternCase(c.elements.map(_.fold(f)))) + case _ @PatternCase.UnitPatternCase => f(PatternCase.UnitPatternCase) + case _ @PatternCase.WildcardPatternCase => f(PatternCase.WildcardPatternCase) + case c @ ValueCase.ApplyCase(_, _) => f(ValueCase.ApplyCase(c.function.fold(f), c.arguments.map(_.fold(f)))) + case c @ ValueCase.ConstructorCase(_) => f(ValueCase.ConstructorCase(c.name)) + case c @ ValueCase.DestructureCase(_, _, _) => + f(ValueCase.DestructureCase(c.pattern.fold(f), c.valueToDestruct.fold(f), c.inValue.fold(f))) + case c @ ValueCase.FieldCase(_, _) => f(ValueCase.FieldCase(c.target.fold(f), c.name)) + case c @ ValueCase.FieldFunctionCase(_) => f(c) + case c @ ValueCase.IfThenElseCase(_, _, _) => + f(ValueCase.IfThenElseCase(c.condition.fold(f), c.thenBranch.fold(f), c.elseBranch.fold(f))) + case c @ ValueCase.LambdaCase(_, _) => f(ValueCase.LambdaCase(c.argumentPattern.fold(f), c.body.fold(f))) + case c @ ValueCase.LetDefinitionCase(_, _, _) => + f(ValueCase.LetDefinitionCase(c.valueName, c.valueDefinition.fold(f), c.inValue.fold(f))) + case c @ ValueCase.LetRecursionCase(_, _) => + f( + ValueCase.LetRecursionCase( + c.valueDefinitions.map { case (name, value) => (name, value.fold(f)) }, + c.inValue.fold(f) + ) + ) + case c @ ValueCase.ListCase(_) => f(ValueCase.ListCase(c.elements.map(_.fold(f)))) + case c @ ValueCase.LiteralCase(_) => f(c) + case c @ ValueCase.NativeApplyCase(_, _) => f(ValueCase.NativeApplyCase(c.function, c.arguments.map(_.fold(f)))) + case c @ ValueCase.PatternMatchCase(_, _) => + f( + ValueCase.PatternMatchCase( + c.branchOutOn.fold(f), + c.cases.map { case (pattern, value) => + (pattern.fold(f), value.fold(f)) + } + ) + ) + case c @ ValueCase.RecordCase(_) => f(ValueCase.RecordCase(c.fields.map { case (k, v) => (k, v.fold(f)) })) + case c @ ValueCase.ReferenceCase(_) => f(c) + case c @ ValueCase.TupleCase(_) => f(ValueCase.TupleCase(c.elements.map(_.fold(f)))) + case _ @ValueCase.UnitCase => f(ValueCase.UnitCase) + case c @ ValueCase.UpdateRecordCase(_, _) => + f( + ValueCase.UpdateRecordCase( + c.valueToUpdate.fold(f), + c.fieldsToUpdate.map { case (name, value) => (name, value.fold(f)) } + ) + ) + case c @ ValueCase.VariableCase(_) => f(c) + } + } + + object Value { + import ValueCase.* + + final case class Literal[+V, +Annotations](value: Lit[V], annotations: ZEnvironment[Annotations]) + extends Value[Annotations] { + def caseValue: LiteralCase = ValueCase.LiteralCase(value) + } + + final case class Record[+Annotations]( + fields: Chunk[(Name, Value[Annotations])], + annotations: ZEnvironment[Annotations] + ) extends Value[Annotations] { + override lazy val caseValue: ValueCase[Value[Annotations]] = RecordCase(fields) + } + + final case class Unit[+Annotations](annotations: ZEnvironment[Annotations]) extends Value[Annotations] { + override def caseValue: UnitCase = ValueCase.UnitCase + } + } + + sealed trait ValueCase[+Self] { self => + import ValueCase.* + + def map[B](f: Self => B): ValueCase[B] = self match { + case c @ ApplyCase(_, _) => ApplyCase(f(c.function), c.arguments.map(f)) + case c @ ConstructorCase(_) => ConstructorCase(c.name) + case c @ DestructureCase(_, _, _) => DestructureCase(f(c.pattern), f(c.valueToDestruct), f(c.inValue)) + case c @ FieldCase(_, _) => FieldCase(f(c.target), c.name) + case c @ FieldFunctionCase(_) => FieldFunctionCase(c.name) + case c @ IfThenElseCase(_, _, _) => + IfThenElseCase(f(c.condition), f(c.thenBranch), f(c.elseBranch)) + case c @ LambdaCase(_, _) => LambdaCase(f(c.argumentPattern), f(c.body)) + case c @ LetDefinitionCase(_, _, _) => LetDefinitionCase(c.valueName, f(c.valueDefinition), f(c.inValue)) + case c @ LetRecursionCase(_, _) => + LetRecursionCase(c.valueDefinitions.map { case (name, value) => (name, f(value)) }, f(c.inValue)) + case c @ ListCase(_) => ListCase(c.elements.map(f)) + case c @ LiteralCase(_) => LiteralCase(c.literal) + case c @ NativeApplyCase(_, _) => NativeApplyCase(c.function, c.arguments.map(f)) + case c @ PatternMatchCase(_, _) => + PatternMatchCase(f(c.branchOutOn), c.cases.map { case (p, v) => (f(p), f(v)) }) + case c @ RecordCase(_) => RecordCase(c.fields.map { case (name, value) => (name, f(value)) }) + case c @ ReferenceCase(_) => c + case c @ TupleCase(_) => TupleCase(c.elements.map(f)) + case _ @UnitCase => UnitCase + case c @ UpdateRecordCase(_, _) => + UpdateRecordCase(f(c.valueToUpdate), c.fieldsToUpdate.map { case (name, self) => (name, f(self)) }) + case c @ VariableCase(_) => c + case c: PatternCase[_] => c.map(f) + + } + } + + object ValueCase { + final case class NativeApplyCase[+Self](function: NativeFunction, arguments: Chunk[Self]) extends ValueCase[Self] + final case class ApplyCase[+Self](function: Self, arguments: List[Self]) extends ValueCase[Self] + final case class ConstructorCase(name: FQName) extends ValueCase[Nothing] + final case class FieldCase[+Self](target: Self, name: Name) extends ValueCase[Self] + final case class FieldFunctionCase(name: Name) extends ValueCase[Nothing] + final case class IfThenElseCase[+Self](condition: Self, thenBranch: Self, elseBranch: Self) extends ValueCase[Self] + final case class ListCase[+Self](elements: Chunk[Self]) extends ValueCase[Self] + final case class LiteralCase(literal: LiteralValue) extends ValueCase[Nothing] + final case class PatternMatchCase[+Self](branchOutOn: Self, cases: List[(Self, Self)]) extends ValueCase[Self] + final case class RecordCase[+Self](fields: Chunk[(Name, Self)]) extends ValueCase[Self] + final case class ReferenceCase(name: FQName) extends ValueCase[Nothing] + final case class TupleCase[+Self](elements: Chunk[Self]) extends ValueCase[Self] + case object UnitCase extends ValueCase[Nothing] + type UnitCase = UnitCase.type + final case class VariableCase(name: Name) extends ValueCase[Nothing] + final case class LetDefinitionCase[+Self](valueName: Name, valueDefinition: Self, inValue: Self) + extends ValueCase[Self] + final case class LetRecursionCase[+Self](valueDefinitions: Map[Name, Self], inValue: Self) extends ValueCase[Self] + final case class UpdateRecordCase[+Self](valueToUpdate: Self, fieldsToUpdate: Chunk[(Name, Self)]) + extends ValueCase[Self] + final case class LambdaCase[+Self](argumentPattern: Self, body: Self) extends ValueCase[Self] + final case class DestructureCase[+Self](pattern: Self, valueToDestruct: Self, inValue: Self) extends ValueCase[Self] + + sealed trait PatternCase[+Self] extends ValueCase[Self] { self => + import PatternCase.* + + override def map[B](f: Self => B): PatternCase[B] = self match { + case c @ AsPatternCase(_, _) => AsPatternCase(f(c.pattern), c.name) + case c @ PatternCase.ConstructorPatternCase(_, _) => + PatternCase.ConstructorPatternCase(c.constructorName, c.argumentPatterns.map(f)) + case EmptyListPatternCase => EmptyListPatternCase + case c @ HeadTailPatternCase(_, _) => HeadTailPatternCase(f(c.head), f(c.tail)) + case c @ PatternCase.LiteralPatternCase(_) => c + case c @ PatternCase.TuplePatternCase(_) => PatternCase.TuplePatternCase(c.elements.map(f)) + case PatternCase.UnitPatternCase => PatternCase.UnitPatternCase + case WildcardPatternCase => WildcardPatternCase + } + + } + + sealed trait Pattern[+Annotations] extends Value[Annotations] { + def annotations: ZEnvironment[Annotations] + } + object Pattern { + // val unit: UnitPattern[Any] = UnitPattern(ZEnvironment.empty) + // def unit[Annotations](annotations: ZEnvironment[Annotations]): UnitPattern[Annotations] = UnitPattern(annotations) + // val wildcard: Wildcard[Any] = Wildcard(ZEnvironment.empty) + // def wildcard[Annotations](annotations: ZEnvironment[Annotations]): Wildcard[Annotations] = Wildcard(annotations) + + // final case class LiteralPattern[+Annotations, +Value](value: Lit[Value], annotations: ZEnvironment[Annotations]) + // extends Pattern[Annotations] + + // final case class UnitPattern[+Annotations](annotations: ZEnvironment[Annotations]) extends Pattern[Annotations] + // final case class Wildcard[+Annotations](annotations: ZEnvironment[Annotations]) extends Pattern[Annotations] + } + object PatternCase { + + final case class AsPatternCase[+Self](pattern: Self, name: Name) extends PatternCase[Self] + final case class ConstructorPatternCase[+Self](constructorName: FQName, argumentPatterns: List[Self]) + extends PatternCase[Self] + case object EmptyListPatternCase extends PatternCase[Nothing] + final case class HeadTailPatternCase[+Self](head: Self, tail: Self) extends PatternCase[Self] + final case class LiteralPatternCase(value: Literal[Nothing]) extends PatternCase[Nothing] + final case class TuplePatternCase[+Self](elements: List[Self]) extends PatternCase[Self] + case object UnitPatternCase extends PatternCase[Nothing] + case object WildcardPatternCase extends PatternCase[Nothing] + + implicit val PatternCaseForEach: ForEach[PatternCase] = + new ForEach[PatternCase] { + def forEach[G[+_]: IdentityBoth: Covariant, A, B](fa: PatternCase[A])(f: A => G[B]): G[PatternCase[B]] = + fa match { + case c @ AsPatternCase(_, _) => f(c.pattern).map(AsPatternCase(_, c.name)) + case c @ ConstructorPatternCase(_, _) => + c.argumentPatterns.forEach(f).map(ConstructorPatternCase(c.constructorName, _)) + case EmptyListPatternCase => EmptyListPatternCase.succeed + case c @ HeadTailPatternCase(_, _) => f(c.head).zipWith(f(c.tail))(HeadTailPatternCase(_, _)) + case c @ LiteralPatternCase(_) => c.succeed + case c @ TuplePatternCase(_) => c.elements.forEach(f).map(TuplePatternCase(_)) + case UnitPatternCase => UnitPatternCase.succeed + case WildcardPatternCase => WildcardPatternCase.succeed + } + } + } + + implicit val ValueCaseCovariant: Covariant[ValueCase] = new Covariant[ValueCase] { + def map[A, B](f: A => B): ValueCase[A] => ValueCase[B] = _.map(f) + } + + implicit val ValueCaseForEach: ForEach[ValueCase] = + new ForEach[ValueCase] { + def forEach[G[+_]: IdentityBoth: Covariant, A, B](fa: ValueCase[A])(f: A => G[B]): G[ValueCase[B]] = + fa match { + case c @ ApplyCase(_, _) => f(c.function).zipWith(c.arguments.forEach(f))(ApplyCase(_, _)) + case c @ ConstructorCase(_) => c.succeed + case c @ DestructureCase(_, _, _) => + (f(c.pattern), f(c.valueToDestruct), f(c.inValue)).mapN(DestructureCase(_, _, _)) + case c @ FieldCase(_, _) => f(c.target).map(FieldCase(_, c.name)) + case c @ FieldFunctionCase(_) => c.succeed + case c @ IfThenElseCase(_, _, _) => + (f(c.condition), f(c.thenBranch), f(c.elseBranch)).mapN(IfThenElseCase(_, _, _)) + case c @ LambdaCase(_, _) => f(c.argumentPattern).zipWith(f(c.body))(LambdaCase(_, _)) + case c @ LetDefinitionCase(_, _, _) => + f(c.valueDefinition).zipWith(f(c.inValue))(LetDefinitionCase(c.valueName, _, _)) + case c @ LetRecursionCase(_, _) => + c.valueDefinitions.forEach(f).zipWith(f(c.inValue))(LetRecursionCase(_, _)) + case c @ ListCase(_) => c.elements.forEach(f).map(ListCase(_)) + case c @ LiteralCase(_) => c.succeed + case c @ NativeApplyCase(_, _) => c.arguments.forEach(f).map(NativeApplyCase(c.function, _)) + case c @ PatternMatchCase(_, _) => + f(c.branchOutOn) + .zipWith(c.cases.forEach { case (key, value) => f(key).zip(f(value)) })(PatternMatchCase(_, _)) + case c @ RecordCase(_) => + c.fields.forEach { case (key, value) => f(value).map(key -> _) }.map(RecordCase(_)) + case c @ ReferenceCase(_) => c.succeed + case c @ TupleCase(_) => c.elements.forEach(f).map(TupleCase(_)) + case _ @UnitCase => UnitCase.succeed + case c @ UpdateRecordCase(_, _) => + f(c.valueToUpdate).zipWith(c.fieldsToUpdate.forEach { case (name, self) => f(self).map(name -> _) })( + UpdateRecordCase(_, _) + ) + case c @ VariableCase(_) => c.succeed + case c: PatternCase[_] => c.forEach(f) + } + } + } + +} diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/ir.recursive.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/ir.recursive.scala index 69949acd..e35f1381 100644 --- a/morphir-ir/shared/src/main/scala/zio/morphir/ir/ir.recursive.scala +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/ir.recursive.scala @@ -249,6 +249,7 @@ object recursive { sealed trait ValueCase[+Self] extends ValueTreeCase[Self] { self => import ValueCase.* + override def map[B](f: Self => B): ValueCase[B] = self match { case c @ ApplyCase(_, _) => ApplyCase(f(c.function), c.arguments.map(f)) case c @ ConstructorCase(_) => ConstructorCase(c.name) @@ -261,8 +262,9 @@ object recursive { case c @ LetDefinitionCase(_, _, _) => LetDefinitionCase(c.valueName, f(c.valueDefinition), f(c.inValue)) case c @ LetRecursionCase(_, _) => LetRecursionCase(c.valueDefinitions.map { case (name, value) => (name, f(value)) }, f(c.inValue)) - case c @ ListCase(_) => ListCase(c.elements.map(f)) - case c @ LiteralCase(_) => LiteralCase(c.literal) + case c @ ListCase(_) => ListCase(c.elements.map(f)) + case c @ LiteralCase(_) => LiteralCase(c.literal) + case c @ NativeApplyCase(_, _) => NativeApplyCase(c.function, c.arguments.map(f)) case c @ PatternMatchCase(_, _) => PatternMatchCase(f(c.branchOutOn), c.cases.map { case (p, v) => (f(p), f(v)) }) case c @ RecordCase(_) => RecordCase(c.fields.map { case (name, value) => (name, f(value)) }) @@ -273,22 +275,24 @@ object recursive { UpdateRecordCase(f(c.valueToUpdate), c.fieldsToUpdate.map { case (name, self) => (name, f(self)) }) case c @ VariableCase(_) => c + case c: PatternCase[_] => c.map(f) } } object ValueCase { - final case class ApplyCase[+Self](function: Self, arguments: List[Self]) extends ValueCase[Self] - final case class ConstructorCase(name: FQName) extends ValueCase[Nothing] - final case class FieldCase[+Self](target: Self, name: Name) extends ValueCase[Self] - final case class FieldFunctionCase(name: Name) extends ValueCase[Nothing] + final case class NativeApplyCase[+Self](function: NativeFunction, arguments: Chunk[Self]) extends ValueCase[Self] + final case class ApplyCase[+Self](function: Self, arguments: List[Self]) extends ValueCase[Self] + final case class ConstructorCase(name: FQName) extends ValueCase[Nothing] + final case class FieldCase[+Self](target: Self, name: Name) extends ValueCase[Self] + final case class FieldFunctionCase(name: Name) extends ValueCase[Nothing] final case class IfThenElseCase[+Self](condition: Self, thenBranch: Self, elseBranch: Self) extends ValueCase[Self] - final case class ListCase[+Self](elements: List[Self]) extends ValueCase[Self] - final case class LiteralCase(literal: LiteralValue) extends ValueCase[Nothing] - final case class PatternMatchCase[+Self](branchOutOn: Self, cases: List[(Self, Self)]) extends ValueCase[Self] - final case class RecordCase[+Self](fields: List[(Name, Self)]) extends ValueCase[Self] - final case class ReferenceCase(name: FQName) extends ValueCase[Nothing] - final case class TupleCase[+Self](elements: List[Self]) extends ValueCase[Self] - case object UnitCase extends ValueCase[Nothing] - final case class VariableCase(name: Name) extends ValueCase[Nothing] + final case class ListCase[+Self](elements: Chunk[Self]) extends ValueCase[Self] + final case class LiteralCase(literal: LiteralValue) extends ValueCase[Nothing] + final case class PatternMatchCase[+Self](branchOutOn: Self, cases: Chunk[(Self, Self)]) extends ValueCase[Self] + final case class RecordCase[+Self](fields: Chunk[(Name, Self)]) extends ValueCase[Self] + final case class ReferenceCase(name: FQName) extends ValueCase[Nothing] + final case class TupleCase[+Self](elements: Chunk[Self]) extends ValueCase[Self] + case object UnitCase extends ValueCase[Nothing] + final case class VariableCase(name: Name) extends ValueCase[Nothing] final case class LetDefinitionCase[+Self](valueName: Name, valueDefinition: Self, inValue: Self) extends ValueCase[Self] final case class LetRecursionCase[+Self](valueDefinitions: Map[Name, Self], inValue: Self) extends ValueCase[Self] @@ -318,8 +322,9 @@ object recursive { f(c.valueDefinition).zipWith(f(c.inValue))(LetDefinitionCase(c.valueName, _, _)) case c @ LetRecursionCase(_, _) => c.valueDefinitions.forEach(f).zipWith(f(c.inValue))(LetRecursionCase(_, _)) - case c @ ListCase(_) => c.elements.forEach(f).map(ListCase(_)) - case c @ LiteralCase(_) => c.succeed + case c @ ListCase(_) => c.elements.forEach(f).map(ListCase(_)) + case c @ LiteralCase(_) => c.succeed + case c @ NativeApplyCase(_, _) => c.arguments.forEach(f).map(NativeApplyCase(c.function, _)) case c @ PatternMatchCase(_, _) => f(c.branchOutOn) .zipWith(c.cases.forEach { case (key, value) => f(key).zip(f(value)) })(PatternMatchCase(_, _)) @@ -333,11 +338,12 @@ object recursive { UpdateRecordCase(_, _) ) case c @ VariableCase(_) => c.succeed + case c: PatternCase[_] => c.forEach(f) } } } - sealed trait PatternCase[+Self] extends MorphirIRCase[Self] { self => + sealed trait PatternCase[+Self] extends ValueCase[Self] { self => import PatternCase.* override def map[B](f: Self => B): PatternCase[B] = self match { @@ -354,6 +360,16 @@ object recursive { } object PatternCase { + // type Pattern a + // = WildcardPattern a + // | AsPattern a (Pattern a) Name + // | TuplePattern a (List (Pattern a)) + // | ConstructorPattern a FQName (List (Pattern a)) + // | EmptyListPattern a + // | HeadTailPattern a (Pattern a) (Pattern a) + // | LiteralPattern a Literal + // | UnitPattern a + final case class AsCase[+Self](pattern: Self, name: Name) extends PatternCase[Self] final case class ConstructorCase[+Self](constructorName: FQName, argumentPatterns: List[Self]) extends PatternCase[Self] diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/package.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/package.scala index fc8e1f14..092a3ade 100644 --- a/morphir-ir/shared/src/main/scala/zio/morphir/ir/package.scala +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/package.scala @@ -2,16 +2,17 @@ package zio.morphir package object ir { - type LiteralValue = Literal[Nothing] + type LiteralValue = Literal[Any] val LiteralValue = Literal - type ModuleName = Module.ModuleName - val ModuleName = Module.ModuleName + type ModuleName = ModuleModule.ModuleName + val ModuleName = ModuleModule.ModuleName - type ModulePath = Module.ModulePath - val ModulePath = Module.ModulePath + type Module = ModuleModule.type + val Module = ModuleModule - type NativeFunction = value.Native.Function + type ModulePath = ModuleModule.ModulePath + val ModulePath = ModuleModule.ModulePath type PackageName = PackageModule.PackageName val PackageName = PackageModule.PackageName diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/value/Interpreter.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/value/Interpreter.scala new file mode 100644 index 00000000..f8563b5b --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/ir/value/Interpreter.scala @@ -0,0 +1,356 @@ +package zio.morphir.ir.value + +import scala.collection.immutable.ListMap +import zio.morphir.ir.MorphirIR +import zio.morphir.ir.recursive.MorphirIRCase +import zio.morphir.ir.recursive.PatternCase +import zio.morphir.ir.recursive.ValueCase +import zio.morphir.ir.Literal +import zio.morphir.ir.LiteralValue +import zio.morphir.ir.Name +import zio.Chunk +import zio.morphir.ir.FQName +import zio.morphir.ir.MorphirIR.Value +import zio.morphir.ir.NativeFunction +import zio.morphir.ir.NativeFunction.Addition + +object Interperter { + def eval[Annotations](ir: MorphirIR[Annotations]): Either[InterpretationError, Any] = { + + def loop( + ir: MorphirIR[Annotations], + variables: Map[Name, Any], + references: Map[FQName, Value[Annotations]] + ): Any = { + println("looping with " + ir.caseValue) + ir.caseValue match { + + case ValueCase.ApplyCase(_, _) => + ??? + + case ValueCase.FieldCase(target, name) => + val record = loop(target, variables, references).asInstanceOf[ListMap[Name, Any]] + record.get(name) match { + case Some(value) => value + case None => + throw new InterpretationError.FieldNotFound(name, s"Field $name not found in $record") + } + + case ValueCase.IfThenElseCase(condition, thenCase, elseCase) => + if (loop(condition, variables, references).asInstanceOf[Boolean]) { + loop(thenCase, variables, references) + } else { + loop(elseCase, variables, references) + } + + case ValueCase.LambdaCase(_, _) => + ??? + + case ValueCase.LetDefinitionCase(name, value, body) => + loop(body, variables + (name -> loop(value, variables, references)), references) + + case ValueCase.ListCase(values) => + values.map(loop(_, variables, references)).toList + + case ValueCase.LiteralCase(value) => + evalLiteralValue(value) + + case ValueCase.NativeApplyCase(function, args) => + evalNativeFunction(function, args.map(loop(_, variables, references))) + + case ValueCase.PatternMatchCase(branchOutOn, cases) => + def matches(body: Any, caseStatement: MorphirIR[Annotations]): Boolean = { + caseStatement.caseValue match { + case PatternCase.WildcardCase => true + case PatternCase.LiteralCase(literal) => + body == evalLiteralValue(literal) + case _ => throw new InterpretationError.Message("we don't know how to handle this pattern yet") + } + } + + val evaluatedBody = loop(branchOutOn, variables, references) + val casesChunk = cases + var i = 0 + val length = casesChunk.length + var rightHandSide: MorphirIR[Annotations] = null + while (i < length) { + if (matches(evaluatedBody, casesChunk(i)._1)) { + rightHandSide = casesChunk(i)._2 + i = length + } else { + i += 1 + } + } + + if (rightHandSide eq null) throw new InterpretationError.MatchError("didn't match") + else loop(rightHandSide, variables, references) + + // cases = List((x -> doSomething, y -> doSomethingElse) + // branchOutOn match { + // case x => doSomething + // case y => doSomethingElse + // } + // } + + case ValueCase.RecordCase(fields) => + println(s"Interpreting RecordCase for $fields") + val values = fields.map { case (name, value) => + name -> loop(value, variables, references) + } + ListMap(values: _*) + + case ValueCase.ReferenceCase(fqName) => + references.get(fqName) match { + case Some(value) => value + case None => throw new InterpretationError.ReferenceNotFound(fqName, s"Reference $fqName not found") + } + + case ValueCase.TupleCase(value) => + evalTuple(value.map(loop(_, variables, references))) + + case ValueCase.UnitCase => + () + + case ValueCase.VariableCase(name) => + variables.get(name) match { + case Some(value) => value + + case None => throw new InterpretationError.VariableNotFound(name, s"Variable $name not found") + } + + case _ => ??? + } + } + + try { + Right(loop(ir, Map.empty, Map.empty)) + } catch { + case interpretationError: InterpretationError => Left(interpretationError) + } + } + + // val x = 1 + // val y = 2 + // x + y + + private def evalLiteralValue(literalValue: LiteralValue): Any = + literalValue match { + case Literal.Bool(value) => value + case Literal.Char(value) => value + case Literal.String(value) => value + case Literal.WholeNumber(value) => value + case Literal.Float(value) => value + } + + // format: off + private def evalTuple(value: Chunk[Any]): Any = + value.toList match { + case a :: Nil => Tuple1(a) + case a :: b :: Nil => (a, b) + case a :: b :: c :: Nil => (a, b, c) + case a :: b :: c :: d :: Nil => (a, b, c, d) + case a :: b :: c :: d :: e :: Nil => (a, b, c, d, e) + case a :: b :: c :: d :: e :: f :: Nil => (a, b, c, d, e, f) + case a :: b :: c :: d :: e :: f :: g :: Nil => (a, b, c, d, e, f, g) + case a :: b :: c :: d :: e :: f :: g :: h :: Nil => (a, b, c, d, e, f, g, h) + case a :: b :: c :: d :: e :: f :: g :: h :: i :: Nil => (a, b, c, d, e, f, g, h, i) + case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: Nil => (a, b, c, d, e, f, g, h, i, j) + case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: Nil => (a, b, c, d, e, f, g, h, i, j, k) + case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: Nil => (a, b, c, d, e, f, g, h, i, j, k, l) + case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: Nil => (a, b, c, d, e, f, g, h, i, j, k, l, m) + case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: Nil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n) + case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: Nil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) + case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: Nil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) + case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: q :: Nil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) + case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: q :: r :: Nil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) + case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: q :: r :: s :: Nil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) + 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) + } + // format: on + + private def evalNativeFunction(function: NativeFunction, args: Chunk[Any]): Any = + function match { + case Addition => evalAddition(args) + } + + 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.") + else if (args(0).isInstanceOf[java.math.BigInteger]) + args.asInstanceOf[Chunk[java.math.BigInteger]].reduce(_ add _) + else + args.asInstanceOf[Chunk[java.math.BigDecimal]].reduce(_ add _) +} + +sealed trait InterpretationError extends Throwable +object InterpretationError { + final case class Message(message: String) extends InterpretationError + final case class VariableNotFound(name: Name, message: String) extends InterpretationError + final case class ReferenceNotFound(name: FQName, message: String) extends InterpretationError + final case class InvalidArguments(args: Chunk[Any], message: String) extends InterpretationError + final case class TupleTooLong(length: Int) extends InterpretationError + final case class FieldNotFound(name: Name, message: String) extends InterpretationError + final case class MatchError(mesage: String) extends InterpretationError +} + +// type alias IR = +// { valueSpecifications : Dict FQName (Value.Specification ()) +// , valueDefinitions : Dict FQName (Value.Definition () (Type ())) +// , typeSpecifications : Dict FQName (Type.Specification ()) +// , typeConstructors : Dict FQName ( FQName, List Name, List ( Name, Type () ) ) +// } + +object Example extends scala.App { + + def add(x: Int, y: Int): Int = x + y + + println("A") + + // val x = 1 + // val y = 2 + // x + y + val myIRCase: MorphirIRCase[MorphirIR[Any]] = + ValueCase.LetDefinitionCase( + Name("x"), + MorphirIR(ValueCase.LiteralCase(LiteralValue.WholeNumber(new java.math.BigInteger("1")))), + MorphirIR( + ValueCase.LetDefinitionCase( + Name("y"), + MorphirIR(ValueCase.LiteralCase(LiteralValue.WholeNumber(new java.math.BigInteger("2")))), + MorphirIR( + ValueCase.NativeApplyCase( + NativeFunction.Addition, + Chunk(MorphirIR(ValueCase.VariableCase(Name("x"))), MorphirIR(ValueCase.VariableCase(Name("y")))) + ) + ) + ) + ) + ) + + println("B") + + val myIR = + MorphirIR(myIRCase) + + println("C") + + val result = Interperter.eval(myIR) + + println(result) + + val result2 = Interperter.eval( + (MorphirIR( + ValueCase.TupleCase( + Chunk( + MorphirIR(ValueCase.LiteralCase(LiteralValue.WholeNumber(new java.math.BigInteger("1")))) + // MorphirIR(ValueCase.LiteralCase(LiteralValue.WholeNumber(new java.math.BigInteger("2")))) + ) + ) + )) + ) + + println(result2) + + val result3 = Interperter.eval( + (MorphirIR( + ValueCase.ListCase( + Chunk( + MorphirIR(ValueCase.LiteralCase(LiteralValue.String("hello"))), + MorphirIR(ValueCase.LiteralCase(LiteralValue.String("world"))) + ) + ) + )) + ) + + println(result3) + + val result4 = Interperter.eval( + (MorphirIR( + ValueCase.IfThenElseCase( + condition = MorphirIR(ValueCase.LiteralCase(Literal.boolean(false))), + thenBranch = MorphirIR(ValueCase.LiteralCase(Literal.string("yes"))), + elseBranch = MorphirIR(ValueCase.LiteralCase(Literal.string("no"))) + ) + )) + ) + + println(result4) + + println("about to define result5") + + val fieldA = Name.fromString("fieldA") + val fieldB = Name.fromString("fieldB") + + println("defined names") + + val value1 = Value.string("hello") + println("defined string") + val value2 = + try { + Value.wholeNumber(new java.math.BigInteger("2")) + } catch { + case t: Throwable => + t.printStackTrace() + throw t + } + + println("defined numbers") + + val element1 = fieldA -> value1 + val element2 = fieldB -> value2 + + println("defined elements") + + val expr5 = Value.record(element1, element2) + + println("about to evaluate result5") + + val result5 = Interperter.eval(expr5) + println(result5) + + { + + println("about to evaluate result6") + + val result6 = Interperter.eval( + Value.field(Name("fielda"))( + Value.record( + Name("fielda") -> Value.wholeNumber(new java.math.BigInteger("42")), + Name("fieldb") -> Value.wholeNumber(new java.math.BigInteger("2")) + ) + ) + ) + + println(result6) + + } + + // 42 match { case _ => 100 } + + import zio.ZEnvironment + + println("about to evaluate result7") + + val result7 = Interperter.eval( + Value.patternMatch( + Value.wholeNumber(new java.math.BigInteger("42")), + Value(PatternCase.WildcardCase, ZEnvironment.empty) -> Value.wholeNumber(new java.math.BigInteger("100")) + ) + ) + + println("before") + println(result7) + println("after") +} + +final case class GenericRecord(fields: ListMap[String, Any]) +final case class LabeledRecord(name: String, fields: ListMap[String, Any]) + +//\a -> a -- Lambda (AsPattern WildcardPattern [ "a" ]) (Variable [ "a" ]) +//\a +// case a @ _ => a + +//\a b -> a -- Lambda (AsPattern WildcardPattern [ "a" ]) (Lambda (AsPattern WildcardPattern [ "b" ]) (Variable [ "a" ])) diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/ir/value/Native.scala b/morphir-ir/shared/src/main/scala/zio/morphir/ir/value/Native.scala deleted file mode 100644 index 567b9e8d..00000000 --- a/morphir-ir/shared/src/main/scala/zio/morphir/ir/value/Native.scala +++ /dev/null @@ -1,5 +0,0 @@ -package zio.morphir.ir.value - -object Native { - type Function -} diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/syntax/AllSyntax.scala b/morphir-ir/shared/src/main/scala/zio/morphir/syntax/AllSyntax.scala new file mode 100644 index 00000000..a1226531 --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/syntax/AllSyntax.scala @@ -0,0 +1,3 @@ +package zio.morphir.syntax + +trait AllSyntax extends TypeSyntax with ValueSyntax 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 new file mode 100644 index 00000000..883c9c61 --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/syntax/TypeSyntax.scala @@ -0,0 +1,3 @@ +package zio.morphir.syntax + +trait TypeSyntax {} diff --git a/morphir-ir/shared/src/main/scala/zio/morphir/syntax/ValueSyntax.scala b/morphir-ir/shared/src/main/scala/zio/morphir/syntax/ValueSyntax.scala new file mode 100644 index 00000000..65d30b87 --- /dev/null +++ b/morphir-ir/shared/src/main/scala/zio/morphir/syntax/ValueSyntax.scala @@ -0,0 +1,26 @@ +package zio.morphir.syntax + +import zio.ZEnvironment +import zio.morphir.ir.{Literal => Lit, ValueModule} +import ValueModule.Value + +trait ValueSyntax { + import Value.* + + final def boolean(value: Boolean): Literal[Boolean, Any] = Literal(Lit.boolean(value), ZEnvironment.empty) + final def boolean[Annotations]( + value: Boolean, + annotations: ZEnvironment[Annotations] + ): Literal[Boolean, Annotations] = + Literal(Lit.boolean(value), annotations) + + final def literal[V](value: Lit[V]): Literal[V, Any] = Literal(value, ZEnvironment.empty) + final def literal[V, Annotations](value: Lit[V], annotations: ZEnvironment[Annotations]): Literal[V, Annotations] = + Literal(value, annotations) + + final def string[Annotations](value: String, annotations: ZEnvironment[Annotations]): Value[Annotations] = + Literal(Lit.string(value), annotations) + + final val unit: Unit[Any] = Unit(ZEnvironment.empty) + final def unit[Annotations](annotations: ZEnvironment[Annotations]): Unit[Annotations] = Unit(annotations) +} diff --git a/morphir-ir/shared/src/test/scala/zio/morphir/ir/ModuleSpec.scala b/morphir-ir/shared/src/test/scala/zio/morphir/ir/ModuleSpec.scala index 8b9f82ac..13c7c88d 100644 --- a/morphir-ir/shared/src/test/scala/zio/morphir/ir/ModuleSpec.scala +++ b/morphir-ir/shared/src/test/scala/zio/morphir/ir/ModuleSpec.scala @@ -8,18 +8,18 @@ object ModuleSpec extends MorphirBaseSpec { suite("Definition")( test("It can be empty") { assertTrue( - Module.emptyDefinition == Module.Definition.empty, - Module.emptyDefinition.types.isEmpty, - Module.emptyDefinition.values.isEmpty + ModuleModule.emptyDefinition == ModuleModule.Definition.empty, + ModuleModule.emptyDefinition.types.isEmpty, + ModuleModule.emptyDefinition.values.isEmpty ) } ), suite("Specification")( test("It can be empty") { assertTrue( - Module.emptySpecification == Module.Specification.empty, - Module.emptySpecification.types.isEmpty, - Module.emptySpecification.values.isEmpty + ModuleModule.emptySpecification == ModuleModule.Specification.empty, + ModuleModule.emptySpecification.types.isEmpty, + ModuleModule.emptySpecification.values.isEmpty ) } ) diff --git a/morphir-ir/shared/src/test/scala/zio/morphir/ir/NameSpec.scala b/morphir-ir/shared/src/test/scala/zio/morphir/ir/NameSpec.scala index 5d38521e..943b79ea 100644 --- a/morphir-ir/shared/src/test/scala/zio/morphir/ir/NameSpec.scala +++ b/morphir-ir/shared/src/test/scala/zio/morphir/ir/NameSpec.scala @@ -37,7 +37,14 @@ object NameSpec extends MorphirBaseSpec { test("Are complete and utter nonsense") { assertTrue(Name.fromString("_-%") == Name.empty) } - ) + ), + test("It splits the name as expected") { + // "fooBar","blahBlah" => ["foo","bar","blah","blah"] + // "fooBar","blahBlah" => ["fooBar","blahBlah"] + assertTrue( + Name.fromString("fooBar").toList == List("foo", "bar") + ) + } ), suite("Name should be convertible to a title-case string:")( test("When the name was originally constructed from a snake-case string") { @@ -90,6 +97,19 @@ object NameSpec extends MorphirBaseSpec { val sut = Name.fromString("ValueInUSD") assertTrue(Name.toHumanWords(sut) == List("value", "in", "USD")) } + ), + suite("fromIterable")( + test("Splits provided names as expected") { + assertTrue(Name.fromIterable(List("fooBar", "fizzBuzz")) == Name("foo", "bar", "fizz", "buzz")) + } @@ TestAspect.ignore @@ TestAspect.tag("TOFIX") + ), + suite("unsafeMake")( + test("Creates the name as provided") { + assertTrue( + Name.unsafeMake("foo", "bar", "baz", "123").toList == List("foo", "bar", "baz", "123") + ) + + } @@ TestAspect.ignore @@ TestAspect.tag("TOFIX") ) ) }