From c1c3bc8b5ad5b5ae693bce5def6abec706c90bc9 Mon Sep 17 00:00:00 2001 From: buraq Date: Fri, 5 Mar 2004 13:53:03 +0000 Subject: markup parsing --- .../tools/scalac/ast/parser/MarkupParser.scala | 235 ++++++++++++++++++++ sources/scala/tools/scalac/ast/parser/Parser.scala | 245 ++------------------- .../scala/tools/scalac/ast/parser/Scanner.scala | 163 ++++++++------ 3 files changed, 340 insertions(+), 303 deletions(-) create mode 100644 sources/scala/tools/scalac/ast/parser/MarkupParser.scala diff --git a/sources/scala/tools/scalac/ast/parser/MarkupParser.scala b/sources/scala/tools/scalac/ast/parser/MarkupParser.scala new file mode 100644 index 0000000000..88afa0c5b5 --- /dev/null +++ b/sources/scala/tools/scalac/ast/parser/MarkupParser.scala @@ -0,0 +1,235 @@ + +import scalac.ast._; +import scalac.atree.AConstant; +import scalac._; +import scalac.util._; +import ch.epfl.lamp.util.Position; +import java.util.{Map, Stack, ArrayList, LinkedList}; +import java.lang.{Integer, Long, Float, Double}; +import scala.Iterator; +import scala.tools.scalac.util.NewArray; +import scala.collection.immutable.ListMap ; +import scala.collection.mutable.Buffer; + +package scala.tools.scalac.ast.parser { + +class MarkupParser( unit:Unit, s:Scanner, p:Parser ) { + + import Tokens.{EMPTY, RBRACE} ; + import scala.tools.scalac.ast.{TreeList => myTreeList} + + /** the tree factory + */ + val make: TreeFactory = unit.global.make; + + /** the tree generator + */ + val gen: TreeGen = unit.global.treeGen; + + /** convenience method */ + def scalaDot(pos: int, name: Name): Tree = + make.Select(pos, make.Ident(pos, Names.scala), name); + + /** convenience method */ + def convertToTypeId(t: Tree): Tree = t match { + case Tree$Ident(name) => + make.Ident(t.pos, name.toTypeName()) + case Tree$Select(qual, name) => + make.Select(t.pos, qual, name.toTypeName()) + case _ => + t + } + + // create scala xml tree + + def mkXML(pos:int, isPattern:boolean, t:Tree, args:Array[Tree]):Tree = { + var symt = scalaDot(s.pos, Names.Symbol); + if( isPattern ) symt = convertToTypeId(symt); + val ts = new myTreeList(); + ts.append(t); + ts.append(args); + make.Apply(pos, symt, ts.toArray()); + } + + def makeXMLpat(pos:int, n:Name, args:Array[Tree]):Tree = + mkXML(pos, true, gen.mkStringLit( pos, n.toString() ), args); + + def makeXML(pos:int, n:Name, args:Array[Tree]):Tree = + mkXML(pos, false, gen.mkStringLit( pos, n.toString() ), args); + + def makeXMLseq( pos:int, args:Array[Tree] ) = { + make.Apply(pos, scalaDot(s.pos, Names.List), args); + } + + def makeXML(pos:int,n:Name,args:Array[Tree],attrMap:ListMap[Name,String]):Tree = { + var t = makeXML( pos, n, args ); + if( attrMap.isEmpty ) { + t + } else { + val attrs = new Array[Tree]( attrMap.size ); + val it = attrMap.elements ; + var i = 0; while( i < attrs.length ) { + val Pair( key, value ) = it.next; + attrs( i ) = make.Apply(pos, scalaDot(s.pos, Names.Tuple2), { + val x = new Array[Tree](2); + x( 0 ) = gen.mkStringLit( pos, key.toString() ); + x( 1 ) = gen.mkStringLit( pos, value.toString() ); + x }); + i = i + 1; + }; + make.Apply(pos, make.Select( pos, t, Names.PERCENT ), { + val x = new Array[Tree](1); + x( 0 ) = make.Apply(pos, + scalaDot(s.pos, Names.List), + attrs); + x }) + } + } + + /** xLiteral = xExpr { xExpr } + * @return Scala representation of this xml literal + * precondition: s.xStartsXML == true + */ + def xLiteral:Tree = { + val pos = s.pos; + var tree = xExpr; s.token = EMPTY; s.nextToken(); + if( s.xStartsXML ) { + val ts = new myTreeList(); ts.append( tree ); + while( s.xStartsXML ) { ts.append( xExpr ); s.nextToken(); } + tree = makeXMLseq( pos, ts.toArray() ); + } + tree + } + + /** parse a start or empty tag. + * [40] STag ::= '<' Name { S Attribute } [S] + * [41] Attribute ::= Name Eq AttValue + * [44] EmptyElemTag ::= '<' Name { S Attribute } [S] + */ + def xTag:Tuple2[Name, ListMap[Name,String]] = { + val elemName = s.xName; + s.xSpaceOpt; + var attrMap = ListMap.Empty[Name,String]; + while( s.xIsNameStart ) { + val attrName = s.xName; + s.xEQ; + val endch = s.ch.asInstanceOf[char]; + val attrValue = endch match { + case '"' | '\'' => + s.xNext; val tmp = s.xAttributeValue( endch ); + s.xNext; s.xSpaceOpt; tmp + case _ => + s.xSyntaxError( "' or \" delimited attribute value expected" ); + "" + } + // well-formedness constraint: unique attribute names + if( attrMap.contains( attrName )) + s.xSyntaxError( "attribute "+attrName+" may only be defined once" ); + + attrMap = attrMap.update( attrName, attrValue ); + } + Tuple2( elemName, attrMap ); + } + + /* [42] '<' xmlEndTag ::= '<' '/' Name S? '>' */ + def xEndTag( n:Name ) = { + s.xToken('/'); + if( n != s.xName ) s.xSyntaxError( "expected closing tag of " + n ); + s.xSpaceOpt; + s.xToken('>') + } + + /** '<' xExpr ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag + * | xmlTag1 '/' '>' + * the caller has to resynchronize with s.token = EMPTY; s.nextToken; + */ + def xExpr:Tree = { + val pos = s.pos; + val Tuple2( elemName, attrMap ) = xTag; + if( s.ch == '/' ) { // empty element + s.xToken('/'); s.xToken('>'); makeXML( pos, elemName, Tree.EMPTY_ARRAY ); + } else { // handle content + s.xToken('>'); s.xSpaceOpt; + + val ts = new myTreeList(); + var exit = false; + while( !exit ) { + /* Console.println("read '"+s.ch.asInstanceOf[char]+"'"); */ + s.ch match { + case '<' => // another tag + s.xNext; s.ch match { + case '/' => exit = true; // end tag + case '!' => s.xComment; + case _ => ts.append( xExpr ); // parse child + } + case '{' => // Scala block(s) + while( s.ch == '{' ) { + s.nextToken(); + s.nextToken(); + val bs = new myTreeList(); + val b = p.expr(true,false); //block( s.pos ); + if( s.token != RBRACE ) { + s.xSyntaxError(" expected end of Scala block"); + } + ts.append( b ); + } + case _ => // text content + ts.append( gen.mkStringLit( s.pos, s.xText )); + } + } + xEndTag( elemName ); + makeXML( pos, elemName, ts.toArray(), attrMap ); + } + } + + /** @see xmlPattern. resynchronizes after succesful parse */ + def xLiteralPattern = { + val t = xPattern; s.nextToken(); t + } + + /** '<' xPattern ::= Name [S] { xmlPattern | '{' pattern3 '}' } ETag + * | Name [S] '/' '>' + */ + def xPattern:Tree = { + val pos = s.pos; + val elemName = s.xName; + s.xSpaceOpt; + if( s.ch == '/' ) { // empty tag + s.xNext; s.xToken('>'); makeXMLpat( pos, elemName, Tree.EMPTY_ARRAY ); + } else { // content + s.xToken('>'); + val ts = new myTreeList(); + var exit = false; + while( !exit ) { + //Console.print("["+s.ch.asInstanceOf[char]+"]"); + s.ch match { + case '<' => { // tag + s.xNext; + if( s.ch != '/' ) { //child + ts.append( xPattern ); + } else { + exit = true + } + } + case '{' => // embedded Scala patterns + while( s.ch == '{' ) { + s.nextToken(); + s.nextToken(); + val ps = p.patterns(); + if( s.token != RBRACE ) { + s.xSyntaxError(" expected end of Scala block"); + } + ts.append( ps ); + } + case _ => // text + val pos = s.pos; + ts.append( gen.mkStringLit( pos, s.xText ) ); + } + } + xEndTag( elemName ); + makeXMLpat( pos, elemName, ts.toArray() ); + } + } + +} /* class MarkupParser */ +} diff --git a/sources/scala/tools/scalac/ast/parser/Parser.scala b/sources/scala/tools/scalac/ast/parser/Parser.scala index 02057a6feb..b4c83e6fcd 100644 --- a/sources/scala/tools/scalac/ast/parser/Parser.scala +++ b/sources/scala/tools/scalac/ast/parser/Parser.scala @@ -36,8 +36,12 @@ class Parser(unit: Unit) { */ val s = new Scanner(unit); - /** the tree factory + /** the markup parser */ + val xmlp = new MarkupParser( unit, s, this ); + + /** the tree factory + */ val make: TreeFactory = unit.global.make; /** the tree generator @@ -360,50 +364,6 @@ class Parser(unit: Unit) { make.LabelDef(pos, lname, new Array[Tree$Ident](0), rhs) } - def mkXML(pos:int, isPattern:boolean, t:Tree, args:Array[Tree]):Tree = { - var symt = scalaDot(s.pos, Names.Symbol); - if( isPattern ) symt = convertToTypeId(symt); - val ts = new myTreeList(); - ts.append(t); - ts.append(args); - make.Apply(pos, symt, ts.toArray()); - } - - def makeXMLpat(pos:int, n:Name, args:Array[Tree]):Tree = - mkXML(pos, true, gen.mkStringLit( pos, n.toString() ), args); - - def makeXML(pos:int, n:Name, args:Array[Tree]):Tree = - mkXML(pos, false, gen.mkStringLit( pos, n.toString() ), args); - - def makeXMLseq( pos:int, args:Array[Tree] ) = { - var symt = scalaDot(s.pos, Names.List); - make.Apply(pos, symt, args); - } - - def makeXML(pos:int, n:Name, args:Array[Tree], attrMap:ListMap[Name,String]):Tree = { - val t = makeXML( pos, n, args ); - val attrs = new Array[Tree]( attrMap.size ); - var i = 0; - for( val Pair( key, value ) <- attrMap.toList ) { - attrs.update( i, make.Apply(pos, - scalaDot(s.pos, Names.Tuple2), - { val x = new Array[Tree](2); - x(0) = gen.mkStringLit( pos, key.toString() ); - x(1) = gen.mkStringLit( pos, value.toString() ); - x } - )); - i = i + 1; - }; - make.Apply(pos, - make.Select( pos, t, Names.PERCENT ), - { val x = new Array[Tree](1); - x( 0 ) = make.Apply(pos, - scalaDot(s.pos, Names.List), - attrs); - x }) - - } - /** Convert tree to formal parameter list */ def convertToParams(t: Tree): Array[Tree$ValDef] = t match { @@ -650,9 +610,9 @@ class Parser(unit: Unit) { if (isSymLit) { val pos = s.pos; if (s.token == LPAREN || s.token == LBRACE) - mkXML( pos, isPattern, t, argumentExprs() ); + xmlp.mkXML( pos, isPattern, t, argumentExprs() ); else - mkXML( pos, isPattern, t, Tree.EMPTY_ARRAY ); + xmlp.mkXML( pos, isPattern, t, Tree.EMPTY_ARRAY ); } else { t } @@ -978,7 +938,7 @@ class Parser(unit: Unit) { } /* SimpleExpr ::= literal - * | xmlExpr + * | xLiteral * | StableRef * | `(' [Expr] `)' * | BlockExpr @@ -994,8 +954,8 @@ class Parser(unit: Unit) { SYMBOLLIT | TRUE | FALSE | NULL => t = literal(false); case IDENTIFIER | THIS | SUPER => - t = if(( s.name == LT )&&( unit.global.xmlMarkup )) { - xmlExprTop(); /* top-level xml expression */ + t = if( s.xStartsXML ) { + xmlp.xLiteral; } else { stableRef(true, false); } @@ -1049,29 +1009,6 @@ class Parser(unit: Unit) { null;//dummy } - /** top level xml expression, resynchronizes after succesful parse - * see xmlExpr - */ - def xmlExprTop():Tree = { - val pos = s.pos; - var t = xmlExpr(); - //Console.println("old:"+token2string( s.token ) ); - s.token = EMPTY; - s.nextToken(); - //Console.println("new:"+token2string( s.token ) ); - //Console.println("line:"+s.cline); - if(( s.token == IDENTIFIER ) && ( s.name == LT )) { - val ts = new myTreeList(); - ts.append( t ); - while(( s.token == IDENTIFIER ) && ( s.name == LT )) { - ts.append( xmlExpr() ); - s.nextToken(); - } - t = makeXMLseq( pos, ts.toArray() ); - } - t - } - /** ArgumentExprs ::= `(' [Exprs] `)' * | BlockExpr */ @@ -1318,7 +1255,7 @@ class Parser(unit: Unit) { /** simplePattern ::= varid * | `_' * | literal - * | `<' xmlPatternTop + * | `<' xLiteralPattern * | StableId [ `(' [Patterns] `)' ] * | `(' [Patterns] `)' * | (word: empty - nothing) @@ -1329,8 +1266,8 @@ class Parser(unit: Unit) { case IDENTIFIER | THIS => if (s.name == BAR) { make.Sequence(s.pos, Tree.EMPTY_ARRAY); // ((nothing)) - } else if(( s.name == LT )&&( unit.global.xmlMarkup )) { - xmlPatternTop() + } else if( s.xStartsXML ) { + xmlp.xLiteralPattern } else { var t = stableId(); while (s.token == LPAREN) { @@ -1364,162 +1301,6 @@ class Parser(unit: Unit) { syntaxError("illegal start of pattern", true) } - def xmlTag():Tuple2[Name, ListMap[Name,String]] = { - var empty = false; - - /* [40] STag ::= '<' Name (S Attribute)* S? - * [41] Attribute ::= Name Eq AttValue - * [44] EmptyElemTag ::= '<' Name (S Attribute)* S? - */ - val elemName = s.xmlName(); - s.xmlSpaceOpt(); - var attrMap = ListMap.Empty[Name,String]; - while( s.xml_isNameStart() ) { - val attrName:Name = s.xmlName(); - s.xmlSpaceOpt(); - s.xmlToken('='); - s.xmlSpaceOpt(); - var attrValue:String = ""; - val endch:char = s.ch.asInstanceOf[char]; - endch match { - case '"' | '\'' => { - s.nextch(); - attrValue = s.xmlAttribValue( endch ); - s.xmlToken( endch.asInstanceOf[char] ); - s.xmlSpaceOpt(); - } - case _ => s.xml_syntaxError("' or \" delimited value expected here"); - } - if( attrMap.contains( attrName )) { - s.xml_syntaxError("attribute "+attrName+" may only be defined once"); - } - attrMap = attrMap.update( attrName, attrValue ); - } - Tuple2( elemName, attrMap ); - } - - /* [42] '<' xmlEndTag ::= '<' '/' Name S? '>' */ - def xmlEndTag(n:Name) = { - s.xmlToken('/'); - if( n != s.xmlName() ) s.xml_syntaxError("expected closing tag of "+n); - s.xmlSpaceOpt(); - s.xmlToken('>') - } - - /** '<' xmlExpr ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag - ** | xmlTag1 '\' '>' - */ - def xmlExpr():Tree = { - val pos = s.pos; - val Tuple2(elemName, attrMap) = xmlTag(); - if( s.ch == '/' ) { - s.xmlToken('/'); - s.xmlToken('>'); - makeXML( pos, elemName, Tree.EMPTY_ARRAY ); - } else { - s.xmlToken('>'); /* handle XML content: */ - s.xmlSpaceOpt(); - val ts = new myTreeList(); - var exit = false; - while( !exit ) { - /* Console.println("read '"+s.ch.asInstanceOf[char]+"'"); */ - s.ch match { - case '<' => { /* tag */ - s.nextch(); - s.ch match { - case '/' => exit = true; - case '!' => - s.xmlComment(); - case _ => - /* search end tag */ - ts.append( xmlExpr() ); - s.xmlSpaceOpt(); - } - } - - case '{' => { /* Scala block */ - while( s.ch == '{' ) { - s.nextToken(); - s.nextToken(); - val bs = new myTreeList(); - val b = expr(true,false); //block( s.pos ); - if( s.token != RBRACE ) { - s.xml_syntaxError(" expected end of Scala block"); - } - s.xmlSpaceOpt(); - ts.append( b ); - } - } - case _ => { /* text ? */ - val pos = s.pos; - ts.append( gen.mkStringLit( pos, s.xmlText() )); - } - } - } - xmlEndTag( elemName ); - if( attrMap.isEmpty ) - makeXML( pos, elemName, ts.toArray() ); - else - makeXML( pos, elemName, ts.toArray(), attrMap ); - } - } - - /** top level xml pattern, resynchronizes after succesful parse - * see xmlPattern - */ - def xmlPatternTop():Tree = { - val t = xmlPattern(); - s.nextToken(); - t - } - - /** '<' xmlPattern ::= Name [S] { xmlPattern | '{' pattern3 '}' } ETag - * | Name [S] '\' '>' - */ - def xmlPattern():Tree = { - val pos = s.pos; - val elemName = s.xmlName(); - s.xmlSpaceOpt(); - if( s.ch == '/' ) { - s.xmlToken('/'); - s.xmlToken('>'); - makeXMLpat( pos, elemName, Tree.EMPTY_ARRAY ); - } else { /* handle XML content: */ - s.xmlToken('>'); - val ts = new myTreeList(); - var exit = false; - while( !exit ) { - //Console.print("["+s.ch.asInstanceOf[char]+"]"); - s.ch match { - case '<' => { /* tag */ - s.nextch(); - if( s.ch != '/' ) { /* search end tag */ - ts.append( xmlPattern() ); - } else { - exit = true - } - } - case '{' => { /* Scala patterns */ - while( s.ch == '{' ) { - s.nextToken(); - s.nextToken(); - val ps = patterns(); - if( s.token != RBRACE ) { - s.xml_syntaxError(" expected end of Scala block"); - } - ts.append( ps ); - } - } - case _ => { /* text */ - val pos = s.pos; - ts.append( gen.mkStringLit( pos, s.xmlText() )); - } - } - } - xmlEndTag( elemName ); - makeXMLpat( pos, elemName, ts.toArray() ); - } - } ////////// MODIFIERS //////////////////////////////////////////////////////////// /** Modifiers ::= {Modifier} diff --git a/sources/scala/tools/scalac/ast/parser/Scanner.scala b/sources/scala/tools/scalac/ast/parser/Scanner.scala index 174040d220..522ec142e0 100644 --- a/sources/scala/tools/scalac/ast/parser/Scanner.scala +++ b/sources/scala/tools/scalac/ast/parser/Scanner.scala @@ -8,6 +8,7 @@ import scalac._; import scalac.util.Name; +import scalac.util.Names; import scalac.util.SourceRepresentation; package scala.tools.scalac.ast.parser { @@ -653,69 +654,79 @@ class Scanner(_unit: Unit) extends TokenData { } } - /* X L + /* X M L */ - /* methods for XML tokenizing, see XML 1.0 rec, available from www.w3.org/xml */ + /* methods for XML tokenizing, see XML 1.0 rec http://www.w3.org/xml */ - /* M - */ - - def xml_syntaxError(s:String) = { - syntaxError("in XML literal: "+s); - xml_nextch(); + def xSyntaxError(s:String) = { + syntaxError("in XML literal: "+s); xNext; } - /* this helper functions updates ccol and cline + /** read the next character. do not skip whitespace. + * treat CR LF as single LF. update ccol and cline + * + * @todo: may XML contain SU, in CDATA sections ? */ - def xml_nextch() = { + def xNext = { nextch(); ch match { - case SU => syntaxError(lastpos, "unclosed XML literal"); token = EOF; + case SU => + syntaxError( lastpos, "unclosed XML literal" ); + token = EOF; + case LF => + ccol = 0; cline = cline + 1; case CR => - cline = cline + 1; - ccol = 0; - nextch(); /* in compliance with XML spec */ - if( ch == LF ) { - ccol = 0; - } - case LF => { - cline = cline + 1; - ccol = 0; - } + nextch(); + if( LF == ch ) { + ccol = 0; cline = cline + 1; + } else { + bp = bp - 1; ch = CR + } case _ => } pos = Position.encode(cline, ccol); } - def xml_isSpace() = ch match { - case ' ' | '\t' | '\r' | '\n' => true + /** scan [S] '=' [S] + */ + def xEQ = { + xSpaceOpt; xToken('='); xSpaceOpt + } + + final val LT = Name.fromString("<"); + + def xStartsXML = { + unit.global.xmlMarkup && ( token == IDENTIFIER )&&( name == LT ); + } + + def xIsSpace = ch match { + case ' ' | '\t' | CR | LF => true case _ => false; } - def xmlSpaceOpt() = { - while( xml_isSpace() ) { - xml_nextch(); - } + /** skip optional space S? + */ + def xSpaceOpt = { + while( xIsSpace ) { xNext; } } - /** [3] S ::= (#x20 | #x9 | #xD | #xA)+ + /** scan [3] S ::= (#x20 | #x9 | #xD | #xA)+ */ - def xmlSpace() = { - if( xml_isSpace() ) { - xml_nextch(); - xmlSpaceOpt() + def xSpace = { + if( xIsSpace ) { + xNext; xSpaceOpt } else { - xml_syntaxError("whitespace expected"); + xSyntaxError("whitespace expected"); } } - /** a subset of - * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender - * - * todo: add unicode letters and digits as well as combining chars and extenders + /** scan [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' + * | CombiningChar | Extender + * partial implementation + * @todo: add unicode letters, add CombiningChar Extender **/ - def xml_isNameChar() = ch match { + def xIsNameChar = ch match { case 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | 'a' | 'b' | 'c' | 'd' | 'e' | @@ -726,7 +737,8 @@ class Scanner(_unit: Unit) extends TokenData { case _ => false } - def xml_isNameStart() = ch match { + /** scan (Letter | '_' | ':') */ + def xIsNameStart = ch match { case 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | 'a' | 'b' | 'c' | 'd' | 'e' | @@ -735,63 +747,72 @@ class Scanner(_unit: Unit) extends TokenData { '_' | ':' => true; case _ => false } - /** a subset of (see isNameChar() ) - * [5] Name ::= (Letter | '_' | ':') (NameChar)* + /** scan [5] Name ::= (Letter | '_' | ':') (NameChar)* */ - def xmlName():Name = { - if( xml_isNameStart() ) { + def xName:Name = { + if( xIsNameStart ) { val index = bp; - while( xml_isNameChar() ) { - xml_nextch(); - } - Name.fromAscii(buf, index, bp - index); + while( xIsNameChar ) { xNext; } + Name.fromAscii( buf, index, bp - index ); } else { - xml_syntaxError("name expected"); - Name.fromString("-error-"); + xSyntaxError( "name expected" ); + Names.EMPTY } } - /* consuming everything up to the next endch */ - def xmlValue(endch:char):String = xmlValue(endch, true); + /** @see xSkipToNext( char, boolean ) */ + def xSkipToNext( endch:char ):String = xSkipToNext( endch, true ); - def xmlValue(endch:char, keep:boolean):String = { + /** move forward to next endch. return a string if needed. + * @param endch the character to which we skip + * @param keep if true, this function returns the string, otherwise null + */ + def xSkipToNext( endch:char, keep:boolean ):String = { lastpos = pos; val index = bp; - while ( ch != endch ) { xml_nextch();}; - pos = Position.encode( cline, ccol ); + while ( ch != endch ) { xNext; }; if( keep ) - new String(buf, index, bp-index); + new String( buf, index, bp-index ); else null } - def xmlAttribValue(endch:char):String = { - val s = xmlValue(endch, true); + /** attribute value, terminated by either ' or ". value may not contain <. + * @param endch either ' or " + */ + def xAttributeValue( endch:char ):String = { + val s = xSkipToNext( endch ); + // well-formedness constraint if( s.indexOf('<') != -1 ) { - xml_syntaxError("'<' not allowed in attrib value"); - "--syntax error--" + xSyntaxError( "'<' not allowed in attrib value" ); "" } else { s } } - def xmlText():String = xmlValue('<'); + /** character data. WRONG + */ + def xText:String = xSkipToNext( '<' ); - def xmlComment() = { - xmlToken('!'); - xmlToken('-'); - xmlToken('-'); - xmlValue('-', false); - xmlToken('-'); - xmlToken('-'); - xmlToken('>'); + /** scan XML comment. + */ + def xComment = { + xToken('!'); + xToken('-'); + xToken('-'); + xSkipToNext('-', false); + xToken('-'); + xToken('-'); + xToken('>'); }; - def xmlToken(that:char):unit = { + /** scan an exected XML token + */ + def xToken(that:char):unit = { if( ch == that ) { - xml_nextch(); + xNext; } else { - xml_syntaxError("'"+that+"' expected instead of '"+ch.asInstanceOf[char]+"'"); + xSyntaxError("'"+that+"' expected instead of '"+ch.asInstanceOf[char]+"'"); } } /* end XML tokenizing */ -- cgit v1.2.3