From 68bac6efb8a40a8a4213646be6976d5c8ede2967 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 18 Apr 2020 22:44:32 -0500 Subject: [PATCH] BPML: grok 2 start events from pizza example - pizza-collaboration.bpmn resource (symlink WIP) - add XML dependency - start porting RhoBuilder to scala - add Json dependency --- build.sbt | 5 + src/main/resources/pizza-collaboration.bpmn | 1 + src/main/scala/bpmn2rho/PizzaOrderTest.scala | 43 +++ src/main/scala/js2rho/RhoBuilder.scala | 308 +++++++++++++++++++ 4 files changed, 357 insertions(+) create mode 120000 src/main/resources/pizza-collaboration.bpmn create mode 100644 src/main/scala/bpmn2rho/PizzaOrderTest.scala create mode 100644 src/main/scala/js2rho/RhoBuilder.scala diff --git a/build.sbt b/build.sbt index 1289a1f..50f1b6c 100644 --- a/build.sbt +++ b/build.sbt @@ -21,3 +21,8 @@ scalaVersion := "2.12.8" libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % Test libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2" // libraryDependencies += "de.srtobi" %%% "escalima" % "0.5" + +// https://github.com/scala/scala-xml/wiki/Getting-started +libraryDependencies += "org.scala-lang.modules" %% "scala-xml" % "1.2.0" +// https://github.com/spray/spray-json +libraryDependencies += "io.spray" %% "spray-json" % "1.3.5" diff --git a/src/main/resources/pizza-collaboration.bpmn b/src/main/resources/pizza-collaboration.bpmn new file mode 120000 index 0000000..4bbaa08 --- /dev/null +++ b/src/main/resources/pizza-collaboration.bpmn @@ -0,0 +1 @@ +/home/connolly/projects/bpmn-js-examples/bundling/resources/pizza-collaboration.bpmn \ No newline at end of file diff --git a/src/main/scala/bpmn2rho/PizzaOrderTest.scala b/src/main/scala/bpmn2rho/PizzaOrderTest.scala new file mode 100644 index 0000000..1a7e33a --- /dev/null +++ b/src/main/scala/bpmn2rho/PizzaOrderTest.scala @@ -0,0 +1,43 @@ +package bpmn2rho + +import js2rho.{RhoBuilder, Name, Process, Printer, RhoPrinter} + +import scala.xml.XML +import scala.xml.Node + +object PizzaOrderTest { + def main(args: Array[String]) = { + println("Hello, world!") + // https://github.com/bpmn-io/bpmn-js-examples/blob/master/bundling/resources/pizza-collaboration.bpmn + val pizzaModelRes = getClass.getResource("/pizza-collaboration.bpmn") + + println(pizzaModelRes) + + val doc = XML.load(pizzaModelRes) + // println(doc \\ "task") + println(doc \\ "startEvent") + + val printer = Printer.fromPrintStream(System.out) + val bld = new RhoPrinter() + + toRho(doc, bld)._printOn(printer) + + } + + // TODO: monadic style instead of Builder? + def toRho(bpml: Node, bld: RhoBuilder): Process = { + val nil = bld.Nil() + def par(ps: Seq[Process]): Process = ps match { + case Seq() => nil + case Seq(first) => first + case first :: rest => bld.Par(first, par(rest)) + } + // xmlns:semantic="http://www.omg.org/spec/BPMN/20100524/MODEL" + val starts = + for (startEvent <- bpml \\ "startEvent"; + id = bld.Var((startEvent \ "@id") text); + name = bld.primitive((startEvent \ "@name") text)) + yield bld.receiving(List((List(bld.Quote(name)), id)), nil) + par(starts) + } +} diff --git a/src/main/scala/js2rho/RhoBuilder.scala b/src/main/scala/js2rho/RhoBuilder.scala new file mode 100644 index 0000000..fb839f4 --- /dev/null +++ b/src/main/scala/js2rho/RhoBuilder.scala @@ -0,0 +1,308 @@ +package js2rho + +import java.io.PrintStream + +import spray.json._ +import DefaultJsonProtocol._ + +/** + * https://github.com/rchain/rchain/blob/dev/rholang/src/main/bnfc/rholang_mercury.cf + */ +trait Miranda { + def _printOn(p: Printer): Unit +} + +trait Printer { + def print(s: String): Unit + def begin(s: String): Unit + def newline(): Unit + def end(s: String): Unit +} + +object Printer { + def fromPrintStream(out: PrintStream): Printer = { + var indent = 0; + return new Printer { + def print(txt: String) { out.print(txt) } + def begin(txt: String) { out.print(txt); indent += 1; this.newline() } + def newline() { out.print("\n" + " ".repeat(indent)) } + def end(txt: String) { + this.newline(); out.print(txt); indent -= 1; this.newline() + } + } + } +} + +trait Process extends Miranda { + def quote(): Name +} + +trait Name extends Miranda { + def deref(): Process +} + +trait RhoBuilder { + def Nil(): Process + def primitive(value: Boolean): Process + def primitive(value: Int): Process + def primitive(value: String): Process + + def Var(id: String): Name + def Quote(p: Process): Name + def Drop(n: Name): Process + + def Par(p: Process, q: Process): Process + // TODO: join + def receiving(rx: Seq[(Seq[Name], Name)], body: Process): Process + + /* + * @property {(procs: Process[]) => Process} listExpr + * @property {(procs: Process[]) => Process} tupleExpr + * @property {(entries: Array<{ key: string, value: Process }>) => Process} mapExpr + * @property {(specimen: Process, cases: { lhs: Process, rhs: Process }) => Process } match + * @property {(dest: Name, procs: Array) => Process} send + * @property {(op: BinOp, lhs: Process, rhs: Process) => Process} binop + * @property {(op: UnOp, arg: Process) => Process} unary + * @property {(name: Name, args: Array, body: Process) => Process} contract + * @property {(n: Name) => Process} Drop + * @property {(p: Process, q: Process) => Process} Par + * @property {(p: Process) => Name} Quote + * @property {(vars: Array, body: Process) => Process} new_ + * + * @typedef {{ lhs: Name[], rhs: Name}[]} Receipt + + */ +} + +class RhoPrinter extends RhoBuilder { + def aNil(): Process = new Process { + def _printOn(out: Printer) = out.print("Nil") + def quote() = Quote(aNil()) + } + val theNil = aNil() + def Nil() = theNil + + def primitive(v: String) = new Process { + def _printOn(out: Printer) = out.print(v.toJson.toString()) + def quote() = Quote(primitive(v)) + } + def primitive(v: Int) = new Process { + def _printOn(out: Printer) = out.print(v.toJson.toString()) + def quote() = Quote(primitive(v)) + } + def primitive(v: Boolean) = new Process { + def _printOn(out: Printer) = out.print(v.toJson.toString()) + def quote() = Quote(primitive(v)) + } + + def Quote(p: Process) = new Name { + def _printOn(out: Printer) = { + out.print("@{ ") + p._printOn(out) + out.print(" }") + } + def deref() = p + } + def Drop(name: Name) = new Process { + def _printOn(out: Printer) = { + out.print("*") + name._printOn(out) + } + def quote() = name + } + def Var(v: String) = new Name { + def _printOn(out: Printer) = out.print(v) + def deref() = Drop(Var(v)) + } + + def Par(p: Process, q: Process) = + if (p == theNil) { q } + else if (q == theNil) { p } + else + new Process { + def _printOn(out: Printer) = { + p._printOn(out) + out.newline() + out.print("|") + out.newline() + q._printOn(out) + } + def quote() = Quote(Par(p, q)) + } + + def receiving(rx: Seq[(Seq[Name], Name)], proc: Process) = new Process { + def _printOn(out: Printer) = { + out.print("for(") + var first = true; + for ((lhs, rhs) <- rx) { + if (!first) { + out.print("; ") + } + printList(out, lhs) + out.print(" <- ") + rhs._printOn(out) + first = false; + } + out.begin(") {") + proc._printOn(out) + out.end("}") + } + def quote() = Quote(receiving(rx, proc)) + } + def printList(out: Printer, items: Iterable[Miranda]) = { + var first = true; + for (item <- items) { + if (!first) { + out.print(", ") + } + item._printOn(out) + first = false + } + } + /* + const listExpr = (procs) => harden({ + _printOn(out) { + out.print('['); + printList(out, procs); + out.print(']'); + }, + quote: () => Quote(listExpr(procs)) + }); + const tupleExpr = (procs) => harden({ + _printOn(out) { + out.print('('); + printList(out, procs); + out.print(')'); + }, + quote: () => Quote(tupleExpr(procs)) + }); + const mapExpr = (entries) => harden({ + _printOn(out) { + let first = true; + out.print('{'); + for (const { key, value } of entries) { + if (!first) { + out.print(', ') + } + out.print(JSON.stringify(key)); + out.print(': '); + value._printOn(out); + first = false; + } + out.print('}'); + }, + quote: () => Quote(mapExpr(entries)) + }); + const match = (specimen, cases) => harden({ + _printOn(out) { + out.newline(); + out.print("match ("); + specimen._printOn(out); + out.begin(") {"); + for (const { lhs, rhs } of cases) { + lhs._printOn(out); + out.begin(" => {"); + rhs._printOn(out); + out.end("}"); + } + out.end("}"); + } + + }); + const printList = (out, items) => { + let first = true; + for (const item of items) { + if (!first) { + out.print(", ") + } + item._printOn(out) + first = false + } + } + const send = (dest, procs) => harden({ + _printOn: (out) => { + dest._printOn(out) + out.print(`!(`) // TODO: !! + printList(out, procs) + out.print(")") + }, + quote: () => Quote(send(dest, procs)) + }); + const binop = (op, lhs, rhs) => harden({ + _printOn: (out) => { + lhs._printOn(out) + out.print(" " + op + " ") + rhs._printOn(out) + }, + quote: () => Quote(binop(op, lhs, rhs)) + }) + const unary = (op, arg) => harden({ + _printOn: (out) => { + out.print(op); + out.print('{'); + arg._printOn(out); + out.print('}') + }, + quote: () => Quote(unary(op, arg)) + }) + const contract = (name, args, body) => harden({ + _printOn: (out) => { + out.print("contract ") + name._printOn(out) + out.print(`(`) + printList(out, args) + out.begin(`) = {`); + body._printOn(out); + out.end("}") + }, + quote: () => Quote(contract(name, args, body)) + }) + + const fmtvdecl = (vd) => typeof vd === 'string' ? vd : `${vd[0]}(\`${vd[1]}\`)`; + /** @type {(vlist: vdecl[], body: Process) => Process} */ + const new_ = (vlist, body) => harden({ + _printOn: (out) => { + out.newline(); + out.print("new ") + let first = true; + for (const item of vlist) { + if (!first) { + if (typeof item === 'string') { + out.print(', '); + } else { + out.print(','); + out.newline(); + } + } + out.print(fmtvdecl(item)); + first = false; + } + out.newline(); + out.begin("in {") + body._printOn(out) + out.end("}"); + }, + quote: () => Quote(new_(vlist, body)) + }) + + return harden({ + Nil, + primitive, + listExpr, + tupleExpr, + mapExpr, + match, + send, + binop, + unary, + receiving, + contract, + Drop, + Var, + Quote, + Par, + new_, + }); +} + */ +}