|
1 | | -import pegs, strutils, ../xml |
| 1 | +import strutils, ../xml |
2 | 2 | from streams import newStringStream |
3 | 3 | from strtabs import hasKey |
4 | 4 |
|
5 | | - |
6 | | -let |
7 | | - attribute = r"[a-zA-Z][a-zA-Z0-9_\-]*" |
8 | | - classes = r"{\.[a-zA-Z0-9_][a-zA-Z0-9_\-]*}" |
9 | | - attributes = r"{\[" & attribute & r"\s*([\*\^\$\~]?\=\s*[\'""]?(\s*\ident\s*)+[\'""]?)?\]}" |
10 | | - pselectors = peg(r"\s*{\ident}?({'#'\ident})? (" & classes & ")* " & attributes & "*") |
11 | | - pattributes = peg(r"{\[{" & attribute & r"}\s*({[\*\^\$\~]?}\=\s*[\'""]?{(\s*\ident\s*)+}[\'""]?)?\]}") |
12 | | - |
13 | 5 | type |
| 6 | + TokenKind = enum |
| 7 | + TAG |
| 8 | + ID |
| 9 | + CLASS |
| 10 | + ATTR |
14 | 11 | Attribute = object |
15 | 12 | name: string |
16 | 13 | operator: char |
|
26 | 23 | QueryContext = object |
27 | 24 | root: seq[XmlNode] |
28 | 25 |
|
29 | | -proc newSelector(tag, id = "", classes: seq[string] = @[], attributes: seq[Attribute] = @[]): Selector = |
| 26 | +proc push(s: var Selector, kind: TokenKind, name: string) = |
| 27 | + case kind |
| 28 | + of TAG: |
| 29 | + s.tag = name |
| 30 | + of ID: |
| 31 | + s.id = name |
| 32 | + of CLASS: |
| 33 | + s.classes.add(name) |
| 34 | + else: |
| 35 | + echo "parse attribute" |
| 36 | + |
| 37 | +proc newSelector*(tag, id = "", classes: seq[string] = @[], attributes: seq[Attribute] = @[]): Selector = |
30 | 38 | result.combinator = ' ' |
31 | 39 | result.tag = tag |
32 | 40 | result.id = id |
@@ -140,32 +148,45 @@ proc searchCombined(parents: var seq[XmlNode], selectors: seq[Selector]) = |
140 | 148 |
|
141 | 149 | parents = found |
142 | 150 |
|
143 | | -proc parseSelector(token: string): Selector = |
| 151 | +proc parseSelector*(token: string): Selector = |
144 | 152 | result = newSelector() |
145 | 153 | # Universal selector |
146 | 154 | if token == "*": |
147 | 155 | result.tag = "*" |
148 | 156 | # Type selector |
149 | | - elif token =~ pselectors: |
150 | | - for i in 0..matches.len-1: |
151 | | - if matches[i].isNil: |
| 157 | + else: |
| 158 | + var |
| 159 | + pos: int |
| 160 | + length = token.len |
| 161 | + kind = TAG |
| 162 | + name = "" |
| 163 | + ch: char |
| 164 | + |
| 165 | + while pos < length: |
| 166 | + ch = token[pos] |
| 167 | + if ch in Whitespace: |
| 168 | + inc(pos) |
152 | 169 | continue |
153 | | - |
154 | | - let ch = matches[i][0] |
155 | | - case ch: |
156 | | - of '#': |
157 | | - matches[i].delete(0, 0) |
158 | | - result.id = matches[i] |
159 | | - of '.': |
160 | | - matches[i].delete(0, 0) |
161 | | - result.classes.add(matches[i]) |
162 | | - of '[': |
163 | | - if matches[i] =~ pattributes: |
164 | | - result.attributes.add(newAttribute(matches[1], matches[2], matches[3])) |
| 170 | + if ch in {'#', '.', '[', '=', ']'}: |
| 171 | + result.push(kind, name) |
| 172 | + if ch == '#': |
| 173 | + kind = ID |
| 174 | + elif ch == '.': |
| 175 | + kind = CLASS |
| 176 | + elif ch == '[': |
| 177 | + kind = ATTR |
| 178 | + name = "" |
| 179 | + #echo "next ", kind |
165 | 180 | else: |
166 | | - result.tag = matches[i] |
167 | | - else: |
168 | | - discard |
| 181 | + #if name.len == 0: |
| 182 | + # assert ch in IdentStartChars |
| 183 | + name.add(ch) |
| 184 | + |
| 185 | + if pos == length-1: |
| 186 | + result.push(kind, name) |
| 187 | + break |
| 188 | + |
| 189 | + inc(pos) |
169 | 190 |
|
170 | 191 | proc select*(q: QueryContext, s: string = ""): seq[XmlNode] = |
171 | 192 | ## Return list of nodes matched by CSS selector |
|
0 commit comments