Add more in-depth tests to core (#489)

This commit is contained in:
Ara Adkins 2020-02-14 16:29:24 +00:00 committed by GitHub
parent 8d8e95e14a
commit adf5fe7db2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 705 additions and 323 deletions

View File

@ -101,7 +101,8 @@ lazy val enso = (project in file("."))
graph,
runner,
gateway,
language_server
language_server,
json_rpc_server
)
.settings(Global / concurrentRestrictions += Tags.exclusive(Exclusive))
@ -336,7 +337,7 @@ lazy val file_manager = (project in file("common/file-manager"))
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.0-M2" % Test,
libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.14.3" % Test,
libraryDependencies += akkaTestkitTyped,
libraryDependencies += "commons-io" % "commons-io" % "2.6",
libraryDependencies += "commons-io" % "commons-io" % "2.6",
// upgrade blocked by gmethvin/directory-watcher#49
libraryDependencies += "io.methvin" % "directory-watcher" % "0.9.6"
)
@ -405,9 +406,9 @@ lazy val polyglot_api = project
.mkString(File.pathSeparator)}"
),
libraryDependencies ++= Seq(
"org.graalvm.sdk" % "polyglot-tck" % graalVersion % "provided",
"org.scalatest" %% "scalatest" % "3.2.0-M2" % Test,
"org.scalacheck" %% "scalacheck" % "1.14.3" % Test
"org.graalvm.sdk" % "polyglot-tck" % graalVersion % "provided",
"org.scalatest" %% "scalatest" % "3.2.0-M2" % Test,
"org.scalacheck" %% "scalacheck" % "1.14.3" % Test
),
addCompilerPlugin(
"org.typelevel" %% "kind-projector" % "0.11.0" cross CrossVersion.full
@ -438,8 +439,8 @@ lazy val language_server = (project in file("engine/language-server"))
libraryDependencies ++= akka ++ Seq(
"org.graalvm.sdk" % "polyglot-tck" % graalVersion % Provided,
akkaTestkit % Test,
"org.scalatest" %% "scalatest" % "3.2.0-M2" % Test,
"org.scalacheck" %% "scalacheck" % "1.14.3" % Test
"org.scalatest" %% "scalatest" % "3.2.0-M2" % Test,
"org.scalacheck" %% "scalacheck" % "1.14.3" % Test
)
)
.dependsOn(polyglot_api)
@ -449,10 +450,10 @@ lazy val gateway = (project in file("engine/gateway"))
.settings(
libraryDependencies ++= akka ++ circe ++ Seq(
"io.circe" %% "circe-generic-extras" % "0.12.2",
"io.circe" %% "circe-literal" % circeVersion,
"io.circe" %% "circe-literal" % circeVersion,
akkaTestkit % Test,
"org.scalatest" %% "scalatest" % "3.2.0-M2" % Test,
"org.scalacheck" %% "scalacheck" % "1.14.3" % Test
"org.scalatest" %% "scalatest" % "3.2.0-M2" % Test,
"org.scalacheck" %% "scalacheck" % "1.14.3" % Test
)
)

View File

@ -4,6 +4,7 @@ import shapeless.ops.hlist._
import shapeless.ops.{hlist, nat}
import shapeless.{::, HList, HNil, IsDistinctConstraint, Nat}
import scala.annotation.unused
import scala.collection.mutable
// Don't use AnyType here, as it gets boxed sometimes.
@ -13,10 +14,9 @@ import io.estatico.newtype.macros.newtype
import shapeless.nat._
/* TODO [AA, WD] The following are features that we want for this graph:
* - Graphviz output for visualisations.
* - Utilities for copying (sub-)graphs.
* - `unsafeRemoveComponent` functionality.
* - Storage should keep a free-list and re-use space in the underlying buffers
* as much as possible.
* as much as possible in `addComponent`.
* - Basic equality testing (that should be overridden as needed).
* - An ability to define fields that store complex data such as `String`.
* - Add a `Default` typeclass, and ensure that all component fields are
@ -34,7 +34,7 @@ import shapeless.nat._
* custom graph structure that provides a safe API.
*/
// ============================================================================
// === HList generic utilities ================================================
// === HList Generic Utilities ================================================
// ============================================================================
// ================
@ -57,8 +57,7 @@ object HListSum {
new HListSum[HNil] { type Out = _0 }
implicit def onCons[H <: Nat, T <: HList, TS <: Nat](
implicit
rest: HListSum.Aux[T, TS],
implicit @unused rest: HListSum.Aux[T, TS],
all: nat.Sum[H, TS]
): HListSum.Aux[H :: T, all.Out] =
new HListSum[H :: T] { type Out = all.Out }
@ -105,20 +104,21 @@ object HListOfNatToVec {
* type HListTakeUntil[Sentinel, MyList]
* }}}
*
* If the sentinel [[T]] is not found in [[List]], the entire list is returned.
* If the sentinel [[T]] is not found in [[Items]], the entire list is
* returned.
*
* @tparam T the sentinel member
* @tparam List the list to take members from
* @tparam Items the list to take members from
*/
trait HListTakeUntil[T, List <: HList] {
trait HListTakeUntil[T, Items <: HList] {
type Out <: HList
}
object HListTakeUntil extends HListTakeUntilDefaults {
type Aux[T, List <: HList, X] = HListTakeUntil[T, List] { type Out = X }
type Aux[T, Items <: HList, X] = HListTakeUntil[T, Items] { type Out = X }
def apply[T, List <: HList](
implicit ev: HListTakeUntil[T, List]
): Aux[T, List, ev.Out] = ev
def apply[T, Items <: HList](
implicit ev: HListTakeUntil[T, Items]
): Aux[T, Items, ev.Out] = ev
implicit def onNil[T]: HListTakeUntil.Aux[T, HNil, HNil] =
new HListTakeUntil[T, HNil] { type Out = HNil }
@ -130,8 +130,7 @@ object HListTakeUntil extends HListTakeUntilDefaults {
trait HListTakeUntilDefaults {
implicit def onConsNotFound[T, Head, Tail <: HList, Tail2 <: HList](
implicit
ev1: HListTakeUntil.Aux[T, Tail, Tail2]
implicit @unused ev1: HListTakeUntil.Aux[T, Tail, Tail2]
): HListTakeUntil.Aux[T, Head :: Tail, Head :: Tail2] =
new HListTakeUntil[T, Head :: Tail] { type Out = Head :: Tail2 }
}
@ -159,15 +158,15 @@ object Sized {
def apply[T](implicit ev: Sized[T]): Aux[T, ev.Out] = ev
implicit def instance[
List <: HList,
ListOfItems <: HList,
ListOfSizes <: HList,
TotalSize <: Nat
](
implicit
ev1: MapSized.Aux[List, ListOfSizes],
ev2: HListSum.Aux[ListOfSizes, TotalSize]
): Sized.Aux[List, TotalSize] =
new Sized[List] { type Out = TotalSize }
@unused ev1: MapSized.Aux[ListOfItems, ListOfSizes],
@unused ev2: HListSum.Aux[ListOfSizes, TotalSize]
): Sized.Aux[ListOfItems, TotalSize] =
new Sized[ListOfItems] { type Out = TotalSize }
}
/** A utility for accessing a the Size of a [[Sized]] object as an [[Int]]. */
@ -177,7 +176,7 @@ trait KnownSize[T] extends Sized[T] {
object KnownSize {
implicit def instance[T, Size <: Nat](
implicit
ev: Sized.Aux[T, Size],
@unused ev: Sized.Aux[T, Size],
sizeEv: nat.ToInt[Size]
): KnownSize[T] = new KnownSize[T] { val asInt: Int = sizeEv() }
}
@ -203,9 +202,8 @@ object MapSized {
new MapSized[HNil] { type Out = HNil }
implicit def onCons[H, T <: HList, TS <: HList, HSize <: Nat](
implicit
rest: MapSized.Aux[T, TS],
headSize: Sized.Aux[H, HSize]
implicit @unused rest: MapSized.Aux[T, TS],
@unused headSize: Sized.Aux[H, HSize]
): MapSized.Aux[H :: T, HSize :: TS] =
new MapSized[H :: T] { type Out = HSize :: TS }
}
@ -220,33 +218,33 @@ object MapSized {
* included in the sum.
*
* @tparam Elem the type of the element to stop computing at
* @tparam List the list of types to compute over
* @tparam Items the list of types to compute over
*/
trait SizeUntil[Elem, List <: HList] {
trait SizeUntil[Elem, Items <: HList] {
type Out <: Nat
val asInt: Int
}
object SizeUntil {
type Aux[Elem, List <: HList, X] = SizeUntil[Elem, List] { type Out = X }
type Aux[Elem, Items <: HList, X] = SizeUntil[Elem, Items] { type Out = X }
def apply[Elem, List <: HList](
implicit ev: SizeUntil[Elem, List]
): Aux[Elem, List, ev.Out] = ev
def apply[Elem, Items <: HList](
implicit ev: SizeUntil[Elem, Items]
): Aux[Elem, Items, ev.Out] = ev
implicit def instance[
Elem,
List <: HList,
Items <: HList,
PriorElems <: HList,
PriorFieldSizes <: HList,
PriorFieldsSize <: Nat
](
implicit
ev1: HListTakeUntil.Aux[Elem, List, PriorElems],
ev2: MapSized.Aux[PriorElems, PriorFieldSizes],
ev3: HListSum.Aux[PriorFieldSizes, PriorFieldsSize],
@unused ev1: HListTakeUntil.Aux[Elem, Items, PriorElems],
@unused ev2: MapSized.Aux[PriorElems, PriorFieldSizes],
@unused ev3: HListSum.Aux[PriorFieldSizes, PriorFieldsSize],
sizeAsInt: nat.ToInt[PriorFieldsSize]
): SizeUntil.Aux[Elem, List, PriorFieldsSize] =
new SizeUntil[Elem, List] {
): SizeUntil.Aux[Elem, Items, PriorFieldsSize] =
new SizeUntil[Elem, Items] {
type Out = PriorFieldsSize
val asInt = sizeAsInt()
}
@ -258,18 +256,18 @@ object SizeUntil {
* The map it produces is a scala [[mutable.Map]]. Additionally, it has a
* constraint that no type may appear twice in the input list.
*
* @tparam List the list to start from
* @tparam Items the list to start from
*/
trait MapsOf[List <: HList] {
trait MapsOf[Items <: HList] {
type Out <: HList
val instance: Out
}
object MapsOf {
type Aux[List <: HList, X] = MapsOf[List] { type Out = X }
type Aux[Items <: HList, X] = MapsOf[Items] { type Out = X }
def apply[List <: HList](
implicit ev: MapsOf[List]
): MapsOf.Aux[List, ev.Out] = ev
def apply[Items <: HList](
implicit ev: MapsOf[Items]
): MapsOf.Aux[Items, ev.Out] = ev
implicit def onNil: MapsOf.Aux[HNil, HNil] =
new MapsOf[HNil] {
@ -279,7 +277,7 @@ object MapsOf {
implicit def onCons[Head, Tail <: HList](
implicit ev: MapsOf[Tail],
distinct: IsDistinctConstraint[Head :: Tail]
@unused distinct: IsDistinctConstraint[Head :: Tail]
): MapsOf.Aux[Head :: Tail, mutable.Map[Int, Head] :: ev.Out] =
new MapsOf[Head :: Tail] {
type Out = mutable.Map[Int, Head] :: ev.Out
@ -447,7 +445,7 @@ object Graph {
* it cannot be specialised for primitive types. [[Array]], on the other
* hand, can be.
*/
final class Storage(elemSize: Int) {
final class Storage(@unused elemSize: Int) {
var length: Int = 0
var array: Array[Int] = new Array[Int](length)
@ -506,8 +504,8 @@ object Graph {
* @tparam C the type of the component to access
* @return a reference to the component at `index`
*/
def componentReferenceFromIndex[C <: Component](index: Int)(
implicit ev: HasComponent[G, C]
def componentRefFromIndex[C <: Component](index: Int)(
implicit @unused ev: HasComponent[G, C]
): Graph.Component.Ref[G, C] = {
Graph.Component.Ref(index)
}
@ -688,8 +686,8 @@ object Graph {
ComponentListLength <: Nat
](
implicit
ev1: Component.List.Aux[G, ComponentList],
ev2: hlist.Length.Aux[ComponentList, ComponentListLength],
@unused ev1: Component.List.Aux[G, ComponentList],
@unused ev2: hlist.Length.Aux[ComponentList, ComponentListLength],
componentSizesEv: ComponentListToSizes[G, ComponentList],
len: nat.ToInt[ComponentListLength]
): GraphInfo[G] = new GraphInfo[G] {
@ -719,13 +717,13 @@ object Graph {
FieldList <: HList
](
implicit
ev1: Component.List.Aux[G, ComponentList],
ev2: Component.Field.List.Aux[G, C, FieldList],
ev3: HListTakeUntil.Aux[C, ComponentList, PrevComponentList],
ev4: hlist.Length.Aux[PrevComponentList, ComponentIndex],
@unused ev1: Component.List.Aux[G, ComponentList],
@unused ev2: Component.Field.List.Aux[G, C, FieldList],
@unused ev3: HListTakeUntil.Aux[C, ComponentList, PrevComponentList],
@unused ev4: hlist.Length.Aux[PrevComponentList, ComponentIndex],
componentIndexEv: nat.ToInt[ComponentIndex],
componentSizeEv: KnownSize[FieldList],
listContainsComponent: Selector[ComponentList, C]
@unused listContainsComponent: Selector[ComponentList, C]
): HasComponent[G, C] = new HasComponent[G, C] {
val componentIndex = componentIndexEv()
val componentSize = componentSizeEv.asInt
@ -753,10 +751,10 @@ object Graph {
FieldList <: HList
](
implicit
ev1: Component.Field.List.Aux[G, C, FieldList],
@unused ev1: Component.Field.List.Aux[G, C, FieldList],
evx: HasComponent[G, C],
fieldOffsetEv: SizeUntil[F, FieldList],
containsField: Selector[FieldList, F]
@unused containsField: Selector[FieldList, F]
): HasComponentField[G, C, F] = new HasComponentField[G, C, F] {
val componentIndex = evx.componentIndex
val componentSize = evx.componentSize
@ -837,7 +835,7 @@ object Graph {
* [[V]]
*/
def unsafeAs[V <: F](
implicit variantIndexed: VariantIndexed[F, V]
implicit @unused variantIndexed: VariantIndexed[F, V]
): Component.Refined[F, V, Component.Ref[G, C]] = {
Component.Refined[F, V, Component.Ref[G, C]](component)
}
@ -859,5 +857,7 @@ object Graph {
None
}
}
// TODO [AA] Add a utility to automatically refine variant branches
}
}

View File

@ -208,8 +208,7 @@ object Macro {
enclosingName,
fieldName,
graphTypeName,
valClassTypeName,
isSimple
valClassTypeName
)
accessorDefs ++ valClassAccessors
@ -478,8 +477,6 @@ object Macro {
* `enclosingName` above)
* @param graphTypeName the name of the graph type
* @param valClassTypeName the typename of the value class
* @param isSimple whather or not the accessors are being generated for a
* simple field or a variant field
* @return the definitions of the value class getter and the value class
* setter
*/
@ -488,8 +485,7 @@ object Macro {
enclosingName: TypeName,
fieldName: TypeName,
graphTypeName: TypeName,
valClassTypeName: TypeName,
isSimple: Boolean
valClassTypeName: TypeName
): List[Tree] = {
val graphTermName = graphTypeName.toTermName

View File

@ -4,9 +4,6 @@ import org.enso.graph.definition.Macro.{component, field, opaque}
import org.enso.graph.{Graph => PrimGraph}
import shapeless.{::, HNil}
// import intentionally left unused
import shapeless.nat._
/** This file provides a small graph implementation for testing purposes.
*
* It creates a small graph implementation that tests both the various features
@ -86,6 +83,7 @@ object GraphTestDefinition {
// === Component Field Definitions ========================================
// ========================================================================
object Node {
@field object Shape {

View File

@ -78,7 +78,7 @@ object CoreGraph {
* would only make sense if the link direction was reversed. Instead, it
* holds unsafe references to the incoming link in the underlying graph.
* These can be turned into the [[Link]]s directly by using
* [[PrimGraph.GraphData.componentReferenceFromIndex()]].
* [[PrimGraph.GraphData.componentRefFromIndex()]].
*
* @param parents a vector containing the raw indices of the parent links
* @tparam G the graph type
@ -561,20 +561,20 @@ object CoreGraph {
PrimGraph.Component.Refined[Node.Shape, V, Node[CoreGraph]](node)
}
/** Sets the shape of the provided [[node]] to [[Shape]].
/** Sets the shape of the provided [[node]] to [[NodeShape]].
*
* @param node the node to set
* @param ev evidence that [[Shape]] belongs to an indexed variant
* @param ev evidence that [[NodeShape]] belongs to an indexed variant
* @param graph the graph to mutate
* @tparam Shape the shape to set the node to
* @tparam NodeShape the shape to set the node to
*/
def setShape[Shape <: Node.Shape](
def setShape[NodeShape <: Node.Shape](
node: Node[CoreGraph]
)(
implicit ev: VariantIndexed[Node.Shape, Shape],
implicit ev: VariantIndexed[Node.Shape, NodeShape],
graph: PrimGraph.GraphData[CoreGraph]
): Unit = {
graph.unsafeSetVariantCase[Nodes, Node.Shape, Shape](node)
graph.unsafeSetVariantCase[Nodes, Node.Shape, NodeShape](node)
}
/** Checks whether a given node represents some kind of language error.

View File

@ -1,15 +1,15 @@
package org.enso.jsonrpcserver
import akka.actor.{ActorRef, ActorSystem, Props}
import akka.testkit.{ImplicitSender, TestKit, TestProbe}
import io.circe.literal._
import io.circe.parser._
import io.circe.{Decoder, Encoder, Json}
import org.scalatest.EitherValues
import org.enso.jsonrpcserver.MessageHandler.{Connected, WebMessage}
import org.scalatest.BeforeAndAfterAll
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike
import scala.concurrent.duration._
import io.circe.parser._
import io.circe.literal._
import org.enso.jsonrpcserver.MessageHandler.{Connected, WebMessage}
class MessageHandlerTest
extends TestKit(ActorSystem("TestSystem"))
@ -34,9 +34,9 @@ class MessageHandlerTest
case object MyError extends Error(15, "Test error")
object MyProtocol {
import cats.syntax.functor._
import io.circe.generic.auto._
import io.circe.syntax._
import cats.syntax.functor._
val encoder: Encoder[PayloadOf[Method]] = Encoder.instance {
case m: MyRequestParams => m.asJson

View File

@ -18,7 +18,7 @@ trait Pass[In, Out] {
* @param metadata any metadata produced by the pass
* @tparam TOut the type of the pass output metadata
*/
final case class Output[TOut](result: Out, metadata: TOut)
sealed case class Output[TOut](result: Out, metadata: TOut)
/**
* Executes the pass on the source, with optional input metadata.

View File

@ -1,15 +1,17 @@
package org.enso.compiler.core
import cats.data.NonEmptyList
import com.oracle.truffle.api.nodes.UnexpectedResultException
import org.enso.core.CoreGraph.DefinitionGen.Node.{
Shape => NodeShape,
LocationVal
LocationVal,
Shape => NodeShape
}
import org.enso.core.CoreGraph.{DefinitionGen => CoreDef}
import org.enso.graph.{Graph => PrimGraph}
import org.enso.syntax.text.{AST, Location => AstLocation}
import scala.annotation.tailrec
import scala.collection.mutable
// TODO [AA] Detailed semantic descriptions for each node shape in future.
// TODO [AA] Refactor over time to remove as much boilerplate as possible.
@ -18,6 +20,7 @@ import scala.annotation.tailrec
// TODO [AA] Need to present a nice interface
// - Copy subsection of graph
// - Check equality for subsection of graph
// - These need to be _very_ careful about cycles
/** [[Core]] is the sophisticated internal representation supported by the
* compiler.
@ -76,8 +79,10 @@ object Core {
// === Components ===========================================================
type Node = CoreDef.Node[Graph]
type Link = CoreDef.Link[Graph]
type Node = CoreDef.Node[Graph]
type Nodes = CoreDef.Nodes
type Link = CoreDef.Link[Graph]
type Links = CoreDef.Links
type RefinedNode[V <: CoreDef.Node.Shape] =
PrimGraph.Component.Refined[NodeShape, V, Node]
@ -152,7 +157,7 @@ object Core {
head: Node,
tail: Node
)(implicit core: Core): ConsErrOr[NodeShape.MetaList] = {
if (Utility.isListNode(tail)) {
if (Utility.ListOps.is(tail)) {
val node = CoreDef.Node.addRefined[NodeShape.MetaList]
val headLink = Link.New.Connected(node, head)
@ -165,7 +170,7 @@ object Core {
Right(node)
} else {
val errorElems = Utility.coreListFrom(tail)
val errorElems = Utility.ListOps.from(tail)
val errorNode = ConstructionError(errorElems, tail.location)
Left(errorNode)
@ -363,13 +368,13 @@ object Core {
definitions: Node,
location: Location
)(implicit core: Core): ConsErrOr[NodeShape.ModuleDef] = {
if (!Utility.isListNode(imports)) {
val errorElems = Utility.coreListFrom(imports)
if (!Utility.ListOps.is(imports)) {
val errorElems = Utility.ListOps.from(imports)
val error = ConstructionError(errorElems, imports.location)
Left(error)
} else if (!Utility.isListNode(definitions)) {
val errorElems = Utility.coreListFrom(definitions)
} else if (!Utility.ListOps.is(definitions)) {
val errorElems = Utility.ListOps.from(definitions)
val error = ConstructionError(errorElems, definitions.location)
Left(error)
@ -401,7 +406,7 @@ object Core {
segments: Node,
location: Location
)(implicit core: Core): ConsErrOr[NodeShape.Import] = {
if (Utility.isListNode(segments)) {
if (Utility.ListOps.is(segments)) {
val node = CoreDef.Node.addRefined[NodeShape.Import]
val segmentsLink = Link.New.Connected(node, segments)
@ -412,7 +417,7 @@ object Core {
Right(node)
} else {
val errList = Utility.coreListFrom(segments)
val errList = Utility.ListOps.from(segments)
val errNode = ConstructionError(errList, segments.location)
Left(errNode)
@ -471,7 +476,7 @@ object Core {
args: Node,
location: Location
)(implicit core: Core): ConsErrOr[NodeShape.AtomDef] = {
if (Utility.isListNode(args)) {
if (Utility.ListOps.is(args)) {
val node = CoreDef.Node.addRefined[NodeShape.AtomDef]
val nameLink = Link.New.Connected(node, name)
@ -484,7 +489,7 @@ object Core {
Right(node)
} else {
val errList = Utility.coreListFrom(args)
val errList = Utility.ListOps.from(args)
val errNode = ConstructionError(errList, args.location)
Left(errNode)
@ -506,13 +511,13 @@ object Core {
body: Node,
location: Location
)(implicit core: Core): ConsErrOr[NodeShape.TypeDef] = {
if (!Utility.isListNode(typeParams)) {
val errList = Utility.coreListFrom(typeParams)
if (!Utility.ListOps.is(typeParams)) {
val errList = Utility.ListOps.from(typeParams)
val errNode = ConstructionError(errList, typeParams.location)
Left(errNode)
} else if (!Utility.isListNode(body)) {
val errList = Utility.coreListFrom(body)
} else if (!Utility.ListOps.is(body)) {
val errList = Utility.ListOps.from(body)
val errNode = ConstructionError(errList, body.location)
Left(errNode)
@ -841,7 +846,7 @@ object Core {
body: Node,
location: Location
)(implicit core: Core): ConsErrOr[NodeShape.FunctionDef] = {
if (Utility.isListNode(args)) {
if (Utility.ListOps.is(args)) {
val node = CoreDef.Node.addRefined[NodeShape.FunctionDef]
val nameLink = Link.New.Connected(node, name)
@ -856,7 +861,7 @@ object Core {
Right(node)
} else {
val errList = Utility.coreListFrom(args)
val errList = Utility.ListOps.from(args)
val errNode = ConstructionError(errList, args.location)
Left(errNode)
@ -900,7 +905,7 @@ object Core {
Right(node)
} else {
val errList = Utility.coreListFrom(function)
val errList = Utility.ListOps.from(function)
val errNode = ConstructionError(errList, function.location)
Left(errNode)
@ -946,7 +951,7 @@ object Core {
default: Node,
location: Location
)(implicit core: Core): ConsErrOr[NodeShape.DefinitionArgument] = {
if (Utility.isBoolNode(suspended)) {
if (Utility.BoolOps.is(suspended)) {
val node = CoreDef.Node.addRefined[NodeShape.DefinitionArgument]
val nameLink = Link.New.Connected(node, name)
@ -961,7 +966,7 @@ object Core {
Right(node)
} else {
val errList = Utility.coreListFrom(suspended)
val errList = Utility.ListOps.from(suspended)
val errNode = ConstructionError(errList, suspended.location)
Left(errNode)
@ -1224,7 +1229,7 @@ object Core {
returnVal: Node,
location: Location
)(implicit core: Core): ConsErrOr[NodeShape.Block] = {
if (Utility.isListNode(expressions)) {
if (Utility.ListOps.is(expressions)) {
val node = CoreDef.Node.addRefined[NodeShape.Block]
val expressionsLink = Link.New.Connected(node, expressions)
@ -1237,7 +1242,7 @@ object Core {
Right(node)
} else {
val errList = Utility.coreListFrom(expressions)
val errList = Utility.ListOps.from(expressions)
val errNode = ConstructionError(errList, expressions.location)
Left(errNode)
@ -1287,7 +1292,7 @@ object Core {
branches: Node,
location: Location
)(implicit core: Core): ConsErrOr[NodeShape.CaseExpr] = {
if (Utility.isListNode(branches)) {
if (Utility.ListOps.is(branches)) {
val node = CoreDef.Node.addRefined[NodeShape.CaseExpr]
val scrutineeLink = Link.New.Connected(node, scrutinee)
@ -1300,7 +1305,7 @@ object Core {
Right(node)
} else {
val errList = Utility.coreListFrom(branches)
val errList = Utility.ListOps.from(branches)
val errNode = ConstructionError(errList, branches.location)
Left(errNode)
@ -1495,7 +1500,7 @@ object Core {
Right(node)
case _ =>
val errList = Utility.coreListFrom(code)
val errList = Utility.ListOps.from(code)
val errNode = ConstructionError(errList, code.location)
Left(errNode)
@ -1539,11 +1544,12 @@ object Core {
erroneousCore: Node,
location: Location
)(implicit core: Core): RefinedNode[NodeShape.ConstructionError] = {
val erroneousCoreList: Node = if (Utility.isListNode(erroneousCore)) {
erroneousCore
} else {
Utility.coreListFrom(erroneousCore)
}
val erroneousCoreList: Node =
if (Utility.ListOps.is(erroneousCore)) {
erroneousCore
} else {
Utility.ListOps.from(erroneousCore)
}
val node = CoreDef.Node.addRefined[NodeShape.ConstructionError]
@ -1584,131 +1590,284 @@ object Core {
/** Utility functions for working with nodes. */
object Utility {
/** Checks if two lists on the core graph are equal.
*
* Equality for lists is defined as the lists containing the same nodes
* as members. The nodes making up the lists themselves need not be
* equal.
*
* @param left the first list
* @param right the second list
* @param core an implicit instance of core
* @return `true` if [[left]] is equal to [[right]], `false` otherwise
*/
def listsAreEqual(
left: RefinedNode[MetaList],
right: RefinedNode[MetaList]
)(implicit core: Core): Boolean = {
@tailrec
def go(
left: Node,
right: Node
): Boolean = {
left match {
case NodeShape.MetaNil.any(_) =>
right match {
case NodeShape.MetaNil.any(_) => true
case _ => false
}
case NodeShape.MetaList.any(left1) =>
right match {
case NodeShape.MetaList.any(right1) =>
(left1.head.target == right1.head.target) && go(
left1.tail.target,
right1.tail.target
)
object ListOps {
/** Checks if two lists on the core graph are equal.
*
* Equality for lists is defined as the lists containing the same nodes
* as members. The nodes making up the lists themselves need not be
* equal.
*
* @param left the first list
* @param right the second list
* @param core an implicit instance of core
* @return `true` if [[left]] is equal to [[right]], `false` otherwise
*/
def equals(
left: RefinedNode[MetaList],
right: RefinedNode[MetaList]
)(implicit core: Core): Boolean = {
val visitedNodesInLeft = mutable.ArrayBuffer[Int]()
val visitedNodesInRight = mutable.ArrayBuffer[Int]()
@tailrec
def go(
left: Node,
right: Node
): Boolean = {
val leftIsVisited = visitedNodesInLeft.contains(left.ix)
val rightIsVisited = visitedNodesInRight.contains(right.ix)
if (leftIsVisited && rightIsVisited) {
true
} else if (!leftIsVisited && !rightIsVisited) {
visitedNodesInLeft.append(left.ix)
visitedNodesInRight.append(right.ix)
left match {
case NodeShape.MetaNil.any(_) =>
right match {
case NodeShape.MetaNil.any(_) => true
case _ => false
}
case NodeShape.MetaList.any(left1) =>
right match {
case NodeShape.MetaList.any(right1) =>
(left1.head.target == right1.head.target) && go(
left1.tail.target,
right1.tail.target
)
case _ => false
}
case _ => false
}
case _ => false
} else {
false
}
}
go(left, right)
}
/** Checks if the provided node is a meta-level list node.
*
* A node is considered to be a list node when it has either the shape
* [[NodeShape.MetaList]] or the shape [[NodeShape.MetaNil]].
*
* @param node the node to check
* @param core an implicit instance of core
* @return `true` if [[node]] is a list node, otherwise `false`
*/
def is(node: Node)(implicit core: Core): Boolean = {
node match {
case NodeShape.MetaNil.any(_) => true
case NodeShape.MetaList.any(_) => true
case _ => false
}
}
go(left, right)
}
/** Finds the end of a list.
*
* @param node the list node to find the end of
* @param core an implicit instance of core
* @return [[Some]] when the [[node]] has an end, otherwise [[None]] if
* [[node]] is cyclic or not a list
*/
def end(node: Node)(implicit core: Core): Option[Node] = {
val visitedNodes = mutable.ArrayBuffer[Int]()
/** Checks if the provided node is a meta-level boolean node.
*
* @param node the node to check
* @param core an implicit instance of core
* @return `true` if [[node]] is a meta boolean, `false` otherwise
*/
def isBoolNode(node: Node)(implicit core: Core): Boolean = {
node match {
case NodeShape.MetaTrue.any(_) => true
case NodeShape.MetaFalse.any(_) => true
case _ => false
@tailrec
def go(node: Node): Option[Node] = {
if (visitedNodes contains node.ix) {
None
} else {
node match {
case NodeShape.MetaNil.any(_) => Some(node)
case NodeShape.MetaList.any(list) => go(list.tail.target)
case _ => None
}
}
}
go(node)
}
/** Determines the length of a list.
*
* @param node the node representing a list
* @param core an implicit instance of core
* @return [[Some]] when [[node]] is a list that is not cyclical,
* [[None]] when [[node]] is not a list or is cylical
*/
def length(node: Node)(implicit core: Core): Option[Int] = {
val visitedNodes = mutable.ArrayBuffer[Int]()
var accumulator = 0
@tailrec
@throws[UnexpectedResultException]
def go(node: Node): Int = {
if (visitedNodes.contains(node.ix)) {
throw new UnexpectedResultException(-1)
} else {
visitedNodes.append(node.ix)
node match {
case NodeShape.MetaNil.any(_) => accumulator
case NodeShape.MetaList.any(list) =>
accumulator += 1
go(list.tail.target)
case _ => throw new UnexpectedResultException(-1)
}
}
}
try {
Some(go(node))
} catch {
case _: UnexpectedResultException => None
}
}
/** Gets the node at a particular index in the list.
*
* @param node the node representing a list
* @param index the index of the item you want to access
* @param core an implicit instance of core
* @return [[Some]] if the item exists, [[None]] if the index is out of
* bounds, [[node]] is not a list, or if [[index]] is past the
* loop point in a cyclical list
*/
def at(node: Node, index: Int)(implicit core: Core): Option[Node] = {
val visitedNodes = mutable.ArrayBuffer[Int]()
@tailrec
def go(node: Node, currentIndex: Int): Option[Node] = {
if (visitedNodes.contains(node.ix)) {
None
} else {
visitedNodes.append(node.ix)
node match {
case NodeShape.MetaNil.any(_) => None
case NodeShape.MetaList.any(list) =>
if (currentIndex == index) {
Some(list.head.target)
} else {
go(list.tail.target, currentIndex + 1)
}
case _ => None
}
}
}
go(node, 0)
}
/** Constructs a meta list on the [[Core]] graph from a single core
* expression.
*
* @param node the expression to turn into a valid list
* @param core an implicit instance of core
* @return a node representing the head of a meta-level list
*/
def from(
node: Node
)(implicit core: Core): RefinedNode[NodeShape.MetaList] = {
from(NonEmptyList(node, List()))
}
/** Constructs a meta list on the [[Core]] graph from a list of [[Core]]
* nodes.
*
* @param nodes the nodes to make a list out of
* @param core an implicit instance of core
* @return a node representing the head of a meta-level list
*/
def from(
nodes: NonEmptyList[Node]
)(implicit core: Core): RefinedNode[NodeShape.MetaList] = {
val nodesWithNil = nodes :+ New.MetaNil().wrapped
// Note [Unsafety in List Construction]
val unrefinedMetaList =
nodesWithNil.toList.reduceRight(
(l, r) =>
New
.MetaList(l, r)
.getOrElse(throw new RuntimeException("Should never happen."))
.wrapped
)
PrimGraph.Component.Refined
.wrap[NodeShape, NodeShape.MetaList, Node](unrefinedMetaList)
}
/* Note [Unsafety in List Construction]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This makes use of internal implementation details to know that calling
* `right` here is always safe. The error condition for the `metaList`
* constructor occurs when the `tail` argument doesn't point to a valid
* list element, but here we construct that element directly and hence we
* know that it is valid.
*
* Furthermore, we can unconditionally refine the type as we know that the
* node we get back must be a MetaList, and we know that the input is not
* empty.
*
* It also bears noting that for this to be safe we _must_ use a right
* reduce, rather than a left reduce, otherwise the elements will not be
* constructed properly. This does, however, mean that this can stack
* overflow when provided with too many elements.
*/
/** Generates a meta list on the core graph of length [[length]], with
* each cell filled with an empty node.
*
* @param length the length of the list to generate
* @param core an implicit instance of core
* @return a list of length [[length]] if `length > 0`, otherwise an
* empty list.
*/
def ofLength(length: Int)(implicit core: Core): Node = {
val nil = Node.New.MetaNil
@tailrec
def go(tail: Node, remainingLength: Int): Node = {
if (remainingLength == 0) {
tail
} else {
val cons = Node.New
.MetaList(Node.New.Empty(), tail)
.getOrElse(throw new RuntimeException("Should never happen"))
go(cons, remainingLength - 1)
}
}
if (length <= 0) {
nil
} else {
go(nil, length)
}
}
}
/** Checks if the provided node is a meta-level list node.
*
* A node is considered to be a list node when it has either the shape
* [[NodeShape.MetaList]] or the shape [[NodeShape.MetaNil]].
*
* @param node the node to check
* @param core an implicit instance of core
* @return `true` if [[node]] is a list node, otherwise `false`
*/
def isListNode(node: Node)(implicit core: Core): Boolean = {
node match {
case NodeShape.MetaNil.any(_) => true
case NodeShape.MetaList.any(_) => true
case _ => false
object BoolOps {
/** Checks if the provided node is a meta-level boolean node.
*
* @param node the node to check
* @param core an implicit instance of core
* @return `true` if [[node]] is a meta boolean, `false` otherwise
*/
def is(node: Node)(implicit core: Core): Boolean = {
node match {
case NodeShape.MetaTrue.any(_) => true
case NodeShape.MetaFalse.any(_) => true
case _ => false
}
}
}
/** Constructs a meta list on the [[Core]] graph from a single core
* expression.
*
* @param node the expression to turn into a valid list
* @param core an implicit instance of core
* @return a node representing the head of a meta-level list
*/
def coreListFrom(
node: Node
)(implicit core: Core): RefinedNode[NodeShape.MetaList] = {
coreListFrom(NonEmptyList(node, List()))
}
/** Constructs a meta list on the [[Core]] graph from a list of [[Core]]
* nodes.
*
* @param nodes the nodes to make a list out of
* @param core an implicit instance of core
* @return a node representing the head of a meta-level list
*/
def coreListFrom(
nodes: NonEmptyList[Node]
)(implicit core: Core): RefinedNode[NodeShape.MetaList] = {
val nodesWithNil = nodes :+ New.MetaNil().wrapped
// Note [Unsafety in List Construction]
val unrefinedMetaList =
nodesWithNil.toList.reduceRight(
(l, r) => New.MetaList(l, r).right.get.wrapped
)
PrimGraph.Component.Refined
.wrap[NodeShape, NodeShape.MetaList, Node](unrefinedMetaList)
}
/* Note [Unsafety in List Construction]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This makes use of internal implementation details to know that calling
* `right` here is always safe. The error condition for the `metaList`
* constructor occurs when the `tail` argument doesn't point to a valid
* list element, but here we construct that element directly and hence we
* know that it is valid.
*
* Furthermore, we can unconditionally refine the type as we know that the
* node we get back must be a MetaList, and we know that the input is not
* empty.
*
* It also bears noting that for this to be safe we _must_ use a right
* reduce, rather than a left reduce, otherwise the elements will not be
* constructed properly. This does, however, mean that this can stack
* overflow when provided with too many elements.
*/
}
}

View File

@ -1,8 +1,8 @@
package org.enso.compiler.test
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike
trait CompilerRunner {}
trait CompilerTest extends AnyFlatSpec with Matchers with CompilerRunner
trait CompilerTest extends AnyWordSpecLike with Matchers with CompilerRunner

View File

@ -1,18 +1,11 @@
package org.enso.compiler.test.core
import org.enso.compiler.test.CompilerTest
import org.enso.core.CoreGraph.DefinitionGen.Node.LocationVal
import org.enso.core.CoreGraph.DefinitionGen.{
AstStorage,
Link,
LiteralStorage,
NameStorage,
Node,
ParentStorage
}
import org.scalatest.BeforeAndAfterEach
import org.enso.graph.{Graph => PrimGraph}
import org.enso.syntax.text.AST
import org.scalatest.BeforeAndAfterEach
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
/** This file tests the primitive, low-level operations on core.
*
@ -22,14 +15,14 @@ import org.enso.syntax.text.AST
* PLEASE NOTE: Many of these tests will be removed once the smart constructors
* exist.
*/
class CorePrimTest extends CompilerTest with BeforeAndAfterEach {
class CorePrimTest extends AnyFlatSpec with Matchers with BeforeAndAfterEach {
// === Test Setup ===========================================================
import org.enso.core.CoreGraph.DefinitionGen._
import org.enso.core.CoreGraph.DefinitionGen.Link.Shape._
import org.enso.core.CoreGraph.DefinitionGen.Node.Shape._
import org.enso.core.CoreGraph.DefinitionGen.Node.Location._
import org.enso.core.CoreGraph.DefinitionGen.Node.ParentLinks._
import org.enso.core.CoreGraph.DefinitionGen.Node.Shape._
import org.enso.core.CoreGraph.DefinitionGen._
// Reassignable mutable fixture elements
implicit var graph: PrimGraph.GraphData[CoreGraph] = _
@ -140,8 +133,8 @@ class CorePrimTest extends CompilerTest with BeforeAndAfterEach {
consNode.parents = Vector()
// Intentional re-assignment in reverse order to check for clobbering
consNode.tail = nilLink
consNode.head = emptyLink
consNode.tail = nilLink
consNode.head = emptyLink
consNode.head shouldEqual emptyLink
consNode.tail shouldEqual nilLink

View File

@ -1,13 +1,246 @@
package org.enso.compiler.test.core
import cats.data.NonEmptyList
import org.enso.compiler.core.Core
import org.enso.compiler.core.Core.Node.Utility
import org.enso.compiler.test.CompilerTest
import org.enso.core.CoreGraph.DefinitionGen.Node.{Shape => NodeShape}
import org.enso.core.CoreGraph.{DefinitionGen => CoreDef}
import org.enso.graph.{Graph => PrimGraph}
// TODO [AA] Tests of the actual core usage in semi-realistic scenarios:
// - Diamond
// - Multi-level tree
// - Larger diamonds
// - Cycle
// - Linked List
// - Parent Link walk to same place
// - etc.
class CoreTest extends CompilerTest {}
class CoreTest extends CompilerTest {
// === Test Setup ===========================================================
import Core._
import CoreDef.Link.Shape._
import CoreDef.Node.ParentLinks._
import CoreDef.Node.Shape._
import PrimGraph.Component.Refined._
import PrimGraph.VariantCast
// === Useful Constants =====================================================
val constantLocationStart = 201
val constantLocationEnd = 1337
val dummyLocation: Core.Location =
CoreDef.Node.LocationVal(constantLocationStart, constantLocationEnd)
// === More Complex Graph Shape Tests =======================================
// TODO [AA] Once deletion and replacement functions exist, expand these tests
// to check that the shapes behave properly under usage of such functions.
"Diamonds constructed on the graph" should {
implicit val core: Core = new Core()
val fnName = Node.New.Name("foo", dummyLocation)
val binding1Name = Node.New.Name("a", dummyLocation)
val binding1 = Node.New.Binding(binding1Name, fnName, dummyLocation)
val binding2Name = Node.New.Name("b", dummyLocation)
val binding2 = Node.New.Binding(binding2Name, fnName, dummyLocation)
val bindingsList = Utility.ListOps.from(binding1)
val block =
Node.New.Block(bindingsList, binding2, dummyLocation).getOrElse(fail)
"have multiple parents for the node at the bottom of the diamond" in {
fnName.parents.size shouldEqual 2
fnName.parents should contain(binding1.expression.ix)
fnName.parents should contain(binding2.expression.ix)
}
"ensure that traversals through both paths reach the same place" in {
val fnNameViaLeftPath = block.expressions.target
.unsafeAs[NodeShape.MetaList]
.head
.target
.unsafeAs[NodeShape.Binding]
.expression
.target
val fnNameViaRightPath =
block.returnVal.target.unsafeAs[NodeShape.Binding].expression.target
fnNameViaLeftPath shouldEqual fnNameViaRightPath
}
}
"Multi-level trees constructed on the graph" should {
implicit val core: Core = new Core()
/* Builds the following tree for purposes of the test
*
* 1
* | \
* 2 3
* | \
* 4 5
* | \
* 6 7
*/
val node7 = Node.New.Empty()
val node6 = Node.New.Empty()
val node5 = Node.New.LeftSection(node6, node7, dummyLocation)
val node4 = Node.New.Empty()
val node3 = Node.New.Empty()
val node2 = Node.New.LeftSection(node4, node5, dummyLocation)
val node1 = Node.New.LeftSection(node2, node3, dummyLocation)
"allow walking to all the leaf nodes" in {
node1.arg.target
.unsafeAs[NodeShape.LeftSection]
.arg
.target shouldEqual node4
node1.operator.target shouldEqual node3
node1.arg.target
.unsafeAs[NodeShape.LeftSection]
.operator
.target
.unsafeAs[NodeShape.LeftSection]
.arg
.target shouldEqual node6
node1.arg.target
.unsafeAs[NodeShape.LeftSection]
.operator
.target
.unsafeAs[NodeShape.LeftSection]
.operator
.target shouldEqual node7
}
"allow walking from leaf to parent via parent links" in {
val node5handle =
core.graph.componentRefFromIndex[Links](node7.parents.head).source
val node2handle =
core.graph
.componentRefFromIndex[Links](node5handle.parents.head)
.source
val node1handle =
core.graph
.componentRefFromIndex[Links](node2handle.parents.head)
.source
node1handle shouldEqual node1
}
}
"Cycles on the graph" should {
implicit val core: Core = new Core()
val empty = Node.New.Empty()
val tempNil = Node.New.MetaNil()
val cons4 = Node.New.MetaList(empty, tempNil).getOrElse(fail)
val cons3 = Node.New.MetaList(empty, cons4).getOrElse(fail)
val cons2 = Node.New.MetaList(empty, cons3).getOrElse(fail)
val cons1 = Node.New.MetaList(empty, cons2).getOrElse(fail)
// Link the nodes in a loop
val loopLink = Link.New.Connected(cons4, cons1)
cons4.tail = loopLink
"allow correct traversal" in {
cons1.tail.target
.unsafeAs[NodeShape.MetaList] // cons2
.tail
.target
.unsafeAs[NodeShape.MetaList] // cons3
.tail
.target
.unsafeAs[NodeShape.MetaList] // cons4
.tail
.target shouldEqual cons1
}
"allow reverse traversal" in {
val cons4ref =
core.graph.componentRefFromIndex[Links](cons1.parents.head).target
val cons3ref =
core.graph.componentRefFromIndex[Links](cons4ref.parents.head).target
val cons2ref =
core.graph.componentRefFromIndex[Links](cons3ref.parents.head).target
val cons1ref =
core.graph.componentRefFromIndex[Links](cons2ref.parents.head).target
cons1ref shouldEqual cons1
}
}
"Linked lists on the graph" should {
implicit val core: Core = new Core()
val myBindingName = Node.New.Name("a", dummyLocation)
val myFnName = Node.New.Name("function", dummyLocation)
val myBinding = Node.New.Binding(myBindingName, myFnName, dummyLocation)
val list = Utility.ListOps.from(
NonEmptyList(
myBinding.wrapped,
List(
myBindingName.wrapped,
myFnName.wrapped
)
)
)
"should allow finding of their end" in {
list.tail.target
.unsafeAs[NodeShape.MetaList] // cons2
.tail
.target
.unsafeAs[NodeShape.MetaList] // cons3
.tail
.target shouldEqual Utility.ListOps.end(list).get
}
"be able to contain complex structures" in {
val bindingRef =
Utility.ListOps.at(list, 0).getOrElse(fail).unsafeAs[NodeShape.Binding]
bindingRef shouldEqual myBinding
bindingRef.name shouldEqual myBindingName
bindingRef.expression shouldEqual myFnName
}
"allow forward traversal" in {
list.tail.target
.unsafeAs[NodeShape.MetaList]
.head
.target shouldEqual myBindingName
}
"allow reverse traversal via parent links" in {
val listEnd = Utility.ListOps.end(list).get
listEnd.is[NodeShape.MetaNil] shouldEqual true
val cons3ref =
core.graph
.componentRefFromIndex[Links](listEnd.parents.head)
.source
.as[NodeShape.MetaList]
.getOrElse(fail)
cons3ref.head.target.is[NodeShape.Name] shouldEqual true
cons3ref.head.target shouldEqual myFnName
val cons2ref = core.graph
.componentRefFromIndex[Links](cons3ref.parents.head)
.source
.unsafeAs[NodeShape.MetaList]
cons2ref.head.target.is[NodeShape.Name] shouldEqual true
cons2ref.head.target shouldEqual myBindingName
val cons1ref = core.graph
.componentRefFromIndex[Links](cons2ref.parents.head)
.source
.unsafeAs[NodeShape.MetaList]
cons1ref.head.target.is[NodeShape.Binding] shouldEqual true
cons1ref.head.target shouldEqual myBinding
}
}
}

View File

@ -2,19 +2,16 @@ package org.enso.compiler.test.core
import cats.data.NonEmptyList
import org.enso.compiler.core.Core
import org.enso.compiler.core.Core.Node.Utility.BoolOps
import org.enso.compiler.core.Core.Node.{Constants, Utility}
import org.enso.compiler.test.CompilerTest
import org.enso.core.CoreGraph.DefinitionGen.Node.{Shape => NodeShape}
import org.enso.core.CoreGraph.{DefinitionGen => CoreDef}
import org.enso.graph.{Graph => PrimGraph}
import org.enso.syntax.text.{AST, Location => AstLocation}
import org.scalatest.{Assertion, BeforeAndAfterEach}
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike
import org.scalatest.Assertion
class SmartConstructorsTest
extends AnyWordSpecLike
with Matchers
with BeforeAndAfterEach {
class SmartConstructorsTest extends CompilerTest {
// === Test Setup ===========================================================
@ -57,14 +54,12 @@ class SmartConstructorsTest
* @return a success if [[maybeErr]] is a failure and the contained error
* matches [[errList]], otherwise a failure
*/
def shouldFailWithResult[T <: CoreDef.Node.Shape](
errList: RefinedNode[MetaList]
): Assertion = {
def shouldFailWithResult(errList: RefinedNode[MetaList]): Assertion = {
maybeErr match {
case Left(err) =>
err.erroneousCore.target match {
case NodeShape.MetaList.any(xs) =>
if (Utility.listsAreEqual(xs, errList)) {
if (Utility.ListOps.equals(xs, errList)) {
succeed
} else {
fail
@ -92,7 +87,8 @@ class SmartConstructorsTest
implicit val core: Core = new Core()
val emptyNode = Node.New.Empty()
val nilNode = Node.New.MetaNil()
val listNode = Node.New.MetaList(emptyNode, nilNode).right.get
val listNode =
Node.New.MetaList(emptyNode, nilNode).getOrElse(fail)
"have valid fields" in {
listNode.location shouldEqual Node.Constants.invalidLocation
@ -114,7 +110,7 @@ class SmartConstructorsTest
"fail to construct if tail isn't a valid meta list" in {
val node = Node.New.MetaList(emptyNode, emptyNode)
node shouldFailWithResult Utility.coreListFrom(emptyNode)
node shouldFailWithResult Utility.ListOps.from(emptyNode)
}
}
@ -229,7 +225,7 @@ class SmartConstructorsTest
val name = Node.New.Name("MyModule", dummyLocation)
val module =
Node.New.ModuleDef(name, importNil, defNil, dummyLocation).right.get
Node.New.ModuleDef(name, importNil, defNil, dummyLocation).getOrElse(fail)
"have valid fields" in {
module.location shouldEqual dummyLocation
@ -253,12 +249,12 @@ class SmartConstructorsTest
"fail to construct if imports isn't a meta list" in {
val node = Node.New.ModuleDef(name, name, defNil, dummyLocation)
node shouldFailWithResult Utility.coreListFrom(name)
node shouldFailWithResult Utility.ListOps.from(name)
}
"fail if construct definitions isn't a meta list" in {
val node = Node.New.ModuleDef(name, importNil, name, dummyLocation)
node shouldFailWithResult Utility.coreListFrom(name)
node shouldFailWithResult Utility.ListOps.from(name)
}
}
@ -267,7 +263,7 @@ class SmartConstructorsTest
val segmentsNil = Node.New.MetaNil()
val empty = Node.New.Empty()
val imp =
Node.New.Import(segmentsNil, dummyLocation).right.get
Node.New.Import(segmentsNil, dummyLocation).getOrElse(fail)
"have valid fields" in {
imp.location shouldEqual dummyLocation
@ -286,7 +282,7 @@ class SmartConstructorsTest
"fail to construct if segments isn't a valid meta list" in {
val node = Node.New.Import(empty, dummyLocation)
node shouldFailWithResult Utility.coreListFrom(empty)
node shouldFailWithResult Utility.ListOps.from(empty)
}
}
@ -298,7 +294,9 @@ class SmartConstructorsTest
val binding =
Node.New.Binding(bindingSrc, bindingTgt, dummyLocation)
val topLevelBinding =
Node.New.TopLevelBinding(emptyModule, binding, dummyLocation).right.get
Node.New
.TopLevelBinding(emptyModule, binding, dummyLocation)
.getOrElse(fail)
"have valid fields" in {
topLevelBinding.location shouldEqual dummyLocation
@ -321,7 +319,7 @@ class SmartConstructorsTest
val node =
Node.New.TopLevelBinding(emptyModule, bindingSrc, dummyLocation)
node shouldFailWithResult Utility.coreListFrom(bindingSrc)
node shouldFailWithResult Utility.ListOps.from(bindingSrc)
}
}
@ -332,9 +330,9 @@ class SmartConstructorsTest
val name = Node.New.Empty()
val argName = Node.New.Empty()
val args = Utility.coreListFrom(argName)
val args = Utility.ListOps.from(argName)
val atomDef = Node.New.AtomDef(name, args, dummyLocation).right.get
val atomDef = Node.New.AtomDef(name, args, dummyLocation).getOrElse(fail)
"have valid fields" in {
atomDef.location shouldEqual dummyLocation
@ -356,7 +354,7 @@ class SmartConstructorsTest
"fail to construct if args is not a valid meta list" in {
val node = Node.New.AtomDef(name, argName, dummyLocation)
node shouldFailWithResult Utility.coreListFrom(argName)
node shouldFailWithResult Utility.ListOps.from(argName)
}
}
@ -365,12 +363,13 @@ class SmartConstructorsTest
val name = Node.New.Empty()
val tParam = Node.New.Empty()
val tParams = Utility.coreListFrom(tParam)
val tParams = Utility.ListOps.from(tParam)
val bodyExpr = Node.New.Empty()
val body = Utility.coreListFrom(bodyExpr)
val body = Utility.ListOps.from(bodyExpr)
val typeDef = Node.New.TypeDef(name, tParams, body, dummyLocation).right.get
val typeDef =
Node.New.TypeDef(name, tParams, body, dummyLocation).getOrElse(fail)
"have valid fields" in {
typeDef.location shouldEqual dummyLocation
@ -394,11 +393,11 @@ class SmartConstructorsTest
"fail to construct of the type params are not a valid meta list" in {
val node = Node.New.TypeDef(name, name, body, dummyLocation)
node shouldFailWithResult Utility.coreListFrom(name)
node shouldFailWithResult Utility.ListOps.from(name)
}
"fail to construct if the body is not a valid meta list" in {
val node = Node.New.TypeDef(name, tParams, name, dummyLocation)
node shouldFailWithResult Utility.coreListFrom(name)
node shouldFailWithResult Utility.ListOps.from(name)
}
}
@ -674,11 +673,11 @@ class SmartConstructorsTest
val name = Node.New.Empty()
val arg = Node.New.Empty()
val args = Utility.coreListFrom(arg)
val args = Utility.ListOps.from(arg)
val body = Node.New.Empty()
val functionDef =
Node.New.FunctionDef(name, args, body, dummyLocation).right.get
Node.New.FunctionDef(name, args, body, dummyLocation).getOrElse(fail)
"have valid fields" in {
functionDef.location shouldEqual dummyLocation
@ -702,7 +701,7 @@ class SmartConstructorsTest
"fail to construct if args is not a meta list" in {
val node = Node.New.FunctionDef(name, name, body, dummyLocation)
node shouldFailWithResult Utility.coreListFrom(name)
node shouldFailWithResult Utility.ListOps.from(name)
}
}
@ -716,7 +715,9 @@ class SmartConstructorsTest
val function = Node.New.Lambda(lamArg, lamBody, dummyLocation)
val methodDef =
Node.New.MethodDef(targetPath, name, function, dummyLocation).right.get
Node.New
.MethodDef(targetPath, name, function, dummyLocation)
.getOrElse(fail)
"have valid fields" in {
methodDef.location shouldEqual dummyLocation
@ -740,7 +741,7 @@ class SmartConstructorsTest
"fail to construct if function is not a valid function representation" in {
val node = Node.New.MethodDef(targetPath, name, name, dummyLocation)
node shouldFailWithResult Utility.coreListFrom(name)
node shouldFailWithResult Utility.ListOps.from(name)
}
}
@ -766,8 +767,7 @@ class SmartConstructorsTest
val arg = Node.New
.DefinitionArgument(name, suspended, default, dummyLocation)
.right
.get
.getOrElse(fail)
"have valid fields" in {
arg.location shouldEqual dummyLocation
@ -791,7 +791,7 @@ class SmartConstructorsTest
"fail to construct if suspended is not a meta boolean" in {
val node = Node.New.DefinitionArgument(name, name, default, dummyLocation)
node shouldFailWithResult Utility.coreListFrom(name)
node shouldFailWithResult Utility.ListOps.from(name)
}
}
@ -1004,10 +1004,11 @@ class SmartConstructorsTest
"Block nodes" should {
implicit val core: Core = new Core()
val expressions = Utility.coreListFrom(Node.New.Empty())
val expressions = Utility.ListOps.from(Node.New.Empty())
val returnVal = Node.New.Empty()
val block = Node.New.Block(expressions, returnVal, dummyLocation).right.get
val block =
Node.New.Block(expressions, returnVal, dummyLocation).getOrElse(fail)
"have valid fields" in {
block.location shouldEqual dummyLocation
@ -1028,7 +1029,7 @@ class SmartConstructorsTest
"fail to construct if expressions is not a valid meta list" in {
val node = Node.New.Block(returnVal, returnVal, dummyLocation)
node shouldFailWithResult Utility.coreListFrom(returnVal)
node shouldFailWithResult Utility.ListOps.from(returnVal)
}
}
@ -1064,10 +1065,10 @@ class SmartConstructorsTest
implicit val core: Core = new Core()
val scrutinee = Node.New.Empty()
val branches = Utility.coreListFrom(Node.New.Empty())
val branches = Utility.ListOps.from(Node.New.Empty())
val caseExpr =
Node.New.CaseExpr(scrutinee, branches, dummyLocation).right.get
Node.New.CaseExpr(scrutinee, branches, dummyLocation).getOrElse(fail)
"have valid fields" in {
caseExpr.location shouldEqual dummyLocation
@ -1088,7 +1089,7 @@ class SmartConstructorsTest
"fail to construct if branches is not a valid meta list" in {
val node = Node.New.CaseExpr(scrutinee, scrutinee, dummyLocation)
node shouldFailWithResult Utility.coreListFrom(scrutinee)
node shouldFailWithResult Utility.ListOps.from(scrutinee)
}
}
@ -1233,7 +1234,7 @@ class SmartConstructorsTest
Node.New.ForeignCodeLiteral("lambda x: x + 1", dummyLocation)
val foreignDefinition =
Node.New.ForeignDefinition(language, code, dummyLocation).right.get
Node.New.ForeignDefinition(language, code, dummyLocation).getOrElse(fail)
"have valid fields" in {
foreignDefinition.location shouldEqual dummyLocation
@ -1254,7 +1255,7 @@ class SmartConstructorsTest
"fail to construct of code is not a valid foreign code literal" in {
val node = Node.New.ForeignDefinition(language, language, dummyLocation)
node shouldFailWithResult Utility.coreListFrom(language)
node shouldFailWithResult Utility.ListOps.from(language)
}
}
@ -1290,7 +1291,7 @@ class SmartConstructorsTest
implicit val core: Core = new Core()
val errNode = Node.New.Empty()
val errList = Utility.coreListFrom(errNode)
val errList = Utility.ListOps.from(errNode)
val error = Node.New.ConstructionError(errList, dummyLocation)
"have valid fields" in {
@ -1317,8 +1318,8 @@ class SmartConstructorsTest
input.wrapped.is[NodeShape.Empty] shouldEqual true
Utility.isListNode(input) should not equal true
Utility.isListNode(err.erroneousCore.target) shouldEqual true
Utility.ListOps.is(input) should not equal true
Utility.ListOps.is(err.erroneousCore.target) shouldEqual true
// The only element in that list should be the single node
err.erroneousCore.target.unsafeAs[MetaList].head.target shouldEqual input
@ -1338,16 +1339,16 @@ class SmartConstructorsTest
val empty1 = Node.New.Empty().wrapped
val empty2 = Node.New.Empty().wrapped
val list1 = Utility.coreListFrom(NonEmptyList(empty1, List(empty2)))
val list2 = Utility.coreListFrom(NonEmptyList(empty1, List(empty2)))
val list3 = Utility.coreListFrom(NonEmptyList(empty2, List(empty1)))
val list1 = Utility.ListOps.from(NonEmptyList(empty1, List(empty2)))
val list2 = Utility.ListOps.from(NonEmptyList(empty1, List(empty2)))
val list3 = Utility.ListOps.from(NonEmptyList(empty2, List(empty1)))
"be defined as equal when the child nodes are equal" in {
Utility.listsAreEqual(list1, list2) shouldEqual true
Utility.ListOps.equals(list1, list2) shouldEqual true
}
"be defined as not equal when the child nodes are not equal" in {
Utility.listsAreEqual(list1, list3) shouldEqual false
Utility.ListOps.equals(list1, list3) shouldEqual false
}
}
@ -1359,9 +1360,9 @@ class SmartConstructorsTest
val falseNode = Node.New.MetaFalse()
"be correctly identified" in {
Utility.isBoolNode(emptyNode) shouldEqual false
Utility.isBoolNode(trueNode) shouldEqual true
Utility.isBoolNode(falseNode) shouldEqual true
BoolOps.is(emptyNode) shouldEqual false
BoolOps.is(trueNode) shouldEqual true
BoolOps.is(falseNode) shouldEqual true
}
}
@ -1369,12 +1370,13 @@ class SmartConstructorsTest
implicit val core: Core = new Core()
val emptyNode = Node.New.Empty()
val nilNode = Node.New.MetaNil()
val consNode = Node.New.MetaList(emptyNode, nilNode).right.get
val consNode =
Node.New.MetaList(emptyNode, nilNode).getOrElse(fail)
"be correctly identified" in {
Utility.isListNode(emptyNode) shouldEqual false
Utility.isListNode(nilNode) shouldEqual true
Utility.isListNode(consNode) shouldEqual true
Utility.ListOps.is(emptyNode) shouldEqual false
Utility.ListOps.is(nilNode) shouldEqual true
Utility.ListOps.is(consNode) shouldEqual true
}
}
@ -1385,9 +1387,9 @@ class SmartConstructorsTest
val emptyNode2 = Node.New.Empty().wrapped
val emptyNode3 = Node.New.Empty().wrapped
val listOfOne = Utility.coreListFrom(emptyNode1)
val listOfMany = Utility
.coreListFrom(NonEmptyList(emptyNode1, List(emptyNode2, emptyNode3)))
val listOfOne = Utility.ListOps.from(emptyNode1)
val listOfMany = Utility.ListOps
.from(NonEmptyList(emptyNode1, List(emptyNode2, emptyNode3)))
"be able to be constructed from arbitrary nodes" in {
listOfOne.head.target shouldEqual emptyNode1