Skip to content

Commit 1dfe656

Browse files
committed
Merge pull request #79 from DarkDimius/noxml
Remove dependency on scala-xml.
2 parents a30efa6 + 517ca50 commit 1dfe656

File tree

8 files changed

+196
-45
lines changed

8 files changed

+196
-45
lines changed

src/dotty/tools/dotc/parsing/MarkupParserCommon.scala

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,16 @@
88
package dotty.tools.dotc
99
package parsing
1010

11-
import scala.xml._
12-
import scala.xml.parsing._
11+
import Utility._
12+
import scala.reflect.internal.Chars.SU
1313

14-
import scala.io.Source
15-
import scala.xml.dtd._
16-
import scala.annotation.switch
17-
import Utility.Escapes.{ pairs => unescape }
1814

19-
import Utility.SU
2015

2116
/** This is not a public trait - it contains common code shared
2217
* between the library level XML parser and the compiler's.
2318
* All members should be accessed through those.
2419
*/
25-
private[dotty] trait MarkupParserCommon extends TokenTests {
20+
private[dotty] trait MarkupParserCommon {
2621
protected def unreachable = scala.sys.error("Cannot be reached.")
2722

2823
// type HandleType // MarkupHandler, SymbolicXMLBuilder

src/dotty/tools/dotc/parsing/MarkupParsers.scala

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@ package parsing
55
import scala.collection.mutable
66
import mutable.{ Buffer, ArrayBuffer, ListBuffer }
77
import scala.util.control.ControlThrowable
8-
import util.SourceFile
9-
import scala.xml.{ Text, TextBuffer }
10-
import scala.xml.Utility.{ isNameStart, isNameChar, isSpace }
11-
import scala.reflect.internal.Chars.{ SU, LF }
8+
import scala.reflect.internal.Chars.SU
129
import Parsers._
1310
import util.Positions._
1411
import core._
15-
import ast.Trees._
1612
import Constants._
13+
import Utility._
1714

1815

1916
// XXX/Note: many/most of the functions in here are almost direct cut and pastes
@@ -50,7 +47,7 @@ object MarkupParsers {
5047

5148
class MarkupParser(parser: Parser, final val preserveWS: Boolean) extends MarkupParserCommon {
5249

53-
import Tokens.{ EMPTY, LBRACE, RBRACE }
50+
import Tokens.{ LBRACE, RBRACE }
5451

5552
type PositionType = Position
5653
type InputType = CharArrayReader
@@ -181,11 +178,20 @@ object MarkupParsers {
181178
}
182179

183180
def appendText(pos: Position, ts: Buffer[Tree], txt: String): Unit = {
184-
val toAppend =
185-
if (preserveWS) Seq(txt)
186-
else TextBuffer.fromString(txt).toText map (_.text)
181+
def append(t: String) = ts append handle.text(pos, t)
187182

188-
toAppend foreach (t => ts append handle.text(pos, t))
183+
if (preserveWS) append(txt)
184+
else {
185+
val sb = new StringBuilder()
186+
187+
txt foreach { c =>
188+
if (!isSpace(c)) sb append c
189+
else if (sb.isEmpty || !isSpace(sb.last)) sb append ' '
190+
}
191+
192+
val trimmed = sb.toString.trim
193+
if (!trimmed.isEmpty) append(trimmed)
194+
}
189195
}
190196

191197
/** adds entity/character to ts as side-effect

src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,10 @@ import Flags._
1313
import Contexts._
1414
import Names._
1515
import ast.Trees._
16-
import ast.TreeInfo
1716
import Decorators._
1817
import StdNames._
1918
import util.Positions._
20-
import Types._
2119
import Constants._
22-
import NameOps._
23-
import util.Chars._
2420
import ScriptParsers._
2521
import annotation.switch
2622
import util.DotClass

src/dotty/tools/dotc/parsing/Scanners.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ package dotty.tools
22
package dotc
33
package parsing
44

5-
import Tokens._
65
import core.Names._, core.Contexts._, core.Decorators._, util.Positions._
76
import core.StdNames._
87
import util.SourceFile
98
import java.lang.Character.isDigit
109
import scala.reflect.internal.Chars._
1110
import Tokens._
1211
import scala.annotation.{ switch, tailrec }
13-
import scala.collection.{ mutable, immutable }
14-
import mutable.{ ListBuffer, ArrayBuffer }
15-
import scala.xml.Utility.isNameStart
12+
import scala.collection.mutable
13+
import mutable.ListBuffer
14+
import Utility.isNameStart
15+
1616

1717
object Scanners {
1818

src/dotty/tools/dotc/parsing/ScriptParsers.scala

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,9 @@ package dotty.tools
22
package dotc
33
package parsing
44

5-
import util.{ SourceFile, FreshNameCreator }
5+
import util.SourceFile
66
import core._
7-
import Flags._
87
import Contexts._
9-
import Names._
10-
import ast.Trees._
11-
import Decorators._
12-
import StdNames._
13-
import util.Chars.isScalaLetter
14-
import util.Positions._
15-
import Types._
16-
import Constants._
17-
import NameOps._
18-
import scala.reflect.internal.Chars._
19-
import annotation.switch
208
import Parsers._
219

2210

src/dotty/tools/dotc/parsing/SymbolicXMLBuilder.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ package dotty.tools
22
package dotc
33
package parsing
44

5-
import scala.collection.{ mutable, immutable }
5+
import scala.collection.mutable
66
import scala.xml.{ EntityRef, Text }
7-
import scala.xml.XML.{ xmlns }
87
import core._
98
import Flags.Mutable
10-
import Names._, NameOps._, StdNames._, Decorators._, ast.Trees._, ast.{tpd, untpd}, Constants._
9+
import Names._, StdNames._, ast.Trees._, ast.{tpd, untpd}
1110
import Symbols._, Contexts._
1211
import util.Positions._
1312
import Parsers.Parser
@@ -203,7 +202,7 @@ class SymbolicXMLBuilder(parser: Parser, preserveWS: Boolean)(implicit ctx: Cont
203202

204203
/** Extract all the namespaces from the attribute map. */
205204
val namespaces: List[Tree] =
206-
for (z <- attrMap.keys.toList ; if z startsWith xmlns) yield {
205+
for (z <- attrMap.keys.toList ; if z startsWith "xmlns") yield {
207206
val ns = splitPrefix(z) match {
208207
case (Some(_), rest) => rest
209208
case _ => null

src/dotty/tools/dotc/parsing/Tokens.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ package dotty.tools
22
package dotc
33
package parsing
44

5-
import collection.mutable
65
import collection.immutable.BitSet
7-
import scala.annotation.switch
86

97
object Tokens {
108

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package dotty.tools.dotc.parsing
2+
3+
import scala.collection.mutable
4+
5+
6+
/**
7+
* The `Utility` object provides utility functions for processing instances
8+
* of bound and not bound XML classes, as well as escaping text nodes.
9+
*
10+
* @author Burak Emir
11+
*/
12+
object Utility {
13+
import scala.reflect.internal.Chars.SU
14+
15+
private val unescMap = Map(
16+
"lt" -> '<',
17+
"gt" -> '>',
18+
"amp" -> '&',
19+
"quot" -> '"',
20+
"apos" -> '\''
21+
)
22+
23+
/**
24+
* Appends unescaped string to `s`, `amp` becomes `&amp;`,
25+
* `lt` becomes `&lt;` etc..
26+
*
27+
* @return `'''null'''` if `ref` was not a predefined entity.
28+
*/
29+
private final def unescape(ref: String, s: StringBuilder): StringBuilder =
30+
((unescMap get ref) map (s append _)).orNull
31+
32+
def parseAttributeValue[T](value: String, text: String => T, entityRef: String => T): List[T] = {
33+
val sb = new StringBuilder
34+
var rfb: StringBuilder = null
35+
val nb = new mutable.ListBuffer[T]()
36+
37+
val it = value.iterator
38+
while (it.hasNext) {
39+
var c = it.next()
40+
// entity! flush buffer into text node
41+
if (c == '&') {
42+
c = it.next()
43+
if (c == '#') {
44+
c = it.next()
45+
val theChar = parseCharRef ({ ()=> c },{ () => c = it.next() },{s => throw new RuntimeException(s)}, {s => throw new RuntimeException(s)})
46+
sb.append(theChar)
47+
}
48+
else {
49+
if (rfb eq null) rfb = new StringBuilder()
50+
rfb append c
51+
c = it.next()
52+
while (c != ';') {
53+
rfb.append(c)
54+
c = it.next()
55+
}
56+
val ref = rfb.toString()
57+
rfb.clear()
58+
unescape(ref,sb) match {
59+
case null =>
60+
if (!sb.isEmpty) { // flush buffer
61+
nb += text(sb.toString())
62+
sb.clear()
63+
}
64+
nb += entityRef(ref) // add entityref
65+
case _ =>
66+
}
67+
}
68+
}
69+
else sb append c
70+
}
71+
72+
if(!sb.isEmpty) // flush buffer
73+
nb += text(sb.toString())
74+
75+
nb.toList
76+
}
77+
78+
/**
79+
* {{{
80+
* CharRef ::= "&amp;#" '0'..'9' {'0'..'9'} ";"
81+
* | "&amp;#x" '0'..'9'|'A'..'F'|'a'..'f' { hexdigit } ";"
82+
* }}}
83+
* See [66]
84+
*/
85+
def parseCharRef(ch: () => Char, nextch: () => Unit, reportSyntaxError: String => Unit, reportTruncatedError: String => Unit): String = {
86+
val hex = (ch() == 'x') && { nextch(); true }
87+
val base = if (hex) 16 else 10
88+
var i = 0
89+
while (ch() != ';') {
90+
ch() match {
91+
case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' =>
92+
i = i * base + ch().asDigit
93+
case 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
94+
| 'A' | 'B' | 'C' | 'D' | 'E' | 'F' =>
95+
if (! hex)
96+
reportSyntaxError("hex char not allowed in decimal char ref\n" +
97+
"Did you mean to write &#x ?")
98+
else
99+
i = i * base + ch().asDigit
100+
case SU =>
101+
reportTruncatedError("")
102+
case _ =>
103+
reportSyntaxError("character '" + ch() + "' not allowed in char ref\n")
104+
}
105+
nextch()
106+
}
107+
new String(Array(i), 0, 1)
108+
}
109+
110+
/** {{{
111+
* (#x20 | #x9 | #xD | #xA)
112+
* }}} */
113+
final def isSpace(ch: Char): Boolean = ch match {
114+
case '\u0009' | '\u000A' | '\u000D' | '\u0020' => true
115+
case _ => false
116+
}
117+
/** {{{
118+
* (#x20 | #x9 | #xD | #xA)+
119+
* }}} */
120+
final def isSpace(cs: Seq[Char]): Boolean = cs.nonEmpty && (cs forall isSpace)
121+
122+
/** {{{
123+
* NameChar ::= Letter | Digit | '.' | '-' | '_' | ':'
124+
* | CombiningChar | Extender
125+
* }}}
126+
* See [4] and Appendix B of XML 1.0 specification.
127+
*/
128+
def isNameChar(ch: Char) = {
129+
import java.lang.Character._
130+
// The constants represent groups Mc, Me, Mn, Lm, and Nd.
131+
132+
isNameStart(ch) || (getType(ch).toByte match {
133+
case COMBINING_SPACING_MARK |
134+
ENCLOSING_MARK | NON_SPACING_MARK |
135+
MODIFIER_LETTER | DECIMAL_DIGIT_NUMBER => true
136+
case _ => ".-:" contains ch
137+
})
138+
}
139+
140+
/** {{{
141+
* NameStart ::= ( Letter | '_' )
142+
* }}}
143+
* where Letter means in one of the Unicode general
144+
* categories `{ Ll, Lu, Lo, Lt, Nl }`.
145+
*
146+
* We do not allow a name to start with `:`.
147+
* See [3] and Appendix B of XML 1.0 specification
148+
*/
149+
def isNameStart(ch: Char) = {
150+
import java.lang.Character._
151+
152+
getType(ch).toByte match {
153+
case LOWERCASE_LETTER |
154+
UPPERCASE_LETTER | OTHER_LETTER |
155+
TITLECASE_LETTER | LETTER_NUMBER => true
156+
case _ => ch == '_'
157+
}
158+
}
159+
160+
/** {{{
161+
* Name ::= ( Letter | '_' ) (NameChar)*
162+
* }}}
163+
* See [5] of XML 1.0 specification.
164+
*/
165+
def isName(s: String) =
166+
s.nonEmpty && isNameStart(s.head) && (s.tail forall isNameChar)
167+
168+
}
169+

0 commit comments

Comments
 (0)