
laika.parse.Parsed.scala Maven / Gradle / Ivy
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package laika.parse
/** Represents the result of a `Parser`, a value of type `T` in case of success,
* a message in case of failure as well as the `ParserContext` for the remaining
* input.
*
* @author Jens Halm
*/
sealed abstract class Parsed[+T] {
/** The context representing the remaining input
* left over by the parser that produced this result.
*/
val next: ParserContext
/** Indicates whether this results represents a successful
* parser invocation. The `get` method will throw on instances
* where this method returns `false`.
*/
val isSuccess: Boolean
/** Indicates whether this results represents an unsuccessful
* parser invocation. The `get` method will throw on instances
* where this method returns `true`.
*/
def isFailure = !isSuccess
/** The result value from the parser invocation.
* This method will throw on instances
* where `isSuccess` returns `false`.
*
* @return
*/
def get: T
/** Returns the result value from the parser invocation if the
* parser succeeded or otherwise the specified fallback value.
*/
def getOrElse[B >: T] (default: => B): B = if (isFailure) default else this.get
/** Returns this `Parsed` instance if the parser suceeded or
* otherwise the specified fallback instance.
*/
def orElse[U >: T] (default: => Parsed[U]): Parsed[U] = if (isFailure) default else this
/** Builds a new `Parsed` instance by applying the specified function
* to the result of this instance.
*/
def map[U] (f: T => U): Parsed[U]
}
/** The success case of `Parsed` containing the result and the remaining input.
*/
case class Success[+T] (result: T, next: ParserContext) extends Parsed[T] {
val isSuccess = true
def get: T = result
def map[U](f: T => U) = Success(f(result), next)
override def toString = s"[${next.position}] parsed: $result"
}
/** The failure case of `Parsed` containing an error message and the remaining input.
*
* Implementation note:
* The message property is of type `Message`, to allow for lazy message creation.
* The former SDK parser combinators which this API is partially inspired by contained
* a lot of unnecessary string concatenations for messages which were then never read.
* This implementation avoids this extra cost and the result is measurable (about 15%
* performance gain for a typical Markdown document for example).
*
* @param msgProvider A provider that produces an error message for this failure based on its ParserContext
* @param next The parser's unconsumed input at the point where the failure occurred.
*/
case class Failure (msgProvider: Message, next: ParserContext) extends Parsed[Nothing] {
/** The message specifying the cause of the failure.
*/
lazy val message = msgProvider.message(next)
val isSuccess = false
def get: Nothing = scala.sys.error("No result available, parsing failed")
def map[U](f: Nothing => U) = this
override def toString = s"[${next.position}] failure: $message\n\n${next.position.lineContentWithCaret}"
}
/** Represents a lazy failure message.
* Implementations can use the specified `ParserContext` to construct
* the actual message, e.g. for adding parts of the input to the message.
*/
trait Message {
def message (context: ParserContext): String
}
/** Message companion providing several pre-built messages
* and factory methods.
*/
object Message {
val UnexpectedEOF = fixed("Unexpected end of input")
val ExpectedFailure = fixed("Expected failure, but parser succeeded")
val ExpectedEOF = fixed("Expected end of input")
val ExpectedStart = fixed("Expected start of input")
val ExpectedEOL = fixed("Expected end of line")
class MessageFactory[T] (f: T => String) extends (T => Message) {
def apply (value: T): Message = new Message {
def message (context: ParserContext): String = f(value)
}
}
/** Builds a message instance for a fixed string,
* independent of the parser context.
*/
def fixed (msg: String): Message = new Message {
def message (context: ParserContext): String = msg
}
/** Builds a message instance for the specified
* factory function.
*/
def forContext (f: ParserContext => String): Message = new Message {
def message (context: ParserContext): String = f(context)
}
/** Builds a factory function that produces new messages
* based on some arbitrary input type. This allows
* to pre-capture some context for the message that
* does not relate to the parser context.
*/
def forRuntimeValue[T] (f: T => String): T => Message = new MessageFactory(f)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy