Implement the primitive core entities (#463)

This commit is contained in:
Ara Adkins 2020-01-31 16:58:35 +00:00 committed by GitHub
parent 4533780d1f
commit ba84ee7e6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1914 additions and 396 deletions

View File

@ -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)

View File

@ -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 ===

View File

@ -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)
}
}
}
}

View File

@ -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 {

View File

@ -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 ================================================================

View File

@ -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

View File

@ -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])
}
}
}

View File

@ -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 {}

View File

@ -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

View File

@ -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
}
}
}

View File

@ -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 {
}

View File

@ -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]()
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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 =