bindingparsers.scala

来自「JAVA 语言的函数式编程扩展」· SCALA 代码 · 共 144 行

SCALA
144
字号
/*                     __                                               *\**     ________ ___   / /  ___     Scala API                            ****    / __/ __// _ | / /  / _ |    (c) 2006-2007, LAMP/EPFL             ****  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **** /____/\___/_/ |_/____/_/ | |                                         ****                          |/                                          **\*                                                                      */// $Id: BindingParsers.scala 14416 2008-03-19 01:17:25Z mihaylov $package scala.util.parsing.combinatorold.syntacticalimport scala.util.parsing.ast._//DISCLAIMER: this code is highly experimental!  /** <p> *    This component augments the generic parsers with support for variable binding. *  </p> *  <p> *    Use <code>bind</code> to decorate a parser that parses a binder (e.g., *    the name of a local variable or an argument name in a list of formal *    arguments): besides the parser, it requires a fresh <code>Binder</code> *    object, which serves as a container for one or more binders with the same *    scope. The result of the parser is added to the binder's elements. Note *    that semantic equality (<code>equals</code>) is used to link a binder to *    its bound occurrences (along with its scope, of course). *  </p> *  <p> *    For example, here's how you'd write a parser (<code>p</code>) for a let *    construct (assuming <code>b: Binder[Name]</code>): *  </p><pre> *   "val" ~! bind(name, b) ~ ":" ~ typeP ~ "=" ~ term ~ "in" ~ in(term, b),</pre> *  <p> *    This can be read as ``The parser that matches <code>val</code> (and then *    does not back-track anymore), a name -- which represents a binder we'll *    call <code>b</code> -- a colon, a type, an equals sign, a term, the *    keyword <code>in</code> and finally a term where `b' is in scope.'' *  </p> *  <p> *    The result of this parser is a nested tuple of depth 3, containing a *    Type, a <code>Term</code> and an <code>UnderBinder[Name, Term]</code>. *    Note that the binder itself is discarded (the <code>UnderBinder</code> *    keeps track of it). *  </p> *  <p> *    <code>newScope</code> makes an empty scope so that you can use *    <code>into</code> to pass it to a function that makes a parser *    whose bound variables end up in this scope: *    In our example, it would be used like this (with <code>b</code> free *    in <code>p</code>): *  </p><pre> *    newScope[Name] into { b => p }</pre> *  <p> *    Finally, <code>bound(p)</code> constructs a parser that checks that the *    result of <code>p</code> is bound by some binder <code>b</code> (i.e., *    <code>b</code> has an element which <code>equals</code> the result of *    <code>p</code>) in the current scope (as delineated by  *    <code>in(scopeP, b)</code>, where <code>p</code> is called during *    `scopeP'). If scoping is indeed respected, <code>bound(p)</code>  *    wraps the result of <code>p</code> in a <code>BoundElement</code>. *  </p> * * @author Adriaan Moors */trait BindingParsers extends Parsers with Binders {  /** A shortcut for `success(new Scope[t])'   *    * Typically used in combination with the `into' combiner as follows:   * <pre>newScope[Name] into { b =>    *    "val" ~! bind(name, b) ~ ":" ~ typeP ~ "=" ~ term ~ "in" ~ in(term, b)}</pre>   */  def newScope[T <: NameElement] = success(new Scope[T])  def nested[T <: NameElement](s: Scope[T]) = success(s.nested)  // TODO: make `bind' and `in' methods of Scope?    /** Generate a UnitParser that parses a binder   *   * The result of `binderParser' (a binder) will be added to the binder container `binder',   * so that `b' can later be used to refer to the binder parsed by `binderParser' (e.g., in the    *  `in' combinator)   *   * @param binderParser a parser that parses a binder (e.g., a variable name)   * @param scope        a scope that will contain the parsed binder   * @return a parser with the same behaviour as `binderParser', except that its result will be    *          added to `scope' and not returned.   */   def bind[bt <: NameElement](binderParser: Parser[bt], scope: Scope[bt]) = new UnitParser {    def apply(in: Input): ParseResult[Unit] = {       binderParser(in).map(x => scope.addBinder(x))    }  }     /** <p>   *    Parse something that is in the scope of the given binders.   *  </p>   *  <p>   *    During the execution of <code>scopeParser</code>, the binders in   *    <code>binder</code> are active: see <code>bound</code> for more   *    information. The result of the decorated parser is wrapped in an   *    <code>UnderBinder</code>.   *  </p>   *   * @param scopeParser the parser that parses something that is in the scope of `binder'   * @param binder      a container of binders, typically populated by `bind'   * @return a parser that has the same behaviour as `scopeParser', but whose result is wrapped    *          in an `UnderBinder'   */  def in[scopeT <% Mappable[scopeT], bt <: NameElement ](scopeParser: Parser[scopeT], scope: Scope[bt]) = new Parser[UnderBinder[bt, scopeT]] {    def apply(in: Input): ParseResult[UnderBinder[bt, scopeT]] = inScope(scope){      scopeParser(in).map(x => UnderBinder(scope, x))    }  }     /** A parser that checks that there are no unbound variables.   *   * `bound(p)' succeeds if the element parsed by p is bound by an active binder (see `in')   *    * @param boundElementParser a parser that parses an element that must be bound   * @return a parser that succeeds if the element parsed by `boundElementParser' was bound,   *          wrapping its result in a `BoundElement'   */  def bound[bt <: NameElement](boundElementParser: Parser[bt]) =     boundElementParser ^? ({case x: NameElement if !findScope(x).isEmpty => BoundElement(x, findScope(x).get)}, (x: bt) => """Unbound variable `"""+x+"""'""")     private var binderEnv: BinderEnv = EmptyBinderEnv   protected def inScope[bt <: NameElement, res](scope: Scope[bt])(block: => res) :res = {    val oldEnv = binderEnv // save old environment        // bring binders in the scope in scope    for(val b <- scope) binderEnv = binderEnv.extend(b, scope)        // return the result of running block (in which these binders are in scope)    // before returning, the binderEnv is restored to its old value    return_{scope.onEnter; block} andDo {scope.onLeft; binderEnv = oldEnv}  }  protected def findScope[bt <: NameElement](x: bt): Option[Scope[bt]] = binderEnv(x)}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?