parsers.scala
来自「JAVA 语言的函数式编程扩展」· SCALA 代码 · 共 961 行 · 第 1/3 页
SCALA
961 行
/* __ *\** ________ ___ / / ___ Scala API **** / __/ __// _ | / / / _ | (c) 2006-2007, LAMP/EPFL **** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **** /____/\___/_/ |_/____/_/ | | **** |/ **\* */// $Id: Parsers.scala 13912 2008-02-07 12:40:59Z moors $package scala.util.parsing.combinatoroldimport scala.util.parsing.input._import scala.collection.mutable.{Map=>MutableMap}// TODO: better error handling (labelling like parsec's <?>)// TODO: memoisation (like packrat parsers?) /** <p> * <code>Parsers</code> is a component that <i>provides</i> generic * parser combinators. * </p> * <p> * It <i>requires</i> the type of the elements these parsers should parse * (each parser is polymorphic in the type of result it produces). * </p> * <p> * There are two aspects to the result of a parser: (1) success or failure, * and (2) the result. A <code>Parser[T]</code> provides both kinds of * information, but a <code>UnitParser</code> only signals success/failure. * When composing a `UnitParser' with a normal <code>Parser</code>, the * <code>UnitParser</code> only contributes to whether the combined parser * is successful (i.e., its result is discarded). * </p> * <p> * The term ``parser combinator'' refers to the fact that these parsers * are constructed from primitive parsers and composition operators, such * as sequencing, alternation, optionality, repetition, lifting, and so on. * </p> * <p> * A ``primitive parser'' is a parser that accepts or rejects a single * piece of input, based on a certain criterion, such as whether the * input... * </p><ul> * <li> is equal to some given object, </li> * <li> satisfies a certain predicate, </li> * <li> is in the domain of a given partial function,.... </li> * </ul> * <p> * Even more primitive parsers always produce the same result, irrespective * of the input. * </p> * * @requires Elem the type of elements the provided parsers consume * (When consuming invidual characters, a parser is typically called a ``scanner'', * which produces ``tokens'' that are consumed by what is normally called a ``parser''. * Nonetheless, the same principles apply, regardless of the input type.)</p> *<p> * @provides Input = Reader[Elem] * The type of input the parsers in this component expect.</p> *<p> * @provides Parser[+T] extends (Input => ParseResult[T]) * Essentially, a `Parser[T]' is a function from `Input' to `ParseResult[T]'.</p> *<p> * @provides ParseResult[+T] is like an `Option[T]', in the sense that it is either * `Success[T]', which consists of some result (:T) (and the rest of the input) or * `Failure[T]', which provides an error message (and the rest of the input).</p> * * @author Martin Odersky, Iulian Dragos, Adriaan Moors */trait Parsers { /** the type of input elements */ type Elem /** The parser input is an abstract reader of input elements */ type Input = Reader[Elem] /** A base class for parser results. * A result is either successful or not (failure may be fatal, i.e., * an Error, or not, i.e., a Failure) * On success, provides a result of type <code>T</code>. */ sealed abstract class ParseResult[+T] { /** Functional composition of ParseResults * * @param `f' the function to be lifted over this result * @return `f' applied to the result of this `ParseResult', packaged up as a new `ParseResult' */ def map[U](f: T => U): ParseResult[U] /** Partial functional composition of ParseResults * * @param `f' the partial function to be lifted over this result * @param error a function that takes the same argument as `f' and produces an error message * to explain why `f' wasn't applicable (it is called when this is the case) * @return <i>if `f' f is defined at the result in this `ParseResult',</i> * `f' applied to the result of this `ParseResult', packaged up as a new `ParseResult'. * If `f' is not defined, `Failure'. */ def mapPartial[U](f: PartialFunction[T, U], error: T => String): ParseResult[U] def isEmpty = !successful /** Returns the embedded result */ def get: T def getOrElse[B >: T](default: => B): B = if (isEmpty) default else this.get val next: Input val successful: Boolean } /** The success case of ParseResult: contains the result and the remaining input. * * @param result The parser's output * @param next The parser's remaining input */ case class Success[+T](result: T, override val next: Input) extends ParseResult[T] { def map[U](f: T => U) = Success(f(result), next) def mapPartial[U](f: PartialFunction[T, U], error: T => String): ParseResult[U] = if(f.isDefinedAt(result)) Success(f(result), next) else Failure(error(result), next) def get: T = result /** The toString method of a Success */ override def toString = "["+next.pos+"] parsed: "+result val successful = true } /** A common super-class for unsuccessful parse results */ sealed abstract class NoSuccess(val msg: String, override val next: Input) extends ParseResult[Nothing] { // when we don't care about the difference between Failure and Error val successful = false def map[U](f: Nothing => U) = this def mapPartial[U](f: PartialFunction[Nothing, U], error: Nothing => String): ParseResult[U] = this def get: Nothing = error("No result when parsing failed") } /** The failure case of ParseResult: contains an error-message and the remaining input. * Parsing will back-track when a failure occurs. * * @param msg An error message string describing the failure. * @param next The parser's unconsumed input at the point where the failure occurred. */ case class Failure(override val msg: String, override val next: Input) extends NoSuccess(msg, next) { /** The toString method of a Failure yields an error message */ override def toString = "["+next.pos+"] failure: "+msg+"\n\n"+next.pos.longString } /** The fatal failure case of ParseResult: contains an error-message and the remaining input. * No back-tracking is done when a parser returns an `Error' * * @param msg An error message string describing the error. * @param next The parser's unconsumed input at the point where the error occurred. */ case class Error(override val msg: String, override val next: Input) extends NoSuccess(msg, next) { /** The toString method of an Error yields an error message */ override def toString = "["+next.pos+"] error: "+msg+"\n\n"+next.pos.longString } /** The root class of parsers. * Parsers are functions from the Input type to ParseResult */ abstract class Parser[+T] extends (Input => ParseResult[T]) { /** An unspecified method that defines the behaviour of this parser. */ def apply(in: Input): ParseResult[T] // the operator formerly known as +++, ++, &, but now, behold the venerable ~ // it's short, light (looks like whitespace), has few overloaded meaning (thanks to the recent change from ~ to unary_~) // and we love it! /** A parser combinator for sequential composition * * <p> `p ~ q' succeeds if `p' succeeds and `q' succeeds on the input * left over by `p'.</p> * * @param q a parser that will be executed after `p' (this parser) succeeds * @return a `Parser' that -- on success -- returns a `~' (like a Pair, but easier to pattern match on) * that contains the result of `p' and that of `q'. * The resulting parser fails if either `p' or `q' fails. */ def ~ [U](q: => Parser[U]): Parser[~[T, U]] = new Parser[~[T, U]] { def apply(in: Input) = seq(Parser.this, q)((x, y) => new ~(x,y))(in) override def toString = "~" } /* not really useful: V cannot be inferred because Parser is covariant in first type parameter (V is always trivially Nothing) def ~~ [U, V](q: => Parser[U])(implicit combine: (T, U) => V): Parser[V] = new Parser[V] { def apply(in: Input) = seq(Parser.this, q)((x, y) => combine(x,y))(in) } */ /** A parser combinator for sequential composition with a unit-parser * * <p> `p ~ q' succeeds if `p' succeeds and `q' succeeds on the input * left over by `p'.</p> * * @param q a parser (convertible to a UnitParser) that will be executed after `p' (this parser) * succeeds * @return a `Parser' that -- on success -- returns the result of `p'. * The resulting parser fails if either `p' or `q' fails. */ def ~ [Q <% UnitParser](q: => Q): Parser[T] = new Parser[T] { def apply(in: Input) = seq(Parser.this, q)((x, y) => x)(in) override def toString = "~" } /** A parser combinator for non-back-tracking sequential composition * *<p>`p ~! q' succeeds if `p' succeeds and `q' succeeds on the input * left over by `p'. In case of failure, no back-tracking is performed * (in an earlier parser produced by the | combinator).</p> * * @param q a parser that will be executed after `p' (this parser) succeeds * @return a `Parser' that -- on success -- returns a `~' (like a Pair, but easier to pattern match on) * that contains the result of `p' and that of `q'. * The resulting parser fails if either `p' or `q' fails, this failure is fatal. */ def ~! [U](q: => Parser[U]): Parser[~[T, U]] = new Parser[~[T, U]] with OnceParser[~[T, U]] { def apply(in: Input) = seq(Parser.this, commit(q))((x, y) => new ~(x,y))(in) override def toString = "~!" } /** A parser combinator for non-back-tracking sequential composition with a unit-parser * *<p>`p ~! q' succeeds if `p' succeeds and `q' succeeds on the input * left over by `p'. In case of failure, no back-tracking is performed * (in an earlier parser produced by the | combinator).</p> * * @param q a parser that will be executed after `p' (this parser) succeeds * @return a `Parser' that -- on success -- returns the result of `p'. * The resulting parser fails if either `p' or `q' fails, this failure is fatal. */ def ~! [Q <% UnitParser](q: => Q): Parser[T] = new Parser[T] with OnceParser[T] { def apply(in: Input) = seq(Parser.this, commit(q))((x, y) => x)(in) override def toString = "~!" } /** A parser combinator for alternative composition * *<p>`p | q' succeeds if `p' succeeds or `q' succeeds * Note that `q' is only tried if `p's failure is non-fatal (i.e., back-tracking is * allowed).</p> * * @param q a parser that will be executed if `p' (this parser) fails (and allows back-tracking) * @return a `Parser' that returns the result of the first parser to succeed (out of `p' and `q') * The resulting parser succeeds if (and only if) <ul> * <li> `p' succeeds, <i>or</i> </li> * <li> if `p' fails allowing back-tracking and `q' succeeds. </li> </ul> */ def | [U >: T](q: => Parser[U]): Parser[U] = new Parser[U] { def apply(in: Input) = Parser.this(in) match { case s1 @ Success(_, _) => s1 case e1 @ Error(_, _) => e1 case f1 @ Failure(_, next1) => q(in) match { case s2 @ Success(_, _) => s2 case f2 @ Failure(_, next2) => if (next2.pos < next1.pos) f1 else f2 case e2 @ Error(_, next2) => if (next2.pos < next1.pos) f1 else e2 } } override def toString = "|" } /** A parser combinator for alternative with longest match composition * *<p>`p ||| q' succeeds if `p' succeeds or `q' succeeds * If `p' and `q' both succeed, the parser that consumed the most * characters accepts.</p> * * @param q a parser that accepts if p consumes less characters. * @return a `Parser' that returns the result of the parser consuming the most characteres (out of `p' and `q'). */ def ||| [U >: T](q: => Parser[U]): Parser[U] = new Parser[U] { def apply(in: Input) = { val res1 = Parser.this(in) val res2 = q(in) (res1, res2) match { case (s1 @ Success(_, next1), s2 @ Success(_, next2)) => if (next2.pos < next1.pos) s1 else s2 case (s1 @ Success(_, _), _) => s1 case (_, s2 @ Success(_, _)) => s2 case (e1 @ Error(_, _), _) => e1 case (f1 @ Failure(_, next1), f2 @ Failure(_, next2)) => if (next2.pos < next1.pos) f1 else f2 case (f1 @ Failure(_, next1), e2 @ Error(_, next2)) => if (next2.pos < next1.pos) f1 else e2 } } override def toString = "|||" } /** A parser combinator for function application * *<p>`p ^^ f' succeeds if `p' succeeds; it returns `f' applied to the result of `p'.</p> * * @param f a function that will be applied to this parser's result (see `map' in `ParseResult'). * @return a parser that has the same behaviour as the current parser, but whose result is * transformed by `f'. */ def ^^ [U](f: T => U): Parser[U] = new Parser[U] { def apply(in: Input) = Parser.this(in).map(f) override def toString = Parser.this.toString+"^^" } /** A parser combinator for partial function application * *<p>`p ^? (f, error)' succeeds if `p' succeeds AND `f' is defined at the result of `p'; * in that case, it returns `f' applied to the result of `p'. If `f' is not applicable, * error(the result of `p') should explain why.</p> * * @param f a partial function that will be applied to this parser's result * (see `mapPartial' in `ParseResult'). * @param error a function that takes the same argument as `f' and produces an error message * to explain why `f' wasn't applicable * @return a parser that succeeds if the current parser succeeds <i>and</i> `f' is applicable * to the result. If so, the result will be transformed by `f'.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?