@excom/quark-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.
Files changed (32) hide show
  1. package/.rush/temp/chunked-rush-logs/quark-parser.apply-exports.chunks.jsonl +1 -0
  2. package/.rush/temp/chunked-rush-logs/quark-parser.build_package-metas.chunks.jsonl +1 -0
  3. package/.rush/temp/operation/apply-exports/all.log +1 -0
  4. package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
  5. package/.rush/temp/operation/apply-exports/state.json +3 -0
  6. package/.rush/temp/operation/build_package-metas/all.log +1 -0
  7. package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
  8. package/.rush/temp/operation/build_package-metas/state.json +3 -0
  9. package/.rush/temp/shrinkwrap-deps.json +3 -0
  10. package/config/rig.json +5 -0
  11. package/index.ts +12 -0
  12. package/package.json +39 -0
  13. package/rush-logs/quark-parser.apply-exports.cache.log +1 -0
  14. package/rush-logs/quark-parser.apply-exports.log +1 -0
  15. package/rush-logs/quark-parser.build_package-metas.cache.log +1 -0
  16. package/rush-logs/quark-parser.build_package-metas.log +1 -0
  17. package/src/error.ts +24 -0
  18. package/src/parser.ts +1482 -0
  19. package/src/tables.ts +77 -0
  20. package/src/tokenizer.ts +443 -0
  21. package/src/types.ts +497 -0
  22. package/support/docs/README.md +443 -0
  23. package/support/package-meta.json +33 -0
  24. package/support/tests/grammar-docs.test.ts +109 -0
  25. package/support/tests/parser-at-rules.test.ts +430 -0
  26. package/support/tests/parser-declarations.test.ts +152 -0
  27. package/support/tests/parser-edge-cases.test.ts +296 -0
  28. package/support/tests/parser-expressions.test.ts +413 -0
  29. package/support/tests/parser-real-world.test.ts +429 -0
  30. package/support/tests/parser-selectors.test.ts +169 -0
  31. package/support/tests/tokenizer.test.ts +268 -0
  32. package/tsconfig.json +5 -0
package/src/types.ts ADDED
@@ -0,0 +1,497 @@
1
+ /**
2
+ * Token and AST node types for the Quark language.
3
+ *
4
+ * Quark is a derivative of CSS: its own at-rules plus the CSS constructs
5
+ * the engine runs, with two accessors CSS does not have:
6
+ * - dot accessor: `$obj.field`, `prop("provision").body`, `item.name`
7
+ * - bracket accessor: `$obj["field"]`, `$tags[$index]`
8
+ *
9
+ * This is a real AST: expressions are structured nodes, not token soup, and
10
+ * whitespace and punctuation are not represented.
11
+ */
12
+
13
+ /** Numeric offsets into the original source string. */
14
+ export interface Span {
15
+ start: number;
16
+ end: number;
17
+ }
18
+
19
+ /*
20
+ * ---------------------------------------------------------------------------
21
+ * Tokens
22
+ * ---------------------------------------------------------------------------
23
+ */
24
+
25
+ export type TokenType =
26
+ | "ident"
27
+ | "variable"
28
+ | "at"
29
+ | "string"
30
+ | "number"
31
+ | "hash"
32
+ | "punct"
33
+ | "url"
34
+ | "comment";
35
+
36
+ export interface Token extends Span {
37
+ type: TokenType;
38
+ /**
39
+ * ident name / variable name (no `$`) / at-keyword name (no `@`) / raw
40
+ * string contents (no quotes, escapes preserved) / number text (no unit) /
41
+ * hash text (no `#`) / punctuation characters / raw url contents / comment
42
+ * text (no delimiters).
43
+ */
44
+ value: string;
45
+ /** Unit for number tokens (`px`, `%`, `em`, ...). */
46
+ unit?: string;
47
+ /** Quote character for string tokens. */
48
+ quote?: '"' | "'";
49
+ /** Whether whitespace or a comment directly precedes this token. */
50
+ ws: boolean;
51
+ }
52
+
53
+ export interface TokenizeResult {
54
+ tokens: Token[];
55
+ comments: Token[];
56
+ }
57
+
58
+ /*
59
+ * ---------------------------------------------------------------------------
60
+ * Statements
61
+ * ---------------------------------------------------------------------------
62
+ */
63
+
64
+ export interface BaseNode extends Span {
65
+ type: string;
66
+ }
67
+
68
+ export type Statement = Rule | AtRule | Declaration | CommentNode;
69
+
70
+ export interface Stylesheet extends BaseNode {
71
+ type: "stylesheet";
72
+ body: Statement[];
73
+ }
74
+
75
+ export interface Block extends BaseNode {
76
+ type: "block";
77
+ body: Statement[];
78
+ }
79
+
80
+ export interface CommentNode extends BaseNode {
81
+ type: "comment";
82
+ text: string;
83
+ }
84
+
85
+ export interface Rule extends BaseNode {
86
+ type: "rule";
87
+ selector: SelectorList;
88
+ block: Block;
89
+ }
90
+
91
+ export interface Declaration extends BaseNode {
92
+ type: "declaration";
93
+ property: Property | Variable;
94
+ value: Expression;
95
+ }
96
+
97
+ export interface Property extends BaseNode {
98
+ type: "property";
99
+ name: string;
100
+ }
101
+
102
+ /*
103
+ * ---------------------------------------------------------------------------
104
+ * Selectors
105
+ * ---------------------------------------------------------------------------
106
+ */
107
+
108
+ export interface SelectorList extends BaseNode {
109
+ type: "selector_list";
110
+ selectors: Selector[];
111
+ }
112
+
113
+ export interface Selector extends BaseNode {
114
+ type: "selector";
115
+ parts: SelectorPart[];
116
+ }
117
+
118
+ export type SelectorPart =
119
+ | TypeSelector
120
+ | ClassSelector
121
+ | IdSelector
122
+ | AttributeSelector
123
+ | PseudoClassSelector
124
+ | PseudoElementSelector
125
+ | ParentSelector
126
+ | Combinator;
127
+
128
+ export interface TypeSelector extends BaseNode {
129
+ type: "type_selector";
130
+ /** Tag name or `*`. */
131
+ name: string;
132
+ }
133
+
134
+ export interface ClassSelector extends BaseNode {
135
+ type: "class_selector";
136
+ name: string;
137
+ }
138
+
139
+ export interface IdSelector extends BaseNode {
140
+ type: "id_selector";
141
+ name: string;
142
+ }
143
+
144
+ export interface AttributeSelector extends BaseNode {
145
+ type: "attribute_selector";
146
+ name: string;
147
+ /** `=` `*=` `^=` `$=` `|=` `~=` or `null` for bare `[attr]`. */
148
+ operator: string | null;
149
+ value: Expression | null;
150
+ /** `i` or `s` case-sensitivity modifier. */
151
+ modifier: string | null;
152
+ }
153
+
154
+ export interface PseudoClassSelector extends BaseNode {
155
+ type: "pseudo_class_selector";
156
+ name: string;
157
+ /**
158
+ * Selector list for selector-taking pseudos (`:not`, `:is`, `:where`,
159
+ * `:has`), raw source for the rest (`:nth-child(2n+1)`), `null` when the
160
+ * pseudo has no arguments.
161
+ */
162
+ argument: SelectorList | RawArgument | null;
163
+ }
164
+
165
+ export interface PseudoElementSelector extends BaseNode {
166
+ type: "pseudo_element_selector";
167
+ name: string;
168
+ argument: RawArgument | null;
169
+ }
170
+
171
+ export interface RawArgument extends BaseNode {
172
+ type: "raw";
173
+ value: string;
174
+ }
175
+
176
+ export interface ParentSelector extends BaseNode {
177
+ type: "parent_selector";
178
+ /** Suffix for `&-modifier` style selectors. */
179
+ suffix: string | null;
180
+ }
181
+
182
+ export interface Combinator extends BaseNode {
183
+ type: "combinator";
184
+ value: " " | ">" | "+" | "~";
185
+ }
186
+
187
+ /*
188
+ * ---------------------------------------------------------------------------
189
+ * Expressions
190
+ * ---------------------------------------------------------------------------
191
+ */
192
+
193
+ export type Expression =
194
+ | StringLiteral
195
+ | NumberLiteral
196
+ | ColorLiteral
197
+ | BooleanLiteral
198
+ | NullLiteral
199
+ | Identifier
200
+ | Variable
201
+ | ParentReference
202
+ | Interpolation
203
+ | Url
204
+ | FunctionCall
205
+ | IfFunction
206
+ | Member
207
+ | IndexAccess
208
+ | Unary
209
+ | Binary
210
+ | ListLiteral
211
+ | MapLiteral;
212
+
213
+ export interface StringLiteral extends BaseNode {
214
+ type: "string";
215
+ quote: '"' | "'";
216
+ /** Literal text parts (escapes preserved) interleaved with interpolations. */
217
+ parts: Array<string | Interpolation>;
218
+ /** Full contents when the string has no interpolation, else `null`. */
219
+ value: string | null;
220
+ }
221
+
222
+ export interface NumberLiteral extends BaseNode {
223
+ type: "number";
224
+ value: number;
225
+ unit: string | null;
226
+ }
227
+
228
+ /** Hex color, including the `#`. */
229
+ export interface ColorLiteral extends BaseNode {
230
+ type: "color";
231
+ value: string;
232
+ }
233
+
234
+ export interface BooleanLiteral extends BaseNode {
235
+ type: "boolean";
236
+ value: boolean;
237
+ }
238
+
239
+ export interface NullLiteral extends BaseNode {
240
+ type: "null";
241
+ }
242
+
243
+ /** Unquoted word: `none`, `solid`, `item`, `index`, ... */
244
+ export interface Identifier extends BaseNode {
245
+ type: "identifier";
246
+ name: string;
247
+ }
248
+
249
+ /** `$name` (name stored without the `$`). */
250
+ export interface Variable extends BaseNode {
251
+ type: "variable";
252
+ name: string;
253
+ }
254
+
255
+ /** `&` used inside an expression, e.g. `closest(&)`. */
256
+ export interface ParentReference extends BaseNode {
257
+ type: "parent_reference";
258
+ }
259
+
260
+ /** `#{expression}` */
261
+ export interface Interpolation extends BaseNode {
262
+ type: "interpolation";
263
+ expression: Expression;
264
+ }
265
+
266
+ /** Unquoted `url(...)`. Quoted urls parse as a regular `function` call. */
267
+ export interface Url extends BaseNode {
268
+ type: "url";
269
+ parts: Array<string | Interpolation>;
270
+ }
271
+
272
+ export interface FunctionCall extends BaseNode {
273
+ type: "function";
274
+ /** `Member` callees cover namespaced/method calls: `math.div()`, `item.join()`. */
275
+ callee: Identifier | Member | Interpolation;
276
+ args: Argument[];
277
+ }
278
+
279
+ export interface Argument extends BaseNode {
280
+ type: "argument";
281
+ /** Variable name (without `$`) for named arguments: `f($name: v)`. */
282
+ name: string | null;
283
+ value: Expression;
284
+ /** `f($args...)` */
285
+ spread: boolean;
286
+ }
287
+
288
+ /**
289
+ * CSS-style conditional function:
290
+ * `if($cond: a; $other == 1: b; else: c)`.
291
+ *
292
+ * Arms are `condition: value` pairs separated by `;`, evaluated in order;
293
+ * the optional `else` arm (condition `null`) must be last. A colon-less
294
+ * `if(...)` parses as a regular `function` call instead.
295
+ */
296
+ export interface IfFunction extends BaseNode {
297
+ type: "if";
298
+ arms: IfArm[];
299
+ }
300
+
301
+ export interface IfArm {
302
+ /** `null` for the `else` arm. */
303
+ condition: Expression | null;
304
+ value: Expression;
305
+ }
306
+
307
+ /** Dot accessor: `$obj.field`, `math.$pi`, `prop("provision").body`. */
308
+ export interface Member extends BaseNode {
309
+ type: "member";
310
+ object: Expression;
311
+ property: string;
312
+ /** True for namespaced variables: `math.$pi`. */
313
+ variable: boolean;
314
+ }
315
+
316
+ /** Bracket accessor: `$obj["field"]`, `$list[$i]`. */
317
+ export interface IndexAccess extends BaseNode {
318
+ type: "index";
319
+ object: Expression;
320
+ index: Expression;
321
+ }
322
+
323
+ export interface Unary extends BaseNode {
324
+ type: "unary";
325
+ operator: "-" | "+" | "not";
326
+ argument: Expression;
327
+ }
328
+
329
+ export type BinaryOperator =
330
+ | "or"
331
+ | "and"
332
+ | "=="
333
+ | "!="
334
+ | "<"
335
+ | ">"
336
+ | "<="
337
+ | ">="
338
+ | "+"
339
+ | "-"
340
+ | "*"
341
+ | "/"
342
+ | "%";
343
+
344
+ export interface Binary extends BaseNode {
345
+ type: "binary";
346
+ operator: BinaryOperator;
347
+ left: Expression;
348
+ right: Expression;
349
+ }
350
+
351
+ export interface ListLiteral extends BaseNode {
352
+ type: "list";
353
+ separator: "," | " ";
354
+ items: Expression[];
355
+ /** True for square-bracket lists: `[1, 2, 3]`. */
356
+ brackets: boolean;
357
+ /** True when the list was written wrapped in parentheses. */
358
+ parens: boolean;
359
+ }
360
+
361
+ export interface MapLiteral extends BaseNode {
362
+ type: "map";
363
+ entries: MapEntry[];
364
+ }
365
+
366
+ export interface MapEntry {
367
+ key: Expression;
368
+ value: Expression;
369
+ }
370
+
371
+ /*
372
+ * ---------------------------------------------------------------------------
373
+ * At-rules
374
+ * ---------------------------------------------------------------------------
375
+ */
376
+
377
+ export type AtRule =
378
+ | UseRule
379
+ | ScopeRule
380
+ | ListenerRule
381
+ | ActionRule
382
+ | TransitionRule
383
+ | DelayRule
384
+ | ValueAtRule;
385
+
386
+ export interface AtRuleBase extends BaseNode {
387
+ type: "atrule";
388
+ name: string;
389
+ }
390
+
391
+ /** `@use "url" [as name | as *];`: a `with (…)` clause is a parse error. */
392
+ export interface UseRule extends AtRuleBase {
393
+ name: "use";
394
+ url: string;
395
+ /** `null` = derived from url, `"*"` = global. */
396
+ namespace: string | null;
397
+ }
398
+
399
+ /** `@scope { … }`: anchors its rules to the sheet host. No prelude. */
400
+ export interface ScopeRule extends AtRuleBase {
401
+ name: "scope";
402
+ block: Block;
403
+ }
404
+
405
+ /**
406
+ * One entry of an at-rule options group: `once`, `target: "li"`,
407
+ * `debounce: 300`. A bare name is a flag (`true`). Shared by `@on`,
408
+ * `@dispatch`, `@command` and `@view-transition`.
409
+ */
410
+ export interface ListenerOption extends BaseNode {
411
+ type: "listener_option";
412
+ name: string;
413
+ /** `null` for a bare flag (`once`, `self`, `prevent-default`, …). */
414
+ value: Expression | null;
415
+ }
416
+
417
+ /**
418
+ * One event or command name in an at-rule's name list (`@on click,
419
+ * submit`, `@dispatch cart-add`, `@command --refresh`): a bare identifier
420
+ * or a quoted string.
421
+ */
422
+ export interface EventName extends BaseNode {
423
+ type: "event_name";
424
+ /** The name, without quotes when written as a string. */
425
+ name: string;
426
+ /** True when the name was written as a quoted string. */
427
+ quoted: boolean;
428
+ }
429
+
430
+ /**
431
+ * `@on <event>[, <event>] [(options)] { … }` / `@on <event> (options);`:
432
+ * Quark's listener at-rule. Events are bare identifiers (`click`,
433
+ * `super-form-success`) or strings, comma-separated. An optional
434
+ * parenthesised options group follows (`(target: "li", once, handle:
435
+ * save($draft))`; names are idents, values single expressions; a bare
436
+ * name is a flag). Then either a block — an ordinary rule body the
437
+ * runtime applies once per event — or `;`. A statement without options
438
+ * has nothing to do and is a parse error; a handler list after the
439
+ * events (the pre-2026-09-13 form) is a parse error pointing at
440
+ * `handle:`. `@off` was removed (parse error).
441
+ */
442
+ export interface ListenerRule extends AtRuleBase {
443
+ name: "on";
444
+ /** Event types in source order; at least one. */
445
+ events: EventName[];
446
+ /** Options group in source order; empty when absent. */
447
+ options: ListenerOption[];
448
+ /** The `@on` block body, or `null` for the statement form. */
449
+ block: Block | null;
450
+ }
451
+
452
+ /**
453
+ * `@dispatch <event>[, <event>] [(options)];` and `@command <name>[,
454
+ * <name>] [(options)];`: Quark's outgoing-event at-rules, statements only
455
+ * (a block is a parse error). Names follow the `@on` event grammar; the
456
+ * options group is the same node as `@on`'s. The runtime accepts them
457
+ * inside `@on` blocks only.
458
+ */
459
+ export interface ActionRule extends AtRuleBase {
460
+ name: "dispatch" | "command";
461
+ /** Event / command names in source order; at least one. */
462
+ names: EventName[];
463
+ /** Options group in source order; empty when absent. */
464
+ options: ListenerOption[];
465
+ }
466
+
467
+ /**
468
+ * `@view-transition [(options)] { … }`: Quark's paint-policy block. The
469
+ * optional options group (`(types: "todo", timeout: 500)`) has the same
470
+ * shape as `@on`'s; the block is an ordinary rule body whose writes the
471
+ * runtime applies inside `document.startViewTransition()`. A block is
472
+ * required.
473
+ */
474
+ export interface TransitionRule extends AtRuleBase {
475
+ name: "view-transition";
476
+ /** Options group in source order; empty when absent. */
477
+ options: ListenerOption[];
478
+ block: Block;
479
+ }
480
+
481
+ /**
482
+ * `@delay <ms> { … }`: Quark's deferred-writes at-rule. The duration is
483
+ * one expression (evaluated at runtime, milliseconds); the block is an
484
+ * ordinary rule body the runtime applies once when the timer fires. No
485
+ * statement form: a missing duration or block is a parse error.
486
+ */
487
+ export interface DelayRule extends AtRuleBase {
488
+ name: "delay";
489
+ duration: Expression;
490
+ block: Block;
491
+ }
492
+
493
+ /** `@debug`, `@warn`, `@error`. */
494
+ export interface ValueAtRule extends AtRuleBase {
495
+ name: "debug" | "warn" | "error";
496
+ value: Expression;
497
+ }