summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBurak Emir <emir@epfl.ch>2005-11-03 15:06:02 +0000
committerBurak Emir <emir@epfl.ch>2005-11-03 15:06:02 +0000
commite2bffd2133a3af96028bab2f90d9cea1c24bae05 (patch)
tree7cf175a0a5e36196910cb3efe2b3133cc4f8a8ff
parent6df648d4035a433afae83188956f1ae359dbddf2 (diff)
downloadscala-e2bffd2133a3af96028bab2f90d9cea1c24bae05.tar.gz
scala-e2bffd2133a3af96028bab2f90d9cea1c24bae05.tar.bz2
scala-e2bffd2133a3af96028bab2f90d9cea1c24bae05.zip
reactivate xml literal parsing
-rw-r--r--sources/scala/tools/nsc/ast/parser/MarkupParsers.scala583
-rwxr-xr-xsources/scala/tools/nsc/ast/parser/Scanners.scala69
-rw-r--r--sources/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala349
-rw-r--r--sources/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala2
-rw-r--r--sources/scala/tools/nsc/ast/parser/Tokens.scala3
5 files changed, 954 insertions, 52 deletions
diff --git a/sources/scala/tools/nsc/ast/parser/MarkupParsers.scala b/sources/scala/tools/nsc/ast/parser/MarkupParsers.scala
new file mode 100644
index 0000000000..fe43e39398
--- /dev/null
+++ b/sources/scala/tools/nsc/ast/parser/MarkupParsers.scala
@@ -0,0 +1,583 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author buraq
+ */
+// $Id$
+package scala.tools.nsc.ast.parser;
+
+//import java.lang.{Integer, Long, Float, Double};
+
+//import scalac._;
+//import scalac.ast._;
+//import scalac.atree.AConstant;
+//import scalac.symtab.Modifiers;
+
+import scala.Iterator;
+import scala.collection.immutable.ListMap ;
+import scala.collection.mutable;
+//import scala.tools.scalac.util.NewArray;
+import scala.tools.util.Position;
+import scala.xml.{Text,TextBuffer};
+
+
+trait MarkupParsers: SyntaxAnalyzer {
+
+ import global._ ;
+ import posAssigner.atPos;
+
+class MarkupParser(unit: CompilationUnit, s: Scanner, p: Parser, presWS: boolean) /*with scala.xml.parsing.MarkupParser[Tree,Tree] */{
+
+ import Tokens.{EMPTY, LBRACE, RBRACE};
+
+ final val preserveWS = presWS;
+
+ import p.{symbXMLBuilder => handle};
+ import s.token;
+
+ /** the XML tree factory */
+ //final val handle: SymbolicXMLBuilder = p.symbXMLBuilder;
+ //new SymbolicXMLBuilder(unit.global.make, unit.global.treeGen, p, presWS);
+
+ /** holds the position in the source file */
+ /*[Duplicate]*/ var pos: Int = _;
+
+ /** holds temporary values of pos */
+ /*[Duplicate]*/ var tmppos: Int = _;
+
+ /** holds the next character */
+ /*[Duplicate]*/ var ch: Char = _;
+
+ /** character buffer, for names */
+ /*[Duplicate]*/ protected val cbuf = new StringBuffer();
+
+ /** append Unicode character to name buffer*/
+ /*[Duplicate]*/ protected def putChar(c: char) = cbuf.append( c );
+
+ /*[Duplicate]*/ var xEmbeddedBlock = false;
+
+ /** munch expected XML token, report syntax error for unexpected */
+ /*[Duplicate]*/ def xToken(that: Char): Unit = {
+ if( ch == that )
+ nextch;
+ else
+ reportSyntaxError("'" + that + "' expected instead of '" + ch + "'");
+ }
+
+ var debugLastStartElement = new mutable.Stack[Pair[Int,String]];
+
+ /** 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 = {
+ xEmbeddedBlock =
+ enableEmbeddedExpressions && ( ch == '{' ) && { nextch;( ch != '{' ) };
+ return xEmbeddedBlock;
+ }
+
+ /** parse attribute and add it to listmap
+ * [41] Attributes ::= { S Name Eq AttValue }
+ * AttValue ::= `'` { _ } `'`
+ * | `"` { _ } `"`
+ * | `{` scalablock `}`
+ */
+ /*[Duplicate]*/ def xAttributes = {
+ var aMap = new mutable.HashMap[String, Tree]();
+ while (xml.Parsing.isNameStart(ch)) {
+ val key = xName;
+ xEQ;
+ val delim = ch;
+ val pos1 = pos;
+ val value: /* AttribValue[*/Tree/*]*/ = ch match {
+ case '"' | '\'' =>
+ nextch;
+ val tmp = xAttributeValue( delim );
+ nextch;
+ Literal(Constant(tmp));
+ case '{' if enableEmbeddedExpressions =>
+ nextch;
+ xEmbeddedExpr;
+ case _ =>
+ reportSyntaxError( "' 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 != '>' ))
+ xSpace;
+ };
+ aMap
+ }
+
+ /** attribute value, terminated by either ' or ". value may not contain <.
+ * @param endch either ' or "
+ */
+ /*[Duplicate]*/ def xAttributeValue(endCh: char): String = {
+ while (ch != endCh) {
+ putChar(ch);
+ nextch;
+ };
+ val str = cbuf.toString();
+ cbuf.setLength(0);
+ // @todo: normalize attribute value
+ // well-formedness constraint
+ if (str.indexOf('<') != -1) {
+ reportSyntaxError( "'<' not allowed in attrib value" ); ""
+ } else {
+ str
+ }
+ }
+
+ /** parse a start or empty tag.
+ * [40] STag ::= '<' Name { S Attribute } [S]
+ * [44] EmptyElemTag ::= '<' Name { S Attribute } [S]
+ */
+ /*[Duplicate]*/ def xTag: Pair[String, mutable.Map[String, Tree]] = {
+ val elemName = xName;
+ xSpaceOpt;
+ val aMap = if (xml.Parsing.isNameStart(ch)) {
+ xAttributes;
+ } else {
+ new mutable.HashMap[String, Tree]();
+ }
+ Tuple2( elemName, aMap );
+ }
+
+ /* [42] '<' xmlEndTag ::= '<' '/' Name S? '>' */
+ /*[Duplicate]*/ def xEndTag(n: String) = {
+ xToken('/');
+ val m = xName;
+ if(n != m) reportSyntaxError( "expected closing tag of " + n/* +", not "+m*/);
+ xSpaceOpt;
+ xToken('>')
+ }
+
+ /** '<! CharData ::= [CDATA[ ( {char} - {char}"]]>"{char} ) ']]>'
+ *
+ * see [15]
+ */
+ /*[Duplicate]*/ def xCharData: Tree = {
+ xToken('[');
+ xToken('C');
+ xToken('D');
+ xToken('A');
+ xToken('T');
+ xToken('A');
+ xToken('[');
+ val pos1 = pos;
+ val sb:StringBuffer = new StringBuffer();
+ while (true) {
+ if (ch==']' &&
+ { sb.append( ch ); nextch; ch == ']' } &&
+ { sb.append( ch ); nextch; ch == '>' } ) {
+ sb.setLength( sb.length() - 2 );
+ nextch;
+ return handle.charData( pos1, sb.toString() );
+ } else sb.append( ch );
+ nextch;
+ }
+ Predef.error("this cannot happen");
+ };
+
+ /** CharRef ::= "&#" '0'..'9' {'0'..'9'} ";"
+ * | "&#x" '0'..'9'|'A'..'F'|'a'..'f' { hexdigit } ";"
+ *
+ * see [66]
+ */
+ /*[Duplicate]*/ def xCharRef:String = {
+ val hex = ( ch == 'x' ) && { nextch; true };
+ val base = if (hex) 16 else 10;
+ var i = 0;
+ while (ch != ';') {
+ ch match {
+ case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' =>
+ i = i * base + Character.digit( ch, base );
+ case 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
+ | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' =>
+ if( !hex )
+ reportSyntaxError("hex char not allowed in decimal char ref\n"
+ +"Did you mean to write &#x ?");
+ else
+ i = i * base + Character.digit( ch, base );
+ case _ =>
+ reportSyntaxError("character '"+ch+" not allowed in char ref\n");
+ }
+ nextch;
+ }
+ new String(Predef.Array(i.asInstanceOf[char]))
+ }
+/** Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
+ *
+ * see [15]
+ */
+ /*[Duplicate]*/ def xComment: Tree = {
+ val sb:StringBuffer = new StringBuffer();
+ xToken('-');
+ xToken('-');
+ while (true) {
+ if( ch=='-' && { sb.append( ch ); nextch; ch == '-' } ) {
+ sb.setLength( sb.length() - 1 );
+ nextch;
+ xToken('>');
+ return handle.comment( pos, sb.toString() );
+ } else sb.append( ch );
+ nextch;
+ }
+ Predef.error("this cannot happen");
+ };
+
+ /*[Duplicate]*/ def appendText(pos: int, ts:mutable.Buffer[Tree], txt:String):Unit = {
+ if( !preserveWS )
+ for( val t <- TextBuffer.fromString( txt ).toText ) {
+ ts.append( handle.text( pos, t.text ) );
+ }
+ else
+ ts.append( handle.text( pos, txt ));
+ }
+
+ /*[Duplicate]*/ def content: mutable.Buffer[Tree] = {
+ var ts = new mutable.ArrayBuffer[Tree];
+ var exit = false;
+ while( !exit ) {
+ if( xEmbeddedBlock ) {
+ ts.append( xEmbeddedExpr );
+ } else {
+ tmppos = pos;
+ ch match {
+ case '<' => // another tag
+ nextch;
+ ch match {
+ case '/' =>
+ exit = true; // end tag
+ case '!' =>
+ nextch;
+ if( '[' == ch ) // CDATA
+ ts.append( xCharData );
+ else // comment
+ ts.append( xComment );
+ case '?' => // PI
+ nextch;
+ ts.append( xProcInstr );
+ case _ =>
+ ts.append( element ); // child
+ }
+
+ case '{' =>
+ if( xCheckEmbeddedBlock ) {
+ ts.append(xEmbeddedExpr);
+ } else {
+ val str = new StringBuffer("{");
+ str.append( xText );
+ appendText(tmppos, ts, str.toString());
+ }
+ // postcond: xEmbeddedBlock == false!
+ case '&' => // EntityRef or CharRef
+ nextch;
+ ch match {
+ case '#' => // CharacterRef
+ nextch;
+ val theChar = handle.text( tmppos, xCharRef );
+ xToken(';');
+ ts.append( theChar );
+ case _ => // EntityRef
+ val n = xName ;
+ xToken(';');
+ ts.append( handle.entityRef( tmppos, n ) );
+ }
+ case _ => // text content
+ appendText(tmppos, ts, xText);
+ // here xEmbeddedBlock might be true
+ }
+ }
+ }
+ ts
+ } /* end content */
+
+ /** '<' element ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag
+ * | xmlTag1 '/' '>'
+ */
+ /*[Duplicate]*/ def element: Tree = {
+ val pos1 = pos;
+ val Tuple2(qname, attrMap) = xTag;
+ //Console.println("MarkupParser::element("+qname+","+attrMap+")");
+ if (ch == '/') { // empty element
+ xToken('/');
+ xToken('>');
+ handle.element( pos1, qname, attrMap, new mutable.ListBuffer[Tree] );
+ }
+ else { // handle content
+ xToken('>');
+ debugLastStartElement.push(Pair(pos1,qname));
+ val ts = content;
+ xEndTag( qname );
+ debugLastStartElement.pop;
+ handle.element( pos1, qname, attrMap, ts );
+ }
+ }
+
+
+ /** Name ::= (Letter | '_' | ':') (NameChar)*
+ *
+ * see [5] of XML 1.0 specification
+ */
+ /*[Duplicate]*/ def xName: String = {
+ if( xml.Parsing.isNameStart( ch ) ) {
+ do {
+ putChar( ch );
+ nextch;
+ } while( xml.Parsing.isNameChar( ch ) );
+ val n = cbuf.toString().intern();
+ cbuf.setLength( 0 );
+ n
+ } else {
+ reportSyntaxError( "name expected, but char '"+ch+"' cannot start a name" );
+ new String();
+ }
+ }
+
+
+ /** scan [S] '=' [S]*/
+ /*[Duplicate]*/ def xEQ = { xSpaceOpt; xToken('='); xSpaceOpt }
+
+ /** skip optional space S? */
+ /*[Duplicate]*/ def xSpaceOpt = { while( xml.Parsing.isSpace( ch ) ) { nextch; }}
+
+ /** scan [3] S ::= (#x20 | #x9 | #xD | #xA)+ */
+ /*[Duplicate]*/ def xSpace = {
+ if (xml.Parsing.isSpace(ch)) {
+ nextch; xSpaceOpt
+ }
+ else {
+ reportSyntaxError("whitespace expected");
+ }
+ }
+
+/** '<?' ProcInstr ::= Name [S ({Char} - ({Char}'>?' {Char})]'?>'
+ *
+ * see [15]
+ */
+ /*[Duplicate]*/ def xProcInstr: Tree = {
+ val sb:StringBuffer = new StringBuffer();
+ val n = xName;
+ if( xml.Parsing.isSpace( ch ) ) {
+ xSpace;
+ while( true ) {
+ if( ch=='?' && { sb.append( ch ); nextch; ch == '>' } ) {
+ sb.setLength( sb.length() - 1 );
+ nextch;
+ return handle.procInstr(tmppos, n.toString(), sb.toString());
+ } else
+ sb.append( ch );
+ nextch;
+ }
+ };
+ xToken('?');
+ xToken('>');
+ return handle.procInstr(tmppos, n.toString(), sb.toString());
+ }
+
+ /** 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
+
+ if( xCheckEmbeddedBlock )
+ return ""
+ else {
+ var exit = false;
+ while( !exit ) {
+ putChar( ch );
+ exit = { nextch; xCheckEmbeddedBlock }||( ch == '<' ) || ( ch == '&' );
+ }
+ val str = cbuf.toString();
+ cbuf.setLength( 0 );
+ str
+ }
+ }
+ //override type Tree = handle.Tree;
+ //override type Tree = handle.Tree;
+
+ final val PATTERN = true;
+ final val EXPR = false;
+
+ val enableEmbeddedExpressions: Boolean = true;
+
+ //val cbuf = new StringBuffer();
+
+ /** append Unicode character to name buffer*/
+ //private def putChar(c: char) = cbuf.append( c );
+
+ /** xLiteral = element { element }
+ * @return Scala representation of this xml literal
+ * precondition: s.xStartsXML == true
+ */
+ def xLiteral: Tree = try {
+ init;
+ handle.isPattern = false;
+ val pos = s.pos;
+ var tree = element;
+ xSpaceOpt;
+ // parse more XML ?
+ if (ch == '<') {
+ val ts = new mutable.ArrayBuffer[Tree]();
+ ts.append( tree );
+ while( ch == '<' ) {
+ nextch;
+ //Console.println("DEBUG 1: I am getting char '"+ch+"'"); // DEBUG
+ ts.append( element );
+ xSpaceOpt;
+ }
+ tree = handle.makeXMLseq( pos, ts );
+ }
+ //Console.println("out of xLiteral, parsed:"+tree.toString());
+ s.next.token = EMPTY;
+ s.nextToken(); /* s.fetchToken(); */
+ tree
+ }
+ catch {
+ case _:ArrayIndexOutOfBoundsException =>
+ s.syntaxError(debugLastStartElement.top._1,
+ "missing end tag in XML literal for <"
+ +debugLastStartElement.top._2+">");
+ EmptyTree;
+ }
+
+ /** @see xmlPattern. resynchronizes after succesful parse
+ * @return this xml pattern
+ * precondition: s.xStartsXML == true
+ */
+ def xLiteralPattern:Tree = try {
+ init;
+ val oldMode = handle.isPattern;
+ handle.isPattern = true;
+ val pos = s.pos;
+ var tree = xPattern; xSpaceOpt;
+ //if (ch == '<') {
+ var ts: List[Tree] = List();
+ ts = tree :: ts;
+
+ s.next.token = EMPTY; s.nextToken(); /* ?????????? */
+ while( token == Tokens.XMLSTART ) {// ???????????????????????????
+ //while (ch == '<' /* && lookahead != '-'*/) {
+ nextch;
+ //Console.println("DEBUG 2: I am getting char '"+ch+"'"); // DEBUG
+ ts = xPattern :: ts;
+ //xSpaceOpt; // ????
+ s.next.token = EMPTY; s.nextToken(); /* ?????????? */
+ //Console.println("DEBUG 3: resync'ed, token = '"+s+"'"); // DEBUG
+ }
+ //Console.println("current token == "+s);
+ tree = handle.makeXMLseqPat( pos, ts.reverse );
+ //}
+ handle.isPattern = oldMode;
+ //Console.println("out of xLiteralPattern, parsed:"+tree.toString());
+ // s.next.token = EMPTY; // ??
+ // s.nextToken(); /* s.fetchToken(); */ // ??
+ tree
+ }catch {
+ case _:ArrayIndexOutOfBoundsException =>
+ s.syntaxError(debugLastStartElement.top._1,
+ "missing end tag in XML literal for <"
+ +debugLastStartElement.top._2+">");
+ EmptyTree;
+ }
+
+ def xEmbeddedExpr:Tree = {
+ sync;
+ val b = p.expr(true,false);
+ if(/*s.*/token != RBRACE)
+ reportSyntaxError(" expected end of Scala block");
+ init;
+ //Console.println("[out of xScalaExpr s.ch = "+s.ch+" ch="+ch+"]");
+ return b
+ }
+
+ /** xScalaPatterns ::= patterns
+ */
+ def xScalaPatterns: List[Tree] = {
+ sync;
+ val b = p.patterns();
+ if (/*s.*/token != RBRACE)
+ reportSyntaxError(" expected end of Scala patterns");
+ init;
+ return b
+ }
+
+ //var ch: Char = _;
+
+ /** this method assign the next character to ch and advances in input */
+ def nextch: Unit = { s.in.next; /*s.xNext;*/ ch = s.in.ch ; pos = s.pos; }
+
+ //def lookahead = { s.xLookahead }
+
+ def init: Unit = {
+ ch = s.in.ch;
+ pos = s.pos;
+ //Console.println("\ninit! ch = "+ch);
+ }
+
+ def reportSyntaxError(str: String) = {
+ s.syntaxError("in XML literal: " + str);
+ nextch;
+ }
+
+ def sync: Unit = {
+ xEmbeddedBlock = false;
+ s.xSync;
+ }
+
+ /** '<' xPattern ::= Name [S] { xmlPattern | '{' pattern3 '}' } ETag
+ * | Name [S] '/' '>'
+ */
+ def xPattern:Tree = {
+ //Console.println("xPattern");
+ val pos1 = pos;
+ val qname = xName;
+ debugLastStartElement.push(Pair(pos1,qname));
+ xSpaceOpt;
+ if( ch == '/' ) { // empty tag
+ nextch;
+ xToken('>');
+ return handle.makeXMLpat( pos1, qname, new mutable.ArrayBuffer[Tree]() );
+ };
+
+ // else: tag with content
+ xToken('>');
+ var ts = new mutable.ArrayBuffer[Tree];
+ var exit = false;
+ while (! exit) {
+ val pos2 = pos;
+ if( xEmbeddedBlock ) {
+ ts ++ xScalaPatterns;
+ } else
+ ch match {
+ case '<' => { // tag
+ nextch;
+ if( ch != '/' ) { //child
+ ts.append( xPattern );
+ } else {
+ exit = true
+ }
+ }
+ case '{' => // embedded Scala patterns
+ while( ch == '{' ) {
+ s.in.next;
+ ts ++ xScalaPatterns;
+ }
+ // postcond: xEmbeddedBlock = false;
+ if (xEmbeddedBlock) Predef.error("problem with embedded block"); // assert
+ case _ => // teMaxt
+ appendText( pos2, ts, xText );
+ // here xEmbeddedBlock might be true;
+ //if( xEmbeddedBlock ) throw new ApplicationError("after:"+text); // assert
+ }
+ }
+ xEndTag(qname);
+ debugLastStartElement.pop;
+ handle.makeXMLpat(pos1, qname, ts);
+ }
+
+} /* class MarkupParser */
+}
diff --git a/sources/scala/tools/nsc/ast/parser/Scanners.scala b/sources/scala/tools/nsc/ast/parser/Scanners.scala
index 8e0b8b9e97..e90f0b8817 100755
--- a/sources/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/sources/scala/tools/nsc/ast/parser/Scanners.scala
@@ -137,7 +137,7 @@ import scala.tools.nsc.util.CharArrayReader;
this.copyFrom(prev);
}
}
- //Console.println("<" + this + ">");//DEBUG
+ // Console.println("<" + this + ">");//DEBUG
}
}
@@ -173,8 +173,22 @@ import scala.tools.nsc.util.CharArrayReader;
in.next;
getIdentRest; // scala-mode: wrong indent for multi-line case blocks
return;
+
+ case '<' => // is XMLSTART?
+ val last = in.last;
+ in.next;
+ last match {
+ case ' '|'\t'|'\n'|'{'|'('|'>' if xml.Parsing.isNameStart(in.ch) =>
+ token = XMLSTART;
+ case _ =>
+ // Console.println("found '<', but last is '"+in.last+"'"); // DEBUG
+ putChar('<');
+ getOperatorRest;
+ }
+ return;
+
case '~' | '!' | '@' | '#' | '%' |
- '^' | '*' | '+' | '-' | '<' |
+ '^' | '*' | '+' | '-' | /*'<' | */
'>' | '?' | ':' | '=' | '&' |
'|' | '\\' =>
putChar(in.ch);
@@ -641,61 +655,12 @@ import scala.tools.nsc.util.CharArrayReader;
}
// XML lexing----------------------------------------------------------------
-
-/*
- // start XML tokenizing methods
- // prod. [i] refers to productions in http://www.w3.org/TR/REC-xml
-
- /** calls nextToken, starting the scanning of Scala tokens,
- * after XML tokens.
- */
def xSync = {
token = SEMI; // avoid getting SEMI from nextToken if last was RBRACE
//in.next;
nextToken();
}
- def xSync2 = fetchToken();
-
- def xLookahead = srcIterator.lookahead1;
-
- /** 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 xNext = {
- lastpos = pos;
- ch = srcIterator.raw; ccol = ccol + 1; // = in.next without unicode
- ch match {
- case SU =>
- syntaxError(lastpos, "unclosed XML literal");
- token = EOF;
- case LF =>
- ccol = 0; cline = cline + 1;
- case CR =>
- if (LF == srcIterator.lookahead1) {
- srcIterator.raw; ccol = 0; cline = cline + 1;
- }
- case _ =>
- //Console.print(ch.asInstanceOf[char]); // DEBUG
- }
- pos = Position.encode(cline, ccol);
- //Console.print(ch);
- }
-
- final val LT = Name.fromString("<");
-
- def xStartsXML = {
- /* unit.global.xmlMarkup && */ (token == IDENTIFIER) &&(name == LT);
- /* || // support for proc instr, cdata, comment... ?
- {val s = name.toString();
- s.charAt(0) == '<' && (s.charAt(1)=='?' || s.charAt(1)=='!')}) */
- }
-
- // end XML tokenizing
-
-*/
// Errors -----------------------------------------------------------------
/** generate an error at the given position
@@ -841,6 +806,8 @@ import scala.tools.nsc.util.CharArrayReader;
"case class"
case CASEOBJECT =>
"case object"
+ case XMLSTART =>
+ "$XMLSTART$<"
case _ =>
try {
"'" + tokenName(token) + "'"
diff --git a/sources/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala b/sources/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala
new file mode 100644
index 0000000000..afe1f3996e
--- /dev/null
+++ b/sources/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala
@@ -0,0 +1,349 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author buraq
+ */
+// $Id$
+package scala.tools.nsc.ast.parser;
+
+import scala.tools.util.Position;
+import scala.Iterator;
+import scala.collection.immutable.{ Map, ListMap };
+import scala.collection.mutable;
+import scala.xml.{Text,TextBuffer};
+
+import symtab.Flags.MUTABLE;
+
+/** this class builds instance of Tree that represent XML */
+abstract class SymbolicXMLBuilder(make: TreeBuilder, p: Parsers # Parser, preserveWS: Boolean ) {
+
+ val global: Global;
+ import global._;
+ import posAssigner.atPos;
+
+ //import scala.tools.scalac.ast.{TreeList => myTreeList}
+
+ var isPattern:Boolean = _;
+
+ import nme.{
+ _Attribute ,
+ _MetaData ,
+ _NamespaceBinding ,
+ _NodeBuffer ,
+
+ _Null ,
+
+ _PrefixedAttribute ,
+ _UnprefixedAttribute ,
+ _Elem ,
+ _Seq ,
+ _immutable ,
+ _mutable ,
+ _append ,
+ _plus ,
+ _collection ,
+ _toList ,
+ _xml ,
+ _Comment ,
+ _CharData ,
+ _Node ,
+ _ProcInstr ,
+ _Text ,
+ _EntityRef ,
+ _md,
+ _scope,
+ _tmpscope};
+
+
+ // convenience methods
+ private def LL[A](x:A*):List[List[A]] = List(List(x:_*));
+
+ private def _scala(name: Name) = Select(Ident(nme.scala_), name);
+
+ private def _scala_Seq = _scala( _Seq );
+ private def _scala_xml(name: Name) = Select( _scala(_xml), name );
+
+ private def _scala_xml_MetaData = _scala_xml( _MetaData );
+ private def _scala_xml_NamespaceBinding = _scala_xml( _NamespaceBinding );
+ private def _scala_xml_Null = _scala_xml( _Null );
+ private def _scala_xml_PrefixedAttribute = _scala_xml(_PrefixedAttribute);
+ private def _scala_xml_UnprefixedAttribute= _scala_xml(_UnprefixedAttribute);
+ private def _scala_xml_Node = _scala_xml( _Node );
+ private def _scala_xml_NodeBuffer = _scala_xml( _NodeBuffer );
+ private def _scala_xml_EntityRef = _scala_xml( _EntityRef );
+ private def _scala_xml_Comment = _scala_xml( _Comment );
+ private def _scala_xml_CharData = _scala_xml( _CharData );
+ private def _scala_xml_ProcInstr = _scala_xml( _ProcInstr );
+ private def _scala_xml_Text = _scala_xml( _Text );
+ private def _scala_xml_Elem = _scala_xml( _Elem );
+ private def _scala_xml_Attribute = _scala_xml( _Attribute );
+
+
+ private def bufferToArray(buf: mutable.Buffer[Tree]): Array[Tree] = {
+ val arr = new Array[Tree]( buf.length );
+ var i = 0;
+ for (val x <- buf.elements) { arr(i) = x; i = i + 1; }
+ arr;
+ }
+
+ // create scala xml tree
+
+ /**
+ * @arg namespace: a Tree of type defs.STRING_TYPE
+ * @arg label: a Tree of type defs.STRING_TYPE
+ * @todo map: a map of attributes !!!
+ */
+
+ protected def mkXML(pos: int, isPattern: boolean, pre: Tree, label: Tree, attrs: /*Array[*/Tree/*]*/ , scope:Tree, children: mutable.Buffer[Tree]): Tree = {
+ if( isPattern ) {
+ //val ts = new mutable.ArrayBuffer[Tree]();
+ convertToTextPat( children );
+ atPos (pos) {
+ Apply( _scala_xml_Elem, List(
+ pre, label, Ident( nme.WILDCARD ) /* attributes? */ , Ident( nme.WILDCARD )) /* scope? */ ::: children.toList )
+ }
+ } else {
+ var ab = List(pre, label, attrs, scope);
+ if(( children.length ) > 0 )
+ ab = ab ::: List(Typed(makeXMLseq(pos, children), Ident(nme.WILDCARD_STAR.toTypeName)));
+ atPos(pos) { New( _scala_xml_Elem, List(ab) )}
+ }
+ }
+
+ final def entityRef( pos:int, n: String ) = {
+ atPos(pos) { New( _scala_xml_EntityRef, LL(Literal(Constant( n )))) };
+
+ };
+ // create scala.xml.Text here <: scala.xml.Node
+ final def text( pos: Int, txt:String ):Tree = {
+ //makeText( isPattern, gen.mkStringLit( txt ));
+ val txt1 = Literal(Constant(txt));
+ atPos(pos) {
+ if( isPattern )
+ makeTextPat( txt1 );
+ else
+ makeText1( txt1 );
+ }
+ }
+
+ // create scala.xml.Text here <: scala.xml.Node
+ def makeTextPat(txt:Tree ) = Apply(_scala_xml_Text, List(txt));
+ def makeText1(txt:Tree ) = New( _scala_xml_Text, LL( txt ));
+
+ // create
+ def comment( pos: int, text: String ):Tree =
+ atPos(pos) { Comment( Literal(Constant(text))) };
+
+ // create
+ def charData( pos: int, txt: String ):Tree =
+ atPos(pos) { CharData( Literal(Constant(txt))) };
+
+ // create scala.xml.Text here <: scala.xml.Node
+ def procInstr( pos: int, target: String, txt: String ) =
+ atPos(pos) { ProcInstr(Literal(Constant(target)), Literal(Constant(txt))) };
+
+ protected def CharData(txt: Tree) = New( _scala_xml_CharData, LL(txt));
+ protected def Comment(txt: Tree) = New( _scala_xml_Comment, LL(txt));
+ protected def ProcInstr(target: Tree, txt: Tree) =
+ New(_scala_xml_ProcInstr, LL( target, txt ));
+
+
+ /** @todo: attributes */
+ def makeXMLpat(pos: int, n: String, args: mutable.Buffer[Tree]): Tree =
+ mkXML(pos,
+ true,
+ Ident( nme.WILDCARD ),
+ Literal(Constant(n)),
+ null, //Predef.Array[Tree](),
+ null,
+ args);
+
+ protected def convertToTextPat(t: Tree): Tree = t match {
+ case _:Literal => makeTextPat(t);
+ case _ => t
+ }
+
+ protected def convertToTextPat(buf: mutable.Buffer[Tree]): Unit = {
+ var i = 0; while( i < buf.length ) {
+ val t1 = buf( i );
+ val t2 = convertToTextPat( t1 );
+ if (!t1.eq(t2)) {
+ buf.remove(i);
+ buf.insert(i,t2);
+ }
+ i = i + 1;
+ }
+ }
+
+ def freshName(prefix:String): Name;
+
+ def isEmptyText(t:Tree) = t match {
+ case Literal(Constant("")) => true;
+ case _ => false;
+ }
+ def makeXMLseq( pos:int, args:mutable.Buffer[Tree] ) = {
+ var _buffer = New( _scala_xml_NodeBuffer, List(Nil));
+ val it = args.elements;
+ while( it.hasNext ) {
+ val t = it.next;
+ if( !isEmptyText( t )) {
+ _buffer = Apply(Select(_buffer, _plus), List( t ));
+ }
+ }
+ atPos(pos) { Select(_buffer, _toList) };
+ }
+
+ def makeXMLseqPat( pos:int, args:List[Tree] ) = atPos(pos) {Apply( _scala_Seq, args )};
+
+
+ /** returns Some(prefix) if pre:name, None otherwise */
+ def getPrefix( name:String ):Option[String] = {
+ val i = name.indexOf(':');
+ if( i != -1 ) Some( name.substring(0, i) ) else None
+ }
+
+ /** splits */
+ protected def qualifiedAttr( pos:Int, namespace:String, name:String ):Pair[String,String] = {
+ getPrefix( name ) match {
+ case Some( pref ) =>
+ val newLabel = name.substring( pref.length()+1, name.length() );
+ // if( newLabel.indexOf(':') != -1 ) syntaxError
+ Pair( "namespace$"+pref, newLabel );
+ case None =>
+ Pair( namespace, name );
+ }
+ }
+ protected def qualified( pos:Int, name:String ):Pair[String,String] =
+ getPrefix( name ) match {
+ case Some( pref ) =>
+ val newLabel = name.substring( pref.length()+1, name.length() );
+ // if( newLabel.indexOf(':') != -1 ) syntaxError
+ Pair( "namespace$"+pref, newLabel );
+ case None =>
+ Pair( "namespace$default", name );
+ }
+
+
+ /** makes an element */
+ def element(pos: int, qname: String, attrMap: mutable.Map[String,Tree], args: mutable.Buffer[Tree]): Tree = {
+ //Console.println("SymbolicXMLBuilder::element("+pos+","+qname+","+attrMap+","+args+")");
+ var setNS = new mutable.HashMap[String, Tree];
+
+ var tlist: List[Tree] = List();
+
+ /* pre can be null */
+ def handleNamespaceBinding(pre:String , uri:Tree): Unit = {
+ val t = Assign(Ident(_tmpscope), New( _scala_xml_NamespaceBinding,
+ LL(Literal(Constant(pre)), uri, Ident( _tmpscope))));
+ tlist = t :: tlist;
+ //Console.println("SymbolicXMLBuilder::handleNamespaceBinding:");
+ //Console.println(t.toString());
+ }
+
+ /* DEBUG */
+ val attrIt = attrMap.keys;
+ while( attrIt.hasNext ) {
+ val z = attrIt.next;
+ if( z.startsWith("xmlns") ) { // handle namespace
+ val i = z.indexOf(':');
+ if( i == -1 )
+ handleNamespaceBinding(null, attrMap( z ));
+ //setNS.update("default", attrMap( z ) );
+ else {
+ val zz = z.substring( i+1, z.length() );
+ //setNS.update( zz, attrMap( z ) );
+ handleNamespaceBinding(zz, attrMap( z ));
+ }
+ attrMap -= z;
+ }
+ }
+
+ val moreNamespaces = (0 < tlist.length);
+
+ /* */
+ val i = qname.indexOf(':');
+
+ var newlabel = qname;
+ val pre = getPrefix(qname) match {
+ case Some(p) =>
+ newlabel = qname.substring(p.length()+1, qname.length());
+ p;
+ case None =>
+ null
+ }
+ var tlist2: List[Tree] = List();
+
+ // make attributes
+
+ def handlePrefixedAttribute(pre:String, key:String, value:Tree): Unit = {
+ val t = atPos(pos) {
+ Assign(Ident(_md), New( _scala_xml_PrefixedAttribute,
+ LL(
+ Literal(Constant(pre)),
+ Literal(Constant(key)),
+ value,
+ Ident(_md)
+ )))};
+ tlist2 = t :: tlist2;
+ // Console.println("SymbolicXMLBuilder::handlePrefixed :");
+ // Console.println(t.toString());
+ }
+
+ def handleUnprefixedAttribute(key:String, value:Tree): Unit = {
+ val t = atPos(pos) {
+ Assign(Ident(_md), New(_scala_xml_UnprefixedAttribute,
+ LL(Literal(Constant(key)),value,Ident(_md))
+ ))};
+ tlist2 = t :: tlist2;
+ }
+
+ var it = attrMap.elements;
+ while (it.hasNext) {
+ val ansk = it.next;
+ getPrefix(ansk._1) match {
+ case Some(pre) =>
+ val key = ansk._1.substring(pre.length()+1, ansk._1.length());
+ handlePrefixedAttribute(pre, key, ansk._2);
+ case None =>
+ handleUnprefixedAttribute(ansk._1, ansk._2);
+ }
+ }
+ // attrs
+
+
+ val moreAttributes = (0 < tlist2.length);
+
+ var ts: List[Tree] = tlist;
+ var ts2: List[Tree] = List();
+
+ if(moreAttributes) {
+ ts2 = atPos(pos) {ValDef(MUTABLE,
+ _md,
+ _scala_xml_MetaData,
+ _scala_xml_Null)} :: tlist2;
+ }
+ if(moreNamespaces) {
+ ts = atPos(pos) {
+ ValDef(MUTABLE,
+ _tmpscope,
+ _scala_xml_NamespaceBinding,
+ Ident(_scope))} :: ts;
+
+ ts2 = ValDef(0, _scope, _scala_xml_NamespaceBinding, Ident(_tmpscope)) :: ts2;
+ }
+
+ val makeSymbolicAttrs = {
+ if (moreAttributes) Ident(_md) else _scala_xml_Null;
+ }
+
+ var t = mkXML(pos,
+ false,
+ Literal(Constant(pre)) /* can be null */ ,
+ Literal(Constant(newlabel)):Tree,
+ makeSymbolicAttrs,
+ Ident(_scope),
+ args);
+
+ atPos(pos) { Block(ts, Block( ts2, t)) }
+ }
+}
+
diff --git a/sources/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/sources/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
index 641a14abf6..b4a3b507b3 100644
--- a/sources/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
+++ b/sources/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
@@ -7,7 +7,7 @@ package scala.tools.nsc.ast.parser;
/** An nsc sub-component.
*/
-abstract class SyntaxAnalyzer extends SubComponent with Parsers with Scanners {
+abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParsers with Scanners {
val phaseName = "parser";
def newPhase(prev: Phase): StdPhase = new ParserPhase(prev);
class ParserPhase(prev: scala.tools.nsc.Phase) extends StdPhase(prev) {
diff --git a/sources/scala/tools/nsc/ast/parser/Tokens.scala b/sources/scala/tools/nsc/ast/parser/Tokens.scala
index cd6a4ded54..dc9dda7f7c 100644
--- a/sources/scala/tools/nsc/ast/parser/Tokens.scala
+++ b/sources/scala/tools/nsc/ast/parser/Tokens.scala
@@ -90,4 +90,7 @@ object Tokens {
final val RBRACKET = 93;
final val LBRACE = 94;
final val RBRACE = 95;
+
+ /** XML mode */
+ final val XMLSTART = 96;
}