Skip to content

Instantly share code, notes, and snippets.

@fanf
Last active March 30, 2021 14:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fanf/25cb3a4a74853a50ed8d78f5a5c2119d to your computer and use it in GitHub Desktop.
Save fanf/25cb3a4a74853a50ed8d78f5a5c2119d to your computer and use it in GitHub Desktop.
A value lexer for zio-json that stores said value in a frame buffer
package zio.json.internal
object StoreLexer {
/*
* An utility method that store the next value in a string so that
* something else can parse it.
* Copied fomr zio.json.Lexer#skipValue
*/
import zio.json.JsonDecoder._
import zio.json.internal.Lexer._
import scala.annotation.switch
private[this] val ull: Array[Char] = "ull".toCharArray
private[this] val alse: Array[Char] = "alse".toCharArray
private[this] val rue: Array[Char] = "rue".toCharArray
def storeValue(trace: List[JsonError], in: RetractReader, sb: StringBuilder): Unit =
(in.nextNonWhitespace(): @switch) match {
case 'n' => readChars(trace, in, ull, "null") ; sb.append("null")
case 'f' => readChars(trace, in, alse, "false") ; sb.append("false")
case 't' => readChars(trace, in, rue, "true") ; sb.append("true")
case '{' =>
if (firstField(trace, in)) {
sb.append('{')
do {
Lexer.char(trace, in, '"')
storeString(trace, in, sb)
Lexer.char(trace, in, ':') ; sb.append(':')
storeValue(trace, in, sb)
} while (nextFieldStoreSep(trace, in, sb))
} else {
sb.append("{}")
}
case '[' =>
if (firstArrayElement(in)) {
sb.append('[')
do storeValue(trace, in, sb) while (nextArrayElementStoreSep(trace, in, sb))
} else {
sb.append("[]")
}
case '"' =>
storeString(trace, in, sb)
case c@('-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') =>
sb.append(c)
storeNumber(in, sb)
case c => throw UnsafeJson(JsonError.Message(s"unexpected '$c'") :: trace)
}
def storeNumber(in: RetractReader, sb: StringBuilder): Unit = {
var cont = true
while (cont) {
val c = in.readChar()
cont = isNumber(c)
if(cont) {
sb.append(c)
}
}
in.retract()
}
def storeString(trace: List[JsonError], in: OneCharReader, sb: StringBuilder): Unit = {
sb.append('"')
val stream = new EscapedString(trace, in)
var i: Int = 0
do { i = stream.read(); if(i!= -1) {sb.append(i.toChar)} } while (i != -1)
sb.append('"')
}
// True if we got a comma, and False for }
def nextFieldStoreSep(trace: List[JsonError], in: OneCharReader, sb: StringBuilder): Boolean =
(in.nextNonWhitespace(): @switch) match {
case ',' => sb.append(',') ; true
case '}' => sb.append('}') ; false
case c =>
throw UnsafeJson(
JsonError.Message(s"expected ',' or '}' got '$c'") :: trace
)
}
def nextArrayElementStoreSep(trace: List[JsonError], in: OneCharReader, sb: StringBuilder): Boolean =
(in.nextNonWhitespace(): @switch) match {
case ',' => sb.append(','); true
case ']' => sb.append(']'); false
case c =>
throw UnsafeJson(
JsonError.Message(s"expected ',' or ']' got '$c'") :: trace
)
}
// non-positional for performance
@inline private[this] def isNumber(c: Char): Boolean =
(c: @switch) match {
case '+' | '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' | 'e' | 'E' =>
true
case _ => false
}
}
/*
use it with:
implicit val configDecoder: JsonDecoder[Config] = new JsonDecoder[Config] {
override def unsafeDecode(trace: List[JsonDecoder.JsonError], in: RetractReader): Config = {
val sb = new StringBuilder()
StoreLexer.storeValue(trace, in, sb)
GenericProperty.parseSerialisedConfig(sb.toString()) match {
case Right(value) => value
case Left(err) => throw UnsafeJson(
JsonError.Message(s"Cannot parse Config value: ${err.fullMsg}") :: trace
)
}
}
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment