From 9c222f1013bc6c252e9ee16f614e8be87267e442 Mon Sep 17 00:00:00 2001 From: alari Date: Sat, 8 Sep 2018 16:39:58 +0200 Subject: [PATCH] Writes simple 3-line program without conditionals --- build.sbt | 4 ++ .../fluence/hackethberlin/CodeChunk.scala | 46 +++++++++++++++++ .../scala/fluence/hackethberlin/Expr.scala | 50 ++++++++++++++----- .../scala/fluence/hackethberlin/FuncDef.scala | 24 +++++---- .../fluence/hackethberlin/MakeVyperApp.scala | 20 +++++++- .../hackethberlin/types/DataVyper.scala | 10 ++-- .../hackethberlin/types/PrimitiveType.scala | 3 +- .../hackethberlin/types/ProductType.scala | 10 ++-- .../fluence/hackethberlin/types/Type.scala | 2 +- .../fluence/hackethberlin/types/Void.scala | 4 +- 10 files changed, 139 insertions(+), 34 deletions(-) create mode 100644 src/main/scala/fluence/hackethberlin/CodeChunk.scala diff --git a/build.sbt b/build.sbt index 71d90d8..9c3b3e8 100644 --- a/build.sbt +++ b/build.sbt @@ -4,6 +4,10 @@ version := "0.1" scalaVersion := "2.12.6" +resolvers += Resolver.sonatypeRepo("releases") + +addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.7") + version := "0.1" fork in Test := true parallelExecution in Test := false diff --git a/src/main/scala/fluence/hackethberlin/CodeChunk.scala b/src/main/scala/fluence/hackethberlin/CodeChunk.scala new file mode 100644 index 0000000..d6f06d8 --- /dev/null +++ b/src/main/scala/fluence/hackethberlin/CodeChunk.scala @@ -0,0 +1,46 @@ +package fluence.hackethberlin + +import cats.data.Writer +import cats.{~>, Monad, Monoid} + +import scala.collection.immutable.Queue + +sealed trait CodeChunk { + def toVyper(indent: Int): String +} + +case class CodeBlock(lines: Queue[String]) extends CodeChunk { + override def toVyper(indent: Int): String = + lines.mkString(CodeChunk.spaces(indent), ";\n" + CodeChunk.spaces(indent), ";\n") +} + +case class CodeLine(line: String) extends CodeChunk { + override def toVyper(indent: Int): String = + CodeChunk.spaces(indent) + line +} + +object CodeChunk { + val Space = " " + + def spaces(indent: Int): String = Space * indent + + implicit object codeChunkMonoid extends Monoid[CodeChunk] { + override def empty: CodeChunk = CodeBlock(Queue.empty) + + override def combine(x: CodeChunk, y: CodeChunk): CodeChunk = + (x, y) match { + case (CodeBlock(lines), CodeLine(line)) ⇒ CodeBlock(lines enqueue line) + case (CodeLine(l1), CodeLine(l2)) ⇒ CodeBlock(Queue(l1, l2)) + case (CodeBlock(ls1), CodeBlock(ls2)) ⇒ CodeBlock(ls1 enqueue ls2) + case (CodeLine(line), CodeBlock(lines)) ⇒ CodeBlock(line +: lines) + } + } + + type W[A] = Writer[CodeChunk, A] + + object fromExpr extends (Expr ~> W) { + override def apply[A](fa: Expr[A]): W[A] = + Writer(CodeLine(fa.toVyper), fa.boxedValue) + + } +} diff --git a/src/main/scala/fluence/hackethberlin/Expr.scala b/src/main/scala/fluence/hackethberlin/Expr.scala index 5001888..194fb8d 100644 --- a/src/main/scala/fluence/hackethberlin/Expr.scala +++ b/src/main/scala/fluence/hackethberlin/Expr.scala @@ -1,27 +1,53 @@ package fluence.hackethberlin -sealed trait Expr[T <: types.Type] { - def toVyper(depth: Int): String +import types._ - protected def spaces(depth: Int): String = " " * depth +sealed trait Expr[T] { + def boxedValue: T + + def toVyper: String } -sealed trait InlineExpr[T <: types.Type] extends Expr[T]{ - override def toVyper(depth: Int): String = spaces(depth) + toInlineVyper - - def toInlineVyper: String - +sealed trait InlineExpr[T <: types.Type] extends Expr[T] { def toReturn: Expr.Return[T] = Expr.Return[T](this) + + def :=:(name: Symbol): Expr[Expr.Ref[T]] = Expr.Assign[T](Expr.Ref[T](name.name, boxedValue), this) } object Expr { - case class Ref[T <: types.Type](name: String) extends InlineExpr[T] { - override def toInlineVyper: String = name + case class Ref[T <: types.Type](name: String, boxedValue: T) extends InlineExpr[T] { + override def toVyper: String = name + } + + case class Infix[L <: types.Type, R <: types.Type, T <: types.Type]( + op: String, + left: InlineExpr[L], + right: InlineExpr[R], + boxedValue: T + ) extends InlineExpr[T] { + override def toVyper: String = left.toVyper + s" $op " + right.toVyper + } + + case class Assign[T <: Type](ref: Ref[T], value: InlineExpr[T]) extends Expr[Ref[T]] { + override def boxedValue: Ref[T] = ref + + override def toVyper: String = + s"${ref.toVyper} = ${value.toVyper}" } case class Return[T <: types.Type](ret: InlineExpr[T]) extends Expr[T] { - override def toVyper(depth: Int): String = - spaces(depth) + "return "+ret.toInlineVyper + override def boxedValue: T = ret.boxedValue + + override def toVyper: String = + "return " + ret.toVyper } + + trait Defs { + + def `++`(a: InlineExpr[uint256.type], b: InlineExpr[uint256.type]): InlineExpr[uint256.type] = + Infix("+", a, b, uint256) + } + + object Defs extends Defs } diff --git a/src/main/scala/fluence/hackethberlin/FuncDef.scala b/src/main/scala/fluence/hackethberlin/FuncDef.scala index c577b9f..0a58368 100644 --- a/src/main/scala/fluence/hackethberlin/FuncDef.scala +++ b/src/main/scala/fluence/hackethberlin/FuncDef.scala @@ -1,18 +1,24 @@ package fluence.hackethberlin +import cats.Monad import fluence.hackethberlin.types.{DataVyper, ProductType} import shapeless._ +import cats.free.Free +import cats.syntax.functor._ class FuncDef[Args <: HList, Ret <: types.Type]( - name: String, - argsDef: ProductType[Args], - ret: Option[Ret], - body: ProductType[Args] ⇒ Expr[Ret], - decorators: Set[Decorator] = Set.empty + name: String, + argsDef: ProductType[Args], + ret: Option[Ret], + body: ProductType[Args] ⇒ Free[Expr, Ret], + decorators: Set[Decorator] = Set.empty ) { + def bodyVyper: String = + body(argsDef).foldMap(CodeChunk.fromExpr).run._1.toVyper(1) + def toVyper: String = - s"${decorators.map(_.toVyper).mkString("\n")}\ndef $name(${argsDef.toArgsVyper})${ret.fold("")(" -> " + _.toVyper)}:\n${body(argsDef).toVyper(1)};\n" + s"${decorators.map(_.toVyper).mkString("\n")}\ndef $name(${argsDef.toArgsVyper})${ret.fold("")(" -> " + _.toVyper)}:\n$bodyVyper\n" def @:(decorator: Decorator): FuncDef[Args, Ret] = new FuncDef[Args, Ret](name, argsDef, ret, body, decorators + decorator) @@ -24,13 +30,13 @@ object FuncDef { name: String, argsDef: Args, ret: Ret - )(body: ProductType[Args] ⇒ Expr.Return[Ret]): FuncDef[Args, Ret] = + )(body: ProductType[Args] ⇒ Free[Expr, Ret]): FuncDef[Args, Ret] = new FuncDef(name, ProductType(argsDef), Some(ret), body) def apply[Args <: HList: DataVyper]( name: String, argsDef: Args - )(body: ProductType[Args] ⇒ Expr[types.Void]): FuncDef[Args, types.Void] = - new FuncDef(name, ProductType(argsDef), None, body) + )(body: ProductType[Args] ⇒ Free[Expr, Unit]): FuncDef[Args, types.Void] = + new FuncDef(name, ProductType(argsDef), None, args ⇒ body(args).map(_ ⇒ types.Void)) } diff --git a/src/main/scala/fluence/hackethberlin/MakeVyperApp.scala b/src/main/scala/fluence/hackethberlin/MakeVyperApp.scala index 18724f4..5c739c9 100644 --- a/src/main/scala/fluence/hackethberlin/MakeVyperApp.scala +++ b/src/main/scala/fluence/hackethberlin/MakeVyperApp.scala @@ -3,6 +3,7 @@ package fluence.hackethberlin import shapeless._ import types._ import Decorator._ +import cats.free.Free import syntax.singleton._ object MakeVyperApp extends App { @@ -27,7 +28,7 @@ object MakeVyperApp extends App { "myFunc", ('addr ->> address) :: HNil, address - )(args ⇒ args.ref('addr).toReturn) + )(args ⇒ Free.liftF(args.ref('addr).toReturn)) val recordStruct = ProductType( ('record_address ->> address) :: ('other_some ->> uint256) :: HNil @@ -41,6 +42,21 @@ object MakeVyperApp extends App { println(func.toVyper) - println((`@public` @: func).toVyper) + val sumArgs = ProductType(('a ->> uint256) :: ('b ->> uint256) :: HNil) + + import Expr.Defs._ + + println( + ( + `@public` @: + sumArgs.funcDef("sum", uint256) { args ⇒ + for { + c ← Free.liftF('c :=: `++`(args.ref('a), args.ref('b))) + d ← Free.liftF('d :=: `++`(args.ref('b), c)) + sum ← Free.liftF[Expr, uint256.type](`++`(args.ref('a), d).toReturn) + } yield sum + } + ).toVyper + ) } diff --git a/src/main/scala/fluence/hackethberlin/types/DataVyper.scala b/src/main/scala/fluence/hackethberlin/types/DataVyper.scala index 46c4788..68f9734 100644 --- a/src/main/scala/fluence/hackethberlin/types/DataVyper.scala +++ b/src/main/scala/fluence/hackethberlin/types/DataVyper.scala @@ -24,7 +24,7 @@ sealed trait LowPriorityDataVyperImplicits { } implicit def recDataVyper[K <: Symbol, V <: Type](implicit wk: Witness.Aux[K]): DataVyper[FieldType[K, V]] = - new DataVyper[FieldType[K, V]]{ + new DataVyper[FieldType[K, V]] { override def toVyperDefinitions(data: FieldType[K, V]): List[String] = s"${wk.value.name}: ${data.toVyper}" :: Nil } @@ -34,13 +34,17 @@ object DataVyper extends LowPriorityDataVyperImplicits { def apply[T](implicit dataVyper: DataVyper[T]): DataVyper[T] = dataVyper - implicit def pairDataIndexedVyper[K <: Symbol, T <: Type](implicit wk: Witness.Aux[K]): DataVyper[FieldType[K, T @@ Indexed]] = + implicit def pairDataIndexedVyper[K <: Symbol, T <: Type]( + implicit wk: Witness.Aux[K] + ): DataVyper[FieldType[K, T @@ Indexed]] = new DataVyper[FieldType[K, T @@ Indexed]] { override def toVyperDefinitions(data: FieldType[K, T @@ Indexed]): List[String] = s"${wk.value.name}: indexed(${data.toVyper})" :: Nil } - implicit def pairDataPublicVyper[K <: Symbol, T <: Type](implicit wk: Witness.Aux[K]): DataVyper[FieldType[K, T @@ Public]] = + implicit def pairDataPublicVyper[K <: Symbol, T <: Type]( + implicit wk: Witness.Aux[K] + ): DataVyper[FieldType[K, T @@ Public]] = new DataVyper[FieldType[K, T @@ Public]] { override def toVyperDefinitions(data: FieldType[K, T @@ Public]): List[String] = s"${wk.value.name}: public(${data.toVyper})" :: Nil diff --git a/src/main/scala/fluence/hackethberlin/types/PrimitiveType.scala b/src/main/scala/fluence/hackethberlin/types/PrimitiveType.scala index e4a436c..124ae40 100644 --- a/src/main/scala/fluence/hackethberlin/types/PrimitiveType.scala +++ b/src/main/scala/fluence/hackethberlin/types/PrimitiveType.scala @@ -9,6 +9,7 @@ abstract sealed class PrimitiveType(name: String) extends Type { } object PrimitiveType { + trait Defs { case object address extends PrimitiveType("address") case object bool extends PrimitiveType("bool") @@ -16,4 +17,4 @@ object PrimitiveType { case object uint256 extends PrimitiveType("uint256") case object decimal extends PrimitiveType("decimal") } -} \ No newline at end of file +} diff --git a/src/main/scala/fluence/hackethberlin/types/ProductType.scala b/src/main/scala/fluence/hackethberlin/types/ProductType.scala index eee9829..820a7f0 100644 --- a/src/main/scala/fluence/hackethberlin/types/ProductType.scala +++ b/src/main/scala/fluence/hackethberlin/types/ProductType.scala @@ -1,13 +1,14 @@ package fluence.hackethberlin.types +import cats.free.Free import fluence.hackethberlin.{Expr, FuncDef} import shapeless.{HList, Witness} import shapeless.ops.record.Selector class ProductType[D <: HList](dataDef: D, dv: DataVyper[D]) extends Type { - def ref[T <: Symbol, V <: Type](k: Witness.Aux[T])(implicit selector : Selector.Aux[D, T, V]): Expr.Ref[V] = - Expr.Ref[V](k.value.name) + def ref[T <: Symbol, V <: Type](k: Witness.Aux[T])(implicit selector: Selector.Aux[D, T, V]): Expr.Ref[V] = + Expr.Ref[V](k.value.name, selector(dataDef)) // type in type definition somewhere override def toVyper: String = @@ -21,12 +22,13 @@ class ProductType[D <: HList](dataDef: D, dv: DataVyper[D]) extends Type { def toArgsVyper: String = dv.toVyperDefinitions(dataDef).mkString(", ") - def funcDef[Ret <: Type](name: String, ret: Ret)(body: ProductType[D] ⇒ Expr.Return[Ret]): FuncDef[D, Ret] = + def funcDef[Ret <: Type](name: String, ret: Ret)(body: ProductType[D] ⇒ Free[Expr, Ret]): FuncDef[D, Ret] = new FuncDef[D, Ret](name, this, Some(ret), body) } object ProductType { + def apply[D <: HList](dataDef: D)(implicit dv: DataVyper[D]): ProductType[D] = new ProductType[D](dataDef, dv) -} \ No newline at end of file +} diff --git a/src/main/scala/fluence/hackethberlin/types/Type.scala b/src/main/scala/fluence/hackethberlin/types/Type.scala index c08dc98..2f87ad9 100644 --- a/src/main/scala/fluence/hackethberlin/types/Type.scala +++ b/src/main/scala/fluence/hackethberlin/types/Type.scala @@ -2,4 +2,4 @@ package fluence.hackethberlin.types trait Type { def toVyper: String -} \ No newline at end of file +} diff --git a/src/main/scala/fluence/hackethberlin/types/Void.scala b/src/main/scala/fluence/hackethberlin/types/Void.scala index da1b309..3b616e6 100644 --- a/src/main/scala/fluence/hackethberlin/types/Void.scala +++ b/src/main/scala/fluence/hackethberlin/types/Void.scala @@ -3,6 +3,6 @@ package fluence.hackethberlin.types sealed trait Void extends Type // TODO: should it exist as an instance? -case object Void extends Type { +case object Void extends Void { override def toVyper: String = "" -} \ No newline at end of file +}