Skip to content

Instantly share code, notes, and snippets.

@johnynek
Created December 28, 2019 02:05
Show Gist options
  • Save johnynek/ca0bca529f01b98c1d66aac04273380b to your computer and use it in GitHub Desktop.
Save johnynek/ca0bca529f01b98c1d66aac04273380b to your computer and use it in GitHub Desktop.
attempt at using staging to make a parser in scala 3
/**
* Error:
*
[info] Compiling 1 Scala source to /home/oscar/oss/olelo/target/scala-0.21/classes ...
[error] -- Error: /home/oscar/oss/olelo/src/main/scala/Olelo.scala:13:14 ---------------
[error] 13 | p.toExpr
[error] | ^
[error] | access to type A from wrong staging level:
[error] | - the definition is at level 0,
[error] | - but the access is at level 1.
[error] |
[error] | The access would be accepted with an implict scala.quoted.Type[A]
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
*/
package olelo
import scala.quoted._
import scala.quoted.staging._
object Olelo {
sealed abstract class Parser[+A] {
def toExpr[A1 >: A](given QuoteContext, Type[A1]): Expr[String => Either[Int, (String, A1)]]
}
inline def compile[A](p: => Parser[A])(given Toolbox): String => Either[Int, (String, A)] = {
run {
p.toExpr
}
}
def str(s: String): Parser[Unit] =
Impl.Literal(s)
given [A](parser: Parser[A]) extended with {
def |(that: Parser[A]): Parser[A] =
Impl.Or(parser :: that :: Nil)
}
object Impl {
case class Literal(target: String) extends Parser[Unit] {
def toExpr[A1 >: Unit](given QuoteContext, Type[A1]) = {
val targ = Expr(target)
if (target.isEmpty) '{
val good = Right(("", ()))
val bad = Left(0)
{ (s: String) => if (s.isEmpty) good else bad }
}
else {
'{
val tlen = ${targ}.length
{ (s: String) =>
var idx = 0
val slen = s.length
var res: Either[Int, (String, Unit)] = null
while (idx < tlen) {
if (idx < slen) {
if (s.charAt(idx) == ${targ}.charAt(idx)) {
idx = idx + 1
}
else {
res = Left(idx)
idx = tlen
}
}
else {
res = Left(idx)
idx = tlen
}
}
if (res == null) Right((s.substring(idx), ())) else res
}
}
}
}
}
case class Or[A](items: List[Parser[A]]) extends Parser[A] {
def toExpr[A1 >: A](given QuoteContext, Type[A1]) = {
def loop(items: List[Parser[A]]): Expr[String => Either[Int, (String, A1)]] =
items match {
case Nil =>
'{ (s: String) => Left(0) }
case h :: tail =>
val tailFn = loop(tail)
val headFn = h.toExpr[A1]
'{ (s: String) =>
val res = ${Expr.betaReduce(headFn)('s)}
if (res.isLeft) ${Expr.betaReduce(tailFn)('s)}
else res
}
}
loop(items)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment