@projectwallace/css-parser 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -0
- package/dist/arena.d.ts +84 -0
- package/dist/at-rule-prelude-parser-DlqYQAYH.js +464 -0
- package/dist/at-rule-prelude-parser.d.ts +24 -0
- package/dist/char-types.d.ts +7 -0
- package/dist/css-node.d.ts +31 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +28 -0
- package/dist/lexer-DXablYMZ.js +479 -0
- package/dist/lexer.d.ts +27 -0
- package/dist/parse-atrule-prelude.d.ts +8 -0
- package/dist/parse-atrule-prelude.js +11 -0
- package/dist/parse-selector.d.ts +7 -0
- package/dist/parse-selector.js +19 -0
- package/dist/parse.d.ts +9 -0
- package/dist/parse.js +512 -0
- package/dist/parser.d.ts +34 -0
- package/dist/selector-parser-2b3tGyri.js +365 -0
- package/dist/selector-parser.d.ts +25 -0
- package/dist/string-utils-B-rJII-E.js +440 -0
- package/dist/string-utils.d.ts +29 -0
- package/dist/token-types.d.ts +34 -0
- package/dist/tokenize.d.ts +8 -0
- package/dist/tokenize.js +14 -0
- package/dist/value-parser.d.ts +19 -0
- package/dist/walk.d.ts +20 -0
- package/package.json +78 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { L as Lexer, q as TOKEN_COMMA, y as TOKEN_EOF, l as TOKEN_WHITESPACE, a as TOKEN_FUNCTION, o as TOKEN_COLON, r as TOKEN_LEFT_BRACKET, h as TOKEN_DELIM, c as TOKEN_HASH, T as TOKEN_IDENT, s as TOKEN_RIGHT_BRACKET, t as TOKEN_LEFT_PAREN, u as TOKEN_RIGHT_PAREN } from './lexer-DXablYMZ.js';
|
|
2
|
+
import { d as NODE_SELECTOR, m as NODE_SELECTOR_LIST, J as is_whitespace, o as NODE_SELECTOR_CLASS, q as NODE_SELECTOR_ATTRIBUTE, M as trim_boundaries, s as NODE_SELECTOR_PSEUDO_ELEMENT, r as NODE_SELECTOR_PSEUDO_CLASS, n as NODE_SELECTOR_TYPE, p as NODE_SELECTOR_ID, u as NODE_SELECTOR_UNIVERSAL, v as NODE_SELECTOR_NESTING, t as NODE_SELECTOR_COMBINATOR } from './string-utils-B-rJII-E.js';
|
|
3
|
+
|
|
4
|
+
class SelectorParser {
|
|
5
|
+
lexer;
|
|
6
|
+
arena;
|
|
7
|
+
source;
|
|
8
|
+
selector_end;
|
|
9
|
+
constructor(arena, source) {
|
|
10
|
+
this.arena = arena;
|
|
11
|
+
this.source = source;
|
|
12
|
+
this.lexer = new Lexer(source, false);
|
|
13
|
+
this.selector_end = 0;
|
|
14
|
+
}
|
|
15
|
+
// Parse a selector range into selector nodes
|
|
16
|
+
// Always returns a NODE_SELECTOR wrapper with detailed selector nodes as children
|
|
17
|
+
parse_selector(start, end, line = 1) {
|
|
18
|
+
this.selector_end = end;
|
|
19
|
+
this.lexer.pos = start;
|
|
20
|
+
this.lexer.line = line;
|
|
21
|
+
let innerSelector = this.parse_selector_list();
|
|
22
|
+
if (innerSelector === null) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
let selectorWrapper = this.arena.create_node();
|
|
26
|
+
this.arena.set_type(selectorWrapper, NODE_SELECTOR);
|
|
27
|
+
this.arena.set_start_offset(selectorWrapper, start);
|
|
28
|
+
this.arena.set_length(selectorWrapper, end - start);
|
|
29
|
+
this.arena.set_start_line(selectorWrapper, line);
|
|
30
|
+
this.arena.set_first_child(selectorWrapper, innerSelector);
|
|
31
|
+
this.arena.set_last_child(selectorWrapper, innerSelector);
|
|
32
|
+
return selectorWrapper;
|
|
33
|
+
}
|
|
34
|
+
// Parse comma-separated selectors
|
|
35
|
+
parse_selector_list() {
|
|
36
|
+
let selectors = [];
|
|
37
|
+
let list_start = this.lexer.pos;
|
|
38
|
+
while (this.lexer.pos < this.selector_end) {
|
|
39
|
+
let selector = this.parse_complex_selector();
|
|
40
|
+
if (selector !== null) {
|
|
41
|
+
selectors.push(selector);
|
|
42
|
+
}
|
|
43
|
+
this.skip_whitespace();
|
|
44
|
+
if (this.lexer.pos >= this.selector_end) break;
|
|
45
|
+
this.lexer.next_token_fast(false);
|
|
46
|
+
let token_type = this.lexer.token_type;
|
|
47
|
+
if (token_type === TOKEN_COMMA) {
|
|
48
|
+
this.skip_whitespace();
|
|
49
|
+
continue;
|
|
50
|
+
} else {
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (selectors.length === 1) {
|
|
55
|
+
return selectors[0];
|
|
56
|
+
}
|
|
57
|
+
if (selectors.length > 1) {
|
|
58
|
+
let list_node = this.arena.create_node();
|
|
59
|
+
this.arena.set_type(list_node, NODE_SELECTOR_LIST);
|
|
60
|
+
this.arena.set_start_offset(list_node, list_start);
|
|
61
|
+
this.arena.set_length(list_node, this.lexer.pos - list_start);
|
|
62
|
+
this.arena.set_start_line(list_node, this.lexer.line);
|
|
63
|
+
this.arena.set_first_child(list_node, selectors[0]);
|
|
64
|
+
this.arena.set_last_child(list_node, selectors[selectors.length - 1]);
|
|
65
|
+
for (let i = 0; i < selectors.length - 1; i++) {
|
|
66
|
+
this.arena.set_next_sibling(selectors[i], selectors[i + 1]);
|
|
67
|
+
}
|
|
68
|
+
return list_node;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
// Parse a complex selector (with combinators)
|
|
73
|
+
// e.g., "div.class > p + span"
|
|
74
|
+
parse_complex_selector() {
|
|
75
|
+
let components = [];
|
|
76
|
+
this.skip_whitespace();
|
|
77
|
+
while (this.lexer.pos < this.selector_end) {
|
|
78
|
+
if (this.lexer.pos >= this.selector_end) break;
|
|
79
|
+
let compound = this.parse_compound_selector();
|
|
80
|
+
if (compound !== null) {
|
|
81
|
+
components.push(compound);
|
|
82
|
+
} else {
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
let combinator = this.try_parse_combinator();
|
|
86
|
+
if (combinator !== null) {
|
|
87
|
+
components.push(combinator);
|
|
88
|
+
this.skip_whitespace();
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
let pos_before = this.lexer.pos;
|
|
92
|
+
this.skip_whitespace();
|
|
93
|
+
if (this.lexer.pos >= this.selector_end) break;
|
|
94
|
+
this.lexer.next_token_fast(false);
|
|
95
|
+
let token_type = this.lexer.token_type;
|
|
96
|
+
if (token_type === TOKEN_COMMA || this.lexer.pos >= this.selector_end) {
|
|
97
|
+
this.lexer.pos = pos_before;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
this.lexer.pos = pos_before;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
if (components.length === 0) return null;
|
|
104
|
+
for (let i = 0; i < components.length - 1; i++) {
|
|
105
|
+
this.arena.set_next_sibling(components[i], components[i + 1]);
|
|
106
|
+
}
|
|
107
|
+
return components[0];
|
|
108
|
+
}
|
|
109
|
+
// Parse a compound selector (no combinators)
|
|
110
|
+
// e.g., "div.class#id[attr]:hover"
|
|
111
|
+
parse_compound_selector() {
|
|
112
|
+
let parts = [];
|
|
113
|
+
while (this.lexer.pos < this.selector_end) {
|
|
114
|
+
let pos_before = this.lexer.pos;
|
|
115
|
+
this.lexer.next_token_fast(false);
|
|
116
|
+
if (this.lexer.token_start >= this.selector_end) break;
|
|
117
|
+
let token_type = this.lexer.token_type;
|
|
118
|
+
if (token_type === TOKEN_EOF) break;
|
|
119
|
+
let part = this.parse_simple_selector();
|
|
120
|
+
if (part !== null) {
|
|
121
|
+
parts.push(part);
|
|
122
|
+
} else {
|
|
123
|
+
this.lexer.pos = pos_before;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (parts.length === 0) return null;
|
|
128
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
129
|
+
this.arena.set_next_sibling(parts[i], parts[i + 1]);
|
|
130
|
+
}
|
|
131
|
+
return parts[0];
|
|
132
|
+
}
|
|
133
|
+
// Parse a simple selector (single component)
|
|
134
|
+
parse_simple_selector() {
|
|
135
|
+
let token_type = this.lexer.token_type;
|
|
136
|
+
let start = this.lexer.token_start;
|
|
137
|
+
let end = this.lexer.token_end;
|
|
138
|
+
switch (token_type) {
|
|
139
|
+
case TOKEN_IDENT:
|
|
140
|
+
return this.create_type_selector(start, end);
|
|
141
|
+
case TOKEN_HASH:
|
|
142
|
+
return this.create_id_selector(start, end);
|
|
143
|
+
case TOKEN_DELIM:
|
|
144
|
+
let ch = this.source.charCodeAt(start);
|
|
145
|
+
if (ch === 46) {
|
|
146
|
+
return this.parse_class_selector(start);
|
|
147
|
+
} else if (ch === 42) {
|
|
148
|
+
return this.create_universal_selector(start, end);
|
|
149
|
+
} else if (ch === 38) {
|
|
150
|
+
return this.create_nesting_selector(start, end);
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
case TOKEN_LEFT_BRACKET:
|
|
154
|
+
return this.parse_attribute_selector(start);
|
|
155
|
+
case TOKEN_COLON:
|
|
156
|
+
return this.parse_pseudo(start);
|
|
157
|
+
case TOKEN_FUNCTION:
|
|
158
|
+
return this.parse_pseudo_function(start, end);
|
|
159
|
+
case TOKEN_WHITESPACE:
|
|
160
|
+
case TOKEN_COMMA:
|
|
161
|
+
return null;
|
|
162
|
+
default:
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Parse combinator (>, +, ~, or descendant space)
|
|
167
|
+
try_parse_combinator() {
|
|
168
|
+
let start = this.lexer.pos;
|
|
169
|
+
let has_whitespace = false;
|
|
170
|
+
while (this.lexer.pos < this.selector_end) {
|
|
171
|
+
let ch = this.source.charCodeAt(this.lexer.pos);
|
|
172
|
+
if (is_whitespace(ch)) {
|
|
173
|
+
has_whitespace = true;
|
|
174
|
+
this.lexer.pos++;
|
|
175
|
+
} else {
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (this.lexer.pos >= this.selector_end) return null;
|
|
180
|
+
this.lexer.next_token_fast(false);
|
|
181
|
+
if (this.lexer.token_type === TOKEN_DELIM) {
|
|
182
|
+
let ch = this.source.charCodeAt(this.lexer.token_start);
|
|
183
|
+
if (ch === 62 || ch === 43 || ch === 126) {
|
|
184
|
+
return this.create_combinator(start, this.lexer.token_end);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (has_whitespace) {
|
|
188
|
+
this.lexer.pos = start;
|
|
189
|
+
while (this.lexer.pos < this.selector_end) {
|
|
190
|
+
let ch = this.source.charCodeAt(this.lexer.pos);
|
|
191
|
+
if (is_whitespace(ch)) {
|
|
192
|
+
this.lexer.pos++;
|
|
193
|
+
} else {
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return this.create_combinator(start, this.lexer.pos);
|
|
198
|
+
}
|
|
199
|
+
this.lexer.pos = start;
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
// Parse class selector (.classname)
|
|
203
|
+
parse_class_selector(dot_pos) {
|
|
204
|
+
this.lexer.next_token_fast(false);
|
|
205
|
+
if (this.lexer.token_type !== TOKEN_IDENT) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
let node = this.arena.create_node();
|
|
209
|
+
this.arena.set_type(node, NODE_SELECTOR_CLASS);
|
|
210
|
+
this.arena.set_start_offset(node, dot_pos);
|
|
211
|
+
this.arena.set_length(node, this.lexer.token_end - dot_pos);
|
|
212
|
+
this.arena.set_start_line(node, this.lexer.line);
|
|
213
|
+
this.arena.set_content_start(node, this.lexer.token_start);
|
|
214
|
+
this.arena.set_content_length(node, this.lexer.token_end - this.lexer.token_start);
|
|
215
|
+
return node;
|
|
216
|
+
}
|
|
217
|
+
// Parse attribute selector ([attr], [attr=value], etc.)
|
|
218
|
+
parse_attribute_selector(start) {
|
|
219
|
+
let bracket_depth = 1;
|
|
220
|
+
let end = this.lexer.token_end;
|
|
221
|
+
while (this.lexer.pos < this.selector_end && bracket_depth > 0) {
|
|
222
|
+
this.lexer.next_token_fast(false);
|
|
223
|
+
let token_type = this.lexer.token_type;
|
|
224
|
+
if (token_type === TOKEN_LEFT_BRACKET) {
|
|
225
|
+
bracket_depth++;
|
|
226
|
+
} else if (token_type === TOKEN_RIGHT_BRACKET) {
|
|
227
|
+
bracket_depth--;
|
|
228
|
+
if (bracket_depth === 0) {
|
|
229
|
+
end = this.lexer.token_end;
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
let node = this.arena.create_node();
|
|
235
|
+
this.arena.set_type(node, NODE_SELECTOR_ATTRIBUTE);
|
|
236
|
+
this.arena.set_start_offset(node, start);
|
|
237
|
+
this.arena.set_length(node, end - start);
|
|
238
|
+
this.arena.set_start_line(node, this.lexer.line);
|
|
239
|
+
let trimmed = trim_boundaries(this.source, start + 1, end - 1);
|
|
240
|
+
if (trimmed) {
|
|
241
|
+
this.arena.set_content_start(node, trimmed[0]);
|
|
242
|
+
this.arena.set_content_length(node, trimmed[1] - trimmed[0]);
|
|
243
|
+
}
|
|
244
|
+
return node;
|
|
245
|
+
}
|
|
246
|
+
// Parse pseudo-class or pseudo-element (:hover, ::before)
|
|
247
|
+
parse_pseudo(start) {
|
|
248
|
+
let is_pseudo_element = false;
|
|
249
|
+
if (this.lexer.pos < this.selector_end && this.source.charCodeAt(this.lexer.pos) === 58) {
|
|
250
|
+
is_pseudo_element = true;
|
|
251
|
+
this.lexer.pos++;
|
|
252
|
+
}
|
|
253
|
+
this.lexer.next_token_fast(false);
|
|
254
|
+
let token_type = this.lexer.token_type;
|
|
255
|
+
if (token_type === TOKEN_IDENT) {
|
|
256
|
+
let node = this.arena.create_node();
|
|
257
|
+
this.arena.set_type(node, is_pseudo_element ? NODE_SELECTOR_PSEUDO_ELEMENT : NODE_SELECTOR_PSEUDO_CLASS);
|
|
258
|
+
this.arena.set_start_offset(node, start);
|
|
259
|
+
this.arena.set_length(node, this.lexer.token_end - start);
|
|
260
|
+
this.arena.set_start_line(node, this.lexer.line);
|
|
261
|
+
this.arena.set_content_start(node, this.lexer.token_start);
|
|
262
|
+
this.arena.set_content_length(node, this.lexer.token_end - this.lexer.token_start);
|
|
263
|
+
return node;
|
|
264
|
+
} else if (token_type === TOKEN_FUNCTION) {
|
|
265
|
+
return this.parse_pseudo_function_after_colon(start, is_pseudo_element);
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
// Parse pseudo-class function (:nth-child(), :is(), etc.)
|
|
270
|
+
parse_pseudo_function(_start, _end) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
// Parse pseudo-class function after we've seen the colon
|
|
274
|
+
parse_pseudo_function_after_colon(start, is_pseudo_element) {
|
|
275
|
+
let func_name_start = this.lexer.token_start;
|
|
276
|
+
let func_name_end = this.lexer.token_end - 1;
|
|
277
|
+
let paren_depth = 1;
|
|
278
|
+
let end = this.lexer.token_end;
|
|
279
|
+
while (this.lexer.pos < this.selector_end && paren_depth > 0) {
|
|
280
|
+
this.lexer.next_token_fast(false);
|
|
281
|
+
let token_type = this.lexer.token_type;
|
|
282
|
+
if (token_type === TOKEN_LEFT_PAREN) {
|
|
283
|
+
paren_depth++;
|
|
284
|
+
} else if (token_type === TOKEN_RIGHT_PAREN) {
|
|
285
|
+
paren_depth--;
|
|
286
|
+
if (paren_depth === 0) {
|
|
287
|
+
end = this.lexer.token_end;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
let node = this.arena.create_node();
|
|
293
|
+
this.arena.set_type(node, is_pseudo_element ? NODE_SELECTOR_PSEUDO_ELEMENT : NODE_SELECTOR_PSEUDO_CLASS);
|
|
294
|
+
this.arena.set_start_offset(node, start);
|
|
295
|
+
this.arena.set_length(node, end - start);
|
|
296
|
+
this.arena.set_start_line(node, this.lexer.line);
|
|
297
|
+
this.arena.set_content_start(node, func_name_start);
|
|
298
|
+
this.arena.set_content_length(node, func_name_end - func_name_start);
|
|
299
|
+
return node;
|
|
300
|
+
}
|
|
301
|
+
// Create simple selector nodes
|
|
302
|
+
create_type_selector(start, end) {
|
|
303
|
+
let node = this.arena.create_node();
|
|
304
|
+
this.arena.set_type(node, NODE_SELECTOR_TYPE);
|
|
305
|
+
this.arena.set_start_offset(node, start);
|
|
306
|
+
this.arena.set_length(node, end - start);
|
|
307
|
+
this.arena.set_start_line(node, this.lexer.line);
|
|
308
|
+
this.arena.set_content_start(node, start);
|
|
309
|
+
this.arena.set_content_length(node, end - start);
|
|
310
|
+
return node;
|
|
311
|
+
}
|
|
312
|
+
create_id_selector(start, end) {
|
|
313
|
+
let node = this.arena.create_node();
|
|
314
|
+
this.arena.set_type(node, NODE_SELECTOR_ID);
|
|
315
|
+
this.arena.set_start_offset(node, start);
|
|
316
|
+
this.arena.set_length(node, end - start);
|
|
317
|
+
this.arena.set_start_line(node, this.lexer.line);
|
|
318
|
+
this.arena.set_content_start(node, start + 1);
|
|
319
|
+
this.arena.set_content_length(node, end - start - 1);
|
|
320
|
+
return node;
|
|
321
|
+
}
|
|
322
|
+
create_universal_selector(start, end) {
|
|
323
|
+
let node = this.arena.create_node();
|
|
324
|
+
this.arena.set_type(node, NODE_SELECTOR_UNIVERSAL);
|
|
325
|
+
this.arena.set_start_offset(node, start);
|
|
326
|
+
this.arena.set_length(node, end - start);
|
|
327
|
+
this.arena.set_start_line(node, this.lexer.line);
|
|
328
|
+
this.arena.set_content_start(node, start);
|
|
329
|
+
this.arena.set_content_length(node, end - start);
|
|
330
|
+
return node;
|
|
331
|
+
}
|
|
332
|
+
create_nesting_selector(start, end) {
|
|
333
|
+
let node = this.arena.create_node();
|
|
334
|
+
this.arena.set_type(node, NODE_SELECTOR_NESTING);
|
|
335
|
+
this.arena.set_start_offset(node, start);
|
|
336
|
+
this.arena.set_length(node, end - start);
|
|
337
|
+
this.arena.set_start_line(node, this.lexer.line);
|
|
338
|
+
this.arena.set_content_start(node, start);
|
|
339
|
+
this.arena.set_content_length(node, end - start);
|
|
340
|
+
return node;
|
|
341
|
+
}
|
|
342
|
+
create_combinator(start, end) {
|
|
343
|
+
let node = this.arena.create_node();
|
|
344
|
+
this.arena.set_type(node, NODE_SELECTOR_COMBINATOR);
|
|
345
|
+
this.arena.set_start_offset(node, start);
|
|
346
|
+
this.arena.set_length(node, end - start);
|
|
347
|
+
this.arena.set_start_line(node, this.lexer.line);
|
|
348
|
+
this.arena.set_content_start(node, start);
|
|
349
|
+
this.arena.set_content_length(node, end - start);
|
|
350
|
+
return node;
|
|
351
|
+
}
|
|
352
|
+
// Helper to skip whitespace
|
|
353
|
+
skip_whitespace() {
|
|
354
|
+
while (this.lexer.pos < this.selector_end) {
|
|
355
|
+
let ch = this.source.charCodeAt(this.lexer.pos);
|
|
356
|
+
if (is_whitespace(ch)) {
|
|
357
|
+
this.lexer.pos++;
|
|
358
|
+
} else {
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export { SelectorParser as S };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CSSDataArena } from './arena';
|
|
2
|
+
export declare class SelectorParser {
|
|
3
|
+
private lexer;
|
|
4
|
+
private arena;
|
|
5
|
+
private source;
|
|
6
|
+
private selector_end;
|
|
7
|
+
constructor(arena: CSSDataArena, source: string);
|
|
8
|
+
parse_selector(start: number, end: number, line?: number): number | null;
|
|
9
|
+
private parse_selector_list;
|
|
10
|
+
private parse_complex_selector;
|
|
11
|
+
private parse_compound_selector;
|
|
12
|
+
private parse_simple_selector;
|
|
13
|
+
private try_parse_combinator;
|
|
14
|
+
private parse_class_selector;
|
|
15
|
+
private parse_attribute_selector;
|
|
16
|
+
private parse_pseudo;
|
|
17
|
+
private parse_pseudo_function;
|
|
18
|
+
private parse_pseudo_function_after_colon;
|
|
19
|
+
private create_type_selector;
|
|
20
|
+
private create_id_selector;
|
|
21
|
+
private create_universal_selector;
|
|
22
|
+
private create_nesting_selector;
|
|
23
|
+
private create_combinator;
|
|
24
|
+
private skip_whitespace;
|
|
25
|
+
}
|