Skip to content

Commit

Permalink
Flesh out the IR (#6)
Browse files Browse the repository at this point in the history
* Addd millw script for Windows builds

* Add additional test-case

* Create basic Name type #1

* Add the Path type - #1

* Add more to the IR
  • Loading branch information
DamianReeves authored Mar 5, 2020
1 parent b2b0fe1 commit 8db7797
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 14 deletions.
13 changes: 0 additions & 13 deletions core/src/com/morganstanley/morphir/ir/AccessControl.scala

This file was deleted.

31 changes: 31 additions & 0 deletions core/src/com/morganstanley/morphir/ir/AccessControlled.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.morganstanley.morphir.ir
sealed trait AccessControlled[+A] {

def withPublicAccess: Option[A] = this match {
case AccessControlled.Public(v) => Some(v)
case _ => None
}

def withPrivateAccess: A = this match {
case AccessControlled.Public(v) => v
case AccessControlled.Private(v) => v
}
}

object AccessControlled {

case class Public[+A] private (value: A) extends AccessControlled[A]
case class Private[+A] private (value: A) extends AccessControlled[A]

def `public`[A](value: A): Public[A] =
Public(value)

def `private`[A](value: A): Private[A] =
Private(value)

@inline def withPublicAccess[A](ac: AccessControlled[A]): Option[A] =
ac.withPublicAccess

@inline def withPrivateAccess[A](ac: AccessControlled[A]): A =
ac.withPrivateAccess
}
9 changes: 9 additions & 0 deletions core/src/com/morganstanley/morphir/ir/FQName.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.morganstanley.morphir.ir

case class FQName(packagePath: Path, modulePath: Path, localName: Name) {}

object FQName {
val fQName = (packagePath: Path) =>
(modulePath: Path) =>
(localName: Name) => FQName(packagePath, modulePath, localName)
}
3 changes: 3 additions & 0 deletions core/src/com/morganstanley/morphir/ir/Name.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import scala.annotation.tailrec
case class Name private[ir] (value: List[String]) extends AnyVal

object Name {
def apply(firstWord: String, otherWords: String*): Name =
Name(firstWord :: otherWords.toList)

def fromString(str: String): Name = {
val pattern = """[a-zA-Z][a-z]*|[0-9]+""".r
Name(pattern.findAllIn(str).toList.map(_.toLowerCase()))
Expand Down
41 changes: 41 additions & 0 deletions core/src/com/morganstanley/morphir/ir/Path.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.morganstanley.morphir.ir

case class Path(value: List[Name]) extends AnyVal {
@inline def toList: List[Name] = value
def mapSegments[A](fn: Name => A): List[A] =
value.map(fn)
}

object Path {

val empty: Path = Path(List.empty)

def apply(head: Name, rest: Name*): Path =
Path(head :: rest.toList)

def fromString(str: String): Path = {
val separatorRegex = """[^\w\s]+""".r
fromList(separatorRegex.split(str).map(Name.fromString).toList)
}

def toString(nameToString: Name => String, sep: String, path: Path): String =
path.mapSegments(nameToString).mkString(sep)

def fromList(names: List[Name]): Path =
Path(names)

def toList(path: Path): List[Name] = path.value

def isPrefixOf(prefix: Path, path: Path): Boolean = (prefix, path) match {
// empty path is a prefix of any other path
case (Path.empty, _) => true
// empty path has no prefixes except the empty prefix captured above
case (_, Path.empty) => false
case (Path(prefixHead :: prefixTail), Path(pathHead :: pathTail)) =>
if (prefixHead == pathHead)
isPrefixOf(Path(prefixTail), Path(pathTail))
else
false
}

}
22 changes: 22 additions & 0 deletions core/src/com/morganstanley/morphir/ir/QName.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.morganstanley.morphir.ir

case class QName(modulePath: Path, localName: Name) {
def toTuple: (Path, Name) = modulePath -> localName
}

object QName {
@inline def toTuple(qname: QName): (Path, Name) = qname.toTuple
def fromTuple(path: Path, name: Name): QName = QName(path, name)
@inline def getModulePath(qname: QName): Path = qname.modulePath
@inline def getLocalName(qname: QName): Name = qname.localName

def toString(
pathPartToString: Name => String,
nameToString: Name => String,
sep: String,
qname: QName
) =
(qname.modulePath.toList.map(pathPartToString) ++ nameToString(
qname.localName
)).mkString(sep)
}
13 changes: 12 additions & 1 deletion core/src/com/morganstanley/morphir/ir/advanced/Type.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
package com.morganstanley.morphir.ir.advanced
import com.morganstanley.morphir.ir.Name
import com.morganstanley.morphir.ir.{FQName, Name}

sealed trait Type[+Extra]
object Type {
case class Variable[A](name: Name, extra: A) extends Type[A]
case class Reference[A](
typeName: FQName,
typeParameters: List[Type[A]],
extra: A
) extends Type[A]

case class Tuple[A](elementTypes: List[Type[A]], extra: A) extends Type[A]
case class Record[A](fieldTypes: List[Field[A]], extra: A) extends Type[A]
case class ExtensibleRecord[A]() extends Type[A]
case class Function[A](argType: Type[A], returnType: Type[A], extra: A)
extends Type[A]
case class Unit[A](extra: A) extends Type[A]

case class Field[A](name: Name, fieldType: Type[A])
}
82 changes: 82 additions & 0 deletions core/test/src/com/morganstanley/morphir/ir/PathSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.morganstanley.morphir.ir

import zio.test._
import zio.test.Assertion._
import zio.test.environment._
import scala.language.implicitConversions

object PathSpec extends DefaultRunnableSpec {

def spec = suite("PathSpec")(
suite("Creating a Path from a String")(
test("Should be possible when given a simple string") {
assert(Path.fromString("Person"))(
equalTo(Path(List(Name.fromString("person"))))
)
},
test("Should be possible when given a dotted string") {
assert(Path.fromString("blog.Author"))(
equalTo(
Path(
List(Name.fromList(List("blog")), Name.fromList(List("author")))
)
)
)
}
),
suite("Transforming a Path into a String")(
test("Should be supported (a)") {
val input =
Path(
Name("foo", "bar"),
Name("baz")
)

assert(Path.toString(Name.toTitleCase, ".", input))(
equalTo("FooBar.Baz")
)
},
test("Should be supported (b)") {
val input =
Path(
Name("foo", "bar"),
Name("baz")
)

assert(Path.toString(Name.toSnakeCase, "/", input))(
equalTo("foo_bar/baz")
)
}
),
suite("Transforming to a list of Names")(
test("Should be support via the toList function") {
assert(
Path.toList(Path(Name("Com", "Example"), Name("Hello", "World")))
)(
equalTo(List(Name("Com", "Example"), Name("Hello", "World")))
)
}
),
suite("Checking if one Path is a prefix of another should:")(
test("""Return true: Given path is "foo/bar" and prefix is "foo" """) {
val sut = Path.fromString("foo/bar")
val prefix = Path.fromString("foo")

assert(Path.isPrefixOf(prefix = prefix, path = sut))(isTrue)
},
test("""Return false: Given path is "foo/foo" and prefix is "bar" """) {
val sut = Path.fromString("foo/foo")
val prefix = Path.fromString("bar")

assert(Path.isPrefixOf(prefix = prefix, path = sut))(isFalse)
},
test("""Return true: Given equal paths""") {
val sut = Path.fromString("foo/bar/baz")
val prefix = sut

assert(Path.isPrefixOf(prefix = prefix, path = sut))(isTrue)
}
)
)

}
12 changes: 12 additions & 0 deletions core/test/src/com/morganstanley/morphir/ir/QNameSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.morganstanley.morphir.ir

import zio.test._
import zio.test.Assertion._
import zio.test.environment._
import scala.language.implicitConversions

object QNameSpec extends DefaultRunnableSpec {

def spec = suite("QNameSpec")()

}

0 comments on commit 8db7797

Please sign in to comment.