Skip to content

Commit

Permalink
Creating the initial IR (#5)
Browse files Browse the repository at this point in the history
* Add millw script for Windows builds

* Add additional test-case

* Create basic Name type #1
  • Loading branch information
DamianReeves authored Mar 5, 2020
1 parent c8297eb commit b2b0fe1
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 88 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ output/
.DS_STORE
.idea_modules
.idea
.ionide
.vscode
out/
/.bloop/
/.metals/
Expand Down
42 changes: 20 additions & 22 deletions build.sc
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@
import mill._
import mill.scalalib._
import mill.scalalib.scalafmt._
import $ivy.`com.lihaoyi::mill-contrib-bloop:$MILL_VERSION`

object core extends Cross[CoreModule]("2.12.10", "2.13.1") {
object core extends Cross[CoreModule]("2.12.10", "2.13.1") {}

class CoreModule(val crossScalaVersion: String)
extends CrossScalaModule
with MorphirModule {
def ivyDeps = Agg(
ivy"com.lihaoyi::upickle::1.0.0"
)
object test extends Tests with MorphirTestModule {}
}

class CoreModule(val crossScalaVersion:String) extends CrossScalaModule with MorphirModule {
def ivyDeps = Agg(
ivy"com.lihaoyi::upickle::1.0.0"
)
object test extends Tests with MorphirTestModule {

}
}
trait MorphirModule extends ScalafmtModule { this: ScalaModule => }

trait MorphirModule extends ScalafmtModule { this:ScalaModule =>
}

trait MorphirTestModule extends TestModule with ScalaModule {
def ivyDeps = Agg(
ivy"org.scalatest::scalatest:3.1.1",
ivy"dev.zio::zio-test:${Versions.zio}",
ivy"dev.zio::zio-test-sbt:${Versions.zio}",

)
def testFrameworks = Seq("org.scalatest.tools.Framework", "zio.test.sbt.ZTestFramework")
trait MorphirTestModule extends TestModule {
def ivyDeps = Agg(
ivy"org.scalatest::scalatest:3.1.1",
ivy"dev.zio::zio-test:${Versions.zio}",
ivy"dev.zio::zio-test-sbt:${Versions.zio}"
)
def testFrameworks =
Seq("org.scalatest.tools.Framework", "zio.test.sbt.ZTestFramework")
}

object Versions {
val zio = "1.0.0-RC17"
}
val zio = "1.0.0-RC18"
}
39 changes: 38 additions & 1 deletion core/src/com/morganstanley/morphir/ir/Name.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.morganstanley.morphir.ir

import scala.util.matching.Regex
import java.nio.charset.StandardCharsets
import scala.annotation.tailrec

case class Name private[ir] (value: List[String]) extends AnyVal

Expand All @@ -11,7 +12,7 @@ object Name {
Name(pattern.findAllIn(str).toList.map(_.toLowerCase()))
}

def fromList(words: List[String]): Name =
implicit def fromList(words: List[String]): Name =
Name(words)

def toList(name: Name): List[String] = name.value
Expand All @@ -27,4 +28,40 @@ object Name {
case head :: tail =>
(head :: (tail.map(_.capitalize))).mkString("")
}

implicit def toSnakeCase(name: Name): String =
toHumanWords(name).mkString("_")

implicit def toKebabCase(name: Name): String =
toHumanWords(name).mkString("-")

implicit def toHumanWords(name: Name): List[String] = {
val words = toList(name)
val join: List[String] => String = abbrev =>
abbrev.map(_.toUpperCase()).mkString("")

@tailrec
def process(
prefix: List[String],
abbrev: List[String],
suffix: List[String]
): List[String] =
suffix match {
case Nil =>
abbrev match {
case Nil => prefix
case _ => prefix ++ List(join(abbrev))
}
case first :: rest =>
if (first.length() == 1)
process(prefix, abbrev ++ List(first), rest)
else
abbrev match {
case Nil => process(prefix ++ List(first), List.empty, rest)
case _ =>
process(prefix ++ List(join(abbrev), first), List.empty, rest)
}
}
process(List.empty, List.empty, words)
}
}
9 changes: 6 additions & 3 deletions core/src/com/morganstanley/morphir/ir/advanced/Type.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.morganstanley.morphir.ir.advanced
import com.morganstanley.morphir.ir.Name

sealed trait Type[+Extra]
object Type {
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 Variable[A](name: Name, extra: 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]
}
161 changes: 99 additions & 62 deletions core/test/src/com/morganstanley/morphir/ir/NameSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,73 +3,110 @@ package com.morganstanley.morphir.ir
import zio.test._
import zio.test.Assertion._
import zio.test.environment._

object NameSpec
extends DefaultRunnableSpec(
suite("NameSpec")(
suite("Make a Name from a string and check that:")(
suite("Name should be creatable from a single word that:")(
test("Starts with a capital letter") {
assert(
Name.fromString("Marco"),
equalTo(Name(List("marco")))
)
},
test("Is all lowercase") {
assert(Name.fromString("polo"), equalTo(Name(List("polo"))))
}
),
suite("Name should be creatable from compound words that:")(
test("Are formed from a snake case word") {
assert(
Name.fromString("super_mario_world"),
equalTo(Name(List("super", "mario", "world")))
)
},
test("Contain many kinds of word delimiters") {
val result = Name.fromString("fooBar_baz 123")
assert(result, equalTo(Name(List("foo", "bar", "baz", "123"))))
},
test("Are formed from a camel-cased string") {
val result = Name.fromString("valueInUSD")
assert(result, equalTo(Name(List("value", "in", "u", "s", "d"))))
},
test("Are formed from a title-cased string") {
val result = Name.fromString("ValueInUSD")
assert(result, equalTo(Name(List("value", "in", "u", "s", "d"))))
},
test("Complete and utter nonsense") {
assert(Name.fromString("_-%"), equalTo(Name(List.empty)))
}
)
),
suite("Name should be convertible to a title-case string:")(
test(
"When the name was originally constructed from a snake-case string"
) {
val sut = Name.fromString("snake_case_input")
assert(Name.toTitleCase(sut), equalTo("SnakeCaseInput"))
import scala.language.implicitConversions
object NameSpec extends DefaultRunnableSpec {
def spec =
suite("NameSpec")(
suite("Make a Name from a string and check that:")(
suite("Name should be creatable from a single word that:")(
test("Starts with a capital letter") {
assert(
Name.fromString("Marco"),
equalTo(Name(List("marco")))
)
},
test(
"When the name was originally constructed from a camel-case string"
) {
val sut = Name.fromString("camelCaseInput")
assert(Name.toTitleCase(sut), equalTo("CamelCaseInput"))
test("Is all lowercase") {
assert(Name.fromString("polo"), equalTo(Name(List("polo"))))
}
),
suite("Name should be convertible to a camel-case string:")(
test(
"When the name was originally constructed from a snake-case string"
) {
val sut = Name.fromString("snake_case_input")
assert(Name.toCamelCase(sut), equalTo("snakeCaseInput"))
suite("Name should be creatable from compound words that:")(
test("Are formed from a snake case word") {
assert(
Name.fromString("super_mario_world"),
equalTo(Name(List("super", "mario", "world")))
)
},
test("Contain many kinds of word delimiters") {
val result = Name.fromString("fooBar_baz 123")
assert(result, equalTo(Name(List("foo", "bar", "baz", "123"))))
},
test("Are formed from a camel-cased string") {
val result = Name.fromString("valueInUSD")
assert(result, equalTo(Name(List("value", "in", "u", "s", "d"))))
},
test(
"When the name was originally constructed from a camel-case string"
) {
val sut = Name.fromString("camelCaseInput")
assert(Name.toCamelCase(sut), equalTo("camelCaseInput"))
test("Are formed from a title-cased string") {
val result = Name.fromString("ValueInUSD")
assert(result, equalTo(Name(List("value", "in", "u", "s", "d"))))
},
test("Are have a number in the middle") {
val result = Name.fromString("Nintendo64VideoGameSystem")
assert(
result,
equalTo(Name(List("nintendo", "64", "video", "game", "system")))
)
},
test("Complete and utter nonsense") {
assert(Name.fromString("_-%"), equalTo(Name(List.empty)))
}
)
),
suite("Name should be convertible to a title-case string:")(
test(
"When the name was originally constructed from a snake-case string"
) {
val sut = Name.fromString("snake_case_input")
assert(Name.toTitleCase(sut), equalTo("SnakeCaseInput"))
},
test(
"When the name was originally constructed from a camel-case string"
) {
val sut = Name.fromString("camelCaseInput")
assert(Name.toTitleCase(sut), equalTo("CamelCaseInput"))
}
),
suite("Name should be convertible to a camel-case string:")(
test(
"When the name was originally constructed from a snake-case string"
) {
val sut = Name.fromString("snake_case_input")
assert(Name.toCamelCase(sut), equalTo("snakeCaseInput"))
},
test(
"When the name was originally constructed from a camel-case string"
) {
val sut = Name.fromString("camelCaseInput")
assert(Name.toCamelCase(sut), equalTo("camelCaseInput"))
}
),
suite("Name should be convertible to snake-case")(
test("When given a name constructed from a list of words") {
val input = Name.fromList(List("foo", "bar", "baz", "123"))
assert(Name.toSnakeCase(input))(equalTo("foo_bar_baz_123"))
},
test("When the name has parts of an abbreviation") {
val name = Name.fromList(List("value", "in", "u", "s", "d"))
assert(Name.toSnakeCase(name))(
equalTo("value_in_USD")
)
}
),
suite("Name should be convertible to kebab-case")(
test("When given a name constructed from a list of words") {
val input = Name.fromList(List("foo", "bar", "baz", "123"))
assert(Name.toKebabCase(input))(equalTo("foo-bar-baz-123"))
},
test("When the name has parts of an abbreviation") {
val name = Name.fromList(List("value", "in", "u", "s", "d"))
assert(Name.toKebabCase(name))(
equalTo("value-in-USD")
)
}
),
suite("Name toHumanWords should provide a list of words from a Name")(
test("When the name is from a camelCase string") {
val sut = Name.fromString("ValueInUSD")
assert(Name.toHumanWords(sut), equalTo(List("value", "in", "USD")))
}
)
)
}
83 changes: 83 additions & 0 deletions millw.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
@echo off

rem This is a wrapper script, that automatically download mill from GitHub release pages
rem You can give the required mill version with --mill-version parameter
rem If no version is given, it falls back to the value of DEFAULT_MILL_VERSION
rem
rem Project page: https://github.com/lefou/millw
rem
rem If you want to improve this script, please also contribute your changes back!
rem
rem Licensed under the Apache License, Version 2.0

rem setlocal seems to be unavailable on Windows 95/98/ME
rem but I don't think we need to support them in 2019
setlocal enabledelayedexpansion

set "DEFAULT_MILL_VERSION=0.5.0"

rem %~1% removes surrounding quotes
if [%~1%]==[--mill-version] (
rem shift command doesn't work within parentheses
if not [%~2%]==[] (
set MILL_VERSION=%~2%
) else (
echo You specified --mill-version without a version.
echo Please provide a version that matches one provided on
echo https://github.com/lihaoyi/mill/releases
exit /b 1
)
)

if [!MILL_VERSION!]==[] (
if exist .mill-version (
set /p MILL_VERSION=<.mill-version
)
)

if [!MILL_VERSION!]==[] (
set MILL_VERSION=%DEFAULT_MILL_VERSION%
)

set MILL_DOWNLOAD_PATH=%USERPROFILE%\.mill\download

rem without bat file extension, cmd doesn't seem to be able to run it
set MILL=%MILL_DOWNLOAD_PATH%\!MILL_VERSION!.bat

if not exist "%MILL%" (
set VERSION_PREFIX=%MILL_VERSION:~0,4%
set DOWNLOAD_SUFFIX=-assembly
if [!VERSION_PREFIX!]==[0.0.] set DOWNLOAD_SUFFIX=
if [!VERSION_PREFIX!]==[0.1.] set DOWNLOAD_SUFFIX=
if [!VERSION_PREFIX!]==[0.2.] set DOWNLOAD_SUFFIX=
if [!VERSION_PREFIX!]==[0.3.] set DOWNLOAD_SUFFIX=
if [!VERSION_PREFIX!]==[0.4.] set DOWNLOAD_SUFFIX=
set VERSION_PREFIX=

rem there seems to be no way to generate a unique temporary file path (on native Windows)
set DOWNLOAD_FILE=%MILL%.tmp

echo Downloading mill %MILL_VERSION% from https://github.com/lihaoyi/mill/releases ...

rem curl is bundled with recent Windows 10
rem but I don't think we can expect all the users to have it in 2019
rem bitadmin seems to be available on Windows 7
rem without /dynamic, github returns 403
rem bitadmin is sometimes needlessly slow but it looks better with /priority foreground
if not exist "%MILL_DOWNLOAD_PATH%" mkdir "%MILL_DOWNLOAD_PATH%"
bitsadmin /transfer millDownloadJob /dynamic /priority foreground "https://github.com/lihaoyi/mill/releases/download/%MILL_VERSION%/%MILL_VERSION%!DOWNLOAD_SUFFIX!" "!DOWNLOAD_FILE!"
if not exist "!DOWNLOAD_FILE!" (
echo Could not download mill %MILL_VERSION%
exit 1
)

move /y "!DOWNLOAD_FILE!" "%MILL%"

set DOWNLOAD_FILE=
set DOWNLOAD_SUFFIX=
)

set MILL_DOWNLOAD_PATH=
set MILL_VERSION=

%MILL% -i %*

0 comments on commit b2b0fe1

Please sign in to comment.