All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.rojoma.json.v3.util.JValueProducer.scala Maven / Gradle / Ivy

The newest version!
package com.rojoma.json.v3
package util

import scala.annotation.tailrec

import io._
import ast.JValue

import JValueProducer._

// End-to-end character data to JValue push parser
final class JValueProducer private (lexer: JsonTokenGenerator, parser: JsonEventGenerator, valuator: JValueGenerator) {
  def consume(data: WrappedCharArray): SuccessfulResult = apply(data) match {
    case r: SuccessfulResult => r
    case LexerError(e) => JsonTokenGenerator.throwError(e)
    case ParserError(e) => JsonEventGenerator.throwError(e)
  }

  def apply(data: WrappedCharArray): Result = lexer(data) match {
    case JsonTokenGenerator.Token(token, newLexer, remainingData) =>
      processDatum(token, newLexer, parser, valuator, remainingData)
    case JsonTokenGenerator.More(newLexer) =>
      More(new JValueProducer(newLexer, parser, valuator))
    case e: JsonTokenGenerator.Error =>
      LexerError(e)
  }

  def endOfInput(): EndResult = {
    lexer.endOfInput() match {
      case JsonTokenGenerator.EndOfInput(pos) =>
        parser.endOfInput(pos) match {
          case JsonEventGenerator.Finished => UnexpectedEndOfInput(pos)
          case JsonEventGenerator.Unfinished(endPos) => UnexpectedEndOfInput(endPos)
          case e: JsonEventGenerator.UnexpectedToken => ParserError(e)
        }
      case JsonTokenGenerator.FinalToken(t, pos) =>
        processDatum(t, JsonTokenGenerator.newPositionedGenerator(pos), parser, valuator, WrappedCharArray.empty) match {
          case Value(v, _, _) => FinalValue(v, pos)
          case More(_) => UnexpectedEndOfInput(pos)
          case e: LexerError => e
          case e: ParserError => e
        }
      case e: JsonTokenGenerator.EndError =>
        LexerError(e)
    }
  }

  def finish(): SuccessfulEndResult = {
    endOfInput() match {
      case r: SuccessfulEndResult => r
      case ParserError(e) => JsonEventGenerator.throwError(e)
      case LexerError(e) => JsonTokenGenerator.throwError(e)
      case UnexpectedEndOfInput(p) => throw new JsonParserEOF(p)
    }
  }

  @tailrec
  final def processDatum(token: JsonToken, lexer: JsonTokenGenerator, parser: JsonEventGenerator, valuator: JValueGenerator, data: WrappedCharArray): Result = {
    // At this point we know we're handing a datum.  And "token" is part of it.
    parser(token) match {
      case JsonEventGenerator.Event(ev, newParser) =>
        valuator(ev) match {
          case JValueGenerator.Value(jvalue) =>
            assert(newParser.atTopLevel)
            Value(jvalue, lexer, data)
          case JValueGenerator.More(newValuator) =>
            assert(!newParser.atTopLevel)
            lexer(data) match {
              case JsonTokenGenerator.Token(newToken, newLexer, remainingData) =>
                processDatum(newToken, newLexer, newParser, newValuator, remainingData)
              case JsonTokenGenerator.More(newLexer) =>
                More(new JValueProducer(newLexer, newParser, newValuator))
              case e: JsonTokenGenerator.Error =>
                LexerError(e)
            }
          case _ : JValueGenerator.BadParse =>
            // this shouldn't happen
            throw new Exception("Bad parse from JValueGenerator")
          case JValueGenerator.UnknownIdentifier(i, pos) =>
            val identToken = TokenIdentifier(i)(pos)
            ParserError(JsonEventGenerator.UnexpectedToken(identToken, "datum"))
        }
      case JsonEventGenerator.More(newParser) =>
        // yeah, this is an almost exact copy of the JValueGenerator.More case right above.  SURE WOULD BE
        // NICE if the jvm had proper tailcall elimination so this could be factored out into a sister
        // method.
        lexer(data) match {
          case JsonTokenGenerator.Token(newToken, newLexer, remainingData) =>
            processDatum(newToken, newLexer, newParser, valuator, remainingData)
          case JsonTokenGenerator.More(newLexer) =>
            More(new JValueProducer(newLexer, newParser, valuator))
          case e: JsonTokenGenerator.Error =>
            LexerError(e)
        }
      case e: JsonEventGenerator.Error =>
        ParserError(e)
    }
  }
 }

object JValueProducer {
  sealed abstract class Result
  sealed abstract class SuccessfulResult extends Result
  sealed abstract class Error extends Result

  sealed trait EndResult
  sealed trait SuccessfulEndResult extends EndResult
  sealed trait EndError extends EndResult

  case class More(nextState: JValueProducer) extends SuccessfulResult
  case class Value(value: JValue, tokenGenerator: JsonTokenGenerator, remainingInput: WrappedCharArray) extends SuccessfulResult

  case class FinalValue(value: JValue, position: Position) extends SuccessfulEndResult

  case class LexerError(err: JsonTokenGenerator.AnyError) extends Error with EndError
  case class ParserError(err: JsonEventGenerator.AnyError) extends Error with EndError
  case class UnexpectedEndOfInput(position: Position) extends EndError

  class Builder private (lexer: JsonTokenGenerator, fieldCache: FieldCache) {
    def this() = this(null, null)

    private def copy(
      lexer: JsonTokenGenerator = this.lexer,
      fieldCache: FieldCache = this.fieldCache
    ): Builder =
      new Builder(lexer, fieldCache)

    def withLexer(lexer: JsonTokenGenerator) = copy(lexer = lexer)
    def withFieldCache(fieldCache: FieldCache) = copy(fieldCache = fieldCache)

    def build = {
      val trueLexer = if(lexer == null) JsonTokenGenerator.newGenerator else lexer
      val trueFieldCache = if(fieldCache == null) IdentityFieldCache else fieldCache
      new JValueProducer(trueLexer, JsonEventGenerator.newGenerator(trueFieldCache), JValueGenerator.newGenerator)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy