diff options
author | Paul Phillips <paulp@improving.org> | 2009-09-21 04:01:11 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2009-09-21 04:01:11 +0000 |
commit | ced5ee337f45d0209ec3e7c69a6e04e956257ec0 (patch) | |
tree | 2cea532438b8498abcc10fc03998b3863e9287a9 /src/compiler | |
parent | 6255db9edc682351934a3a45c703798ae4423e0f (diff) | |
download | scala-ced5ee337f45d0209ec3e7c69a6e04e956257ec0.tar.gz scala-ced5ee337f45d0209ec3e7c69a6e04e956257ec0.tar.bz2 scala-ced5ee337f45d0209ec3e7c69a6e04e956257ec0.zip |
Attention drawn toward MarkupParsers, I found I...
Attention drawn toward MarkupParsers, I found I could not look away.
Mostly finished giving it a nice veneer of sanity.
Diffstat (limited to 'src/compiler')
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala | 587 |
1 files changed, 260 insertions, 327 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala index 049967f64d..a11bf77b03 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala @@ -8,18 +8,33 @@ package scala.tools.nsc package ast.parser import scala.collection.mutable +import mutable.{ Buffer, ArrayBuffer, ListBuffer, HashMap } import scala.util.control.ControlException import scala.tools.nsc.util.{Position,NoPosition,SourceFile,CharArrayReader} import scala.xml.{Text, TextBuffer} import SourceFile.{SU,LF} import scala.annotation.switch +// XXX/Note: many/most of the functions in here are almost direct cut and pastes +// from another file - scala.xml.parsing.MarkupParser, it looks like. +// (It was like that when I got here.) They used to be commented "[Duplicate]" but +// since approximately all of them were, I snipped it as noise. As far as I can +// tell this wasn't for any particularly good reason, but slightly different +// compiler and library parser interfaces meant it would take some setup. +// +// I rewrote most of these, but not as yet the library versions: so if you are +// tempted to touch any of these, please be aware of that situation and try not +// to let it get any worse. -- paulp + /** This trait ... * * @author Burak Emir * @version 1.0 */ -trait MarkupParsers {self: Parsers => +trait MarkupParsers +{ + self: Parsers => + case object MissingEndTagException extends RuntimeException with ControlException { override def getMessage = "start tag was here: " } @@ -34,42 +49,62 @@ trait MarkupParsers {self: Parsers => import global._ - class MarkupParser(parser: UnitParser, presWS: Boolean) /*with scala.xml.parsing.MarkupParser[Tree,Tree] */{ + class MarkupParser(parser: UnitParser, final val preserveWS: Boolean) { - import Tokens.{EMPTY, LBRACE, RBRACE} + import Tokens.{ EMPTY, LBRACE, RBRACE } - final val preserveWS = presWS var input : CharArrayReader = _ - import parser.{symbXMLBuilder => handle, o2p, r2p} + import parser.{ symbXMLBuilder => handle, o2p, r2p } def curOffset : Int = input.charOffset - 1 var tmppos : Position = NoPosition def ch = input.ch /** this method assign the next character to ch and advances in input */ - def nextch = { val result = input.ch; input.nextChar(); result } // { s.in.next; /*s.xNext;*/ ch = s.in.ch ; pos = s.in.cpos } + def nextch = { val result = input.ch; input.nextChar(); result } var xEmbeddedBlock = false + /** Execute body with a variable saved and restored after execution */ + def saving[A,B](getter: A, setter: (A) => Unit)(body: => B): B = { + val saved = getter + try body + finally setter(saved) + } + /** munch expected XML token, report syntax error for unexpected. * * @param that ... */ - /*[Duplicate]*/ def xToken(that: Char): Unit = if (ch == that) nextch else if (ch == SU) throw TruncatedXML - else reportSyntaxError("'" + that + "' expected instead of '" + ch + "'") + else reportSyntaxError("'%s' expected instead of '%s'".format(that, ch)) - var debugLastStartElement = new mutable.Stack[(Int, String)] + private var debugLastStartElement = new mutable.Stack[(Int, String)] + private def debugLastPos = debugLastStartElement.top._1 + private def debugLastElem = debugLastStartElement.top._2 + private def unreachable = Predef.error("Cannot be reached.") + + private def errorBraces() = { + reportSyntaxError("in XML content, please use '}}' to express '}'") + throw ConfusedAboutBracesException + } + private def errorNoEnd(tag: String) = { + reportSyntaxError("expected closing tag of " + tag) + throw MissingEndTagException + } + private def errorAndResult[T](msg: String, x: T): T = { + reportSyntaxError(msg) + x + } /** checks whether next character starts a Scala block, if yes, skip it. * @return true if next character starts a scala block */ - /*[Duplicate]*/ def xCheckEmbeddedBlock: Boolean = { + def xCheckEmbeddedBlock: Boolean = { // attentions, side-effect, used in xText xEmbeddedBlock = (ch == '{') && { nextch; (ch != '{') } - //Console.println("pos = "+pos+" xEmbeddedBlock returns "+xEmbeddedBlock) xEmbeddedBlock } @@ -79,25 +114,25 @@ trait MarkupParsers {self: Parsers => * | `"` { _ } `"` * | `{` scalablock `}` */ - /*[Duplicate]*/ def xAttributes = { - var aMap = new mutable.HashMap[String, Tree]() + def xAttributes = { + val aMap = new HashMap[String, Tree]() + while (xml.Parsing.isNameStart(ch)) { val start = curOffset val key = xName xEQ val delim = ch val mid = curOffset - val value: /* AttribValue[*/Tree/*]*/ = ch match { + val value: Tree = ch match { case '"' | '\'' => nextch val tmp = xAttributeValue(delim) nextch - try { - handle.parseAttribute(r2p(start, mid, curOffset), tmp) - } catch { - case e => - reportSyntaxError("error parsing attribute value") - parser.errorTermTree + + try handle.parseAttribute(r2p(start, mid, curOffset), tmp) + catch { + case e: RuntimeException => + errorAndResult("error parsing attribute value", parser.errorTermTree) } case '{' => @@ -106,18 +141,15 @@ trait MarkupParsers {self: Parsers => case SU => throw TruncatedXML case _ => - reportSyntaxError("' or \" delimited attribute value" + - " or '{' scala-expr '}' expected" ) - Literal(Constant("<syntax-error>")) + errorAndResult("' or \" delimited attribute value or '{' scala-expr '}' expected", Literal(Constant("<syntax-error>"))) } // well-formedness constraint: unique attribute names - if (aMap.contains(key)) { - reportSyntaxError( "attribute "+key+" may only be defined once" ) - } - aMap.update(key, value) - if ((ch != '/') && (ch != '>')) { + if (aMap contains key) + reportSyntaxError("attribute %s may only be defined once" format key) + + aMap(key) = value + if (ch != '/' && ch != '>') xSpace - } } aMap } @@ -125,52 +157,45 @@ trait MarkupParsers {self: Parsers => /** attribute value, terminated by either ' or ". value may not contain <. * @param endch either ' or " */ - /*[Duplicate]*/ def xAttributeValue(endCh: Char): String = { + def xAttributeValue(endCh: Char): String = { val buf = new StringBuilder while (ch != endCh) { - if (ch == SU) - throw TruncatedXML - buf append ch - nextch + // well-formedness constraint + if (ch == '<') return errorAndResult("'<' not allowed in attrib value", "") + else if (ch == SU) throw TruncatedXML + else buf append nextch } - val str = buf.toString() - // @todo: normalize attribute value - // well-formedness constraint - if (str.indexOf('<') != -1) { - reportSyntaxError( "'<' not allowed in attrib value" ); "" - } else { - str - } + buf.toString } /** parse a start or empty tag. * [40] STag ::= '<' Name { S Attribute } [S] * [44] EmptyElemTag ::= '<' Name { S Attribute } [S] */ - /*[Duplicate]*/ def xTag: (String, mutable.Map[String, Tree]) = { + def xTag: (String, mutable.Map[String, Tree]) = { val elemName = xName xSpaceOpt - val aMap = - if (xml.Parsing.isNameStart(ch)) xAttributes - else new mutable.HashMap[String, Tree]() - (elemName, aMap) + + (elemName, xAttributes) } /** [42] '<' xmlEndTag ::= '<' '/' Name S? '>' */ - /*[Duplicate]*/ def xEndTag(startName: String) { xToken('/') - val endName = xName - if (endName != startName) { - reportSyntaxError("expected closing tag of " + startName) - throw MissingEndTagException - } + if (xName != startName) + errorNoEnd(startName) + xSpaceOpt xToken('>') } + /** Create a non-destructive lookahead reader and see if the head + * of the input would match the given String. If yes, return true + * and drop the entire String from input; if no, return false + * and leave input unchanged. + */ private def peek(lookingFor: String): Boolean = { val la = input.lookaheadReader for (c <- lookingFor) { @@ -183,43 +208,45 @@ trait MarkupParsers {self: Parsers => true } - /** '<! CharData ::= [CDATA[ ( {char} - {char}"]]>"{char} ) ']]>' - * - * see [15] + /** Take characters from input stream until given String "until" + * is seen. Once seen, the accumulated characters are passed + * along with the current Position to the supplied handler function. */ - /*[Duplicate]*/ - def xCharData: Tree = { - val start = curOffset - "[CDATA[" foreach xToken - val mid = curOffset + private def xTakeUntil[T]( + handler: (Position, String) => T, + positioner: () => Position, + until: String): T = + { val sb = new StringBuilder + val head = until charAt 0 + val rest = until drop 1 while (true) { - if (ch == ']' && peek("]>")) - return handle.charData(r2p(start, mid, curOffset), sb.toString) + if (ch == head && peek(rest)) + return handler(positioner(), sb.toString) else if (ch == SU) throw TruncatedXML sb append ch nextch } - Predef.error("this cannot happen") + unreachable } - def xUnparsed: Tree = { + /** '<! CharData ::= [CDATA[ ( {char} - {char}"]]>"{char} ) ']]>' + * + * see [15] + */ + def xCharData: Tree = { val start = curOffset - val sb = new StringBuilder - - while (true) { - if (ch == '<' && peek("/xml:unparsed>")) - return handle.unparsed(r2p(start, start, curOffset), sb.toString) - else if (ch == SU) - throw TruncatedXML + "[CDATA[" foreach xToken + val mid = curOffset + xTakeUntil(handle.charData, () => r2p(start, mid, curOffset), "]]>") + } - sb append ch - nextch - } - Predef.error("this cannot happen") + def xUnparsed: Tree = { + val start = curOffset + xTakeUntil(handle.unparsed, () => r2p(start, start, curOffset), "</xml:unparsed>") } /** CharRef ::= "&#" '0'..'9' {'0'..'9'} ";" @@ -227,7 +254,6 @@ trait MarkupParsers {self: Parsers => * * see [66] */ - /*[Duplicate]*/ def xCharRef: String = { val hex = (ch == 'x') && { nextch; true } val base = if (hex) 16 else 10 @@ -257,31 +283,13 @@ trait MarkupParsers {self: Parsers => * * see [15] */ - /*[Duplicate]*/ def xComment: Tree = { val start = curOffset - 2 // Rewinding to include "<!" - val sb = new StringBuilder "--" foreach xToken - - while (true) { - if (ch == '-' && peek("->")) - return handle.comment(r2p(start, start, curOffset), sb.toString) - else if (ch == SU) - throw TruncatedXML - - sb append ch - nextch - } - Predef.error("this cannot happen") + xTakeUntil(handle.comment, () => r2p(start, start, curOffset), "-->") } - /** - * @param pos ... - * @param ts ... - * @param txt ... - */ - /*[Duplicate]*/ - def appendText(pos: Position, ts: mutable.Buffer[Tree], txt: String) { + def appendText(pos: Position, ts: Buffer[Tree], txt: String) { val toAppend = if (preserveWS) Seq(txt) else TextBuffer.fromString(txt).toText map (_.text) @@ -289,29 +297,31 @@ trait MarkupParsers {self: Parsers => toAppend foreach (t => ts append handle.text(pos, t)) } - /** adds entity/character to to ts as side-effect + /** adds entity/character to ts as side-effect * @precond ch == '&' */ - def content_AMP(ts: mutable.ArrayBuffer[Tree]) { + def content_AMP(ts: ArrayBuffer[Tree]) { nextch - ch match { + val toAppend = ch match { case '#' => // CharacterRef nextch val theChar = handle.text(tmppos, xCharRef) xToken(';') - ts.append(theChar) - case _ => // EntityRef + theChar + case _ => // EntityRef val n = xName xToken(';') - ts.append(handle.entityRef(tmppos, n)) + handle.entityRef(tmppos, n) } + + ts append toAppend } /** * @precond ch == '{' * @postcond: xEmbeddedBlock == false! */ - def content_BRACE(p: Position, ts: mutable.ArrayBuffer[Tree]): Unit = + def content_BRACE(p: Position, ts: ArrayBuffer[Tree]): Unit = if (xCheckEmbeddedBlock) ts append xEmbeddedExpr else appendText(p, ts, xText) @@ -321,7 +331,7 @@ trait MarkupParsers {self: Parsers => * @param ts ... * @return ... */ - private def content_LT(ts: mutable.ArrayBuffer[Tree]): Boolean = { + private def content_LT(ts: ArrayBuffer[Tree]): Boolean = { if (ch == '/') return true // end tag @@ -335,44 +345,38 @@ trait MarkupParsers {self: Parsers => false } - /*[Duplicate]*/ - def content: mutable.Buffer[Tree] = { - var ts = new mutable.ArrayBuffer[Tree] - var exit = false - while (!exit) { + def content: Buffer[Tree] = { + val ts = new ArrayBuffer[Tree] + while (true) { if (xEmbeddedBlock) - ts.append(xEmbeddedExpr) + ts append xEmbeddedExpr else { tmppos = o2p(curOffset) ch match { - case '<' => // end tag, cdata, comment, pi or child node - nextch - exit = content_LT(ts) - case '{' => // either the character '{' or an embedded scala block - content_BRACE(tmppos, ts) - case '&' => // EntityRef or CharRef - content_AMP(ts) - case SU => - exit = true - case _ => // text content - appendText(tmppos, ts, xText) - // here xEmbeddedBlock might be true + // end tag, cdata, comment, pi or child node + case '<' => nextch ; if (content_LT(ts)) return ts + // either the character '{' or an embedded scala block } + case '{' => content_BRACE(tmppos, ts) // } + // EntityRef or CharRef + case '&' => content_AMP(ts) + case SU => return ts + // text content - here xEmbeddedBlock might be true + case _ => appendText(tmppos, ts, xText) } } } - ts + unreachable } /** '<' element ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag * | xmlTag1 '/' '>' */ - /*[Duplicate]*/ def element: Tree = { + def element: Tree = { val start = curOffset val (qname, attrMap) = xTag if (ch == '/') { // empty element - xToken('/') - xToken('>') - handle.element(r2p(start, start, curOffset), qname, attrMap, new mutable.ListBuffer[Tree]) + "/>" foreach xToken + handle.element(r2p(start, start, curOffset), qname, attrMap, new ListBuffer[Tree]) } else { // handle content xToken('>') @@ -399,195 +403,152 @@ trait MarkupParsers {self: Parsers => * pre-condition: ch != ':' // assured by definition of XMLSTART token * post-condition: name does neither start, nor end in ':' */ - /*[Duplicate]*/ def xName: String = { - if (ch == SU) { - throw TruncatedXML - } else if ( !xml.Parsing.isNameStart(ch)) { - reportSyntaxError("name expected, but char '"+ch+"' cannot start a name") - return "" - } + def xName: String = { + if (ch == SU) throw TruncatedXML + else if (!xml.Parsing.isNameStart(ch)) + return errorAndResult("name expected, but char '%s' cannot start a name" format ch, "") + val buf = new StringBuilder - do { - buf append ch; nextch - } while (xml.Parsing.isNameChar(ch)) - if (':' == buf.last) { + + do buf append nextch + while (xml.Parsing.isNameChar(ch)) + + if (buf.last == ':') { reportSyntaxError( "name cannot end in ':'" ) - buf.setLength(buf.length - 1) + buf setLength (buf.length - 1) } - val n = buf.toString().intern() - //cbuf.length = 0 - n + buf.toString.intern } /** scan [S] '=' [S]*/ - /*[Duplicate]*/ def xEQ = { xSpaceOpt; xToken('='); xSpaceOpt } + def xEQ = { xSpaceOpt; xToken('='); xSpaceOpt } /** skip optional space S? */ - /*[Duplicate]*/ def xSpaceOpt = { while (xml.Parsing.isSpace(ch)) { nextch }} + def xSpaceOpt = { while (xml.Parsing.isSpace(ch)) { nextch }} /** scan [3] S ::= (#x20 | #x9 | #xD | #xA)+ */ - /*[Duplicate]*/ def xSpace = + def xSpace = if (xml.Parsing.isSpace(ch)) { nextch; xSpaceOpt } - else if (ch == SU) - throw TruncatedXML + else if (ch == SU) throw TruncatedXML else reportSyntaxError("whitespace expected") /** '<?' ProcInstr ::= Name [S ({Char} - ({Char}'>?' {Char})]'?>' * * see [15] */ - /*[Duplicate]*/ def xProcInstr: Tree = { - val sb = new StringBuilder() + // <?xml2 version="1.0" encoding="UTF-8" standalone="yes"?> + def xProcInstr: Tree = { val n = xName - - if (xml.Parsing.isSpace(ch)) { - xSpace - while (true) { - if (ch == '?' && { sb.append(ch); nextch; ch == '>' }) { - sb.length = sb.length - 1 - nextch - return handle.procInstr(tmppos, n, sb.toString) - } else - sb.append(ch); - nextch - } - } - xToken('?') - xToken('>') - handle.procInstr(tmppos, n, sb.toString) + xSpaceOpt + xTakeUntil(handle.procInstr(_: Position, n, _:String), () => tmppos, "?>") } /** parse character data. * precondition: xEmbeddedBlock == false (we are not in a scala block) */ - /*[Duplicate]*/ def xText: String = { - if (xEmbeddedBlock) Predef.error("internal error: encountered embedded block"); // assert - var exit = false + def xText: String = { + assert(!xEmbeddedBlock, "internal error: encountered embedded block") val buf = new StringBuilder - while (!exit && (ch!=SU)) { - val expectRBRACE = ch == '}' - if (expectRBRACE) { - val la = input.lookaheadReader - la.nextChar() - if (la.ch != '}') { - reportSyntaxError("in XML content, please use '}}' to express '}'") - throw ConfusedAboutBracesException - } - nextch + def done = buf.toString + + while (ch != SU) { + if (ch == '}') { + if (charComingAfter(nextch) == '}') nextch + else errorBraces } + buf append ch nextch - exit = xCheckEmbeddedBlock ||(ch == '<') || (ch == '&') + if (xCheckEmbeddedBlock || ch == '<' || ch == '&') + return done } - buf.toString + done } - /** append Unicode character to name buffer*/ - //private def putChar(c: char) = cbuf.append(c) + /** Some try/catch/finally logic used by xLiteral and xLiteralPattern. */ + private def xLiteralCommon(f: () => Tree, ifTruncated: Exception => Unit): Tree = + try f() + catch { + case ex: RuntimeException => + ex match { + case c @ TruncatedXML => + ifTruncated(c) + case c @ (MissingEndTagException | ConfusedAboutBracesException) => + parser.syntaxError(debugLastPos, c.getMessage + debugLastElem + ">") + case _: ArrayIndexOutOfBoundsException => + parser.syntaxError(debugLastPos, "missing end tag in XML literal for <%s>" format debugLastElem) + case _ => throw ex + } + EmptyTree + } + finally parser.in resume Tokens.XMLSTART + + /** Use a lookahead parser to run speculative body, and return the first char afterward. */ + private def charComingAfter(body: => Unit): Char = { + input = input.lookaheadReader + body + val res = ch + input = parser.in + res + } /** xLiteral = element { element } * @return Scala representation of this xml literal - * precondition: s.xStartsXML == true */ - def xLiteral: Tree = try { - input = parser.in - handle.isPattern = false - - //val pos = s.currentPos - var tree:Tree = null - val ts = new mutable.ArrayBuffer[Tree]() - val start = curOffset - tmppos = o2p(curOffset) // Iuli: added this line, as it seems content_LT uses tmppos when creating trees -// assert(ch == '<') -// nextch - content_LT(ts) - //Console.println("xLiteral:ts = "+ts.toList) - //lastend = s.in.bp - //lastch = s.in.ch - //if (settings.debug.value) { - // Console.println("DEBUG 1: I am getting char '"+ch+"' at lastend "+lastend+" pos = "+pos); // DEBUG - //} - input = input.lookaheadReader - xSpaceOpt - // parse more XML ? - if (ch == '<') { + def xLiteral: Tree = xLiteralCommon( + () => { input = parser.in - xSpaceOpt - while (ch == '<') { - nextch - ts.append(element) + handle.isPattern = false + + val ts = new ArrayBuffer[Tree] + val start = curOffset + tmppos = o2p(curOffset) // Iuli: added this line, as it seems content_LT uses tmppos when creating trees + content_LT(ts) + + // parse more XML ? + if (charComingAfter(xSpaceOpt) == '<') { xSpaceOpt + while (ch == '<') { + nextch + ts append element + xSpaceOpt + } + handle.makeXMLseq(r2p(start, start, curOffset), ts) } - tree = handle.makeXMLseq(r2p(start, start, curOffset), ts) - } else { - input = parser.in - assert(ts.length == 1) - tree = ts(0) - } - tree - } catch { - case c @ TruncatedXML => - parser.incompleteInputError(c.getMessage) - EmptyTree - - case c @ (MissingEndTagException | ConfusedAboutBracesException) => - parser.syntaxError((debugLastStartElement.top._1):Int, - c.getMessage + debugLastStartElement.top._2+">") - EmptyTree - - case _:ArrayIndexOutOfBoundsException => - parser.syntaxError((debugLastStartElement.top._1), - "missing end tag in XML literal for <" - +debugLastStartElement.top._2+">"); - EmptyTree - } finally { - parser.in.resume(Tokens.XMLSTART) - } + else { + assert(ts.length == 1) + ts(0) + } + }, + ex => parser.incompleteInputError(ex.getMessage) + ) /** @see xmlPattern. resynchronizes after successful parse * @return this xml pattern - * precondition: s.xStartsXML == true */ - def xLiteralPattern: Tree = try { - input = parser.in - val oldMode = handle.isPattern; - handle.isPattern = true -// assert(ch == '<') -// nextch - var tree = xPattern; xSpaceOpt; - handle.isPattern = oldMode; - tree - } catch { - case c @ TruncatedXML => - parser.syntaxError(curOffset, c.getMessage) - EmptyTree - - case c @ (MissingEndTagException | ConfusedAboutBracesException) => - parser.syntaxError((debugLastStartElement.top._1), - c.getMessage + debugLastStartElement.top._2+">") - EmptyTree - - case _:ArrayIndexOutOfBoundsException => - parser.syntaxError((debugLastStartElement.top._1), - "missing end tag in XML literal for <" - +debugLastStartElement.top._2+">") - EmptyTree - } finally { - parser.in.resume(Tokens.XMLSTART) - } + def xLiteralPattern: Tree = xLiteralCommon( + () => { + input = parser.in + saving[Boolean, Tree](handle.isPattern, handle.isPattern = _) { + handle.isPattern = true + val tree = xPattern + xSpaceOpt + tree + } + }, + ex => parser.syntaxError(curOffset, ex.getMessage) + ) def escapeToScala[A](op: => A, kind: String) = { xEmbeddedBlock = false - val savedSepRegions = parser.in.sepRegions - parser.in.resume(LBRACE) - - try op - finally { - parser.in.sepRegions = savedSepRegions // parser.in.sepRegions.tail - if (parser.in.token != RBRACE) { - reportSyntaxError(" expected end of Scala "+kind) - } + val res = saving[List[Int], A](parser.in.sepRegions, parser.in.sepRegions = _) { + parser.in resume LBRACE + op } + if (parser.in.token != RBRACE) + reportSyntaxError(" expected end of Scala "+kind) + + res } def xEmbeddedExpr: Tree = escapeToScala(parser.block(), "block") @@ -596,35 +557,11 @@ trait MarkupParsers {self: Parsers => */ def xScalaPatterns: List[Tree] = escapeToScala(parser.patterns(true), "pattern") - var scannerState: List[List[Int]] = Nil - -/* - private def pushScannerState { - scannerState = s.sepRegions :: scannerState - s.sepRegions = Nil - } - private def popScannerState { - s.sepRegions = scannerState.head - scannerState = scannerState.tail - } - */ -/* - private def init { - ch = s.in.ch - curOffset = s.in.ccurOffset - } - */ - def reportSyntaxError(str: String) = { parser.syntaxError(curOffset, "in XML literal: " + str) nextch } -/* - private def sync { - xEmbeddedBlock = false - s.xSync - } -*/ + /** '<' xPattern ::= Name [S] { xmlPattern | '{' pattern3 '}' } ETag * | Name [S] '/' '>' */ @@ -633,50 +570,46 @@ trait MarkupParsers {self: Parsers => val qname = xName debugLastStartElement.push((start, qname)) xSpaceOpt - if (ch == '/') { // empty tag - nextch - xToken('>') - return handle.makeXMLpat(r2p(start, start, curOffset), qname, new mutable.ArrayBuffer[Tree]()) - } - // else: tag with content + val ts = new ArrayBuffer[Tree] + val isEmptyTag = (ch == '/') && { nextch ; true } xToken('>') - var ts = new mutable.ArrayBuffer[Tree] - var exit = false - while (! exit) { - val start1 = curOffset - if (xEmbeddedBlock) { - ts ++= xScalaPatterns - } else - ch match { - case '<' => // tag + + if (!isEmptyTag) { + // recurses until it hits a termination condition, then returns + def doPattern: Boolean = { + val start1 = curOffset + if (xEmbeddedBlock) ts ++= xScalaPatterns + else ch match { + case '<' => // tag nextch - if (ch != '/') { //child - ts.append(xPattern) - } else { - exit = true - } - case '{' => // embedded Scala patterns + if (ch != '/') ts append xPattern // child + else return false // terminate + + case '{' => // embedded Scala patterns while (ch == '{') { nextch ts ++= xScalaPatterns } - // postcond: xEmbeddedBlock = false; - if (xEmbeddedBlock) Predef.error("problem with embedded block"); // assert + assert(!xEmbeddedBlock, "problem with embedded block") - case SU => + case SU => throw TruncatedXML - case _ => // text + case _ => // text appendText(r2p(start1, start1, curOffset), ts, xText) - // here xEmbeddedBlock might be true; - //if( xEmbeddedBlock ) throw new ApplicationError("after:"+text); // assert + // here xEmbeddedBlock might be true: + // if (xEmbeddedBlock) throw new ApplicationError("after:"+text); // assert } + true + } + + while (doPattern) { } // call until false + xEndTag(qname) + debugLastStartElement.pop } - xEndTag(qname) - debugLastStartElement.pop + handle.makeXMLpat(r2p(start, start, curOffset), qname, ts) } - } /* class MarkupParser */ } |