mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 00:11:45 +03:00
Implement the primitive core entities (#463)
This commit is contained in:
parent
4533780d1f
commit
ba84ee7e6e
93
build.sbt
93
build.sbt
@ -302,7 +302,8 @@ lazy val graph = (project in file("common/graph/"))
|
||||
"io.estatico" %% "newtype" % "0.4.3",
|
||||
"org.scalatest" %% "scalatest" % "3.2.0-SNAP10" % Test,
|
||||
"org.scalacheck" %% "scalacheck" % "1.14.0" % Test,
|
||||
"com.github.julien-truffaut" %% "monocle-core" % "2.0.0"
|
||||
"com.github.julien-truffaut" %% "monocle-core" % "2.0.0",
|
||||
"org.apache.commons" % "commons-lang3" % "3.9"
|
||||
),
|
||||
libraryDependencies ++= Seq(
|
||||
compilerPlugin(
|
||||
@ -374,6 +375,35 @@ val truffleRunOptionsSettings = Seq(
|
||||
javaOptions ++= truffleRunOptions
|
||||
)
|
||||
|
||||
lazy val core_definition = (project in file("engine/core-definition"))
|
||||
.configs(Benchmark)
|
||||
.settings(
|
||||
version := "0.1",
|
||||
inConfig(Compile)(truffleRunOptionsSettings),
|
||||
inConfig(Benchmark)(Defaults.testSettings),
|
||||
parallelExecution in Test := false,
|
||||
logBuffered in Test := false,
|
||||
libraryDependencies ++= jmh ++ Seq(
|
||||
"com.chuusai" %% "shapeless" % "2.3.3",
|
||||
"org.scalacheck" %% "scalacheck" % "1.14.0" % Test,
|
||||
"org.scalactic" %% "scalactic" % "3.0.8" % Test,
|
||||
"org.scalatest" %% "scalatest" % "3.2.0-SNAP10" % Test,
|
||||
"org.typelevel" %% "cats-core" % "2.0.0-M4",
|
||||
"com.github.julien-truffaut" %% "monocle-core" % "2.0.0"
|
||||
),
|
||||
addCompilerPlugin(
|
||||
"org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full
|
||||
),
|
||||
addCompilerPlugin("io.tryp" % "splain" % "0.5.0" cross CrossVersion.patch),
|
||||
scalacOptions ++= Seq(
|
||||
"-P:splain:infix:true",
|
||||
"-P:splain:foundreq:true",
|
||||
"-P:splain:implicits:true",
|
||||
"-P:splain:tree:true"
|
||||
)
|
||||
)
|
||||
.dependsOn(graph)
|
||||
|
||||
lazy val runtime = (project in file("engine/runtime"))
|
||||
.configs(Benchmark)
|
||||
.settings(
|
||||
@ -384,22 +414,27 @@ lazy val runtime = (project in file("engine/runtime"))
|
||||
parallelExecution in Test := false,
|
||||
logBuffered in Test := false,
|
||||
libraryDependencies ++= jmh ++ Seq(
|
||||
"com.chuusai" %% "shapeless" % "2.3.3",
|
||||
"org.apache.commons" % "commons-lang3" % "3.9",
|
||||
"org.apache.tika" % "tika-core" % "1.21",
|
||||
"org.graalvm.sdk" % "graal-sdk" % graalVersion % "provided",
|
||||
"org.graalvm.sdk" % "polyglot-tck" % graalVersion % "provided",
|
||||
"org.graalvm.truffle" % "truffle-api" % graalVersion % "provided",
|
||||
"org.graalvm.truffle" % "truffle-dsl-processor" % graalVersion % "provided",
|
||||
"org.graalvm.truffle" % "truffle-tck" % graalVersion % "provided",
|
||||
"org.graalvm.truffle" % "truffle-tck-common" % graalVersion % "provided",
|
||||
"org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.4",
|
||||
"org.scalacheck" %% "scalacheck" % "1.14.0" % Test,
|
||||
"org.scalactic" %% "scalactic" % "3.0.8" % Test,
|
||||
"org.scalatest" %% "scalatest" % "3.2.0-SNAP10" % Test,
|
||||
"org.graalvm.truffle" % "truffle-api" % graalVersion % Benchmark,
|
||||
"org.typelevel" %% "cats-core" % "2.0.0-M4"
|
||||
)
|
||||
"com.chuusai" %% "shapeless" % "2.3.3",
|
||||
"org.apache.commons" % "commons-lang3" % "3.9",
|
||||
"org.apache.tika" % "tika-core" % "1.21",
|
||||
"org.graalvm.sdk" % "graal-sdk" % graalVersion % "provided",
|
||||
"org.graalvm.sdk" % "polyglot-tck" % graalVersion % "provided",
|
||||
"org.graalvm.truffle" % "truffle-api" % graalVersion % "provided",
|
||||
"org.graalvm.truffle" % "truffle-dsl-processor" % graalVersion % "provided",
|
||||
"org.graalvm.truffle" % "truffle-tck" % graalVersion % "provided",
|
||||
"org.graalvm.truffle" % "truffle-tck-common" % graalVersion % "provided",
|
||||
"org.scalacheck" %% "scalacheck" % "1.14.0" % Test,
|
||||
"org.scalactic" %% "scalactic" % "3.0.8" % Test,
|
||||
"org.scalatest" %% "scalatest" % "3.2.0-SNAP10" % Test,
|
||||
"org.graalvm.truffle" % "truffle-api" % graalVersion % Benchmark,
|
||||
"org.typelevel" %% "cats-core" % "2.0.0-M4"
|
||||
),
|
||||
// Note [Unmanaged Classpath]
|
||||
Compile / unmanagedClasspath += (core_definition / Compile / packageBin).value,
|
||||
Test / unmanagedClasspath += (core_definition / Compile / packageBin).value,
|
||||
Compile / compile := (Compile / compile)
|
||||
.dependsOn(core_definition / Compile / packageBin)
|
||||
.value
|
||||
)
|
||||
.settings(
|
||||
(Compile / javacOptions) ++= Seq(
|
||||
@ -408,6 +443,13 @@ lazy val runtime = (project in file("engine/runtime"))
|
||||
),
|
||||
addCompilerPlugin(
|
||||
"org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full
|
||||
),
|
||||
addCompilerPlugin("io.tryp" % "splain" % "0.5.0" cross CrossVersion.patch),
|
||||
scalacOptions ++= Seq(
|
||||
"-P:splain:infix:true",
|
||||
"-P:splain:foundreq:true",
|
||||
"-P:splain:implicits:true",
|
||||
"-P:splain:tree:true"
|
||||
)
|
||||
)
|
||||
.settings(
|
||||
@ -435,6 +477,20 @@ lazy val runtime = (project in file("engine/runtime"))
|
||||
.dependsOn(graph)
|
||||
.dependsOn(polyglot_api)
|
||||
|
||||
/* Note [Unmanaged Classpath]
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* As the definition of the core primitives in `core_definition` is achieved
|
||||
* entirely using the graph macros, this means that the IDE experience for those
|
||||
* using these primitives is very poor.
|
||||
*
|
||||
* To get around this, we want to treat the core definition as a .jar dependency
|
||||
* to force the IDE to depend on bytecode for its diagnostics, rather than the
|
||||
* source code (as this means it sees the macros expanded). A standard workflow
|
||||
* with local publishing would not recompile the definition automatically on
|
||||
* changes, so the `unmanagedClasspath` route allows us to get automatic
|
||||
* recompilation but still convince the IDE that it is a .jar dependency.
|
||||
*/
|
||||
|
||||
lazy val runner = project
|
||||
.in(file("engine/runner"))
|
||||
.settings(
|
||||
@ -522,4 +578,5 @@ lazy val polyglot_api = project
|
||||
"org.scalatest" %% "scalatest" % "3.2.0-SNAP10" % Test,
|
||||
"org.scalacheck" %% "scalacheck" % "1.14.0" % Test
|
||||
)
|
||||
).dependsOn(pkg)
|
||||
)
|
||||
.dependsOn(pkg)
|
||||
|
@ -293,6 +293,23 @@ object MapsOf {
|
||||
}
|
||||
}
|
||||
|
||||
/** A representation of the index of a variant case within the variant field.
|
||||
*
|
||||
* A variant node field is one that may take on many different forms under the
|
||||
* umbrella of a single field. The prototypical example of this is `Shape`,
|
||||
* which can be anything from `Empty` to `TypeDef`. We use the following
|
||||
* terminology to describe variant fields:
|
||||
* - "The variant" refers to the umbrella type (`Shape` in the example above).
|
||||
* - "Branch" revers to the particular variant _element_ which the variant is
|
||||
* taking on at any given time.
|
||||
*
|
||||
* @tparam F the variant field type
|
||||
* @tparam V the variant branch type
|
||||
*/
|
||||
trait VariantIndexed[F <: Graph.Component.Field, V <: F] {
|
||||
val ix: Int
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// === Graph ==================================================================
|
||||
// ============================================================================
|
||||
@ -454,7 +471,7 @@ object Graph {
|
||||
ev: HasComponentField[G, C, T]
|
||||
): Option[Component.Refined[T, V, Component.Ref[G, C]]] = {
|
||||
val variantIndexByteOffset = 0
|
||||
if (graph.unsafeReadField[C, T](arg.ix, variantIndexByteOffset) == ix)
|
||||
if (graph.unsafeReadFieldByIndex[C, T](arg.ix, variantIndexByteOffset) == ix)
|
||||
Some(Component.Refined[T, V, Component.Ref[G, C]](arg))
|
||||
else None
|
||||
}
|
||||
@ -477,7 +494,68 @@ object Graph {
|
||||
var components: Array[Component.Storage] =
|
||||
this.componentSizes.map(size => new Component.Storage(size)).to[Array]
|
||||
|
||||
/** Gets a reference to the graph component at the specified index.
|
||||
*
|
||||
* No bounds checking is done on the provided index.
|
||||
*
|
||||
* @param index the index of the component to access
|
||||
* @param ev evidence that the graph [[G]] has a component [[C]]
|
||||
* @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]
|
||||
): Graph.Component.Ref[G, C] = {
|
||||
Graph.Component.Ref(index)
|
||||
}
|
||||
|
||||
/** Sets a variant node field to the specified case.
|
||||
*
|
||||
* @param component the component to set the field in
|
||||
* @param info evidence that the component [[C]] has field [[F]] in [[G]]
|
||||
* @param indexed evidence that [[V]] is an indexed field in the variant
|
||||
* [[F]]
|
||||
* @tparam C the component type
|
||||
* @tparam F the field type
|
||||
* @tparam V the variant branch type
|
||||
*/
|
||||
def unsafeSetVariantCase[C <: Component, F <: Component.Field, V <: F](
|
||||
component: Component.Ref[G, C]
|
||||
)(
|
||||
implicit info: HasComponentField[G, C, F],
|
||||
indexed: VariantIndexed[F, V]
|
||||
): Unit = {
|
||||
unsafeWriteField[C, F](component, indexed.ix)
|
||||
}
|
||||
|
||||
/** Reads the data for a specified field [[F]] in a component [[C]] from the
|
||||
* graph for the component instance [[component]].
|
||||
*
|
||||
* @param component the component instance to read from
|
||||
* @param info evidence that component [[C]] has field [[F]] in [[G]]
|
||||
* @tparam C the component type
|
||||
* @tparam F the field type
|
||||
* @return the raw field data from [[component]] specified by [[F]]
|
||||
*/
|
||||
def unsafeGetFieldData[C <: Component, F <: Component.Field](
|
||||
component: Component.Ref[G, C]
|
||||
)(implicit info: HasComponentField[G, C, F]): (Array[Int], Int) = {
|
||||
unsafeGetFieldDataByIndex[C, F](component.ix, info.fieldOffset)
|
||||
}
|
||||
|
||||
/** Reads the data for a specified field [[F]] in a component [[C]] from the
|
||||
* graph for the component instance represented by [[componentIx]].
|
||||
*
|
||||
* No bounds checking is performed on any of the provided indices.
|
||||
*
|
||||
* @param componentIx the index representing the component to read from
|
||||
* @param fieldIx the index of the field [[F]] in the component [[C]]
|
||||
* @param info evidence that component [[C]] has field [[F]] in [[G]]
|
||||
* @tparam C the component type
|
||||
* @tparam F the field type
|
||||
* @return the raw field data from [[component]] specified by [[F]]
|
||||
*/
|
||||
def unsafeGetFieldDataByIndex[C <: Component, F <: Component.Field](
|
||||
componentIx: Int,
|
||||
fieldIx: Int
|
||||
)(implicit info: HasComponentField[G, C, F]): (Array[Int], Int) = {
|
||||
@ -486,23 +564,84 @@ object Graph {
|
||||
(arr, idx)
|
||||
}
|
||||
|
||||
/** Reads the spcified field [[F]] from [[component]].
|
||||
*
|
||||
* @param component the component instance to read from
|
||||
* @param ev evidence that component [[C]] has field [[F]] in [[G]]
|
||||
* @tparam C the component type
|
||||
* @tparam F the field type
|
||||
* @return the value of field [[F]] in [[component]]
|
||||
*/
|
||||
def unsafeReadField[C <: Component, F <: Component.Field](
|
||||
component: Component.Ref[G, C]
|
||||
)(implicit ev: HasComponentField[G, C, F]): Int = {
|
||||
unsafeReadFieldByIndex[C, F](component.ix, ev.fieldOffset)
|
||||
}
|
||||
|
||||
/** Reads the spcified field [[F]] from the instance of [[C]] at
|
||||
* [[componentIx]].
|
||||
*
|
||||
* No bounds checking is performed on any of the provided indices.
|
||||
*
|
||||
* @param componentIx the index representing the component [[C]]
|
||||
* @param fieldIx the index of the field [[F]] in the component [[C]]
|
||||
* @param ev evidence that component [[C]] has field [[F]] in [[G]]
|
||||
* @tparam C the component type
|
||||
* @tparam F the field type
|
||||
* @return the value of field [[F]] in [[component]]
|
||||
*/
|
||||
def unsafeReadFieldByIndex[C <: Component, F <: Component.Field](
|
||||
componentIx: Int,
|
||||
fieldIx: Int
|
||||
)(implicit ev: HasComponentField[G, C, F]): Int = {
|
||||
val (arr, idx) = unsafeGetFieldData(componentIx, fieldIx)
|
||||
val (arr, idx) = unsafeGetFieldDataByIndex(componentIx, fieldIx)
|
||||
arr(idx)
|
||||
}
|
||||
|
||||
/** Writes the value of field [[F]] in [[component]] to be [[value]].
|
||||
*
|
||||
* @param component the instance of [[C]] to write to
|
||||
* @param value the value to write to the field [[F]]
|
||||
* @param ev evidence that component [[C]] has field [[F]] in [[G]]
|
||||
* @tparam C the component type
|
||||
* @tparam F the field type
|
||||
*/
|
||||
def unsafeWriteField[C <: Component, F <: Component.Field](
|
||||
component: Component.Ref[G, C],
|
||||
value: Int
|
||||
)(
|
||||
implicit ev: HasComponentField[G, C, F]
|
||||
): Unit = {
|
||||
unsafeWriteFieldByIndex[C, F](component.ix, ev.fieldOffset, value)
|
||||
}
|
||||
|
||||
/** Sets the field at [[fieldIx]] in the instance of [[C]] represented by
|
||||
* [[componentIx]] to have the value [[value]].
|
||||
*
|
||||
* No bounds checking is performed on any of the provided indices.
|
||||
*
|
||||
* @param componentIx the index representing an instance of [[C]]
|
||||
* @param fieldIx the index of the field [[F]] in [[C]]
|
||||
* @param value the value to set [[F]] to
|
||||
* @param ev evidence that component [[C]] has field [[F]] in [[G]]
|
||||
* @tparam C the component type
|
||||
* @tparam F the field type
|
||||
*/
|
||||
def unsafeWriteFieldByIndex[C <: Component, F <: Component.Field](
|
||||
componentIx: Int,
|
||||
fieldIx: Int,
|
||||
value: Int
|
||||
)(implicit ev: HasComponentField[G, C, F]): Unit = {
|
||||
val (arr, idx) = unsafeGetFieldData(componentIx, fieldIx)
|
||||
val (arr, idx) = unsafeGetFieldDataByIndex(componentIx, fieldIx)
|
||||
arr(idx) = value
|
||||
}
|
||||
|
||||
/** Adds a new instance of component [[C]] to the graph.
|
||||
*
|
||||
* @param info evidence that the graph [[G]] has component [[C]]
|
||||
* @tparam C the component type
|
||||
* @return a reference to the new component [[C]]
|
||||
*/
|
||||
def addComponent[C <: Component]()(
|
||||
implicit info: HasComponent[G, C]
|
||||
): Component.Ref[G, C] = {
|
||||
@ -514,6 +653,13 @@ object Graph {
|
||||
}
|
||||
}
|
||||
object GraphData {
|
||||
|
||||
/** Gets the [[GraphInfo]] from the [[GraphData]] instance.
|
||||
*
|
||||
* @param g the graph data
|
||||
* @tparam G the graph type
|
||||
* @return the graph info for [[G]]
|
||||
*/
|
||||
implicit def getInfo[G <: Graph](g: GraphData[G]): GraphInfo[G] = g.info
|
||||
}
|
||||
|
||||
@ -544,9 +690,9 @@ object Graph {
|
||||
componentSizesEv: ComponentListToSizes[G, ComponentList],
|
||||
len: nat.ToInt[ComponentListLength]
|
||||
): GraphInfo[G] = new GraphInfo[G] {
|
||||
val componentCount = len()
|
||||
val componentSizes = componentSizesEv.sizes
|
||||
}
|
||||
val componentCount = len()
|
||||
val componentSizes = componentSizesEv.sizes
|
||||
}
|
||||
}
|
||||
|
||||
// === HasComponent ===
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.enso.graph.definition
|
||||
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
import scala.annotation.{compileTimeOnly, StaticAnnotation}
|
||||
import scala.reflect.macros.whitebox
|
||||
|
||||
@ -260,7 +262,10 @@ object Macro {
|
||||
implicit graph: $graphTermName.GraphData[G],
|
||||
ev: $graphTermName.HasComponentField[G, C, $enclosingTypeName]
|
||||
): $paramType = {
|
||||
graph.unsafeReadField[C, $enclosingTypeName](node.ix, $index)
|
||||
graph.unsafeReadFieldByIndex[C, $enclosingTypeName](
|
||||
node.ix,
|
||||
$index
|
||||
)
|
||||
}
|
||||
"""
|
||||
} else {
|
||||
@ -270,7 +275,7 @@ object Macro {
|
||||
ev: $graphTermName.HasComponentField[G, C, $enclosingTypeName]
|
||||
): $paramType = {
|
||||
$graphTermName.Component.Ref(
|
||||
graph.unsafeReadField[C, $enclosingTypeName](
|
||||
graph.unsafeReadFieldByIndex[C, $enclosingTypeName](
|
||||
$graphTermName.Component.Refined.unwrap(node),
|
||||
$index
|
||||
)
|
||||
@ -313,7 +318,10 @@ object Macro {
|
||||
ev: $graphTermName.HasComponentField[G, C, $enclosingTypeName]
|
||||
): $paramType = {
|
||||
$graphTermName.Component.Ref(
|
||||
graph.unsafeReadField[C, $enclosingTypeName](node.ix, $index)
|
||||
graph.unsafeReadFieldByIndex[C, $enclosingTypeName](
|
||||
node.ix,
|
||||
$index
|
||||
)
|
||||
)
|
||||
}
|
||||
"""
|
||||
@ -324,7 +332,7 @@ object Macro {
|
||||
ev: $graphTermName.HasComponentField[G, C, $enclosingTypeName]
|
||||
): $paramType = {
|
||||
$graphTermName.Component.Ref(
|
||||
graph.unsafeReadField[C, $enclosingTypeName](
|
||||
graph.unsafeReadFieldByIndex[C, $enclosingTypeName](
|
||||
$graphTermName.Component.Refined.unwrap(node).ix,
|
||||
$index
|
||||
)
|
||||
@ -367,7 +375,7 @@ object Macro {
|
||||
implicit graph: $graphTermName.GraphData[G],
|
||||
ev: $graphTermName.HasComponentField[G, C, $enclosingTypeName]
|
||||
): Unit = {
|
||||
graph.unsafeWriteField[C, $enclosingTypeName](
|
||||
graph.unsafeWriteFieldByIndex[C, $enclosingTypeName](
|
||||
node.ix, $index, value
|
||||
)
|
||||
}
|
||||
@ -378,7 +386,7 @@ object Macro {
|
||||
implicit graph: $graphTermName.GraphData[G],
|
||||
ev: $graphTermName.HasComponentField[G, C, $enclosingTypeName]
|
||||
): Unit = {
|
||||
graph.unsafeWriteField[C, $enclosingTypeName](
|
||||
graph.unsafeWriteFieldByIndex[C, $enclosingTypeName](
|
||||
$graphTermName.Component.Refined.unwrap(node).ix,
|
||||
$index,
|
||||
value
|
||||
@ -420,7 +428,7 @@ object Macro {
|
||||
implicit graph: $graphTermName.GraphData[G],
|
||||
ev: $graphTermName.HasComponentField[G, C, $enclosingTypeName]
|
||||
): Unit = {
|
||||
graph.unsafeWriteField[C, $enclosingTypeName](
|
||||
graph.unsafeWriteFieldByIndex[C, $enclosingTypeName](
|
||||
node.ix, $index, value.ix
|
||||
)
|
||||
}
|
||||
@ -431,7 +439,7 @@ object Macro {
|
||||
implicit graph: $graphTermName.GraphData[G],
|
||||
ev: $graphTermName.HasComponentField[G, C, $enclosingTypeName]
|
||||
): Unit = {
|
||||
graph.unsafeWriteField[C, $enclosingTypeName](
|
||||
graph.unsafeWriteFieldByIndex[C, $enclosingTypeName](
|
||||
$graphTermName.Component.Refined.unwrap(node).ix,
|
||||
$index,
|
||||
value.ix
|
||||
@ -442,6 +450,23 @@ object Macro {
|
||||
}
|
||||
}
|
||||
|
||||
/** Makes a type accessor name from a type name.
|
||||
*
|
||||
* This basically means converting from `PascalCase` to `camelCase`.
|
||||
*
|
||||
* @param fieldName the name of the field
|
||||
* @return the accessor name for [[fieldName]]
|
||||
*/
|
||||
def makeTypeAccessorName(fieldName: TypeName): String = {
|
||||
StringUtils.uncapitalize(fieldName.toString)
|
||||
// val matcher = "([A-Z])(.*)".r
|
||||
//
|
||||
// matcher.findFirstMatchIn(fieldName.toString) match {
|
||||
// case Some(t) => s"${t.group(1).toLowerCase()}${t.group(2)}"
|
||||
// case None => fieldName.toString.toLowerCase
|
||||
// }
|
||||
}
|
||||
|
||||
/** Generates accessor methods for the 'value class', the one that can
|
||||
* represent the graph values as a scala object.
|
||||
*
|
||||
@ -467,10 +492,9 @@ object Macro {
|
||||
val graphTermName = graphTypeName.toTermName
|
||||
|
||||
val valClassTermName = valClassTypeName.toTermName
|
||||
val valGetterName = TermName(fieldName.toString.toLowerCase)
|
||||
val valSetterName = TermName(
|
||||
fieldName.toString.toLowerCase + "_$eq"
|
||||
)
|
||||
val valGetterName = TermName(makeTypeAccessorName(fieldName))
|
||||
val valSetterName =
|
||||
TermName(makeTypeAccessorName(fieldName) + "_$eq")
|
||||
|
||||
val opaqueStorageImplicits =
|
||||
generateOpaqueStorageImplicitArguments(subfields)
|
||||
@ -847,6 +871,10 @@ object Macro {
|
||||
implicit def sized = new Sized[$typeName] {
|
||||
type Out = $natSubfields
|
||||
}
|
||||
implicit def indexed =
|
||||
new VariantIndexed[$parentName, $typeName] {
|
||||
val ix = index
|
||||
}
|
||||
}
|
||||
""".asInstanceOf[ModuleDef]
|
||||
|
||||
@ -1261,7 +1289,8 @@ object Macro {
|
||||
*
|
||||
* Please note that the type `T` must be fully applied. The macro must also
|
||||
* be applied to a case class, and that case class must define a single
|
||||
* value member `opaque`.
|
||||
* value member `opaque`. This means that the opaque storage cannot be used
|
||||
* to store graph elements.
|
||||
*
|
||||
* By way of example, consider the following macro invocation:
|
||||
*
|
||||
@ -1276,6 +1305,10 @@ object Macro {
|
||||
* val backref: mutable.Map[Int, Vector[Int]] = mutable.Map()
|
||||
* }
|
||||
* }}}
|
||||
*
|
||||
* Please note that while it is possible to define a field with multiple
|
||||
* opaque subfields, those fields _must_ not use the same underlying storage
|
||||
* type.
|
||||
*/
|
||||
@compileTimeOnly("please enable macro paradise to expand macro annotations")
|
||||
class opaque extends StaticAnnotation {
|
||||
@ -1362,4 +1395,51 @@ object Macro {
|
||||
* [[opaque]] macro
|
||||
*/
|
||||
trait OpaqueData[T, Storage]
|
||||
|
||||
/** This macro is used to annotate the object that defines the graph. It will
|
||||
* output the macro results into a separate object nested at the same level,
|
||||
* which can help with autocompletion in an IDE.
|
||||
*
|
||||
* It is used as follows:
|
||||
*
|
||||
* {{{
|
||||
* @genGraph object Definition { ... }
|
||||
* }}}
|
||||
*
|
||||
* If your graph definition is in an object called Foo, the new object will
|
||||
* be called `FooGen`. The definition object must not be the top-level object
|
||||
* in the file due to restrictions of Macro Paradise.
|
||||
*/
|
||||
@compileTimeOnly("please enable macro paradise to expand macro annotations")
|
||||
class genGraph extends StaticAnnotation {
|
||||
def macroTransform(annottees: Any*): Any = macro GenGraph.impl
|
||||
}
|
||||
object GenGraph {
|
||||
def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
|
||||
import c.universe._
|
||||
val members = annottees.map(_.tree).toList
|
||||
|
||||
if (members.length != 1) {
|
||||
c.error(
|
||||
c.enclosingPosition,
|
||||
"You must apply the `@genGraph` macro to a single object."
|
||||
)
|
||||
}
|
||||
|
||||
val objectDef = members.head
|
||||
objectDef match {
|
||||
case ModuleDef(mods, name, body) =>
|
||||
val genDef = ModuleDef(mods, TermName(name.toString + "Gen"), body)
|
||||
|
||||
c.Expr(genDef)
|
||||
case _ =>
|
||||
c.error(
|
||||
c.enclosingPosition,
|
||||
"Your macro must be applied to an object definition."
|
||||
)
|
||||
|
||||
c.Expr(objectDef)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ class FieldMacroTest extends FlatSpec with Matchers {
|
||||
// === Components for Tests =================================================
|
||||
@component case class Nodes() { type Node[G <: Graph] }
|
||||
@component case class Edges() { type Edge[G <: Graph] }
|
||||
@opaque case class Backref(opaque: Vector[Int])
|
||||
@opaque case class Str(opaque: String)
|
||||
@opaque case class Backref[G <: PrimGraph](opaque: Vector[Int])
|
||||
@opaque case class Str[G <: PrimGraph](opaque: String)
|
||||
|
||||
// === Tests ================================================================
|
||||
subject should "not explicitly depend on the graph type name" in {
|
||||
|
@ -33,25 +33,25 @@ class GraphTest extends FlatSpec with Matchers {
|
||||
n3.parent = e1
|
||||
|
||||
// Change `n1` to be `App`
|
||||
graph.unsafeWriteField[Nodes, GraphImpl.Node.Shape](
|
||||
n1.ix,
|
||||
0,
|
||||
Node.Shape.App.index
|
||||
)
|
||||
graph.unsafeSetVariantCase[
|
||||
Nodes,
|
||||
GraphImpl.Node.Shape,
|
||||
GraphImpl.Node.Shape.App
|
||||
](n1)
|
||||
|
||||
// Change `n2` to be `Name`
|
||||
graph.unsafeWriteField[Nodes, GraphImpl.Node.Shape](
|
||||
n2.ix,
|
||||
0,
|
||||
Node.Shape.Name.index
|
||||
)
|
||||
graph.unsafeSetVariantCase[
|
||||
Nodes,
|
||||
GraphImpl.Node.Shape,
|
||||
GraphImpl.Node.Shape.Name
|
||||
](n2)
|
||||
|
||||
// Change `n3` to be `Nul`
|
||||
graph.unsafeWriteField[Nodes, GraphImpl.Node.Shape](
|
||||
n3.ix,
|
||||
0,
|
||||
Node.Shape.Nul.index
|
||||
)
|
||||
graph.unsafeSetVariantCase[
|
||||
Nodes,
|
||||
GraphImpl.Node.Shape,
|
||||
GraphImpl.Node.Shape.Nul
|
||||
](n3)
|
||||
|
||||
// ==========================================================================
|
||||
// === Tests ================================================================
|
||||
|
@ -4,6 +4,8 @@ import org.enso.graph.definition.Macro.{component, field, opaque}
|
||||
import org.enso.graph.{Graph => PrimGraph}
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
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
|
||||
@ -108,6 +110,9 @@ object GraphTestDefinition {
|
||||
// implicit def sized =
|
||||
// new Sized[Nul] { type Out = _0 }
|
||||
//
|
||||
// implicit def indexed =
|
||||
// new VariantIndexed[Shape, Nul] { val ix = index }
|
||||
//
|
||||
// def unapply[G <: PrimGraph, C <: PrimGraph.Component](
|
||||
// arg: PrimGraph.Component.Ref[G, C]
|
||||
// )(
|
||||
@ -145,6 +150,9 @@ object GraphTestDefinition {
|
||||
// implicit def sized =
|
||||
// new Sized[App] { type Out = _2 }
|
||||
//
|
||||
// implicit def indexed =
|
||||
// new VariantIndexed[Shape, App] { val ix = index }
|
||||
//
|
||||
// def unapply[G <: PrimGraph, C <: PrimGraph.Component](
|
||||
// arg: PrimGraph.Component.Ref[G, C]
|
||||
// )(
|
||||
@ -165,7 +173,7 @@ object GraphTestDefinition {
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): Edge[G] =
|
||||
// PrimGraph.Component.Ref(
|
||||
// graph.unsafeReadField[C, Shape](
|
||||
// graph.primUnsafeReadField[C, Shape](
|
||||
// PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
// 0
|
||||
// )
|
||||
@ -175,7 +183,7 @@ object GraphTestDefinition {
|
||||
// implicit graph: PrimGraph.GraphData[G],
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): Unit =
|
||||
// graph.unsafeWriteField[C, Shape](
|
||||
// graph.primUnsafeWriteField[C, Shape](
|
||||
// PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
// 0,
|
||||
// value.ix
|
||||
@ -186,7 +194,7 @@ object GraphTestDefinition {
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): Edge[G] =
|
||||
// PrimGraph.Component.Ref(
|
||||
// graph.unsafeReadField[C, Shape](
|
||||
// graph.primUnsafeReadField[C, Shape](
|
||||
// PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
// 1
|
||||
// )
|
||||
@ -196,7 +204,7 @@ object GraphTestDefinition {
|
||||
// implicit graph: PrimGraph.GraphData[G],
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): Unit =
|
||||
// graph.unsafeWriteField[C, Shape](
|
||||
// graph.primUnsafeWriteField[C, Shape](
|
||||
// PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
// 1,
|
||||
// value.ix
|
||||
@ -228,9 +236,11 @@ object GraphTestDefinition {
|
||||
// object Centre {
|
||||
// val index = 2
|
||||
// val any =
|
||||
// PrimGraph.Component.VariantMatcher[Shape, App](index)
|
||||
// PrimGraph.Component.VariantMatcher[Shape, Centre](index)
|
||||
// implicit def sized =
|
||||
// new Sized[Centre] { type Out = _1 }
|
||||
// implicit def indexed =
|
||||
// new VariantIndexed[Shape, Centre] { val ix = index }
|
||||
//
|
||||
// def unapply[G <: PrimGraph, C <: PrimGraph.Component](
|
||||
// arg: PrimGraph.Component.Ref[G, C]
|
||||
@ -255,7 +265,7 @@ object GraphTestDefinition {
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): Edge[G] =
|
||||
// PrimGraph.Component.Ref(
|
||||
// graph.unsafeReadField[C, Shape](
|
||||
// graph.primUnsafeReadField[C, Shape](
|
||||
// PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
// 0
|
||||
// )
|
||||
@ -265,7 +275,7 @@ object GraphTestDefinition {
|
||||
// implicit graph: PrimGraph.GraphData[G],
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): Unit =
|
||||
// graph.unsafeWriteField[C, Shape](
|
||||
// graph.primUnsafeWriteField[C, Shape](
|
||||
// PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
// 0,
|
||||
// value.ix
|
||||
@ -277,7 +287,7 @@ object GraphTestDefinition {
|
||||
// ): CentreVal[G] = {
|
||||
// CentreVal(
|
||||
// PrimGraph.Component.Ref(
|
||||
// graph.unsafeReadField[C, Shape](
|
||||
// graph.primUnsafeReadField[C, Shape](
|
||||
// PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
// 0
|
||||
// )
|
||||
@ -289,7 +299,7 @@ object GraphTestDefinition {
|
||||
// implicit graph: PrimGraph.GraphData[G],
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): Unit = {
|
||||
// graph.unsafeWriteField[C, Shape](
|
||||
// graph.primUnsafeWriteField[C, Shape](
|
||||
// PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
// 0,
|
||||
// value.fn.ix
|
||||
@ -308,12 +318,14 @@ object GraphTestDefinition {
|
||||
// val any = PrimGraph.Component.VariantMatcher[Shape, Name](index)
|
||||
// implicit def sized =
|
||||
// new Sized[Centre] { type Out = _1 } // Due to one field being opaque
|
||||
// implicit def indexed =
|
||||
// new VariantIndexed[Shape, Name] { val ix = index }
|
||||
//
|
||||
// def unapply[G <: PrimGraph, C <: PrimGraph.Component](
|
||||
// arg: PrimGraph.Component.Ref[G, C]
|
||||
// )(
|
||||
// implicit graph: PrimGraph.GraphData[G],
|
||||
// map: StringStorage,
|
||||
// map: StrStorage,
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): Option[scala.Tuple1[String]] = {
|
||||
// any.unapply(arg).map(t => scala.Tuple1(t.str))
|
||||
@ -331,18 +343,18 @@ object GraphTestDefinition {
|
||||
// ) {
|
||||
// def str(
|
||||
// implicit graph: PrimGraph.GraphData[G],
|
||||
// map: StringStorage,
|
||||
// map: StrStorage,
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): String = {
|
||||
// map.string(PrimGraph.Component.Refined.unwrap(node).ix)
|
||||
// map.str(PrimGraph.Component.Refined.unwrap(node).ix)
|
||||
// }
|
||||
//
|
||||
// def str_=(value: String)(
|
||||
// implicit graph: PrimGraph.GraphData[G],
|
||||
// map: StringStorage,
|
||||
// map: StrStorage,
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): Unit = {
|
||||
// map.string(PrimGraph.Component.Refined.unwrap(node).ix) = value
|
||||
// map.str(PrimGraph.Component.Refined.unwrap(node).ix) = value
|
||||
// }
|
||||
//
|
||||
// def linkEdge(
|
||||
@ -350,7 +362,7 @@ object GraphTestDefinition {
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): Edge[G] = {
|
||||
// PrimGraph.Component.Ref(
|
||||
// graph.unsafeReadField[C, Shape](
|
||||
// graph.primUnsafeReadField[C, Shape](
|
||||
// PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
// 0 // as the other field is opaque
|
||||
// )
|
||||
@ -361,7 +373,7 @@ object GraphTestDefinition {
|
||||
// implicit graph: PrimGraph.GraphData[G],
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): Unit = {
|
||||
// graph.unsafeWriteField[C, Shape](
|
||||
// graph.primUnsafeWriteField[C, Shape](
|
||||
// PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
// 0,
|
||||
// value.ix
|
||||
@ -370,7 +382,7 @@ object GraphTestDefinition {
|
||||
//
|
||||
// def name(
|
||||
// implicit graph: PrimGraph.GraphData[G],
|
||||
// map: StringStorage,
|
||||
// map: StrStorage,
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): NameVal[G] = {
|
||||
// NameVal(
|
||||
@ -381,7 +393,7 @@ object GraphTestDefinition {
|
||||
//
|
||||
// def name_=(value: NameVal[G])(
|
||||
// implicit graph: PrimGraph.GraphData[G],
|
||||
// map: StringStorage,
|
||||
// map: StrStorage,
|
||||
// ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
// ): Unit = {
|
||||
// this.str = value.str
|
||||
|
@ -0,0 +1,607 @@
|
||||
package org.enso.core
|
||||
|
||||
import org.enso.graph.definition.Macro.{component, field, genGraph, opaque}
|
||||
import org.enso.graph.{Sized, VariantIndexed, Graph => PrimGraph}
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
// TODO [AA] More detailed semantic descriptions for each node shape in future.
|
||||
object CoreGraph {
|
||||
@genGraph object Definition {
|
||||
|
||||
// ========================================================================
|
||||
// === The Graph Definition ===============================================
|
||||
// ========================================================================
|
||||
|
||||
/** This type denotes the core graph itself. */
|
||||
case class CoreGraph() extends PrimGraph
|
||||
|
||||
/** The list of components that make up a [[CoreGraph]]. */
|
||||
implicit def components =
|
||||
new PrimGraph.Component.List[CoreGraph] {
|
||||
type Out = Nodes :: Links :: HNil
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// === Opaque Storage =====================================================
|
||||
// ========================================================================
|
||||
|
||||
/** Storage for string literals. */
|
||||
@opaque case class Literal(opaque: String)
|
||||
|
||||
/** Storage for name literals. */
|
||||
@opaque case class Name(opaque: String)
|
||||
|
||||
/** Storage for parents for a given node.
|
||||
*
|
||||
* An entry in the vector will be the index of an [[Edge]] in the graph
|
||||
* that has the containing node as its `target` field.
|
||||
*/
|
||||
@opaque case class Parent(opaque: Vector[Int])
|
||||
|
||||
// ========================================================================
|
||||
// === Node ===============================================================
|
||||
// ========================================================================
|
||||
|
||||
/** A node in the [[CoreGraph]]. */
|
||||
@component case class Nodes() { type Node[G <: PrimGraph] }
|
||||
|
||||
/** The list of fields that a [[Node]] has in a [[CoreGraph]]. */
|
||||
implicit def nodeFields =
|
||||
new PrimGraph.Component.Field.List[CoreGraph, Nodes] {
|
||||
type Out = Node.Shape :: Node.ParentLinks :: Node.Location :: HNil
|
||||
}
|
||||
|
||||
object Node {
|
||||
|
||||
// ======================================================================
|
||||
// === Field Definitions ================================================
|
||||
// ======================================================================
|
||||
|
||||
/** A location describes which portion of the source code this particular
|
||||
* node in the graph represents.
|
||||
*
|
||||
* @param sourceStart the start position in the source code
|
||||
* @param sourceEnd the end position in the source code
|
||||
* @tparam G the graph type
|
||||
*/
|
||||
@field case class Location[G <: PrimGraph](
|
||||
sourceStart: Int,
|
||||
sourceEnd: Int
|
||||
)
|
||||
|
||||
/** This type represents all the incoming [[Link]]s to the current node.
|
||||
*
|
||||
* It should be noted that it _does not_ store the links directly. This
|
||||
* 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()]].
|
||||
*
|
||||
* @param parents a vector containing the raw indices of the parent links
|
||||
* @tparam G the graph type
|
||||
*/
|
||||
@field case class ParentLinks[G <: PrimGraph](
|
||||
parents: OpaqueData[Vector[Int], ParentStorage]
|
||||
)
|
||||
|
||||
/** The shape of a node represents all the different forms that a node can
|
||||
* take.
|
||||
*/
|
||||
@field object Shape {
|
||||
type G = PrimGraph
|
||||
|
||||
// === Base Shapes ====================================================
|
||||
/** A representation of a node that has no particular shape. */
|
||||
case class Empty()
|
||||
|
||||
/** A representation of a cons cell for building linked lists on the
|
||||
* graph.
|
||||
*
|
||||
* These should be used _very_ sparingly, if at all, but they provide a
|
||||
* way to store dynamically-sized core components providing they can be
|
||||
* broken down into statically sized components.
|
||||
*
|
||||
* The [[tail]] parameter should always point to either another node
|
||||
* with shape [[MetaList]] or a node with shape [[MetaNil]].
|
||||
*
|
||||
* It should be noted that, given that each [[Node]] contains a field
|
||||
* of [[ParentLinks]], that constructing this properly provides a
|
||||
* doubly-linked list, as no [[MetaList]] or [[MetaNil]] should have
|
||||
* more than one parent.
|
||||
*
|
||||
* @param head the current, arbitrary, element in the list
|
||||
* @param tail the rest of the list
|
||||
*/
|
||||
case class MetaList(head: Link[G], tail: Link[G])
|
||||
|
||||
/** A representation of the end of a linked-list on the graph. */
|
||||
case class MetaNil()
|
||||
|
||||
/** A node representing boolean true. */
|
||||
case class MetaTrue()
|
||||
|
||||
/** A node representing boolean false. */
|
||||
case class MetaFalse()
|
||||
|
||||
// === Literals =======================================================
|
||||
|
||||
/** A representation of a numeric literal.
|
||||
*
|
||||
* @param number the numeric literal
|
||||
*/
|
||||
case class NumericLiteral(number: OpaqueData[String, LiteralStorage])
|
||||
|
||||
/** A representation of a textual literal.
|
||||
*
|
||||
* @param text the textual literal
|
||||
*/
|
||||
case class TextLiteral(text: OpaqueData[String, LiteralStorage])
|
||||
|
||||
/** A representation of literal text from a foreign code block.
|
||||
*
|
||||
* @param code the foreign code literal
|
||||
*/
|
||||
case class ForeignCodeLiteral(code: OpaqueData[String, LiteralStorage])
|
||||
|
||||
// === Names ==========================================================
|
||||
|
||||
/** A name.
|
||||
*
|
||||
* @param nameLiteral the literal string representing the name
|
||||
*/
|
||||
case class Name(nameLiteral: OpaqueData[String, LiteralStorage])
|
||||
|
||||
/** A representation of the `this` reserved name */
|
||||
case class ThisName()
|
||||
|
||||
/** A representation of the `here` reserved name */
|
||||
case class HereName()
|
||||
|
||||
// === Module =========================================================
|
||||
|
||||
/** The core representation of a top-level Enso module.
|
||||
*
|
||||
* @param name the name of the module
|
||||
* @param imports the module's imports as a [[MetaList]], where
|
||||
* each list member points to an import
|
||||
* @param definitions the module's definitions as a [[MetaList]], where
|
||||
* each list member points to a binding
|
||||
*/
|
||||
case class ModuleDef(
|
||||
name: Link[G],
|
||||
imports: Link[G],
|
||||
definitions: Link[G]
|
||||
)
|
||||
|
||||
/** An import statement.
|
||||
*
|
||||
* @param segments the segments of the import path, represented as a
|
||||
* [[NameLiteral]].
|
||||
*/
|
||||
case class Import(segments: Link[G])
|
||||
|
||||
/** A module-level binding.
|
||||
*
|
||||
* @param module a link to the module in which this binding is found
|
||||
* @param binding the binding itself
|
||||
*/
|
||||
case class TopLevelBinding(module: Link[G], binding: Link[G])
|
||||
|
||||
// === Type Definitions ===============================================
|
||||
|
||||
/** An atom definition.
|
||||
*
|
||||
* @param name the name of the atom
|
||||
* @param args the atom's arguments as a [[MetaList]]
|
||||
*/
|
||||
case class AtomDef(name: Link[G], args: Link[G])
|
||||
|
||||
/** An expanded-form type definition, with a body.
|
||||
*
|
||||
* @param name the name of the aggregate type
|
||||
* @param typeParams the type parameters to the definition
|
||||
* @param body the body of the type definition, represented as a
|
||||
* [[MetaList]] of bindings
|
||||
*/
|
||||
case class TypeDef(
|
||||
name: Link[G],
|
||||
typeParams: Link[G],
|
||||
body: Link[G]
|
||||
)
|
||||
|
||||
// === Typing =========================================================
|
||||
|
||||
/** A type signature.
|
||||
*
|
||||
* @param typed the expression being ascribed a type
|
||||
* @param sig the signature being ascribed to [[typed]]
|
||||
*/
|
||||
case class TypeAscription(typed: Link[G], sig: Link[G])
|
||||
|
||||
/** The `in` portion of a type signature that represents the monadic
|
||||
* contexts.
|
||||
*
|
||||
* @param typed the type being put in a context
|
||||
* @param context the context
|
||||
*/
|
||||
case class ContextAscription(typed: Link[G], context: Link[G])
|
||||
|
||||
/** A representation of a typeset member.
|
||||
*
|
||||
* PLEASE NOTE: This is here more as a note than anything, and will not
|
||||
* be exposed to users yet. It is currently used for Atom arguments.
|
||||
*
|
||||
* @param label the member's label, if given
|
||||
* @param memberType the member's type, if given
|
||||
* @param value the member's value, if given
|
||||
*/
|
||||
case class TypesetMember(
|
||||
label: Link[G],
|
||||
memberType: Link[G],
|
||||
value: Link[G]
|
||||
)
|
||||
|
||||
/** The typset subsumption judgement `<:`.
|
||||
*
|
||||
* This construct does not represent a user-facing language element.
|
||||
*
|
||||
* @param left the left type in the subsumption judgement
|
||||
* @param right the right type in the subsumption judgement
|
||||
*/
|
||||
case class TypesetSubsumption(left: Link[G], right: Link[G])
|
||||
|
||||
/** The typeset equality judgement `~`.
|
||||
*
|
||||
* This construct does not represent a user-facing language element.
|
||||
*
|
||||
* @param left the left operand
|
||||
* @param right the right operand
|
||||
*/
|
||||
case class TypesetEquality(left: Link[G], right: Link[G])
|
||||
|
||||
/** The typeset concatenation operator `,`.
|
||||
*
|
||||
* @param left the left operand
|
||||
* @param right the right operand
|
||||
*/
|
||||
case class TypesetConcat(left: Link[G], right: Link[G])
|
||||
|
||||
/** The typeset union operator `|`.
|
||||
*
|
||||
* @param left the left operand
|
||||
* @param right the right operand
|
||||
*/
|
||||
case class TypesetUnion(left: Link[G], right: Link[G])
|
||||
|
||||
/** The typeset intersection operator `&`.
|
||||
*
|
||||
* @param left the left operand
|
||||
* @param right the right operand
|
||||
*/
|
||||
case class TypesetIntersection(left: Link[G], right: Link[G])
|
||||
|
||||
/** The typeset subtraction operator `\`.
|
||||
*
|
||||
* @param left the left operand
|
||||
* @param right the right operand
|
||||
*/
|
||||
case class TypesetSubtraction(left: Link[G], right: Link[G])
|
||||
|
||||
// === Function =======================================================
|
||||
|
||||
/** A lambda expression, the `->` function arrrow.
|
||||
*
|
||||
* Note that all lambdas in Enso are explicitly single-argument.
|
||||
*
|
||||
* @param arg the argument to the lambda
|
||||
* @param body the body of the lambda
|
||||
*/
|
||||
case class Lambda(arg: Link[G], body: Link[G])
|
||||
|
||||
/** A sugared function definition.
|
||||
*
|
||||
* @param name the name of the function
|
||||
* @param args the function arguments, as a [[MetaList]]
|
||||
* @param body the body of the function
|
||||
*/
|
||||
case class FunctionDef(name: Link[G], args: Link[G], body: Link[G])
|
||||
|
||||
/** A method definition.
|
||||
*
|
||||
* @param targetPath the path of the method
|
||||
* @param name the name of the method
|
||||
* @param function the function that is executed (can be any callable
|
||||
* representation)
|
||||
*/
|
||||
case class MethodDef(
|
||||
targetPath: Link[G],
|
||||
name: Link[G],
|
||||
function: Link[G]
|
||||
)
|
||||
|
||||
// === Definition-Site Argument Types =================================
|
||||
|
||||
/** An ignored function argument, denoted by `_`.
|
||||
*
|
||||
* This can commonly be seen in use where an API requires a function
|
||||
* take an argument, but a particular implementation doesn't need it:
|
||||
* `_ -> ...`.
|
||||
*/
|
||||
case class IgnoredArgument()
|
||||
|
||||
/** A function argument definition.
|
||||
*
|
||||
* @param name the name of the argument
|
||||
* @param suspended whether or not the argument uses suspended
|
||||
* evaluation (should be [[MetaTrue]] or [[MetaFalse]]
|
||||
* @param default the default value for the argument, if present
|
||||
*/
|
||||
case class DefinitionArgument(
|
||||
name: Link[G],
|
||||
suspended: Link[G],
|
||||
default: Link[G]
|
||||
)
|
||||
|
||||
// === Applications ===================================================
|
||||
|
||||
/** A function application.
|
||||
*
|
||||
* All functions in Enso are curried by default, and are represented in
|
||||
* the [[CoreGraph]] as single-argument functions.
|
||||
*
|
||||
* @param function function expression being called
|
||||
* @param argument the argument to the function
|
||||
*/
|
||||
case class Application(function: Link[G], argument: Link[G])
|
||||
|
||||
/** An infix function application.
|
||||
*
|
||||
* @param left the left argument
|
||||
* @param operator the function being applied
|
||||
* @param right the right argument
|
||||
*/
|
||||
case class InfixApplication(
|
||||
left: Link[G],
|
||||
operator: Link[G],
|
||||
right: Link[G]
|
||||
)
|
||||
|
||||
/** A left section operator application.
|
||||
*
|
||||
* @param arg the left argument to [[operator]]
|
||||
* @param operator the function being sectioned
|
||||
*/
|
||||
case class LeftSection(arg: Link[G], operator: Link[G])
|
||||
|
||||
/** A right section operator application.
|
||||
*
|
||||
* @param operator the function being sectioned
|
||||
* @param arg the right argument to [[operator]]
|
||||
*/
|
||||
case class RightSection(operator: Link[G], arg: Link[G])
|
||||
|
||||
/** A centre section operator application.
|
||||
*
|
||||
* @param operator the operator being sectioned
|
||||
*/
|
||||
case class CentreSection(operator: Link[G])
|
||||
|
||||
/** A representatin of a term that is explicitly forced.
|
||||
*
|
||||
* An explicitly forced term is one where the user has explicitly
|
||||
* called the `force` operator on it. This is useful only while the
|
||||
* compiler does not _automatically_ handle suspensions and forcing.
|
||||
*
|
||||
* PLEASE NOTE: This is temporary and will be removed as soon as the
|
||||
* compiler is capable enough to not require it.
|
||||
*
|
||||
* @param expression
|
||||
*/
|
||||
case class ForcedTerm(expression: Link[G])
|
||||
|
||||
// === Call-Site Argument Types =======================================
|
||||
|
||||
/** Used to represent `_` arguments that are shorthand for the creation
|
||||
* of lambdas.
|
||||
*/
|
||||
case class LambdaShorthandArgument()
|
||||
|
||||
/** A function call-site argument.
|
||||
*
|
||||
* @param expression the argument expression
|
||||
* @param name the name of the argument, if given
|
||||
*/
|
||||
case class CallSiteArgument(expression: Link[G], name: Link[G])
|
||||
|
||||
/** The `...` argument that may be passed to a function to suspend the
|
||||
* execution of its default arguments.
|
||||
*/
|
||||
case class SuspendDefaultsOperator()
|
||||
|
||||
// === Structure ======================================================
|
||||
|
||||
/** A block expression.
|
||||
*
|
||||
* @param expressions the expressions in the block as a [[MetaList]]
|
||||
* @param returnVal the final expression of the block
|
||||
*/
|
||||
case class Block(expressions: Link[G], returnVal: Link[G])
|
||||
|
||||
/** A binding expression of the form `name = expr`.
|
||||
*
|
||||
* @param name the name being bound to
|
||||
* @param expression the expression being bound to [[name]]
|
||||
*/
|
||||
case class Binding(name: Link[G], expression: Link[G])
|
||||
|
||||
// === Case Expression ================================================
|
||||
|
||||
/** A case expression.
|
||||
*
|
||||
* @param scrutinee the case expression's scrutinee
|
||||
* @param branches the match branches, as a [[MetaList]]
|
||||
*/
|
||||
case class CaseExpr(scrutinee: Link[G], branches: Link[G])
|
||||
|
||||
/** A case branch.
|
||||
*
|
||||
* All case patterns will initially be desugared to a
|
||||
* [[StructuralMatch]] and will be refined during further desugaring
|
||||
* passes, some of which may depend on type checking.
|
||||
*
|
||||
* @param pattern the pattern to match the scrutinee against
|
||||
* @param expression the expression
|
||||
*/
|
||||
case class CaseBranch(pattern: Link[G], expression: Link[G])
|
||||
|
||||
/** A pattern that matches on the scrutinee based on its structure.
|
||||
*
|
||||
* @param matchExpression the expression representing the possible
|
||||
* structure of the scrutinee
|
||||
*/
|
||||
case class StructuralMatch(matchExpression: Link[G])
|
||||
|
||||
/** A pattern that matches on the scrutinee purely based on a type
|
||||
* subsumption judgement.
|
||||
*
|
||||
* @param matchExpression the expression representing the possible type
|
||||
* of the scrutinee
|
||||
*/
|
||||
case class TypeMatch(matchExpression: Link[G])
|
||||
|
||||
/** A pattern that matches on the scrutinee based on a type subsumption
|
||||
* judgement and assigns a new name to it for use in the branch.
|
||||
*
|
||||
* @param matchExpression the expression representing the possible type
|
||||
* of the scrutinee, and its new name
|
||||
*/
|
||||
case class NamedMatch(matchExpression: Link[G])
|
||||
|
||||
/** A pattern that matches on any scrutinee. */
|
||||
case class FallbackMatch()
|
||||
|
||||
// === Comments =======================================================
|
||||
|
||||
/** A documentation comment.
|
||||
*
|
||||
* @param commented the commented entity
|
||||
* @param doc a [[TextLiteral]] containing the documentation comment
|
||||
*/
|
||||
case class DocComment(commented: Link[G], doc: Link[G])
|
||||
|
||||
// === Foreign ========================================================
|
||||
|
||||
/** A foreign code definition.
|
||||
*
|
||||
* @param language the name of the foreign programming language
|
||||
* @param code the foreign code, represented as a [[ForeignCodeLiteral]]
|
||||
*/
|
||||
case class ForeignDefinition(language: Link[G], code: Link[G])
|
||||
|
||||
// === Errors =========================================================
|
||||
|
||||
/** A syntax error.
|
||||
*
|
||||
* @param errorNode the node representation of the syntax error
|
||||
*/
|
||||
case class SyntaxError(errorNode: Link[G])
|
||||
|
||||
// TODO [AA] Fill in the error types as they become evident
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// === Utility Functions ================================================
|
||||
// ======================================================================
|
||||
|
||||
/** Sets the shape of the provided [[node]] to [[Shape]].
|
||||
*
|
||||
* @param node the node to set
|
||||
* @param ev evidence that [[Shape]] belongs to an indexed variant
|
||||
* @param graph the graph to mutate
|
||||
* @tparam Shape the shape to set the node to
|
||||
*/
|
||||
def setShape[Shape <: Node.Shape](
|
||||
node: Node[CoreGraph]
|
||||
)(
|
||||
implicit ev: VariantIndexed[Node.Shape, Shape],
|
||||
graph: PrimGraph.GraphData[CoreGraph]
|
||||
): Unit = {
|
||||
graph.unsafeSetVariantCase[Nodes, Node.Shape, Shape](node)
|
||||
}
|
||||
|
||||
/** Checks whether a given node represents some kind of language error.
|
||||
*
|
||||
* @param node the node to check
|
||||
* @return `true` if [[node]] represents an errors `false` otherwise
|
||||
*/
|
||||
def isErrorNode(
|
||||
node: Node[CoreGraph]
|
||||
)(implicit graph: PrimGraph.GraphData[CoreGraph]): Boolean = {
|
||||
node match {
|
||||
case Shape.SyntaxError.any(_) => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks whether a given node represents syntactic sugar.
|
||||
*
|
||||
* @param node the node to check
|
||||
* @return `true` if [[node]] represents syntax sugar, `false` otherwise
|
||||
*/
|
||||
def shapeIsSugar(
|
||||
node: Node[CoreGraph]
|
||||
)(implicit graph: PrimGraph.GraphData[CoreGraph]): Boolean = {
|
||||
node match {
|
||||
case Shape.TypeDef.any(_) => true
|
||||
case Shape.FunctionDef.any(_) => true
|
||||
case Shape.InfixApplication.any(_) => true
|
||||
case Shape.LeftSection.any(_) => true
|
||||
case Shape.RightSection.any(_) => true
|
||||
case Shape.CentreSection.any(_) => true
|
||||
case Shape.ForcedTerm.any(_) => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks whether a given node represents primitive language constructs.
|
||||
*
|
||||
* @param node the node to check
|
||||
* @return `true` if [[Node]] has a primitive shape, `false` otherwise
|
||||
*/
|
||||
def shapeIsPrimitive(
|
||||
node: Node[CoreGraph]
|
||||
)(implicit graph: PrimGraph.GraphData[CoreGraph]): Boolean = {
|
||||
!shapeIsSugar(node)
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// === Link ===============================================================
|
||||
// ========================================================================
|
||||
|
||||
/** A link between nodes in the [[CoreGraph]]. */
|
||||
@component case class Links() { type Link[G <: PrimGraph] }
|
||||
|
||||
/** The list of fields that a [[Link]] has in a [[CoreGraph]]. */
|
||||
implicit def linkFields =
|
||||
new PrimGraph.Component.Field.List[CoreGraph, Links] {
|
||||
type Out = Link.Shape :: HNil
|
||||
}
|
||||
|
||||
object Link {
|
||||
|
||||
// ======================================================================
|
||||
// === Field Definitions ================================================
|
||||
// ======================================================================
|
||||
|
||||
/** The shape of a link is static and represents a standard directional
|
||||
* edge in a graph.
|
||||
*
|
||||
* @param source the node at the start of the link
|
||||
* @param target the node at the end of the link
|
||||
* @tparam G the graph type
|
||||
*/
|
||||
@field case class Shape[G <: PrimGraph](source: Node[G], target: Node[G])
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,6 @@
|
||||
package org.enso.compiler.core
|
||||
|
||||
import org.enso.graph.{Sized, Graph => PrimGraph}
|
||||
import org.enso.syntax.text.Location
|
||||
import shapeless.nat._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
// TODO [AA] We may need to _re-export_ things instead
|
||||
|
||||
// TODO [AA] How do we store concrete types like `Location` in nodes?
|
||||
// TODO [AA] Can I do a deeply-nested hierarchy without breaking things?
|
||||
import org.enso.graph.{Graph => PrimGraph}
|
||||
|
||||
/** [[Core]] is the sophisticated internal representation supported by the
|
||||
* compiler.
|
||||
@ -19,293 +11,4 @@ import shapeless.{::, HNil}
|
||||
* - High levels of type-safety to reduce the incidence of bugs.
|
||||
* - Mutable links to represent program structure.
|
||||
*/
|
||||
object Core {
|
||||
|
||||
// ==========================================================================
|
||||
// === Graph ================================================================
|
||||
// ==========================================================================
|
||||
|
||||
/** This the underlying graph representation for the core language. */
|
||||
case class CoreGraph() extends PrimGraph
|
||||
|
||||
sealed case class Nodes() extends PrimGraph.Component
|
||||
type Node[G <: PrimGraph] = PrimGraph.Component.Ref[G, Nodes]
|
||||
implicit class GraphWithNodes[G <: PrimGraph](graph: PrimGraph.GraphData[G]) {
|
||||
def addNode()(implicit ev: PrimGraph.HasComponent[G, Nodes]): Node[G] = {
|
||||
graph.addComponent[Nodes]()
|
||||
}
|
||||
}
|
||||
|
||||
sealed case class Links() extends PrimGraph.Component
|
||||
type Link[G <: PrimGraph] = PrimGraph.Component.Ref[G, Links]
|
||||
implicit class GraphWithLinks[G <: PrimGraph](graph: PrimGraph.GraphData[G]) {
|
||||
def addLink()(implicit ev: PrimGraph.HasComponent[G, Links]): Link[G] = {
|
||||
graph.addComponent[Links]()
|
||||
}
|
||||
}
|
||||
|
||||
implicit def components =
|
||||
new PrimGraph.Component.List[CoreGraph] {
|
||||
type Out = Nodes :: Links :: HNil // TODO [AA] Actually add the proper components
|
||||
}
|
||||
|
||||
implicit def nodeFields =
|
||||
new PrimGraph.Component.Field.List[CoreGraph, Nodes] {
|
||||
type Out = Node.Shape :: Node.ParentLink :: HNil // TODO [AA] Actually add the proper components
|
||||
}
|
||||
// ^ Should not compile -> traversal only on use
|
||||
|
||||
implicit def linkFields =
|
||||
new PrimGraph.Component.Field.List[CoreGraph, Links] {
|
||||
type Out = Link.Shape :: HNil // TODO [AA] Actually add the proper components
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// === Node =================================================================
|
||||
// ==========================================================================
|
||||
|
||||
/** Defines the fields of a node. */
|
||||
object Node {
|
||||
sealed trait Shape extends PrimGraph.Component.Field
|
||||
object Shape {
|
||||
|
||||
implicit def sized = new Sized[Shape] { type Out = _3 }
|
||||
|
||||
sealed case class Null() extends Shape
|
||||
object Null {
|
||||
val any = PrimGraph.Component.VariantMatcher[Shape, Null](0)
|
||||
implicit def sized = new Sized[Null] { type Out = _0 }
|
||||
}
|
||||
|
||||
sealed case class Name() extends Shape
|
||||
// ???
|
||||
|
||||
sealed case class App() extends Shape
|
||||
object App {
|
||||
implicit def sized = new Sized[App] { type Out = _2 }
|
||||
|
||||
val any = PrimGraph.Component.VariantMatcher[Shape, App](1)
|
||||
|
||||
def unapply[G <: PrimGraph, C <: PrimGraph.Component](
|
||||
arg: PrimGraph.Component.Ref[G, C]
|
||||
)(
|
||||
implicit graph: PrimGraph.GraphData[G],
|
||||
ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
): Option[(Link[G], Link[G])] = {
|
||||
any.unapply(arg).map(t => (t.fn, t.arg))
|
||||
}
|
||||
|
||||
implicit class AppInstance[G <: PrimGraph, C <: PrimGraph.Component](
|
||||
node: PrimGraph.Component.Refined[
|
||||
Shape,
|
||||
App,
|
||||
PrimGraph.Component.Ref[G, C]
|
||||
]
|
||||
) {
|
||||
def fn(
|
||||
implicit graph: PrimGraph.GraphData[G],
|
||||
ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
): Link[G] = {
|
||||
PrimGraph.Component.Ref(
|
||||
graph.unsafeReadField[C, Shape](
|
||||
PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def fn_=(value: Link[G])(
|
||||
implicit graph: PrimGraph.GraphData[G],
|
||||
ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
): Unit = {
|
||||
graph.unsafeWriteField[C, Shape](
|
||||
PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
1,
|
||||
value.ix
|
||||
)
|
||||
}
|
||||
|
||||
def arg(
|
||||
implicit graph: PrimGraph.GraphData[G],
|
||||
ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
): Link[G] = {
|
||||
PrimGraph.Component.Ref(
|
||||
graph.unsafeReadField[C, Shape](
|
||||
PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def arg_=(value: Link[G])(
|
||||
implicit graph: PrimGraph.GraphData[G],
|
||||
ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
): Unit = {
|
||||
graph.unsafeWriteField[C, Shape](
|
||||
PrimGraph.Component.Refined.unwrap(node).ix,
|
||||
2,
|
||||
value.ix
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed case class ParentLink() extends PrimGraph.Component.Field
|
||||
object ParentLink {
|
||||
implicit def sized = new Sized[ParentLink] { type Out = _1 }
|
||||
|
||||
implicit class ParentLinkInstance[
|
||||
G <: PrimGraph,
|
||||
C <: PrimGraph.Component
|
||||
](
|
||||
node: PrimGraph.Component.Ref[G, C]
|
||||
) {
|
||||
def parent(
|
||||
implicit graph: PrimGraph.GraphData[G],
|
||||
ev: PrimGraph.HasComponentField[G, C, ParentLink]
|
||||
): Link[G] = {
|
||||
PrimGraph.Component.Ref(
|
||||
graph.unsafeReadField[C, ParentLink](node.ix, 0)
|
||||
)
|
||||
}
|
||||
|
||||
def parent_=(value: Link[G])(
|
||||
implicit graph: PrimGraph.GraphData[G],
|
||||
ev: PrimGraph.HasComponentField[G, C, ParentLink]
|
||||
): Unit = {
|
||||
graph.unsafeWriteField[C, ParentLink](node.ix, 0, value.ix)
|
||||
}
|
||||
}
|
||||
|
||||
implicit def ParentLink_transInstance[
|
||||
F <: PrimGraph.Component.Field,
|
||||
R,
|
||||
G <: PrimGraph,
|
||||
C <: PrimGraph.Component
|
||||
](
|
||||
t: PrimGraph.Component.Refined[F, R, PrimGraph.Component.Ref[G, C]]
|
||||
): ParentLinkInstance[G, C] = t.wrapped
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// === Link =================================================================
|
||||
// ==========================================================================
|
||||
|
||||
/** Defines the fields of a link. */
|
||||
object Link {
|
||||
sealed case class Shape() extends PrimGraph.Component.Field
|
||||
object Shape {
|
||||
implicit def sized = new Sized[Shape] { type Out = _2 }
|
||||
|
||||
implicit class ShapeInstance[
|
||||
G <: PrimGraph,
|
||||
C <: PrimGraph.Component
|
||||
](node: PrimGraph.Component.Ref[G, C]) {
|
||||
def source(
|
||||
implicit graph: PrimGraph.GraphData[G],
|
||||
ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
): Link[G] = {
|
||||
PrimGraph.Component.Ref(
|
||||
graph.unsafeReadField[C, Shape](node.ix, 0)
|
||||
)
|
||||
}
|
||||
|
||||
def source_=(value: Link[G])(
|
||||
implicit graph: PrimGraph.GraphData[G],
|
||||
ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
): Unit = {
|
||||
graph.unsafeWriteField[C, Shape](node.ix, 0, value.ix)
|
||||
}
|
||||
|
||||
def sink(
|
||||
implicit graph: PrimGraph.GraphData[G],
|
||||
ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
): Link[G] = {
|
||||
PrimGraph.Component.Ref(
|
||||
graph.unsafeReadField[C, Shape](node.ix, 1)
|
||||
)
|
||||
}
|
||||
|
||||
def sink_=(value: Link[G])(
|
||||
implicit graph: PrimGraph.GraphData[G],
|
||||
ev: PrimGraph.HasComponentField[G, C, Shape]
|
||||
): Unit = {
|
||||
graph.unsafeWriteField[C, Shape](node.ix, 1, value.ix)
|
||||
}
|
||||
}
|
||||
|
||||
implicit def Shape_transInstance[
|
||||
F <: PrimGraph.Component.Field,
|
||||
R,
|
||||
G <: PrimGraph,
|
||||
C <: PrimGraph.Component
|
||||
](
|
||||
t: PrimGraph.Component.Refined[F, R, PrimGraph.Component.Ref[G, C]]
|
||||
): ShapeInstance[G, C] = t.wrapped
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// === Components ===========================================================
|
||||
// ==========================================================================
|
||||
|
||||
/** This contains the primitive components of [[Core]].
|
||||
*
|
||||
* The primitive components of [[Core]] are those which have no simpler
|
||||
* representation and are hence fundamental building blocks of the Enso
|
||||
* language. The idea is that most of the analysis performed on [[Core]]
|
||||
* takes place on this [[Primitive]] representation, thereby greatly
|
||||
* simplifying the number of constructs with which said analyses will need to
|
||||
* contend.
|
||||
*
|
||||
* Must contain:
|
||||
* - Module
|
||||
* - Name (should they be separate or not?)
|
||||
* - Block
|
||||
* - Lambda (+ arg definition)
|
||||
* - Assignment
|
||||
* - Atom definitions
|
||||
* - Type signatures
|
||||
* - Application (+ call arguments)
|
||||
* - Case expression
|
||||
* - Number and text literals
|
||||
* - Records
|
||||
* - Comment nodes (doc and disable)
|
||||
*/
|
||||
object Primitive {}
|
||||
|
||||
/** This contains all the components of [[Core]] that can be expressed in
|
||||
* terms of [[Core.Primitive]].
|
||||
*
|
||||
* While some analyses may need to contend with the constructs included
|
||||
* herein, most notably alias analysis, most analyses should not. To this
|
||||
* end, the desugaring passes should lower constructs from [[Sugar]] to
|
||||
* constructs from [[Primitive]] as soon as possible.
|
||||
*
|
||||
* Must contain:
|
||||
* - Grouping (parentheses)
|
||||
* - Sections
|
||||
* - `_` arguments
|
||||
* - Mixfix applications
|
||||
* - Complex function definitions
|
||||
* - Complex type definitions
|
||||
* - Foreign definitions
|
||||
* - Blank
|
||||
*/
|
||||
object Sugar {}
|
||||
|
||||
/** This contains all the components of [[Core]] that are used to represent
|
||||
* various kinds of error cases.
|
||||
*
|
||||
* [[Core.Error]] is used by both [[Core.Primitive]] and [[Core.Sugar]] to
|
||||
* represent erroneous conditions. These errors are then handled by passes
|
||||
* that can collate and display or otherwise process the errors.
|
||||
*
|
||||
* Must contain:
|
||||
* - Syntax errors
|
||||
* -
|
||||
*/
|
||||
object Error {}
|
||||
}
|
||||
class Core {}
|
||||
|
@ -2,12 +2,10 @@ package org.enso.compiler.core
|
||||
|
||||
import java.util.Optional
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils
|
||||
import org.enso.syntax.text.Location
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.language.postfixOps
|
||||
import scala.util.parsing.combinator._
|
||||
|
||||
trait AstExpressionVisitor[+T] {
|
||||
def visitLong(l: AstLong): T
|
||||
|
@ -0,0 +1,910 @@
|
||||
package org.enso.compiler.test.core
|
||||
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
import org.enso.core.CoreGraph.DefinitionGen.{
|
||||
Link,
|
||||
LiteralStorage,
|
||||
NameStorage,
|
||||
Node,
|
||||
ParentStorage
|
||||
}
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import org.enso.graph.{Graph => PrimGraph}
|
||||
|
||||
/** This file tests the primitive, low-level operations on core.
|
||||
*
|
||||
* It does _not_ utilise the high-level API, and instead works directly with
|
||||
* the defined graph primitives.
|
||||
*
|
||||
* PLEASE NOTE: Many of these tests will be removed once the smart constructors
|
||||
* exist.
|
||||
*/
|
||||
class CorePrimTest extends CompilerTest with BeforeAndAfterEach {
|
||||
|
||||
// === Test Setup ===========================================================
|
||||
import org.enso.core.CoreGraph.DefinitionGen.CoreGraph
|
||||
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._
|
||||
|
||||
// Reassignable mutable fixture elements
|
||||
implicit var graph: PrimGraph.GraphData[CoreGraph] = _
|
||||
implicit var literalStorage: LiteralStorage = _
|
||||
implicit var parentStorage: ParentStorage = _
|
||||
implicit var nameStorage: NameStorage = _
|
||||
|
||||
override def beforeEach(): Unit = {
|
||||
graph = PrimGraph[CoreGraph]()
|
||||
literalStorage = LiteralStorage()
|
||||
parentStorage = ParentStorage()
|
||||
nameStorage = NameStorage()
|
||||
}
|
||||
|
||||
// === Tests for Links ======================================================
|
||||
|
||||
val link = "A link"
|
||||
|
||||
link should "only be equal to itself" in {
|
||||
val l1: Link[CoreGraph] = graph.addLink()
|
||||
val l2: Link[CoreGraph] = graph.addLink()
|
||||
|
||||
l1 shouldEqual l1
|
||||
l1 should not equal l2
|
||||
}
|
||||
|
||||
link should "have a source and a target" in {
|
||||
val l1: Link[CoreGraph] = graph.addLink()
|
||||
val srcNode: Node[CoreGraph] = graph.addNode()
|
||||
val destNode: Node[CoreGraph] = graph.addNode()
|
||||
|
||||
l1.source = srcNode
|
||||
l1.target = destNode
|
||||
|
||||
val expectedShape = Link.ShapeVal(srcNode, destNode)
|
||||
|
||||
l1.shape shouldEqual expectedShape
|
||||
}
|
||||
|
||||
// === Tests for Nodes ======================================================
|
||||
|
||||
val node = "A node"
|
||||
val nodeShape = "A node's shape"
|
||||
|
||||
node should "only be equal to itself" in {
|
||||
val n1 = graph.addNode()
|
||||
val n2 = graph.addNode()
|
||||
|
||||
n1 shouldEqual n1
|
||||
n1 should not equal n2
|
||||
}
|
||||
|
||||
node should "contain source location information" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
n1.sourceStart = 302
|
||||
n1.sourceEnd = 364
|
||||
|
||||
val expectedLocation = Node.LocationVal(302, 364)
|
||||
|
||||
n1.location shouldEqual expectedLocation
|
||||
}
|
||||
|
||||
node should "be able to have multiple parent edges" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
val l3 = graph.addLink()
|
||||
|
||||
l1.target = n1
|
||||
l2.target = n1
|
||||
l3.target = n1
|
||||
|
||||
val parentIndices: Vector[Int] = Vector(l1.ix, l2.ix, l3.ix)
|
||||
n1.parents = parentIndices
|
||||
|
||||
n1.parents shouldEqual parentIndices
|
||||
n1.parents.length shouldEqual 3
|
||||
}
|
||||
|
||||
node should "be able to take on multiple shapes" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[Empty](n1)
|
||||
|
||||
n1 match {
|
||||
case Empty.any(_) =>
|
||||
Node.setShape[AtomDef](n1)
|
||||
|
||||
n1 match {
|
||||
case AtomDef.any(_) => succeed
|
||||
case _ => fail
|
||||
}
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to be empty" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[Empty](n1)
|
||||
|
||||
val isEmpty = n1 match {
|
||||
case Empty.any(_) => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
isEmpty shouldEqual true
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a list cons cell" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[MetaList](n1)
|
||||
|
||||
n1 match {
|
||||
case MetaList.any(n1) =>
|
||||
n1.head = l1
|
||||
n1.tail = l2
|
||||
|
||||
n1.metaList shouldEqual MetaListVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent boolean true" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[MetaNil](n1)
|
||||
|
||||
n1 match {
|
||||
case MetaNil.any(_) => succeed
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a nil cell" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[MetaTrue](n1)
|
||||
|
||||
n1 match {
|
||||
case MetaTrue.any(_) => succeed
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent boolean false" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[MetaFalse](n1)
|
||||
|
||||
n1 match {
|
||||
case MetaFalse.any(_) => succeed
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a numeric literal" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[NumericLiteral](n1)
|
||||
|
||||
n1 match {
|
||||
case NumericLiteral.any(n1) =>
|
||||
val testText = "1e-1"
|
||||
n1.number = testText
|
||||
|
||||
n1.numericLiteral shouldEqual NumericLiteralVal(testText)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a text literal" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[TextLiteral](n1)
|
||||
|
||||
n1 match {
|
||||
case TextLiteral.any(n1) =>
|
||||
val testText = "Lorem ipsum"
|
||||
n1.text = testText
|
||||
|
||||
n1.textLiteral shouldEqual TextLiteralVal(testText)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a foreign code literal" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[ForeignCodeLiteral](n1)
|
||||
|
||||
n1 match {
|
||||
case ForeignCodeLiteral.any(n1) =>
|
||||
val testText = "lambda x: x + 1"
|
||||
n1.code = testText
|
||||
|
||||
n1.foreignCodeLiteral shouldEqual ForeignCodeLiteralVal(testText)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a name" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[Name](n1)
|
||||
|
||||
n1 match {
|
||||
case Name.any(n1) =>
|
||||
val testName = "Name"
|
||||
n1.nameLiteral = testName
|
||||
|
||||
n1.name shouldEqual NameVal(testName)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent `this`" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[ThisName](n1)
|
||||
|
||||
n1 match {
|
||||
case ThisName.any(_) => succeed
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent `here`" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[HereName](n1)
|
||||
|
||||
n1 match {
|
||||
case HereName.any(_) => succeed
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a module" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
val l3 = graph.addLink()
|
||||
|
||||
Node.setShape[ModuleDef](n1)
|
||||
|
||||
n1 match {
|
||||
case ModuleDef.any(n1) =>
|
||||
n1.name = l1
|
||||
n1.imports = l2
|
||||
n1.definitions = l3
|
||||
|
||||
n1.moduleDef shouldEqual ModuleDefVal(l1, l2, l3)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent an import" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
|
||||
Node.setShape[Import](n1)
|
||||
|
||||
n1 match {
|
||||
case Import.any(n1) =>
|
||||
n1.segments = l1
|
||||
|
||||
n1.`import` shouldEqual ImportVal(l1)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a lop-level binding" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[TopLevelBinding](n1)
|
||||
|
||||
n1 match {
|
||||
case TopLevelBinding.any(n1) =>
|
||||
n1.module = l1
|
||||
n1.binding = l2
|
||||
|
||||
n1.topLevelBinding shouldEqual TopLevelBindingVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent an atom definition" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[AtomDef](n1)
|
||||
|
||||
n1 match {
|
||||
case AtomDef.any(n1) =>
|
||||
n1.name = l1
|
||||
n1.args = l2
|
||||
|
||||
n1.atomDef shouldEqual AtomDefVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a complex type definition" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
val l3 = graph.addLink()
|
||||
|
||||
Node.setShape[TypeDef](n1)
|
||||
|
||||
n1 match {
|
||||
case TypeDef.any(n1) =>
|
||||
n1.name = l1
|
||||
n1.typeParams = l2
|
||||
n1.body = l3
|
||||
|
||||
n1.typeDef shouldEqual TypeDefVal(l1, l2, l3)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a type signature" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[TypeAscription](n1)
|
||||
|
||||
n1 match {
|
||||
case TypeAscription.any(n1) =>
|
||||
n1.typed = l1
|
||||
n1.sig = l2
|
||||
|
||||
n1.typeAscription shouldEqual TypeAscriptionVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent monadic context ascription" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[ContextAscription](n1)
|
||||
|
||||
n1 match {
|
||||
case ContextAscription.any(n1) =>
|
||||
n1.typed = l1
|
||||
n1.context = l2
|
||||
|
||||
n1.contextAscription shouldEqual ContextAscriptionVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a typeset member" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
val l3 = graph.addLink()
|
||||
|
||||
Node.setShape[TypesetMember](n1)
|
||||
|
||||
n1 match {
|
||||
case TypesetMember.any(n1) =>
|
||||
n1.label = l1
|
||||
n1.memberType = l2
|
||||
n1.value = l3
|
||||
|
||||
n1.typesetMember shouldEqual TypesetMemberVal(l1, l2, l3)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent the subsumption judgement" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[TypesetSubsumption](n1)
|
||||
|
||||
n1 match {
|
||||
case TypesetSubsumption.any(n1) =>
|
||||
n1.left = l1
|
||||
n1.right = l2
|
||||
|
||||
n1.typesetSubsumption shouldEqual TypesetSubsumptionVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent the eqquality judgement" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[TypesetEquality](n1)
|
||||
|
||||
n1 match {
|
||||
case TypesetEquality.any(n1) =>
|
||||
n1.left = l1
|
||||
n1.right = l2
|
||||
|
||||
n1.typesetEquality shouldEqual TypesetEqualityVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent the concatenation operator" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[TypesetConcat](n1)
|
||||
|
||||
n1 match {
|
||||
case TypesetConcat.any(n1) =>
|
||||
n1.left = l1
|
||||
n1.right = l2
|
||||
|
||||
n1.typesetConcat shouldEqual TypesetConcatVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent the union operator" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[TypesetUnion](n1)
|
||||
|
||||
n1 match {
|
||||
case TypesetUnion.any(n1) =>
|
||||
n1.left = l1
|
||||
n1.right = l2
|
||||
|
||||
n1.typesetUnion shouldEqual TypesetUnionVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent the intersection operator" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[TypesetIntersection](n1)
|
||||
|
||||
n1 match {
|
||||
case TypesetIntersection.any(n1) =>
|
||||
n1.left = l1
|
||||
n1.right = l2
|
||||
|
||||
n1.typesetIntersection shouldEqual TypesetIntersectionVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent the subtraction operator" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[TypesetSubtraction](n1)
|
||||
|
||||
n1 match {
|
||||
case TypesetSubtraction.any(n1) =>
|
||||
n1.left = l1
|
||||
n1.right = l2
|
||||
|
||||
n1.typesetSubtraction shouldEqual TypesetSubtractionVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a lambda" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[Lambda](n1)
|
||||
|
||||
n1 match {
|
||||
case Lambda.any(n1) =>
|
||||
n1.arg = l1
|
||||
n1.body = l2
|
||||
|
||||
n1.lambda shouldEqual LambdaVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a function definition" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
val l3 = graph.addLink()
|
||||
|
||||
Node.setShape[FunctionDef](n1)
|
||||
|
||||
n1 match {
|
||||
case FunctionDef.any(n1) =>
|
||||
n1.name = l1
|
||||
n1.args = l2
|
||||
n1.body = l3
|
||||
|
||||
n1.functionDef shouldEqual FunctionDefVal(l1, l2, l3)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a method definition" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
val l3 = graph.addLink()
|
||||
|
||||
Node.setShape[MethodDef](n1)
|
||||
|
||||
n1 match {
|
||||
case MethodDef.any(n1) =>
|
||||
n1.targetPath = l1
|
||||
n1.name = l2
|
||||
n1.function = l3
|
||||
|
||||
n1.methodDef shouldEqual MethodDefVal(l1, l2, l3)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent an ignored argument" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[IgnoredArgument](n1)
|
||||
|
||||
n1 match {
|
||||
case IgnoredArgument.any(_) => succeed
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent a definition-site argument" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
val l3 = graph.addLink()
|
||||
|
||||
Node.setShape[DefinitionArgument](n1)
|
||||
|
||||
n1 match {
|
||||
case DefinitionArgument.any(n1) =>
|
||||
n1.name = l1
|
||||
n1.suspended = l2
|
||||
n1.default = l3
|
||||
|
||||
n1.definitionArgument shouldEqual DefinitionArgumentVal(l1, l2, l3)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent prefix function application" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[Application](n1)
|
||||
|
||||
n1 match {
|
||||
case Application.any(n1) =>
|
||||
n1.function = l1
|
||||
n1.argument = l2
|
||||
|
||||
n1.application shouldEqual ApplicationVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent infix function application" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
val l3 = graph.addLink()
|
||||
|
||||
Node.setShape[InfixApplication](n1)
|
||||
|
||||
n1 match {
|
||||
case InfixApplication.any(n1) =>
|
||||
n1.left = l1
|
||||
n1.operator = l2
|
||||
n1.right = l3
|
||||
|
||||
n1.infixApplication shouldEqual InfixApplicationVal(l1, l2, l3)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent left sections" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[LeftSection](n1)
|
||||
|
||||
n1 match {
|
||||
case LeftSection.any(n1) =>
|
||||
n1.arg = l1
|
||||
n1.operator = l2
|
||||
|
||||
n1.leftSection shouldEqual LeftSectionVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent right sections" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[RightSection](n1)
|
||||
|
||||
n1 match {
|
||||
case RightSection.any(n1) =>
|
||||
n1.operator = l1
|
||||
n1.arg = l2
|
||||
|
||||
n1.rightSection shouldEqual RightSectionVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent centre sections" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
|
||||
Node.setShape[CentreSection](n1)
|
||||
|
||||
n1 match {
|
||||
case CentreSection.any(n1) =>
|
||||
n1.operator = l1
|
||||
|
||||
n1.centreSection shouldEqual CentreSectionVal(l1)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent forced terms" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
|
||||
Node.setShape[ForcedTerm](n1)
|
||||
|
||||
n1 match {
|
||||
case ForcedTerm.any(n1) =>
|
||||
n1.expression = l1
|
||||
|
||||
n1.forcedTerm shouldEqual ForcedTermVal(l1)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent _ arguments" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[LambdaShorthandArgument](n1)
|
||||
|
||||
n1 match {
|
||||
case LambdaShorthandArgument.any(_) => succeed
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent call site arguments" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[CallSiteArgument](n1)
|
||||
|
||||
n1 match {
|
||||
case CallSiteArgument.any(n1) =>
|
||||
n1.expression = l1
|
||||
n1.name = l2
|
||||
|
||||
n1.callSiteArgument shouldEqual CallSiteArgumentVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent the defaults suspension operator" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[SuspendDefaultsOperator](n1)
|
||||
|
||||
n1 match {
|
||||
case SuspendDefaultsOperator.any(_) => succeed
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent block expressions" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[Block](n1)
|
||||
|
||||
n1 match {
|
||||
case Block.any(n1) =>
|
||||
n1.expressions = l1
|
||||
n1.returnVal = l2
|
||||
|
||||
n1.block shouldEqual BlockVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent bindings" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[Binding](n1)
|
||||
|
||||
n1 match {
|
||||
case Binding.any(n1) =>
|
||||
n1.name = l1
|
||||
n1.expression = l2
|
||||
|
||||
n1.binding shouldEqual BindingVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent case expressions" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[CaseExpr](n1)
|
||||
|
||||
n1 match {
|
||||
case CaseExpr.any(n1) =>
|
||||
n1.scrutinee = l1
|
||||
n1.branches = l2
|
||||
|
||||
n1.caseExpr shouldEqual CaseExprVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent case branches" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[CaseBranch](n1)
|
||||
|
||||
n1 match {
|
||||
case CaseBranch.any(n1) =>
|
||||
n1.pattern = l1
|
||||
n1.expression = l2
|
||||
|
||||
n1.caseBranch shouldEqual CaseBranchVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent structural patterns" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
|
||||
Node.setShape[StructuralMatch](n1)
|
||||
|
||||
n1 match {
|
||||
case StructuralMatch.any(n1) =>
|
||||
n1.matchExpression = l1
|
||||
|
||||
n1.structuralMatch shouldEqual StructuralMatchVal(l1)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent type-based patterns" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
|
||||
Node.setShape[TypeMatch](n1)
|
||||
|
||||
n1 match {
|
||||
case TypeMatch.any(n1) =>
|
||||
n1.matchExpression = l1
|
||||
|
||||
n1.typeMatch shouldEqual TypeMatchVal(l1)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent named type-based patterns" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
|
||||
Node.setShape[NamedMatch](n1)
|
||||
|
||||
n1 match {
|
||||
case NamedMatch.any(n1) =>
|
||||
n1.matchExpression = l1
|
||||
|
||||
n1.namedMatch shouldEqual NamedMatchVal(l1)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent fallback patterns" in {
|
||||
val n1 = graph.addNode()
|
||||
|
||||
Node.setShape[FallbackMatch](n1)
|
||||
|
||||
n1 match {
|
||||
case FallbackMatch.any(_) => succeed
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent doc comments" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[DocComment](n1)
|
||||
|
||||
n1 match {
|
||||
case DocComment.any(n1) =>
|
||||
n1.commented = l1
|
||||
n1.doc = l2
|
||||
|
||||
n1.docComment shouldEqual DocCommentVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent foreign code segments" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
val l2 = graph.addLink()
|
||||
|
||||
Node.setShape[ForeignDefinition](n1)
|
||||
|
||||
n1 match {
|
||||
case ForeignDefinition.any(n1) =>
|
||||
n1.language = l1
|
||||
n1.code = l2
|
||||
|
||||
n1.foreignDefinition shouldEqual ForeignDefinitionVal(l1, l2)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
|
||||
nodeShape should "be able to represent syntax errors" in {
|
||||
val n1 = graph.addNode()
|
||||
val l1 = graph.addLink()
|
||||
|
||||
Node.setShape[SyntaxError](n1)
|
||||
|
||||
n1 match {
|
||||
case SyntaxError.any(n1) =>
|
||||
n1.errorNode = l1
|
||||
|
||||
n1.syntaxError shouldEqual SyntaxErrorVal(l1)
|
||||
case _ => fail
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package org.enso.compiler.test.core
|
||||
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
// 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 {
|
||||
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package org.enso.compiler.test.core
|
||||
|
||||
import org.enso.compiler.core.Core
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
import org.enso.graph.{Graph => PrimGraph}
|
||||
|
||||
class GraphTest extends CompilerTest {
|
||||
import Core._
|
||||
implicit val graph: PrimGraph.GraphData[Core.CoreGraph] = PrimGraph[Core.CoreGraph]()
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
package org.enso.interpreter.test
|
||||
|
||||
import java.io.{ByteArrayOutputStream, StringReader}
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
import com.oracle.truffle.api.instrumentation.EventBinding
|
||||
import org.enso.interpreter.Constants
|
||||
import org.graalvm.polyglot.{Context, Source, Value}
|
||||
import org.enso.interpreter.instrument.{
|
||||
FunctionCallExtractorInstrument,
|
||||
ReplDebuggerInstrument,
|
||||
@ -13,6 +11,7 @@ import org.enso.interpreter.instrument.{
|
||||
}
|
||||
import org.enso.interpreter.test.CodeLocationsTestInstrument.LocationsEventListener
|
||||
import org.enso.polyglot.{ExecutionContext, Function, LanguageInfo}
|
||||
import org.graalvm.polyglot.{Context, Value}
|
||||
import org.scalatest.{Assertions, FlatSpec, Matchers}
|
||||
|
||||
case class LocationsInstrumenter(instrument: CodeLocationsTestInstrument) {
|
||||
|
@ -2,15 +2,9 @@ package org.enso.interpreter.test
|
||||
|
||||
import java.io.File
|
||||
|
||||
import org.enso.interpreter.Constants
|
||||
import org.enso.pkg.Package
|
||||
import org.enso.polyglot.{
|
||||
ExecutionContext,
|
||||
LanguageInfo,
|
||||
RuntimeOptions,
|
||||
TopScope
|
||||
}
|
||||
import org.graalvm.polyglot.{Context, Source, Value}
|
||||
import org.enso.polyglot.{ExecutionContext, LanguageInfo, RuntimeOptions}
|
||||
import org.graalvm.polyglot.{Context, Value}
|
||||
import org.scalatest.{FlatSpec, Matchers}
|
||||
|
||||
trait PackageTest extends FlatSpec with Matchers with ValueEquality {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.enso.interpreter.test.instrument
|
||||
import java.util.function.Consumer
|
||||
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode.FunctionCall
|
||||
import org.enso.interpreter.test.InterpreterTest
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
package org.enso.interpreter.test.instrument
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode.FunctionCall
|
||||
import org.enso.interpreter.test.InterpreterTest
|
||||
import org.graalvm.polyglot.Value
|
||||
|
@ -11,6 +11,7 @@ class ValueExtractorTest extends InterpreterTest {
|
||||
val results = mutable.HashMap[(Int, Int), Any]()
|
||||
getValueExtractorInstrument.bindTo("Test.main", 7, 5, { res =>
|
||||
results.put((7, 5), res)
|
||||
()
|
||||
})
|
||||
eval(code)
|
||||
results shouldEqual Map((7, 5) -> 4)
|
||||
@ -29,12 +30,15 @@ class ValueExtractorTest extends InterpreterTest {
|
||||
val instrumenter = getValueExtractorInstrument
|
||||
instrumenter.bindTo("Test.main", 23, 7, { x =>
|
||||
results.put("x", x)
|
||||
()
|
||||
})
|
||||
instrumenter.bindTo("Test.main", 39, 5, { y =>
|
||||
results.put("y", y)
|
||||
()
|
||||
})
|
||||
instrumenter.bindTo("Test.main", 53, 5, { z =>
|
||||
results.put("z", z)
|
||||
()
|
||||
})
|
||||
|
||||
getMain(code).execute(5L.asInstanceOf[AnyRef])
|
||||
@ -53,6 +57,7 @@ class ValueExtractorTest extends InterpreterTest {
|
||||
var results = List[Any]()
|
||||
getValueExtractorInstrument.bindTo("Test.main", 23, 7, { x =>
|
||||
results ::= x
|
||||
()
|
||||
})
|
||||
|
||||
getMain(code).execute(5L.asInstanceOf[AnyRef])
|
||||
@ -73,6 +78,7 @@ class ValueExtractorTest extends InterpreterTest {
|
||||
var result: Option[Any] = None
|
||||
getValueExtractorInstrument.bindTo("Test.main", 33, 7, { x =>
|
||||
result = Some(x)
|
||||
()
|
||||
})
|
||||
getMain(code).execute(5L.asInstanceOf[AnyRef])
|
||||
result shouldEqual Some(6)
|
||||
@ -90,6 +96,7 @@ class ValueExtractorTest extends InterpreterTest {
|
||||
var zVal: Option[Any] = None
|
||||
getValueExtractorInstrument.bindTo("Test.main", 72, 7, { z =>
|
||||
zVal = Some(z)
|
||||
()
|
||||
})
|
||||
getMain(code).execute(5L.asInstanceOf[AnyRef])
|
||||
zVal shouldEqual Some(15)
|
||||
@ -108,9 +115,11 @@ class ValueExtractorTest extends InterpreterTest {
|
||||
val instrumenter = getValueExtractorInstrument
|
||||
instrumenter.bindTo("Test.main", 23, 7, { x =>
|
||||
values.put("x", x)
|
||||
()
|
||||
})
|
||||
instrumenter.bindTo("Test.main", 39, 5, { y =>
|
||||
values.put("y", y)
|
||||
()
|
||||
})
|
||||
getMain(code).execute(5L.asInstanceOf[AnyRef])
|
||||
val visualizationModule =
|
||||
|
Loading…
Reference in New Issue
Block a user