Skip to content

Commit

Permalink
Scala code-gen (#22)
Browse files Browse the repository at this point in the history
* Move into morphir from org.morphir

* File ops

* Reorg and starting to update IR types

* Added a Show type class instance for name

* Include path support

* Added support for all Name variations

* Cleanup tests and add AccessControlled

* Started work on encoding

* Provide a sealed coproduct hiearchy for ExprKind

* Rollup common Expr functionality

* Add full representation of Value AST

* Add remaining types for Type Expressions and normalize attributes parameter order

* Add attribute mapping for Types

* Encode Name as a Value class

* This isn't elm so promote some of these functions to methods

* attribute mapping

* attribute mapping

* Working on the VFile abstraction

* Working on the VFile abstraction

* Major cleanup

* Workspace work

* Start of work to integrate Scala backend
  • Loading branch information
DamianReeves authored May 18, 2020
1 parent 5a76745 commit 91609c1
Show file tree
Hide file tree
Showing 20 changed files with 480 additions and 18 deletions.
28 changes: 16 additions & 12 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ addCommandAlias(
"check",
"all scalafmtSbtCheck scalafmtCheck test:scalafmtCheck"
)

addCommandAlias(
"testSdkCore",
"; +morphirSdkCoreJVM/test ; morphirSdkCoreJS/test"
Expand All @@ -52,7 +53,9 @@ lazy val root = project
.aggregate(
morphirIRJVM,
morphirIRJS,
morphirCliJVM
morphirCliJVM,
morphirSdkCoreJVM,
morphirSdkCoreJS
)

lazy val morphirSdkCore = crossProject(JSPlatform, JVMPlatform)
Expand Down Expand Up @@ -82,7 +85,7 @@ lazy val morphirIR = crossProject(JVMPlatform, JSPlatform)
.settings(buildInfoSettings("morphir.ir"))
.settings(
libraryDependencies ++= Seq(
"org.scalameta" %%% "scalameta" % "4.3.10",
"org.scalameta" %%% "scalameta" % Versions.scalameta,
"dev.zio" %%% "zio-streams" % Versions.zio,
"io.circe" %%% "circe-core" % Versions.circe,
"io.circe" %%% "circe-generic" % Versions.circe,
Expand Down Expand Up @@ -110,24 +113,25 @@ lazy val morphirCli = crossProject(JVMPlatform)
.settings(
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % Versions.zio,
"dev.zio" %% "zio-logging" % Versions.zioLogging,
"dev.zio" %% "zio-config" % Versions.zioConfig,
"dev.zio" %% "zio-config-magnolia" % Versions.zioConfig,
"dev.zio" %% "zio-config-typesafe" % Versions.zioConfig,
"dev.zio" %% "zio-process" % Versions.zioProcess,
"dev.zio" %% "zio-logging" % Versions.zioLogging,
"io.estatico" %% "newtype" % Versions.newtype,
"com.monovore" %% "decline-effect" % Versions.decline,
"com.lihaoyi" %% "pprint" % Versions.pprint,
"dev.zio" %% "zio-test" % Versions.zio % "test",
"dev.zio" %% "zio-test-sbt" % Versions.zio % "test",
"dev.zio" %% "zio-logging" % "0.2.8",
"dev.zio" %% "zio-config" % "1.0.0-RC17",
"dev.zio" %% "zio-config-magnolia" % "1.0.0-RC17",
"dev.zio" %% "zio-process" % "0.0.3",
"dev.zio" %% "zio-logging" % "0.2.8",
"io.estatico" %% "newtype" % "0.4.4",
"com.monovore" %% "decline-effect" % "1.2.0",
"com.lihaoyi" %% "pprint" % "0.5.9"
"dev.zio" %% "zio-test-sbt" % Versions.zio % "test"
)
)
.settings(macroExpansionSettings)
.settings(testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"))

lazy val morphirCliJVM = morphirCli.jvm.settings(
libraryDependencies ++= Seq(
"io.github.soc" % "directories" % "11"
"io.github.soc" % "directories" % Versions.directories
)
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.morphir.cli

import org.morphir.cli.workspace.Workspace
import org.morphir.workspace._
import zio._
import zio.clock.Clock
import zio.console.Console
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import cats.implicits._
import io.estatico.newtype.macros.newtype
import org.morphir.cli.CliAction
import org.morphir.cli.commands.ElmCompileCommand._
import org.morphir.cli.workspace.Workspace.{ OutputDir, ProjectDir }
import org.morphir.workspace.project.model.{ OutputDir, ProjectDir }

import scala.language.implicitConversions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package org.morphir.cli.commands

import cats.implicits._
import org.morphir.cli.{ CliAction, ExitCode }
import org.morphir.cli.workspace.Workspace.{ OutputDir, ProjectDir }
import org.morphir.workspace.project.model.{ OutputDir, ProjectDir }
import zio._
import zio.blocking.Blocking
import zio.process
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.morphir.workspace

import zio._

import org.morphir.workspace.project.model.ProjectDir

object Workspace {
trait Service {
def resolveProjectDir(maybeProjectDir: Option[ProjectDir]): UIO[ProjectDir]
}

val live: ULayer[Workspace] = ZLayer.succeed(LiveWorkspace())

private case class LiveWorkspace() extends Service {
def resolveProjectDir(maybeProjectDir: Option[ProjectDir]): UIO[ProjectDir] =
UIO.succeed(maybeProjectDir.getOrElse(ProjectDir.fromWorkingDir))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package org.morphir.workspace.config

import java.io.File

import com.typesafe.config.ConfigFactory
import izumi.reflect.Tags.Tag
import zio.config.magnolia.DeriveConfigDescriptor.descriptor
import zio.config.typesafe._
import zio.config.{ ConfigSource, _ }
import zio.{ Has, Task, ZIO, ZLayer }

/**
* Property resolution order:
* - command line arguments
* - system properties
* - environment variables
* - HOCON file
* - properties file
*/
object ConfigLayer {

def createLayer[T: Tag](
args: List[String],
descriptor: ConfigDescriptor[T]
): ZIO[Any, Throwable, ZLayer[Any, ReadError[String], Has[T]]] =
for {
sources <- createSources(args)
desc = descriptor.from(unifySources(sources))
l = ZLayer.fromEffect(ZIO.fromEither(read(desc)))
} yield l

private def unifySources(sources: List[ConfigSource]): ConfigSource =
sources.reduce((s1, s2) => s1.orElse(s2))

private def createSources(args: List[String]): ZIO[Any, Throwable, List[ConfigSource]] = {
val NO_PROFILE = ""
val PROD = "prod"
for {
argsConfigSource <- ZIO.succeed(ConfigSource.fromCommandLineArgs(args, Some('.'), Some(',')))
systemPropsSource <- ConfigSource.fromSystemProperties(Some('_'), Some(','))
envPropsSource <- ConfigSource.fromSystemEnv(Some('_'), Some(','))
profile = getProfile(unifySources(List(argsConfigSource, systemPropsSource, envPropsSource)))
appHoconSource <- profile.hoconFile match {
case Some(value) =>
fromHoconResource(s"/$value")
case None =>
fromHoconResourceIfPresent(
profile.profile.map(_.toLowerCase()).getOrElse(NO_PROFILE) match {
case NO_PROFILE => "/morphir-workspace.conf"
case PROD => "/morphir-workspace.conf"
case profile => s"/morphir-workspace-$profile.conf"
}
)
}
appPropsSource <- profile.propertiesFile match {
case Some(value) =>
fromPropertiesResource(s"/$value", Some('.'), Some(','))
case None =>
fromPropertiesResourceIfPresent(
profile.profile.map(_.toLowerCase()).getOrElse(NO_PROFILE) match {
case NO_PROFILE => "/morphir-workspace.properties"
case PROD => "/morphir-workspace.properties"
case profile => s"/morphir-workspace-$profile.properties"
},
Some('.'),
Some(',')
)
}
} yield List(argsConfigSource, systemPropsSource, envPropsSource, appHoconSource, appPropsSource)
}

/**
* Will fail if the file is not found.
*
* @param file
* @param keyDelimiter
* @param valueDelimiter
* @return
*/
private def fromPropertiesResource[A](
file: String,
keyDelimiter: Option[Char],
valueDelimiter: Option[Char]
): Task[ConfigSource] =
for {
properties <- ZIO.bracket(
ZIO.effect(getClass.getResourceAsStream(file))
)(r => ZIO.effectTotal(r.close())) { inputStream =>
ZIO.effect {
val properties = new java.util.Properties()
properties.load(inputStream)
properties
}
}
} yield ConfigSource.fromProperties(
properties,
file,
keyDelimiter,
valueDelimiter
)

/**
* Will not fail if file is not found. Instead it will create a ConfigSource from an empty java.util.Properties
*
* @param file
* @param keyDelimiter
* @param valueDelimiter
* @return
*/
private def fromPropertiesResourceIfPresent[A](
file: String,
keyDelimiter: Option[Char],
valueDelimiter: Option[Char]
): Task[ConfigSource] =
for {
properties <- ZIO.bracket(
ZIO.effect(getClass.getResourceAsStream(file))
)(r => ZIO.effectTotal(if (r != null) r.close())) { inputStream =>
ZIO.effect {
val properties = new java.util.Properties()
if (inputStream != null) {
properties.load(inputStream)
}
properties
}
}
} yield ConfigSource.fromProperties(
properties,
file,
keyDelimiter,
valueDelimiter
)

/**
* Will fail if the file is not found.
*
* @param file
* @return
*/
private def fromHoconResource[A](file: String): Task[ConfigSource] =
for {
resourceURI <- ZIO
.fromOption(Option(getClass.getResource(file)).map(_.toURI))
.mapError(_ => new RuntimeException(s"$file not found in classpath!"))
fileInstance <- Task(new File(resourceURI))
configSource <- ZIO
.fromEither(
TypesafeConfigSource.fromTypesafeConfig(ConfigFactory.parseFile(fileInstance).resolve)
)
.mapError(error => new RuntimeException(error))
} yield configSource

/**
* Will not fail if file is not found. Instead it will create a ConfigSource from an empty HOCON string
*
* @param file
* @return
*/
private def fromHoconResourceIfPresent[A](file: String): Task[ConfigSource] =
Option(getClass.getResource(file)).map(_.toURI) match {
case Some(uri) =>
Task(new File(uri)).flatMap(fileInstance =>
TypesafeConfigSource
.fromTypesafeConfig(ConfigFactory.parseFile(fileInstance).resolve) match {
case Left(value) => Task.fail(new RuntimeException(value))
case Right(value) => Task.succeed(value)
}
)
case None => Task.succeed(ConfigSource.empty)
}

private final case class Profile(
profile: Option[String],
hoconFile: Option[String],
propertiesFile: Option[String]
)

private def getProfile(configSource: ConfigSource): Profile = {
val desc = descriptor[Profile]
val params = desc.from(configSource)
read(params) match {
case Left(_) => Profile(None, None, None)
case Right(value) => value
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.morphir.workspace.config
import org.morphir.workspace.config.project.{ ProjectInfo, ProjectSet }
import zio.config._

final case class WorkspaceProperties(projects: ProjectSet) {
lazy val projectList: List[ProjectInfo] = projects.toMap.map(ProjectInfo.fromTuple).toList
}

object WorkspaceProperties {
val config: ConfigDescriptor[WorkspaceProperties] =
ProjectSet.configDescriptor("projects")(
WorkspaceProperties.apply,
WorkspaceProperties.unapply
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.morphir.workspace.config

import io.estatico.newtype.macros.newtype
import zio.config._, ConfigDescriptor._

import scala.language.implicitConversions

object project {

final case class ProjectProperties(sourceDirectory: SourceDirectory)
object ProjectProperties {
implicit val configDescriptor: ConfigDescriptor[ProjectProperties] =
(string("sourceDirectory")(SourceDirectory.apply, (srcDir: SourceDirectory) => Option(srcDir.rawPath)))(
ProjectProperties.apply,
ProjectProperties.unapply
)
}

@newtype case class SourceDirectory(rawPath: String)
@newtype case class ProjectName(displayName: String)
object ProjectName {
def unapply(projectName: ProjectName): Option[String] = Option(projectName.displayName)

implicit val configDescriptor: ConfigDescriptor[ProjectName] =
string(ProjectName.apply, ProjectName.unapply)
}
@newtype case class ProjectSet(toMap: Map[String, ProjectProperties])
object ProjectSet {
def configDescriptor(path: String): ConfigDescriptor[ProjectSet] =
map(path)(ProjectProperties.configDescriptor)(ProjectSet.apply, ProjectSet.unapply)

def configDescriptor: ConfigDescriptor[ProjectSet] =
map(ProjectProperties.configDescriptor)(ProjectSet.apply, ProjectSet.unapply)

def unapply(arg: ProjectSet): Option[Map[String, ProjectProperties]] = Option(arg.toMap)
}

final case class ProjectInfo(name: ProjectName, properties: ProjectProperties)

object ProjectInfo {
def apply(tuple: (ProjectName, ProjectProperties)): ProjectInfo =
ProjectInfo(tuple._1, tuple._2)

def fromTuple(tuple: (String, ProjectProperties)): ProjectInfo =
ProjectInfo(ProjectName(tuple._1), tuple._2)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.morphir

import org.morphir.workspace.project.model.ProjectDir
import zio.{ Has, ZIO }

package object workspace {
type Workspace = Has[Workspace.Service]

def resolveProjectDir(maybeProjectDir: Option[ProjectDir]): ZIO[Workspace, Nothing, ProjectDir] =
ZIO.accessM[Workspace](_.get.resolveProjectDir(maybeProjectDir))

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.morphir.workspace.project

import io.estatico.newtype.macros.newtype
import org.morphir.workspace.project.ElmProject.{ ExposedModuleNames, PackageName }
import org.morphir.workspace.config.project.SourceDirectory

import scala.language.implicitConversions

case class ElmProject(name: PackageName, sourceDirectory: SourceDirectory, exposedModules: ExposedModuleNames) {}

object ElmProject {
@newtype case class PackageName(name: String)
@newtype case class ModuleName(name: String)
@newtype case class ExposedModuleNames(toList: List[ModuleName])
}
Loading

0 comments on commit 91609c1

Please sign in to comment.