Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CHANGELOG

## v0.2.34 - 2025-04-25

Added option to show dependency graph and repaired issue #623
29 changes: 15 additions & 14 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
lazy val scala212 = "2.12.17"
lazy val scala213 = "2.13.10"
lazy val scala3 = "3.2.1"
lazy val scala212 = "2.12.20"
lazy val scala213 = "2.13.15"
lazy val scala3 = "3.6.3"
lazy val supportedScalaVersions = List(
scala3,
scala212,
scala213,
scala212
scala3
)

val Java11 = JavaSpec.temurin("11")
val Java11 = JavaSpec.temurin("11")

lazy val srdfVersion = "0.1.125"
lazy val utilsVersion = "0.2.25"
Expand All @@ -19,7 +19,7 @@ lazy val catsVersion = "2.9.0"
lazy val catsEffectVersion = "3.4.4"
lazy val circeVersion = "0.14.2"
lazy val commonsTextVersion = "1.8"
lazy val declineVersion = "2.4.1"
lazy val declineVersion = "2.5.0"
lazy val fansiVersion = "0.3.0"
lazy val fs2Version = "3.4.0"
lazy val jenaVersion = "4.3.2"
Expand Down Expand Up @@ -73,7 +73,6 @@ lazy val wdtkUtil = "org.wikidata.wdtk" % "wdtk-util" % wikidataToolkitVersion
lazy val scalacheck = "org.scalacheck" %% "scalacheck" % scalacheckVersion
lazy val typesafeConfig = "com.typesafe" % "config" % typesafeConfigVersion


lazy val logbackClassic = "ch.qos.logback" % "logback-classic" % logbackVersion
lazy val scalaLogging =
"com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion
Expand Down Expand Up @@ -142,8 +141,8 @@ lazy val shexs = project
catsEffect,
decline,
declineEffect,
slf4jAPI,
slf4jSimple,
// slf4jAPI,
// slf4jSimple,
srdf,
srdf4j,
srdfJena,
Expand Down Expand Up @@ -252,7 +251,7 @@ lazy val wshex = project
.settings(
crossScalaVersions := supportedScalaVersions,
libraryDependencies ++= Seq(
utils,
utils,
catsCore,
catsKernel,
circeCore,
Expand All @@ -269,7 +268,7 @@ lazy val wshex = project
scalaCollCompat,
srdfJena,
munit % Test,
munitEffect % Test,
munitEffect % Test
),
testFrameworks += new TestFramework("munit.Framework")
)
Expand Down Expand Up @@ -580,6 +579,8 @@ lazy val commonSettings = compilationSettings ++ sharedDependencies ++ Seq(
email = "jelabra@gmail.com",
url = url("https://weso.labra.es")
)
),
libraryDependencies += compilerPlugin("com.github.ghik" % "zerowaste" % "0.2.1" cross CrossVersion.full)
)
/*libraryDependencies += compilerPlugin(
("com.github.ghik" % "zerowaste" % "1.0.0").cross(CrossVersion.full)
)*/
) ++ warnUnusedImport
3 changes: 3 additions & 0 deletions examples/non_stratified.shex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
prefix : <http://example.org/>

:S { :p NOT @:S }
4 changes: 4 additions & 0 deletions examples/positive_recursion.shex
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
prefix : <http://example.org/>

# Positive recursion
:S { :p @:S +; :p .* } OR [ :d ]
5 changes: 5 additions & 0 deletions examples/separation.shex
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
prefix : <http://example.org/>

# Separation test
:S { :p @:S + }
:S1 NOT @:S
4 changes: 4 additions & 0 deletions examples/separation1.shex
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
prefix : <http://example.org/>

# Separation test 1
:S { :p @:S + }
4 changes: 4 additions & 0 deletions examples/separation2.shex
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
prefix : <http://example.org/>

# Separation test 1
:S { :p @:S + }
5 changes: 5 additions & 0 deletions examples/separation4.shex
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
prefix : <http://example.org/>

# Separation test
:S { :p @:S + }
:S1 NOT @:S
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ trait DepGraph[Node] {

private def haveNegativeLink(node1: Node, node2: Node): Boolean =
edgeBetween(node1, node2) match {
case Some(Neg) => true
case Some(Pos) => false
case Some(Both) => true
case None => false
case Some(Neg) => true
case Some(Pos) => false
// case Some(Both) => true
case None => false
}

def showEdges(showNode: Node => String = x => x.toString): String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ case class DepGraphJGraphT[Node]() extends DepGraph[Node] with LazyLogging {
// if (!graph.containsVertex(node))
graph.addVertex(node)

private def addEdge(node1: Node, node2: Node, edge: Edge): DepGraph[Node] = {
private def addEdgeInternal(node1: Node, node2: Node, edge: Edge): DepGraph[Node] = {
checkVertex(node1)
checkVertex(node2)
graph.addEdge(node1, node2, edge)
Expand All @@ -61,14 +61,19 @@ case class DepGraphJGraphT[Node]() extends DepGraph[Node] with LazyLogging {
checkVertex(node2)
this.edgeBetween(node1, node2) match {
case Some(pn) =>
// println(s"Edge between $node1 and $node2 already exists with posNeg $pn")
removeEdge(node1, node2)
addEdge(node1, node2, Edge(node1, pn.combine(posNeg), node2))
case None => addEdge(node1, node2, Edge(node1, posNeg, node2))
val newPosNeg = pn.combine(posNeg)
val result = addEdgeInternal(node1, node2, Edge(node1, newPosNeg, node2))
// println(s"New edge added $node1 and $node2 posNeg $newPosNeg")
result
case None => addEdgeInternal(node1, node2, Edge(node1, posNeg, node2))
}
}

override def edgeBetween(node1: Node, node2: Node): Option[PosNeg] = {
val outEdges = graph.edgesOf(node1).asScala.toSet
val outEdges: Set[Edge] = graph.outgoingEdgesOf(node1).asScala.toSet
// println(s"outEdges($node1)=$outEdges")
outEdges.collect { case e: Edge if e.target == node2 => e.posNeg }.headOption
}

Expand All @@ -89,7 +94,7 @@ case class DepGraphJGraphT[Node]() extends DepGraph[Node] with LazyLogging {
}

private def containsNegEdge(g: Graph[Node, Edge]): Boolean =
g.edgeSet.asScala.exists(e => e.posNeg == Neg || e.posNeg == Both)
g.edgeSet.asScala.exists(e => e.posNeg == Neg) // || e.posNeg == Both)

override def negCycles: Set[Set[(Node, Node)]] = {
val scAlg: StrongConnectivityAlgorithm[Node, Edge] =
Expand All @@ -112,9 +117,9 @@ case class DepGraphJGraphT[Node]() extends DepGraph[Node] with LazyLogging {

def showPosNeg(pn: PosNeg): String =
pn match {
case Pos => "-(+)->"
case Neg => "-(-)->"
case Both => "-(-/+)->"
case Pos => "-(+)->"
case Neg => "-(-)->"
// case Both => "-(-/+)->"
}

def showEdges(showNode: Node => String): String = {
Expand Down
24 changes: 13 additions & 11 deletions modules/depGraphs/src/main/scala/es/weso/depgraphs/PosNeg.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,29 @@ package es.weso.depgraphs

sealed trait PosNeg {
def change: PosNeg = this match {
case Neg => Pos
case Pos => Neg
case Both => Both
case Neg => Pos
case Pos => Neg
// case Both => Both
}
def combine(other: PosNeg): PosNeg
}

case object Pos extends PosNeg {
override def combine(other: PosNeg): PosNeg = other match {
case Pos => Pos
case Neg => Both
case Both => Both
case Pos => Pos
case Neg => Neg
// case Neg => Both
// case Both => Both
}
}
case object Neg extends PosNeg {
override def combine(other: PosNeg): PosNeg = other match {
case Pos => Both
case Neg => Neg
case Both => Both
case Pos => Neg // Both
case Neg => Neg
// case Both => Both
}
}
case object Both extends PosNeg {

/*case object Both extends PosNeg {
override def combine(other: PosNeg): PosNeg = Both
}
}*/
35 changes: 22 additions & 13 deletions modules/shex/src/main/scala/es/weso/shex/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ object Dependencies {
* If the set is empy, there are no negated cycles.
*/
def negCycles(schema: Schema): ES[Set[Set[(ShapeLabel, ShapeLabel)]]] =
depGraph(schema).map(_.negCycles)
schema.depGraph.map(_.negCycles)

def oddNegCycles(schema: Schema): ES[Set[Set[(ShapeLabel, ShapeLabel)]]] =
depGraph(schema).map(_.oddNegCycles)
/* for {
dg <- depGraph(schema)
negCycles = dg.negCycles.filter { nc => dg.countNegLinks(nc) % 2 == 1 }
} yield negCycles */
// depGraph(schema).map(_.oddNegCycles)
for {
dg <- schema.depGraph
negCycles = dg.negCycles.filter(nc => dg.countNegLinks(nc) % 2 == 1)
} yield negCycles

/** Returns the dependency graph of a schema
*
Expand All @@ -42,15 +42,19 @@ object Dependencies {
case None => emptyGraph
case Some(shapes) => shapes.foldRight(emptyGraph)(addDependency(schema))
}
// println(s"Dependency graph: $r")
/*r match {
case Left(s) => println(s"Error calculating dependency graph: $s")
case Right(g) =>
println(s"Dependency graph generated:\n${g.showEdges(_.toString)}\n---")
}*/
r
}

def addDependencies(graph: DepGraph[ShapeLabel], deps: Deps): DepGraph[ShapeLabel] =
deps.foldRight(graph)(combine)

def combine(d: Dep, g: DepGraph[ShapeLabel]): DepGraph[ShapeLabel] =
// println(s"Adding edge $d to graph: $g")
// println(s"Adding edge $d to graph: $g")
g.addEdge(d._1, d._2, d._3)

def addDependency(
Expand All @@ -60,13 +64,19 @@ object Dependencies {
g <- graph
label <- getLabel(se)
deps <- dependencies(schema, se, label, Pos)
// _ <- { println(s"dependencies: ${deps}"); Right(()) }
} yield addDependencies(g, deps)

def getLabel(se: ShapeExpr): ES[ShapeLabel] =
Either.fromOption(se.id, s"Shape $se has no label")

def dependencies(schema: Schema, shape: ShapeExpr, source: ShapeLabel, posNeg: PosNeg): ES[Deps] =
// println(s"Calculating dependencies of shape $shape with source label $source and posNeg $posNeg")
def dependencies(
schema: Schema,
shape: ShapeExpr,
source: ShapeLabel,
posNeg: PosNeg
): ES[Deps] =
// println(s"Calculating dependencies of $source with shapeExpr $shape, posNeg $posNeg")
shape match {
case s: ShapeAnd =>
s.shapeExprs.map(dependencies(schema, _, source, posNeg)).sequence[ES, Deps].map(_.flatten)
Expand All @@ -75,7 +85,7 @@ object Dependencies {
s.shapeExprs.map(dependencies(schema, _, source, posNeg)).sequence[ES, Deps].map(_.flatten)

case s: ShapeNot =>
dependencies(schema, s.shapeExpr, source, Neg)
dependencies(schema, s.shapeExpr, source, posNeg.change)

case _: NodeConstraint => noDeps

Expand Down Expand Up @@ -108,7 +118,6 @@ object Dependencies {
tripleExpr: TripleExpr,
posNeg: PosNeg
): ES[Deps] =
// println(s"Calculating dependencies of tripleExpr $tripleExpr with source label $source and posNeg $posNeg")
tripleExpr match {
case t: EachOf =>
// TODO: Take into account max cardinality = 0 as a negative dependency?
Expand All @@ -134,7 +143,7 @@ object Dependencies {
case Some(ve) =>
if (tc.max == IntMax(0)) {
// TODO: Should it be negative dependency?
dependencies(schema, ve, source, posNeg.change)
dependencies(schema, ve, source, posNeg)
} else {
dependencies(schema, ve, source, posNeg)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ case class ResolvedSchema(
resolvedMapShapeExprs: Map[ShapeLabel, ResolvedShapeExpr],
resolvedMapTripleExprs: Map[ShapeLabel, ResolvedTripleExpr],
inheritanceGraph: Inheritance[ShapeLabel, ShapesRelation],
// depGraph: DepGraph[ShapeLabel],
labelLocationMap: Option[Map[ShapeLabel, Location]]
) extends AbstractSchema {

Expand Down
13 changes: 6 additions & 7 deletions modules/shex/src/main/scala/es/weso/shex/Schema.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ case class Schema(
def addTripleExprMap(te: Map[ShapeLabel, TripleExpr]): Schema =
this.copy(optTripleExprMap = Some(te))

def oddNegCycles: Either[String, Set[Set[(ShapeLabel, ShapeLabel)]]] =
lazy val oddNegCycles: Either[String, Set[Set[(ShapeLabel, ShapeLabel)]]] =
Dependencies.oddNegCycles(this)

def negCycles: Either[String, Set[Set[(ShapeLabel, ShapeLabel)]]] =
lazy val negCycles: Either[String, Set[Set[(ShapeLabel, ShapeLabel)]]] =
Dependencies.negCycles(this)

def depGraph: Either[String, DepGraph[ShapeLabel]] =
lazy val depGraph: Either[String, DepGraph[ShapeLabel]] =
Dependencies.depGraph(this)

def showCycles(str: Either[String, Set[Set[(ShapeLabel, ShapeLabel)]]]): String = str match {
Expand Down Expand Up @@ -142,9 +142,9 @@ case class Schema(

lazy val wellFormed: Either[String, Unit] = for {
_ <- checkOddNegCycles
// _ <- { println(s"Passed checkOddNegCycles..."); Right(())}
_ <- { println(s"Passed checkOddNegCycles..."); Right(()) }
_ <- checkBadShapeLabels
// _ <- { println(s"Passed checkBadShapeLabels..."); Right(())}
_ <- { println(s"Passed checkBadShapeLabels..."); Right(()) }
} yield (())

def relativize(maybeBase: Option[IRI]): Schema = maybeBase match {
Expand All @@ -161,7 +161,7 @@ case class Schema(
def resolve(base: Option[IRI], verbose: VerboseLevel): IO[ResolvedSchema] =
ResolvedSchema.resolve(this, base, verbose)

def withId(iri: IRI): Schema = this.copy(id = iri)
def withId(iri: IRI): Schema = this.copy(id = iri)

def withShapes(ses: ShapeExpr*): Schema = this.copy(shapes = ses.toList.some)

Expand All @@ -177,7 +177,6 @@ object Schema {
def emptyWithId(iri: IRI): Schema =
Schema.empty.withId(iri)


def fromIRI(
i: IRI,
base: Option[IRI],
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.8.0
sbt.version=1.10.11
2 changes: 1 addition & 1 deletion project/metals.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@

// This file enables sbt-bloop to create bloop config files.

addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.17")
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "2.0.9")

// format: on
Loading
Loading