Skip to content

Commit

Permalink
Experiment: else ifs in serializer code (#116)
Browse files Browse the repository at this point in the history
Use scalameta to transform lists of mutually exclusive ifs into else ifs, to potentially save a miniscule amount of performance.

Also, restructure metaprogramming transformations to make them easier to chain together.
  • Loading branch information
Ostrzyciel authored Jul 16, 2024
1 parent c7b841d commit 3fefdb5
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 195 deletions.
4 changes: 2 additions & 2 deletions project/Generator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ object Generator {
println(s" Processing ${inputFile.getName}")
val inputStr = IO.read(inputFile)
val outputFile = outputDir / inputFile.name
// Apply transformations: 1, 2
val outputStr = ProtoTransformer2.transform(ProtoTransformer1.transform(inputStr))
// Apply transformations
val outputStr = ProtoTransformer.transform(inputStr)
IO.write(outputFile, outputStr)
outputFile
}
Expand Down
23 changes: 23 additions & 0 deletions project/ProtoTransformer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import scala.meta._

object ProtoTransformer {
val transformations: Seq[Transformer] = Seq(
Transform1.transformer,
Transform2.transformer,
Transform3.transformer,
)

def transform(input: String): String = {
val tree = input.parse[Source].get

val transformer = new Transformer {
override def apply(tree: Tree): Tree = {
transformations.foldLeft(tree) { (t, transformer) =>
transformer(t)
}
}
}

transformer(tree).syntax
}
}
86 changes: 0 additions & 86 deletions project/ProtoTransformer1.scala

This file was deleted.

107 changes: 0 additions & 107 deletions project/ProtoTransformer2.scala

This file was deleted.

80 changes: 80 additions & 0 deletions project/Transform1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import scala.meta._

/**
* Source code transformer that for oneof protos:
* - Unifies naming of is[S/P/O/G]Something methods to isSomething
* - Removes with[S/P/O/G]Something methods
* - Unifies naming of [S/P/O/G]Something classes to Something
* - Unifies naming of [s/p/o/g]Something fields to something
* - Adds base traits (RdfTerm, RdfTermCompanion)
*
* All this must be done before ProtoTransformer2 is executed.
*/
object Transform1 {
val isMethodNamePattern = "^is[SPOG](Iri|Bnode|Literal|TripleTerm|DefaultGraph)$".r
val withMethodNamePattern = "^with[SPOG](Iri|Bnode|Literal|TripleTerm|DefaultGraph)$".r
val classNamePattern = "^[SPOG](Iri|Bnode|Literal|TripleTerm|DefaultGraph)".r
val fieldNamePattern = "^[spog](Iri|Bnode|Literal|TripleTerm|DefaultGraph)".r
val traitNamePattern = "^(Subject|Predicate|Object|Graph)$".r

val transformer = new Transformer {
override def apply(tree: Tree): Tree = tree match {
// Transform method and class names in references
case Term.Name(name) => name match {
case isMethodNamePattern(t) => Term.Name(f"is$t")
case classNamePattern(t) => Term.Name(t)
case fieldNamePattern(t) => Term.Name(f"${t.head.toLower}${t.tail}")
case _ => super.apply(tree)
}

// Transform class names in definitions
case Type.Name(classNamePattern(t)) => Type.Name(t)

// Remove with[S/P/O/G]Something methods
case Template.After_4_4_0(_, _, _, stats, _) => tree.asInstanceOf[Template].copy(
stats = stats.flatMap { stat => stat match {
case Defn.Def.After_4_7_3(_, Term.Name(withMethodNamePattern(t)), _, _, _) => None
case t => Some(apply(t).asInstanceOf[Stat])
}}
)

// Transform traits for RDF terms
case Defn.Trait.After_4_6_0(_, Type.Name(traitNamePattern(name)), _, _, templ) =>
val adapterName = if (name == "Graph") "GraphTerm" else s"SpoTerm"
tree.asInstanceOf[Defn.Trait].copy(
templ = apply(templ.copy(
inits = templ.inits :+ Init.After_4_6_0(
Type.Select(q"eu.ostrzyciel.jelly.core.proto_adapters", Type.Name(adapterName)),
Name.Anonymous(), Nil
)
)).asInstanceOf[Template]
)

// Transform companion objects for RDF terms
case Defn.Object(_, Term.Name(traitNamePattern(name)), templ) =>
val adapterName = if (name == "Graph") "GraphTermCompanion" else s"SpoTermCompanion"
val lastMethod = if (name == "Graph")
q"val makeDefaultGraph: DefaultGraph = DefaultGraph(RdfDefaultGraph.defaultInstance)"
else q"def makeTripleTerm(t: eu.ostrzyciel.jelly.core.proto.v1.RdfTriple): TripleTerm = TripleTerm(t)"
tree.asInstanceOf[Defn.Object].copy(
templ = apply(templ.copy(
inits = templ.inits :+ Init.After_4_6_0(
Type.Apply(
Type.Select(q"eu.ostrzyciel.jelly.core.proto_adapters", Type.Name(adapterName)),
Type.ArgClause(Type.Name(name) :: Nil)
),
Name.Anonymous(), Nil
),
stats = templ.stats ++ Seq(
q"val makeEmpty: Empty.type = Empty",
q"def makeIri(iri: eu.ostrzyciel.jelly.core.proto.v1.RdfIri): Iri = Iri(iri)",
q"def makeBnode(bnode: String): Bnode = Bnode(bnode)",
q"def makeLiteral(literal: eu.ostrzyciel.jelly.core.proto.v1.RdfLiteral): Literal = Literal(literal)"
) ++ Seq(lastMethod),
)).asInstanceOf[Template]
)

case node => super.apply(node)
}
}
}
Loading

0 comments on commit 3fefdb5

Please sign in to comment.