mirror of
https://github.com/enso-org/enso.git
synced 2024-11-27 05:23:48 +03:00
Add more in-depth tests to core (#489)
This commit is contained in:
parent
8d8e95e14a
commit
adf5fe7db2
21
build.sbt
21
build.sbt
@ -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
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user