Skip to content

Commit

Permalink
Merge 5d4bacc into 762bc14
Browse files Browse the repository at this point in the history
  • Loading branch information
reid-spencer committed Sep 22, 2023
2 parents 762bc14 + 5d4bacc commit 88da476
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ trait Types {

sealed trait TypeDefinition extends Definition

sealed trait AggregateDefinition extends TypeDefinition {
def typeEx: TypeExpression
}

/** Base trait of an expression that defines a type
*/
sealed trait TypeExpression extends RiddlValue {
Expand Down Expand Up @@ -245,6 +249,7 @@ trait Types {
brief: Option[LiteralString] = Option.empty[LiteralString],
description: Option[Description] = None
) extends LeafDefinition
with AggregateDefinition
with AlwaysEmpty
with TypeDefinition
with SagaDefinition
Expand All @@ -255,27 +260,74 @@ trait Types {
final val kind: String = "Field"
}

/** An argument to a method */
case class MethodArgument(
loc: At,
key: String,
value: TypeExpression
) extends RiddlNode {

/** Format the node to a string */
def format: String = s"$key: ${value.format}"

}

/** A leaf definition that is a callable method (function) of an aggregation
* type expressions. Methods associate an identifier with a computed type
* expression.
*
* @param loc
* The location of the field definition
* @param id
* The name of the field
* @param args
* The type of the field
* @param brief
* A brief description (one sentence) for use in documentation
* @param description
* An optional description of the field.
*/
case class Method(
loc: At,
id: Identifier,
args: Seq[MethodArgument] = Seq.empty[MethodArgument],
typeEx: TypeExpression,
brief: Option[LiteralString] = Option.empty[LiteralString],
description: Option[Description] = None
) extends LeafDefinition
with AggregateDefinition
with AlwaysEmpty
with TypeDefinition
with SagaDefinition
with StateDefinition
with FunctionDefinition
with ProjectorDefinition {
override def format: String = s"${id.format}(${args.map(_.format).mkString(", ")}): ${typeEx.format}"
final val kind: String = "Field"
}

/** A type expression that contains an aggregation of fields
*
* This is used as the base trait of Aggregations and Messages
*/
trait AggregateTypeExpression extends TypeExpression with Container[Field] {
def fields: Seq[Field]
final lazy val contents: Seq[Field] = fields
override def format: String = s"{ ${fields.map(_.format).mkString(", ")} }"
trait AggregateTypeExpression extends TypeExpression with Container[AggregateDefinition] {
def contents: Seq[AggregateDefinition]
lazy val fields: Seq[Field] = contents.filter(_.isInstanceOf[Field]).asInstanceOf[Seq[Field]]
lazy val methods: Seq[Method] = contents.filter(_.isInstanceOf[Method]).asInstanceOf[Seq[Method]]
override def format: String = s"{ ${contents.map(_.format).mkString(", ")} }"
override def isAssignmentCompatible(other: TypeExpression): Boolean = {

other match {
case oate: AggregateTypeExpression =>
val validity: Seq[Boolean] = for
ofield <- oate.fields
myField <- fields.find(_.id.value == ofield.id.value)
ofield <- oate.contents
myField <- contents.find(_.id.value == ofield.id.value)
myTypEx = myField.typeEx
oTypeEx = ofield.typeEx
yield {
myTypEx.isAssignmentCompatible(oTypeEx)
}
(validity.size == oate.fields.size) && validity.forall(_ == true)
(validity.size == oate.contents.size) && validity.forall(_ == true)
case _ =>
super.isAssignmentCompatible(other)
}
Expand All @@ -289,7 +341,8 @@ trait Types {
* @param fields
* The fields of the aggregation
*/
case class Aggregation(loc: At, fields: Seq[Field] = Seq.empty[Field]) extends AggregateTypeExpression
case class Aggregation(loc: At, contents: Seq[AggregateDefinition] = Seq.empty[AggregateDefinition]) extends
AggregateTypeExpression

object Aggregation {
def empty(loc: At = At.empty): Aggregation = { Aggregation(loc) }
Expand Down Expand Up @@ -391,7 +444,7 @@ trait Types {
case class AggregateUseCaseTypeExpression(
loc: At,
usecase: AggregateUseCase,
fields: Seq[Field] = Seq.empty[Field]
contents: Seq[AggregateDefinition] = Seq.empty[AggregateDefinition]
) extends AggregateTypeExpression {
override def format: String = {
usecase.format.toLowerCase() + " " + super.format
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,20 +376,36 @@ private[parsing] trait TypeParser extends CommonParser {
def field[u: P]: P[Field] = {
P(
location ~ identifier ~ is ~ fieldTypeExpression ~ briefly ~ description
)
.map(tpl => (Field.apply _).tupled(tpl))
).map(tpl => (Field.apply _).tupled(tpl))
}

def arguments[u:P]: P[Seq[MethodArgument]] = {
P(
(
location ~ identifier.map(_.value) ~ Punctuation.colon ~ fieldTypeExpression
).map(tpl => (MethodArgument.apply _).tupled(tpl))
).rep(min=0, Punctuation.comma)
}

def fields[u: P]: P[Seq[Field]] = {
def method[u: P]: P[Method] = {
P(
location ~ identifier ~ Punctuation.roundOpen ~ arguments ~ Punctuation.roundClose ~
is ~ fieldTypeExpression ~ briefly ~ description
).map(tpl => (Method.apply _).tupled(tpl))
}


def aggregateDefinitions[u:P]: P[Seq[AggregateDefinition]] = {
P(
Punctuation.undefinedMark.!.map(_ => Seq.empty[Field]) |
field.rep(min = 0, Punctuation.comma)
undefined(Seq.empty[AggregateDefinition]) |
(field | method).rep(min = 1, Punctuation.comma)
)
}

def aggregation[u: P]: P[Aggregation] = {
P(location ~ Keywords.fields.? ~ open ~ fields ~ close).map { case (loc, fields) =>
Aggregation(loc, fields)
P(location ~ open ~ aggregateDefinitions.? ~ close).map {
case (loc, Some(contents)) => Aggregation(loc, contents)
case (loc, None) => Aggregation(loc, Seq.empty[AggregateDefinition])
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,9 +407,9 @@ case class ResolutionPass(input: PassInput) extends Pass(input) with UsageResolu
case Enumeration(_, enumerators) =>
// if we're at an enumeration type then the numerators are candidates
enumerators
case AggregateUseCaseTypeExpression(_, _, fields) =>
case AggregateUseCaseTypeExpression(_, _, contents) =>
// Any kind of Aggregate's fields are candidates for resolution
fields
contents
case AliasedTypeExpression(_, pid) =>
// if we're at a field that references another type then the candidates
// are that type's fields. To solve this we need to push
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ class ResolutionPassTest extends ResolvingTest {
"""domain A {
| type T is { tp: A.TPrime } // Refers to T.TPrime
| type TPrime is { t: A.T } // Refers to A.T cyclically
| command DoIt is {}
| command DoIt is { }
| context C {
| entity E {
| record fields is {
Expand Down

0 comments on commit 88da476

Please sign in to comment.