Skip to content

Commit

Permalink
Flatten pass command (#710)
Browse files Browse the repository at this point in the history
Various AST clean ups & Flatten Command

* BranchDefinition[CV] → Branch[CV]
* LeafDefinition → Leaf
* Parent → Branch[CV]
* ContentValues → RiddlValue
* Make Contents an opaque type, adding all the methods used from ArrayBuffer. 
  This makes it possible to reimplement Contents in the future.
* Separate FlattenCommand from InputFileCommand
* Finish FlattenCommand implementation.
* Remove redundant inline usage
* Convert sealed trait Token to enumeration
* Convert sealed trait AggregateUseCase to enumeration
  • Loading branch information
reid-spencer authored Dec 27, 2024
1 parent 46b8d3c commit 28d889a
Show file tree
Hide file tree
Showing 55 changed files with 1,222 additions and 1,074 deletions.
5 changes: 4 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import com.typesafe.tools.mima.core.{ProblemFilters, ReversedMissingMethodProble
import de.heikoseeberger.sbtheader.License.ALv2
import de.heikoseeberger.sbtheader.LicenseStyle.SpdxSyntax
import sbt.Append.{appendSeqImplicit, appendSet}
import sbt.Keys.{description, libraryDependencies}
import sbt.Keys.{description, libraryDependencies, scalacOptions}
import sbtbuildinfo.BuildInfoPlugin.autoImport.buildInfoPackage
import sbtcrossproject.{CrossClasspathDependency, CrossProject}
import sbttastymima.TastyMiMaPlugin.autoImport.*

import scala.collection.Seq

Global / onChangedBuildSource := ReloadOnSourceChanges
(Global / excludeLintKeys) ++= Set(mainClass, maintainer)

Expand Down Expand Up @@ -263,6 +265,7 @@ lazy val commands_cp: CrossProject = CrossModule("commands", "riddl-commands")(J
.dependsOn(cpDep(utils_cp), cpDep(language_cp), cpDep(passes_cp), cpDep(diagrams_cp))
.configure(With.typical, With.headerLicense("Apache-2.0"))
.settings(
scalacOptions ++= Seq("-explain", "--explain-types", "--explain-cyclic", "--no-warnings"),
description := "RIDDL Command Infrastructure and command definitions"
)
.jvmConfigure(With.coverage(50))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ case class ToDoListPass(
else
parents.headOption match {
case None => Seq.empty
case Some(parent: Parent) =>
case Some(parent: Branch[?]) =>
authors
.map { (ref: AuthorRef) =>
outputs.refMap.definitionOf[Author](ref.pathId, parent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ case class DotdockWriter(
extras: Map[String, String] = Map.empty[String, String]
): Unit = ???

def containerHead(cont: Parent): Unit = ???
def containerHead(cont: Branch[?]): Unit = ???

def leafHead(definition: LeafDefinition, weight: Int): Unit = ???
def leafHead(definition: Leaf, weight: Int): Unit = ???

def codeBlock(items: Seq[Statements]): Unit = ???

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ case class GeekDocWriter(
sb.append(headTemplate)
}

def containerHead(cont: Parent): Unit = {
def containerHead(cont: Branch[?]): Unit = {
val brief: String =
cont match
case p: Parent if p.contents.filter[BriefDescription].nonEmpty =>
case p: Branch[?] if p.contents.filter[BriefDescription].nonEmpty =>
p.contents.filter[BriefDescription].foldLeft("")((x,y) => x + y.brief.s)
case d: WithMetaData => d.briefString
case _ => cont.id.format + " has no brief description"
Expand All @@ -83,7 +83,7 @@ case class GeekDocWriter(
)
}

def leafHead(definition: LeafDefinition, weight: Int): Unit = {
def leafHead(definition: Leaf, weight: Int): Unit = {
fileHead(
s"${definition.id.format}: ${definition.getClass.getSimpleName}",
weight,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ trait ThemeWriter {
extras: Map[String, String] = Map.empty[String, String]
): Unit

def containerHead(cont: Parent): Unit
def containerHead(cont: Branch[?]): Unit

def leafHead(definition: LeafDefinition, weight: Int): Unit
def leafHead(definition: Leaf, weight: Int): Unit

def codeBlock(items: Seq[Statements]): Unit

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ trait MarkdownWriter(using pc: PlatformContext)
}
end makeDomainIndex

private def makeData(container: Parent, parents: Seq[String]): Level =
private def makeData(container: Branch[?], parents: Seq[String]): Level =
Level(
container.identify,
generator.makeDocLink(container, parents),
Expand Down Expand Up @@ -327,8 +327,7 @@ trait MarkdownWriter(using pc: PlatformContext)
case EntityReferenceTypeExpression(_, pid) => makeTypeName(pid, parents)
case UniqueId(_, pid) => makeTypeName(pid, parents)
case Alternation(_, of) =>
of.map(ate => makeTypeName(ate.pathId, parents))
.mkString("-")
of.toSeq.map(ate => makeTypeName(ate.pathId, parents)).mkString("-")
case _: Mapping => "Mapping"
case _: Aggregation => "Aggregation"
case _: AggregateUseCaseTypeExpression => "Message"
Expand All @@ -349,7 +348,7 @@ trait MarkdownWriter(using pc: PlatformContext)
case uid: UniqueId =>
s"Unique identifier for entity ${makePathIdRef(uid.entityPath, parents)}"
case alt: Alternation =>
val data = alt.of.map { (te: AliasedTypeExpression) =>
val data = alt.of.toSeq.map { (te: AliasedTypeExpression) =>
makePathIdRef(te.pathId, parents)
}
s"Alternation of: " + data.mkString(", ")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class MarkdownWriterTest extends HugoTestBase {
case Left(errors) =>
fail("Parse Failed:\n" + errors.map(_.format).mkString("\n"))
case Right(root) =>
root.contents mustNot be(empty)
root.contents.isEmpty mustNot be(true)
val domain = root.domains.head
val mkd = makeMDW(output, PassesResult.empty)
val diagram = DomainMapDiagram(domain)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,62 @@

package com.ossuminc.riddl.commands

import com.ossuminc.riddl.command.{Command, CommandOptions}
import com.ossuminc.riddl.commands.InputFileCommand.Options
import com.ossuminc.riddl.language.Messages.Messages
import com.ossuminc.riddl.language.parsing.RiddlParserInput
import com.ossuminc.riddl.passes.{PassesResult, Riddl}
import com.ossuminc.riddl.passes.*
import com.ossuminc.riddl.passes.transforms.FlattenPass
import com.ossuminc.riddl.utils.{Await, PlatformContext}
import org.ekrich.config.Config
import scopt.OParser

import java.io.File
import java.nio.file.Path
import scala.+:
import scala.concurrent.ExecutionContext
import scala.concurrent.duration.DurationInt

object FlattenCommand {
final val cmdName = "flatten"
case class Options(inputFile: Option[Path]) extends CommandOptions with PassOptions:
def command: String = cmdName
end Options
}

/** A Command for Parsing RIDDL input
*/
class FlattenCommand(using pc: PlatformContext) extends InputFileCommand(DumpCommand.cmdName) {
import InputFileCommand.Options
class FlattenCommand(using pc: PlatformContext) extends Command[FlattenCommand.Options](FlattenCommand.cmdName) {

import FlattenCommand.Options

def getOptionsParser: (OParser[Unit, Options], Options) = {
import builder.*
cmd(commandName).children(
arg[File]("input-file").action((f, opt) => opt.copy(inputFile = Some(f.toPath)))
) -> FlattenCommand.Options(None)
}

override def interpretConfig(config: Config) =
val obj = config.getObject(commandName).toConfig
val inputFile = Some(Path.of(obj.getString("input-file")))
Options(inputFile)
end interpretConfig

override def run(
options: Options,
outputDirOverride: Option[Path]
): Either[Messages, PassesResult] = {
options.withInputFile { (inputFile: Path) =>
options.withInputFile[PassesResult] { (inputFile: Path) =>
implicit val ec: ExecutionContext = pc.ec
val future = RiddlParserInput.fromPath(inputFile.toString).map { rpi =>
Riddl.parseAndValidate(rpi).map { result =>
// TODO: output the model to System.out without spacing and with a line break only after every Definition
result
}
Riddl.parse(rpi) match
case Left(errors) => return Left(errors)
case Right(root) =>
val input = PassInput(root)
val passes: Seq[PassCreator] = Pass.standardPasses.appended(FlattenPass.creator(options))
Right(Pass.runThesePasses(input, passes))
end match
}
Await.result(future, 10.seconds)
}
Expand All @@ -44,7 +71,8 @@ class FlattenCommand(using pc: PlatformContext) extends InputFileCommand(DumpCom
configFile: Path
): Either[Messages, Options] = {
super.loadOptionsFrom(configFile).map { options =>
resolveInputFileToConfigFile(options, configFile)
val ifco = resolveInputFileToConfigFile(options, configFile)
Options(ifco.inputFile)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ case class DataFlowDiagram(pr: PassesResult)(using pc: PlatformContext) extends
else addLine(s"$fromName -- $how --> $toName")
}

private[mermaid] def participants(connector: Connector, parent: Parent): Seq[Definition] = {
private[mermaid] def participants(connector: Connector, parent: Branch[?]): Seq[Definition] = {
for {
toDef <- pr.refMap.definitionOf[Inlet](connector.to, parent)
fromDef <- pr.refMap.definitionOf[Outlet](connector.from, parent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class EntityRelationshipDiagram(refMap: ReferenceMap)(using pc: PlatformContext)

private def makeTypeName(
pid: PathIdentifier,
parent: Parent
parent: Branch[?]
): String = {
refMap.definitionOf[Definition](pid, parent) match {
case None => s"unresolved path: ${pid.format}"
Expand All @@ -27,15 +27,14 @@ class EntityRelationshipDiagram(refMap: ReferenceMap)(using pc: PlatformContext)

private def makeTypeName(
typeEx: TypeExpression,
parent: Parent
parent: Branch[?]
): String = {
val name = typeEx match {
case AliasedTypeExpression(_, _, pid) => makeTypeName(pid, parent)
case EntityReferenceTypeExpression(_, pid) => makeTypeName(pid, parent)
case UniqueId(_, pid) => makeTypeName(pid, parent)
case Alternation(_, of) =>
of.map(ate => makeTypeName(ate.pathId, parent))
.mkString("-")
of.toSeq.map(ate => makeTypeName(ate.pathId, parent)).mkString("-")
case _: Mapping => "Mapping"
case _: Aggregation => "Aggregation"
case _: AggregateUseCaseTypeExpression => "Message"
Expand All @@ -47,7 +46,7 @@ class EntityRelationshipDiagram(refMap: ReferenceMap)(using pc: PlatformContext)
private def makeERDRelationship(
from: String,
to: Field,
parent: Parent
parent: Branch[?]
): String = {
val typeName = makeTypeName(to.typeEx, parent)
if typeName.nonEmpty then
Expand All @@ -66,7 +65,7 @@ class EntityRelationshipDiagram(refMap: ReferenceMap)(using pc: PlatformContext)
def generate(
name: String,
fields: Seq[Field],
parent: Parent
parent: Branch[?]
): Seq[String] = {

val typ: Seq[String] = s"$name {" +: fields.map { f =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ trait UseCaseDiagramSupport(using pc: PlatformContext) {
def makeDocLink(definition: Definition): String

@JSExport
def getDefinitionFor[T <: Definition: ClassTag](pathId: PathIdentifier, parent: Parent): Option[T] = {
def getDefinitionFor[T <: Definition: ClassTag](pathId: PathIdentifier, parent: Branch[?]): Option[T] = {
passesResult.refMap.definitionOf[T](pathId, parent)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class JVMNativeASTTest extends AbstractTestingBasis {
"allow attachments to be added programmatically" in {
val container = Entity(At.empty, Identifier.empty)
val a = StringAttachment(At.empty, Identifier(At.empty, "foo"), "application/json", LiteralString(At.empty, "{}"))
container.metadata += a
container.metadata.append(a)
container.metadata.filter[StringAttachment] match
case Seq(value) if value == a => succeed
case _ => fail("No go")
Expand All @@ -27,7 +27,7 @@ class JVMNativeASTTest extends AbstractTestingBasis {
"Include" should {
"identify as root container, etc" in {
import com.ossuminc.riddl.utils.URL
val incl = Include(At.empty, URL.empty, Contents.empty)
val incl = Include(At.empty, URL.empty, Contents.empty())
incl.isRootContainer mustBe true
incl.loc mustBe At.empty
incl.format mustBe "include \"\""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ class IncludeAndImportTest extends ParsingTest {
val inc = StringParserInput("", url)
root.domains mustNot be(empty)
root.domains.head.includes mustNot be(empty)
root.domains.head.includes.head.contents mustNot be(empty)
root.domains.head.includes.head.contents.isEmpty mustNot be(true)
val actual = root.domains.head.includes.head.contents.head
val expected = Type(
(1, 1, inc),
Identifier((1, 6, inc), "foo"),
String_((1, 13, inc)),
Contents.empty
Contents.empty()
)
actual mustBe expected
}
Expand All @@ -74,20 +74,20 @@ class IncludeAndImportTest extends ParsingTest {
root.domains mustNot be(empty)
root.domains.head.contexts mustNot be(empty)
root.domains.head.contexts.head.includes mustNot be(empty)
root.domains.head.contexts.head.includes.head.contents mustNot be(empty)
root.domains.head.contexts.head.includes.head.contents.isEmpty mustNot be(true)
val actual = root.domains.head.contexts.head.includes.head.contents.head
val expected = Type(
(1, 1, inc),
Identifier((1, 6, inc), "foo"),
String_((1, 12, inc)),
Contents.empty
Contents.empty()
)
actual mustBe expected
}
"handle 553-Contained-Group-References-Do-Not-Work" in { (td: TestData) =>
val (root, _) = checkFile("Include Group", "includes/includer.riddl")
root.domains mustNot be(empty)
root.domains.head.includes.head.contents mustNot be(empty)
root.domains.head.includes.head.contents.isEmpty mustNot be(true)
}
"warn about duplicate includes" in { (td: TestData) =>
val path = java.nio.file.Path.of(defaultInputDir + "/includes/duplicateInclude.riddl")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ class ParserTest extends ParsingTest with org.scalatest.Inside {
"allow multiple domains" in { (td: TestData) =>
val input = RiddlParserInput(
"""domain foo is { ??? }
|domain bar is { ??? }
|""".stripMargin,
|domain bar is { ??? }
|""".stripMargin,
td
)
parseTopLevelDomains(input) match {
Expand Down Expand Up @@ -140,7 +140,7 @@ class ParserTest extends ParsingTest with org.scalatest.Inside {
val msg = errors.map(_.format).mkString
fail(msg)
case Right(content) =>
content.contents must not be empty
content.contents.isEmpty mustNot be(true)
succeed
}
}
Expand Down Expand Up @@ -277,16 +277,16 @@ class ParserTest extends ParsingTest with org.scalatest.Inside {
case Left(errors) =>
val msg = errors.map(_.format).mkString
fail(msg)
case Right((content, rpi)) =>
content mustBe Adaptor(
case Right((adaptor: Adaptor, rpi: RiddlParserInput)) =>
adaptor mustBe Adaptor(
At(rpi, 0, 44),
Identifier(At(rpi, 8, 13), "fuzz"),
InboundAdaptor(At(rpi, 13, 18)),
ContextRef(
At(rpi, 18, 34),
PathIdentifier(At(rpi, 26, 34), Seq("foo", "bar"))
),
Contents.empty
Contents.empty()
)
}
}
Expand Down Expand Up @@ -317,10 +317,10 @@ class ParserTest extends ParsingTest with org.scalatest.Inside {
_
) =>
val firstExpected =
Field(At(rpi, 32, 43), Identifier(At(rpi, 32, 34), "b"), Bool(At(rpi, 36, 43)), Contents.empty)
Field(At(rpi, 32, 43), Identifier(At(rpi, 32, 34), "b"), Bool(At(rpi, 36, 43)), Contents.empty())
firstAggrContents.head must be(firstExpected)
val secondExpected =
Field(At(rpi, 57, 68), Identifier(At(rpi, 57, 59), "i"), Integer(At(rpi, 61, 68)), Contents.empty)
Field(At(rpi, 57, 68), Identifier(At(rpi, 57, 59), "i"), Integer(At(rpi, 61, 68)), Contents.empty())
secondAggrContents.head must be(secondExpected)
end match

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class TermTest extends ParsingTest {
val msg = errors.map(_.format).mkString
fail(msg)
case Right((model, _)) =>
import com.ossuminc.riddl.language.AST.{Parent, RiddlValue}
import com.ossuminc.riddl.language.AST.{Branch, RiddlValue}
val finder = Finder(model)
val found = finder.find(_.isInstanceOf[Term])
found contains Term(
Expand Down
Loading

0 comments on commit 28d889a

Please sign in to comment.