summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorburaq <buraq@epfl.ch>2004-02-11 18:41:06 +0000
committerburaq <buraq@epfl.ch>2004-02-11 18:41:06 +0000
commitc9ced67aa4af57c87b03af07b421f34ac4218c2b (patch)
tree252e0643e687e4b2f252f5559126ccbec3ea8685
parent6a930f9ca62b11f01afca5e04c8d2d6be2640b07 (diff)
downloadscala-c9ced67aa4af57c87b03af07b421f34ac4218c2b.tar.gz
scala-c9ced67aa4af57c87b03af07b421f34ac4218c2b.tar.bz2
scala-c9ced67aa4af57c87b03af07b421f34ac4218c2b.zip
xml literals!
-rw-r--r--sources/scala/tools/scalac/ast/parser/Parser.scala141
-rw-r--r--sources/scala/tools/scalac/ast/parser/Scanner.scala128
2 files changed, 261 insertions, 8 deletions
diff --git a/sources/scala/tools/scalac/ast/parser/Parser.scala b/sources/scala/tools/scalac/ast/parser/Parser.scala
index b1fb1c8bcb..fcf48c48a5 100644
--- a/sources/scala/tools/scalac/ast/parser/Parser.scala
+++ b/sources/scala/tools/scalac/ast/parser/Parser.scala
@@ -16,6 +16,7 @@ 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 {
@@ -358,6 +359,42 @@ class Parser(unit: Unit) {
make.LabelDef(pos, lname, new Array[Tree$Ident](0), rhs)
}
+ def makeXML(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 makeXML(pos:int, n:Name, args:Array[Tree]):Tree =
+ makeXML(pos, false, gen.mkStringLit( pos, n.toString() ), 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 {
@@ -480,6 +517,7 @@ class Parser(unit: Unit) {
final val STAR = Name.fromString("*");
final val BAR = Name.fromString("|");
final val OPT = Name.fromString("?");
+ final val LT = Name.fromString("<");
def ident(): Name =
if (s.token == IDENTIFIER) {
@@ -602,13 +640,10 @@ class Parser(unit: Unit) {
s.nextToken();
if (isSymLit) {
val pos = s.pos;
- var symt = scalaDot(s.pos, Names.Symbol);
- if (isPattern) symt = convertToTypeId(symt);
- val ts = new myTreeList();
- ts.append(t);
if (s.token == LPAREN || s.token == LBRACE)
- ts.append(argumentExprs());
- make.Apply(pos, symt, ts.toArray());
+ makeXML( pos, isPattern, t, argumentExprs() );
+ else
+ makeXML( pos, isPattern, t, Tree.EMPTY_ARRAY );
} else {
t
}
@@ -932,6 +967,7 @@ class Parser(unit: Unit) {
}
/* SimpleExpr ::= literal
+ * | xmlExpr
* | StableRef
* | `(' [Expr] `)'
* | BlockExpr
@@ -947,7 +983,13 @@ class Parser(unit: Unit) {
SYMBOLLIT | TRUE | FALSE | NULL =>
t = literal(false);
case IDENTIFIER | THIS | SUPER =>
- t = stableRef(true, false);
+ t = if( s.name == LT ) {
+ val tt = xmlExpr(); /* top-level xml expression */
+ s.nextToken();
+ tt
+ } else {
+ stableRef(true, false);
+ }
case LPAREN =>
val pos = s.skipToken();
if (s.token == RPAREN) {
@@ -998,6 +1040,91 @@ class Parser(unit: Unit) {
null;//dummy
}
+ /** xmlExpr ::= '<' ident '>' { xmlExpr | '{' simpleExpr '}' } '<''/'ident'>'
+ */
+ def xmlExpr():Tree = {
+ val pos = s.pos;
+
+ var empty = false;
+
+ /* [40] STag ::= '<' Name (S Attribute)* S? '>'
+ */
+ val elemName = s.xmlName();
+ s.xmlSpaceOpt();
+ var attrMap = ListMap.Empty[Name,String];
+ /* [41] Attribute ::= Name Eq AttValue
+ * [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
+ */
+ while(( s.ch != '>' )&&( !empty )) {
+ if( s.ch == '/' ) {
+ s.nextch();
+ empty = true;
+ } else if( 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.xmlValue( 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 );
+ } else {
+ s.xml_syntaxError("attribute, > or /> expected in element declaration");
+ }
+ }
+ s.xmlToken('>');
+ /* Console.println("startTag of:"+elemName); */
+ s.xmlSpaceOpt();
+ if( empty ) {
+ makeXML( pos, elemName, Tree.EMPTY_ARRAY );
+ } else {
+
+ val ts = new myTreeList();
+
+ var exit = false;
+ while( !exit ) {
+ s.ch match {
+ case '<' => {
+ s.nextch();
+ if( s.ch != '/' ) { /* search end tag */
+ ts.append( xmlExpr() );
+ } else {
+ exit = true
+ }
+ }
+ case _ => {
+ val pos = s.pos;
+ val str = s.xmlText();/* text node */
+ ts.append( gen.mkStringLit( pos, str ));
+ }
+
+ }
+ }
+
+ /* [42] ETag ::= '</' Name S? '>' */
+ s.xmlToken('/');
+ if( elemName != s.xmlName() )
+ s.xml_syntaxError("expected closing tag of "+elemName);
+ else {
+ s.xmlSpaceOpt();
+ s.xmlToken('>')
+ }
+ /* Console.println("endTag of:"+elemName); */
+ s.xmlSpaceOpt();
+ makeXML( pos, elemName, ts.toArray(), attrMap );
+ }
+ }
/** ArgumentExprs ::= `(' [Exprs] `)'
* | BlockExpr
*/
diff --git a/sources/scala/tools/scalac/ast/parser/Scanner.scala b/sources/scala/tools/scalac/ast/parser/Scanner.scala
index 2bbb15801b..6ef2524554 100644
--- a/sources/scala/tools/scalac/ast/parser/Scanner.scala
+++ b/sources/scala/tools/scalac/ast/parser/Scanner.scala
@@ -651,12 +651,138 @@ class Scanner(_unit: Unit) extends TokenData {
}
}
+ /* X L
+ */
+
+ /* methods for XML tokenizing, see XML 1.0 rec, available from www.w3.org/xml */
+
+ /* M
+ */
+
+ def xml_syntaxError(s:String) = {
+ syntaxError("in XML literal: "+s);
+ xml_nextch();
+ }
+
+ /* this helper functions updates ccol and cline, only necessary in whitespace
+ * production
+ */
+ def xml_nextch() = {
+ nextch();
+ ch match {
+ case '\r' => {
+ cline = cline + 1;
+ ccol = 0;
+ nextch(); /* in compliance with XML spec */
+ if( ch == '\n' ) {
+ ccol = 0;
+ }
+ }
+ case '\n' => {
+ cline = cline + 1;
+ ccol = 0;
+ }
+ case _ =>
+ }
+ }
+
+ def xml_isSpace() = ch match {
+ case ' ' | '\t' | '\r' | '\n' => true
+ case _ => false;
+ }
+
+ def xmlSpaceOpt() = {
+ while( xml_isSpace() ) {
+ xml_nextch();
+ }
+ pos = Position.encode(cline, ccol);
+ }
+
+ /** [3] S ::= (#x20 | #x9 | #xD | #xA)+
+ */
+ def xmlSpace() = {
+ if( xml_isSpace() ) {
+ xml_nextch();
+ xmlSpaceOpt()
+ } else {
+ pos = Position.encode(cline, ccol);
+ xml_syntaxError("whitespace expected");
+ }
+ }
+
+ /** a subset of
+ * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender
+ *
+ * todo: add unicode letters and digits as well as combining chars and extenders
+ **/
+ def xml_isNameChar() = 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' |
+ 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' |
+ 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' |
+ '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' |
+ '.' | '-' | '_' | ':' => true;
+ case _ => false
+ }
+
+ def xml_isNameStart() = 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' |
+ 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' |
+ 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' |
+ '_' | ':' => true;
+ case _ => false
+ }
+ /** a subset of (see isNameChar() )
+ * [5] Name ::= (Letter | '_' | ':') (NameChar)*
+ */
+ def xmlName():Name = {
+ if( xml_isNameStart() ) {
+ val index = bp;
+ while( xml_isNameChar() ) {
+ nextch();
+ }
+ pos = Position.encode(cline, ccol);
+ Name.fromAscii(buf, index, bp - index);
+ } else {
+ xml_syntaxError("name expected");
+ Name.fromString("-error-");
+ }
+ }
+
+ def xmlValue(endch:char):String = {
+ pos = Position.encode(cline, ccol);
+ val index = bp;
+ while ( ch != endch ) {
+ if(( ch == '<' )||( ch == '&' ))
+ syntaxError(ch.asInstanceOf[char]+" not allowed here");
+ xml_nextch();
+ };
+ pos = Position.encode(cline, ccol);
+ new String(buf, index, bp-index);
+ }
+
+ def xmlText():String = xmlValue('<');
+
+ def xmlToken(that:char):unit = {
+ if( ch == that ) {
+ xml_nextch();
+ pos = Position.encode(cline, ccol);
+ } else {
+ pos = Position.encode(cline, ccol);
+ xml_syntaxError("'"+that+"' expected instead of '"+ch.asInstanceOf[char]+"'");
+ }
+ }
+ /* end XML tokenizing */
+
def name2token(name: Name): int =
if (name.index <= maxKey) key(name.index) else IDENTIFIER;
def token2string(token: int): String = token match {
case IDENTIFIER =>
- "identifier"
+ "identifier"/* + \""+name+"\""*/
case CHARLIT =>
"character literal"
case INTLIT =>