equal
deleted
inserted
replaced
10 import java.util.regex.Pattern |
10 import java.util.regex.Pattern |
11 |
11 |
12 |
12 |
13 object YXML { |
13 object YXML { |
14 |
14 |
15 /* markers */ |
15 /* chunk markers */ |
16 |
16 |
17 private val X = '\5' |
17 private val X = '\5' |
18 private val Y = '\6' |
18 private val Y = '\6' |
19 |
19 |
20 def detect(source: CharSequence) = { |
20 def detect(source: CharSequence) = { |
21 source.length >= 2 && |
21 source.length >= 2 && |
22 source.charAt(0) == X && |
22 source.charAt(0) == X && |
23 source.charAt(1) == Y |
23 source.charAt(1) == Y |
|
24 } |
|
25 |
|
26 |
|
27 /* iterate over chunks (resembles space_explode in ML) */ |
|
28 |
|
29 private def chunks(sep: Char, source: CharSequence) = new Iterator[CharSequence] { |
|
30 private val end = source.length |
|
31 private var state = if (end == 0) None else get_chunk(-1) |
|
32 private def get_chunk(i: Int) = { |
|
33 if (i < end) { |
|
34 var j = i; do j += 1 while (j < end && source.charAt(j) != sep) |
|
35 Some((source.subSequence(i + 1, j), j)) |
|
36 } |
|
37 else None |
|
38 } |
|
39 |
|
40 def hasNext() = state.isDefined |
|
41 def next() = state match { |
|
42 case Some((s, i)) => { state = get_chunk(i); s } |
|
43 case None => throw new NoSuchElementException("next on empty iterator") |
|
44 } |
24 } |
45 } |
25 |
46 |
26 |
47 |
27 /* parsing */ |
48 /* parsing */ |
28 |
49 |
33 private def err_element() = err("bad element") |
54 private def err_element() = err("bad element") |
34 private def err_unbalanced(name: String) = |
55 private def err_unbalanced(name: String) = |
35 if (name == "") err("unbalanced element") |
56 if (name == "") err("unbalanced element") |
36 else err("unbalanced element \"" + name + "\"") |
57 else err("unbalanced element \"" + name + "\"") |
37 |
58 |
38 private val X_pattern = Pattern.compile(Pattern.quote(X.toString)) |
59 private def parse_attrib(s: CharSequence) = |
39 private val Y_pattern = Pattern.compile(Pattern.quote(Y.toString)) |
60 chunks('=', s).toList match { |
40 private val eq_pattern = Pattern.compile(Pattern.quote("=")) |
61 case Nil => err_attribute() |
41 |
62 case "" :: _ => err_attribute() |
42 private def parse_attrib(s: String) = |
63 case a :: xs => (a.toString, xs.mkString("=")) |
43 eq_pattern.split(s, 2).toList match { |
|
44 case List(x, y) => if (x != "") (x, y) else err_attribute() |
|
45 case _ => err_attribute() |
|
46 } |
64 } |
47 |
65 |
48 |
66 |
49 def parse_body(source: CharSequence) = { |
67 def parse_body(source: CharSequence) = { |
50 |
68 |
68 |
86 |
69 |
87 |
70 /* parse chunks */ |
88 /* parse chunks */ |
71 |
89 |
72 stack = List((("", Nil), Nil)) |
90 stack = List((("", Nil), Nil)) |
73 for (chunk <- X_pattern.split(source) if chunk != "") { |
91 for (chunk <- chunks(X, source) if chunk != "") { |
74 Y_pattern.split(chunk).toList match { |
92 chunks(Y, chunk).toList match { |
75 case Nil => pop() |
93 case List("", "") => pop() |
76 case "" :: name :: atts => push(name, atts.map(parse_attrib)) |
94 case "" :: name :: atts => push(name.toString, atts.map(parse_attrib)) |
77 case txts => for (txt <- txts) add(XML.Text(txt)) |
95 case txts => for (txt <- txts) add(XML.Text(txt.toString)) |
78 } |
96 } |
79 } |
97 } |
80 stack match { |
98 stack match { |
81 case List((("", _), result)) => result.reverse |
99 case List((("", _), result)) => result.reverse |
82 case ((name, _), _) :: _ => err_unbalanced(name) |
100 case ((name, _), _) :: _ => err_unbalanced(name) |