Parser

trait Parser[-In, +Out]

Primary "spac" abstraction which represents a sink for data events.

Parsers are responsible for interpreting a stream of In events as a single result of type Out. The actual interpretation is performed by a Parser.Handler which the Parser is responsible for constructing. Handlers may be internally-mutable, and so they are generally only constructed by the parse helper methods or by other handlers. Parsers themselves are immutable, acting as "handler factories", and so they may be freely reused.

A parser differs from typical "fold" operations in that it may choose to abort early with a result, leaving the remainder of the data stream untouched.

Type parameters:
In

event/input type

Out

result type

Companion:
object
class Object
trait Matchable
class Any
trait Stateless[In, Out]
class ParserDelay[A]
object ParserDrain.type
class ParserFind[In]
class ParserFirst[In]
class ParserFirstOpt[In]
class ParserPure[Out]
class ParserTap[In]
class ParserCompoundN[In, Out]
class ParserDefer[In, Out]
class ParserDeferHandler[In, Out]
class ParserExpectInputs[In, Out]
class ParserFold[In, Out]
class ParserFollowedByParser[In, A, Out]
class ParserInterruptedBy[In, Out]
class ParserMapped[In, A, B]
class ParserOrElseChain[In, Out]
class ParserRethrow[In, Out]
class ParserTry[In, Out]
class ParserWithName[In, Out]
class TransformerIntoParser[In, X, Out]
Parser[In, Out]

Value members

Abstract methods

def newHandler: Handler[In, Out]

Parser's main abstract method; constructs a new Handler representing this parser's logic. Parsers are expected to be immutable, but Handlers may be internally-mutable.

Parser's main abstract method; constructs a new Handler representing this parser's logic. Parsers are expected to be immutable, but Handlers may be internally-mutable.

Concrete methods

Represent this parser as a Transformer which emits this parser's result

Represent this parser as a Transformer which emits this parser's result

def attempt: Parser[In, Either[Throwable, Out]]

Like wrapSafe, but represents exceptions as Left and successful results as Right

Like wrapSafe, but represents exceptions as Left and successful results as Right

def beforeContext[I2 <: In, StackElem](matcher: ContextMatcher[StackElem, Any])(implicit stackable: StackLike[I2, StackElem], pos: CallerPos): Parser[I2, Out]

Specialization of interruptedBy for stack-like input types, such that an interruption will occur upon entering a stack context that can be matched by the given matcher.

Specialization of interruptedBy for stack-like input types, such that an interruption will occur upon entering a stack context that can be matched by the given matcher.

Example:

val preludeContext = * \ "prelude"
val dataContext = * \ "data"
for {
 prelude <- Splitter(preludeContext).firstOption[Prelude].beforeContext(dataContext).followedByStream
 data <- Splitter(dataContext).as[Data]
} yield data
Type parameters:
I2

Subtype of In, or just In (to satisfy contravariance of Parser's In type)

StackElem

Specialization of the In type for when it represents a stack push or pop

Value parameters:
matcher

A matching function that operates on a context stack

stackable

Interprets the inputs as stack push/pop events to accumulate a context stack

Returns:

A parser which will perform an early finish() when a matching context is encountered

def expectInputs[I2 <: In](expectations: List[(String, I2 => Boolean)]): Parser[I2, Out]

Impose expectations on the sequence of inputs to be received by handlers created by this parser. As this parser's handler receives an input, the input will be tested against the head of the expectations list. If the test returns false, the expectation is failed and the handler will throw an exception. If the test returns true, the expectation is satisfied, and the handler will advance to the next expectation. If there are no more expectations left in the list (i.e. N inputs have satisfied the corresponding N expectations), then all expectations have been met and inputs will be treated as normal by the handler. If the handler receives an EOF before all expectations are met, it will throw an exception.

Impose expectations on the sequence of inputs to be received by handlers created by this parser. As this parser's handler receives an input, the input will be tested against the head of the expectations list. If the test returns false, the expectation is failed and the handler will throw an exception. If the test returns true, the expectation is satisfied, and the handler will advance to the next expectation. If there are no more expectations left in the list (i.e. N inputs have satisfied the corresponding N expectations), then all expectations have been met and inputs will be treated as normal by the handler. If the handler receives an EOF before all expectations are met, it will throw an exception.

Value parameters:
expectations

A sequence of label -> test expectations imposed on inputs to this parser

Returns:

A copy of this parser with expectations imposed on its inputs

def flatten(implicit F: FlatMap[F]): Parser[In, F[A]]
Implicitly added by ParserFlatten

Convenience for .map(_.flatten), e.g. to simplify a Parser[In, Option[Option[Out]] to Parser[In, Option[Out]].

Convenience for .map(_.flatten), e.g. to simplify a Parser[In, Option[Option[Out]] to Parser[In, Option[Out]].

Example:

  Splitter.json(...).asNullable[String].parseFirstOpt.flatten
Value parameters:
F

A type constructor that can be flatMapped, such as Option or List

Returns:

A new Parser whose output type has been flattened

Implicitly added by ParserFollowedByOps

Intermediate object for creating a sequenced parser in which the result of this parser will be used to initialize a second parser as soon as it is available.

Intermediate object for creating a sequenced parser in which the result of this parser will be used to initialize a second parser as soon as it is available.

In other words, the source (series of In values) will be fed into this Parser until this parser's handler returns a result of type Out. At that point, the second parser (as specified by using the apply or flatMap methods on the FollowedBy returned by this method) will be instantiated. Any relevant "stack events" (see Stackable) will be replayed so the second parser has the right context, and from that point on, all In values will be sent to the second parser. When that second parser returns a result, that result becomes the output of the combined parser created by this.followedBy(out => makeSecondParser(out))

Examples:

  val p1: Parser[A] = /* ... */
  def getP2(p1Result: A): Parser[B] = /* ... */
  val combined: Parser[B] = p1.followedBy(getP2)

  // alternative `flatMap` syntax
  val combined: Parser[B] = for {
    p1Result <- p1.followedBy
    p2Result <- getP2(p1Result)
  } yield p2Result

See Parser's interruptedBy, which is useful when a transformer.parseFirstOption must be followedBy some other parser.

Implicitly added by ParserFollowedByOps

Alias for followedBy, for use when Cat's ApplyOps gets in the way with its own useless followedBy method.

Alias for followedBy, for use when Cat's ApplyOps gets in the way with its own useless followedBy method.

Implicitly added by ParserFollowedByOps

Intermediate object creating a transformer that depends on this parser. Particularly useful in cases where one or more specific "info" elements precede a stream of other elements which require that "info" to be parsed.

Intermediate object creating a transformer that depends on this parser. Particularly useful in cases where one or more specific "info" elements precede a stream of other elements which require that "info" to be parsed.

Examples:

  val p1: Parser[In, A] = /* ... */
  def getP2Stream(p1Result: A): Transformer[In, B] = /* ... */
  val combined: Transformer[In, B] = p1.andThenStream(getP2Stream)

  // alternative `flatMap` syntax
  val combined: Transformer[In, B] = for {
    p1Result <- p1.andThenStream
    p2Result <- getP2Stream(p1Result)
  } yield p2Result

See followedBy for a general explanation of how the combination works.

See also, interruptedBy, which is useful when a transformer.parseFirstOption must be followedBy some other transformer.

def interruptedBy[I2 <: In](interrupter: Parser[I2, Any]): Parser[I2, Out]

Create a copy of this parser that will treat a result from the interrupter as an early EOF. This is especially useful for creating followedBy chains involving optional elements.

Create a copy of this parser that will treat a result from the interrupter as an early EOF. This is especially useful for creating followedBy chains involving optional elements.

Normally, a parser for an optional item in some context will not finish until that context ends, or until the item is encountered. So if the item is not present, followedBy logic won't work since the followUp parser/transformer will not see any events.

To make sure the leading parser can "fail fast", you can "interrupt" it, typically by creating a parser that immediately returns a result upon entering a particular context, i.e. the context in which the "following" parser will start. Parser#beforeContext provides a convenience for doing so.

Note that if the interrupter throws an exception, that exception will not be caught. If your interrupter might throw, pass interrupter.wrapSafe instead to swallow the exception.

Type parameters:
I2

Subtype of In, or just In (to satisfy contravariance of Parser's In type)

Value parameters:
interrupter

A parser which will be run in parallel with this parser, and whose result will be treated as an early EOF for this parser, forcing an early call to finish().

Returns:

A parser which will perform an early finish() call when the interrupter produces a result.

def map[Out2](f: Out => Out2): Parser[In, Out2]

Create a copy of this Parser whose result is transformed by the given function f.

Create a copy of this Parser whose result is transformed by the given function f.

Type parameters:
Out2

The new parser's result type

Value parameters:
f

Result transformation function

def orElse[In2 <: In, Out2 >: Out](fallback: Parser[In2, Out2]): Parser[In2, Out2]

Combine this parser with the fallback such that failures from the underlying parsers will be ignored as long as at least one succeeds. The result will be the result of whichever underlying parser succeeds first. If all of the underlying parsers fail, a SpacException.FallbackChainFailure will be thrown by the returned parser's handler.

Combine this parser with the fallback such that failures from the underlying parsers will be ignored as long as at least one succeeds. The result will be the result of whichever underlying parser succeeds first. If all of the underlying parsers fail, a SpacException.FallbackChainFailure will be thrown by the returned parser's handler.

Type parameters:
In2

Subtype of In, or just In (to satisfy Parser's contravariance on the In type)

Out2

Supertype of Out, or just Out (to satisfy Parser's covariance on the Out type)

Value parameters:
fallback

another parser of the same(ish) type as this one

Returns:

A new parser that will succeed if either this parser or the fallback succeed

@throws(scala.throws.$lessinit$greater$default$1[io.dylemma.spac.SpacException[_ >: scala.Nothing <: scala.Any]])
def parse(inputs: Iterator[In])(implicit pos: CallerPos): Out

Consume the given inputs iterator to produce an output or possibly throw a SpacException.

Consume the given inputs iterator to produce an output or possibly throw a SpacException.

After calling this method, the inputs should be discarded, since consuming an Iterator is a destructive operation.

Value parameters:
inputs

A series of In values, e.g. XmlEvent or JsonEvent

pos

Captures the caller filename and line number, used to fill in the 'spac trace' if the parser throws an exception

Returns:

The parser result based on the given inputs

def parse(source: Source[In])(implicit pos: CallerPos): Out

Consume the given source to produce an output or possibly throw a SpacException.

Consume the given source to produce an output or possibly throw a SpacException.

The Source[A] type is like Iterable[A] but uses the "lender" pattern to acquire the iterator and close any resources associated with the iterator after the iterator is consumed.

XML and JSON-specific Source constructors are provided by the "parser backend" libraries i.e. xml-spac-javax and json-spac-jackson.

Value parameters:
pos

Captures the caller filename and line number, used to fill in the 'spac trace' if the parser throws an exception

source

An object that can provide a series of In values, e.g. XmlEvent or JsonEvent

Returns:

The parser result based on the given source

def rethrow[T](implicit ev: Out <:< Either[Throwable, T]): Parser[In, T]

Like unwrapSafe, but rethrows exceptions from Left or returns results from Right. This operation is the opposite of attempt.

Like unwrapSafe, but rethrows exceptions from Left or returns results from Right. This operation is the opposite of attempt.

def start(methodName: String)(implicit pos: CallerPos): Handler[In, Out]

Low-level consumer method: creates a new handler and binds the caller position for its SpacTraceElement.

Low-level consumer method: creates a new handler and binds the caller position for its SpacTraceElement.

Used internally by the parse methods. Start with this method if you have some sequence-like datatype that doesn't provide an Iterator.

This is just a convenience for newHandler.asTopLevelhandler which helps construct a useful SpacTraceElement.

Value parameters:
methodName

The method name used to construct the SpacTraceElement for the handler. Defaults to "start"

pos

Captures the caller filename and line number, used to fill in the 'spac trace' if the parser throws an exception

Returns:

A parser handler that can be used to eventually produce a result by calling its step and/or finish methods

def unwrapSafe[T](implicit ev: Out <:< Try[T]): Parser[In, T]

Creates a copy of this parser which unwraps the resulting Try, throwing an exception if the result was a Failure. This operation is the opposite of wrapSafe.

Creates a copy of this parser which unwraps the resulting Try, throwing an exception if the result was a Failure. This operation is the opposite of wrapSafe.

def upcast[Out2](implicit ev: Out <:< Out2): Parser[In, Out2]

Returns this parser, with the output type widened to Out2, which is some supertype of Out. Uses asInstanceOf rather than creating a new parser.

Returns this parser, with the output type widened to Out2, which is some supertype of Out. Uses asInstanceOf rather than creating a new parser.

def withName(name: String): Parser[In, Out]

Creates a copy of this parser, but with a different toString

Creates a copy of this parser, but with a different toString

Value parameters:
name

The new "name" (i.e. toString) for this parser

Returns:

A copy of this parser whose toString returns the given name

def wrapSafe: Parser[In, Try[Out]]

Create a copy of this Parser whose handler will catch NonFatal exceptions thrown by the underlying logic. Caught exceptions will be yielded as a Failure output. Normal results will be wrapped in Success.

Create a copy of this Parser whose handler will catch NonFatal exceptions thrown by the underlying logic. Caught exceptions will be yielded as a Failure output. Normal results will be wrapped in Success.

Returns:

A copy of this parser that will return a Failure instead of throwing exceptions