Writes simple 3-line program without conditionals

This commit is contained in:
alari 2018-09-08 16:39:58 +02:00
parent 8f2a329f73
commit 9c222f1013
10 changed files with 139 additions and 34 deletions

View File

@ -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

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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
)
}

View File

@ -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

View File

@ -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")
}
}
}

View File

@ -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)
}
}

View File

@ -2,4 +2,4 @@ package fluence.hackethberlin.types
trait Type {
def toVyper: String
}
}

View File

@ -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 = ""
}
}