Skip to content

Commit

Permalink
Significantly faster IR Parsing (#384)
Browse files Browse the repository at this point in the history
* Significantly faster IR Parsing

* Cleanup
  • Loading branch information
deusaquilus authored Sep 5, 2023
1 parent 41f0fc1 commit 89311e9
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 31 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.finos.morphir.runtime.parsing

import org.finos.morphir.ir.MorphirIRFile
import org.finos.morphir.ir.distribution.Distribution
import org.finos.morphir.runtime.MorphirIRDecodingError
import zio.System.os
import zio.json.*
import zio.{Runtime, Unsafe, ZIO}

import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Paths}

object ParseTest {
def run[E, A](zio: ZIO[Any, E, A]) = Unsafe.unsafe { implicit u =>
Runtime.default.unsafe.run(zio).getOrThrowFiberFailure()
}

def loadDistributionFromFileZIO(fileName: String) = {
import org.finos.morphir.ir.json.MorphirJsonSupport.*
val start = System.currentTimeMillis()
val output =
run {
for {
fileContents <- ZIO.readFile(fileName)
morphirIRFile <- ZIO.fromEither(fileContents.fromJson[MorphirIRFile])
.mapError(MorphirIRDecodingError(_))
} yield morphirIRFile
}
val end = System.currentTimeMillis()
println(s"Total loading time: ${(end - start).toDouble / 1000.toDouble} for ${fileName}")
output
}

def writeDistrubtionToFile(file: MorphirIRFile, path: String) = {
import org.finos.morphir.ir.json.MorphirJsonSupport.*
val newFileJson = file.toJson
Files.write(
Paths.get(path),
newFileJson.getBytes(StandardCharsets.UTF_8)
)
}

def main(args: Array[String]): Unit = {
println("== Starting File Load experiment ==")
val originalFile = loadDistributionFromFileZIO("examples/morphir-elm-projects/evaluator-tests/morphir-ir.json")
writeDistrubtionToFile(originalFile, "examples/morphir-elm-projects/evaluator-tests/morphir-ir2.json")
val newFile = loadDistributionFromFileZIO("examples/morphir-elm-projects/evaluator-tests/morphir-ir2.json")
println("== Loaded New File ==")
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
package org.finos.morphir.ir
package json

import org.finos.morphir.naming._
import zio._
import zio.json._
import org.finos.morphir.naming.*
import zio.*
import zio.json.*
import zio.json.ast.Json
import org.finos.morphir.ir.distribution.Distribution
import org.finos.morphir.ir.distribution.Distribution._
import org.finos.morphir.ir.distribution.Distribution.*
import org.finos.morphir.ir.Literal.Literal
import org.finos.morphir.ir.Literal.Literal._
import org.finos.morphir.ir.Literal.Literal.*
import org.finos.morphir.ir.PackageModule.{
Definition => PackageDefinition,
Specification => PackageSpecification,
USpecification => UPackageSpecification
Definition as PackageDefinition,
Specification as PackageSpecification,
USpecification as UPackageSpecification
}
import org.finos.morphir.ir.Type.{Constructors, Definition => TypeDefinition, Specification => TypeSpecification, Type}
import org.finos.morphir.ir.Value.{Definition => ValueDefinition, Specification => ValueSpecification}
import org.finos.morphir.ir.Value.{Value, _}
import org.finos.morphir.ir.module.{Definition => ModuleDefinition, Specification => ModuleSpecification}

import org.finos.morphir.ir.Type.{Constructors, Type, Definition as TypeDefinition, Specification as TypeSpecification}
import org.finos.morphir.ir.Value.{Definition as ValueDefinition, Specification as ValueSpecification}
import org.finos.morphir.ir.Value.{Value, *}
import org.finos.morphir.ir.module.{Definition as ModuleDefinition, Specification as ModuleSpecification}
import zio.json.JsonDecoder.{JsonError, UnsafeJson}
import zio.json.internal.RetractReader

import java.nio.CharBuffer
import scala.annotation.{nowarn, unused}
import scala.util.control.Breaks._

trait MorphirJsonDecodingSupport {
implicit val unitDecoder: JsonDecoder[Unit] = Json.decoder.map(_ => ())
Expand Down Expand Up @@ -567,24 +571,26 @@ trait MorphirJsonDecodingSupport {

@nowarn("msg=Implicit resolves to enclosing method valueDecoder")
implicit def valueDecoder[TA: JsonDecoder, VA: JsonDecoder]: JsonDecoder[Value[TA, VA]] =
constructorValueJsonDecoder[VA].widen[Value[TA, VA]] orElse
fieldFunctionValueJsonDecoder[VA].widen[Value[TA, VA]] orElse
literalValueJsonDecoder[VA].widen[Value[TA, VA]] orElse
referenceValueJsonDecoder[VA].widen[Value[TA, VA]] orElse
unitValueJsonDecoder[VA].widen[Value[TA, VA]] orElse
variableValueJsonDecoder[VA].widen[Value[TA, VA]] orElse
applyValueJsonDecoder[TA, VA].widen[Value[TA, VA]] orElse
destructureValueJsonDecoder[TA, VA].widen[Value[TA, VA]] orElse
fieldValueJsonDecoder[TA, VA].widen[Value[TA, VA]] orElse
ifThenElseValueJsonDecoder[TA, VA].widen[Value[TA, VA]] orElse
lambdaValueJsonDecoder[TA, VA].widen[Value[TA, VA]] orElse
letDefinitionValueJsonDecoder[TA, VA].widen[Value[TA, VA]] orElse
letRecursionValueJsonDecoder[TA, VA].widen[Value[TA, VA]] orElse
listValueJsonDecoder[TA, VA].widen[Value[TA, VA]] orElse
patternMatchValueJsonDecoder[TA, VA].widen[Value[TA, VA]] orElse
recordValueJsonDecoder[TA, VA].widen[Value[TA, VA]] orElse
tupleValueJsonDecoder[TA, VA].widen[Value[TA, VA]] orElse
updateRecordValueJsonDecoder[TA, VA].widen[Value[TA, VA]]
zio.json.TagBasedParser[Value[TA, VA]] {
case "Constructor" => constructorValueJsonDecoder[VA].widen
case "FieldFunction" => fieldFunctionValueJsonDecoder[VA].widen
case "Literal" => literalValueJsonDecoder[VA].widen
case "Reference" => referenceValueJsonDecoder[VA].widen
case "Unit" => unitValueJsonDecoder[VA].widen
case "Variable" => variableValueJsonDecoder[VA].widen
case "Apply" => applyValueJsonDecoder[TA, VA].widen
case "Destructure" => destructureValueJsonDecoder[TA, VA].widen
case "Field" => fieldValueJsonDecoder[TA, VA].widen
case "IfThenElse" => ifThenElseValueJsonDecoder[TA, VA].widen
case "Lambda" => lambdaValueJsonDecoder[TA, VA].widen
case "LetDefinition" => letDefinitionValueJsonDecoder[TA, VA].widen
case "LetRecursion" => letRecursionValueJsonDecoder[TA, VA].widen
case "List" => listValueJsonDecoder[TA, VA].widen
case "PatternMatch" => patternMatchValueJsonDecoder[TA, VA].widen
case "Record" => recordValueJsonDecoder[TA, VA].widen
case "Tuple" => tupleValueJsonDecoder[TA, VA].widen
case "UpdateRecord" => updateRecordValueJsonDecoder[TA, VA].widen
}

implicit def distributionLibraryJsonDecoder: JsonDecoder[Library] =
JsonDecoder
Expand Down
42 changes: 42 additions & 0 deletions morphir/toolkit/codec/zio/json/src/zio/json/TagBasedParser.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package zio.json

import org.finos.morphir.ir.Value.Value
import zio.json.JsonDecoder.{JsonError, UnsafeJson}
import zio.json.internal.RetractReader

import scala.util.control.Breaks.{break, breakable}

case class TagBasedParser[T](pf: PartialFunction[String, JsonDecoder[T]]) extends JsonDecoder[T] {
override def unsafeDecode(trace: List[JsonError], inRaw: RetractReader): T = {
val in = new zio.json.internal.WithRecordingReader(inRaw, 64)

def err(msg: String) = throw UnsafeJson(JsonError.Message(msg) :: trace)

if (in.nextNonWhitespace() != '[') err("Expected first char to be '['")
if (in.nextNonWhitespace() != '"') err("Expected second char to be quote ('\"')")

val buff = new StringBuffer()
var readChar: Char = 0
breakable {
while (true) {
readChar = in.nextNonWhitespace()
if (readChar != 0 && readChar.isLetter) {
// buff.append returns itself (so that you can chain it) but we don't care about that here
val _ = buff.append(readChar)
} else {
break()
}
}
}

// rewind to the beginning of the clause
in.rewind()
val term = buff.toString

val output: T =
if (pf.isDefinedAt(term)) pf.apply(term).unsafeDecode(trace, in)
else err(s"Undefined heading: '${term}'")

output
}
}

0 comments on commit 89311e9

Please sign in to comment.