novac 2.2.0 → 2.2.2

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 (122) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +0 -0
  3. package/bin/novac +6 -3
  4. package/bin/nvc +0 -0
  5. package/bin/nvml +0 -0
  6. package/demo.nv +0 -0
  7. package/demo_builtins.nv +0 -0
  8. package/demo_http.nv +0 -0
  9. package/examples/bf.nv +5 -13
  10. package/examples/math.nv +2 -2
  11. package/kits/kitffmpeg/kitdef.js +1174 -0
  12. package/kits/libos/kitdef.js +3135 -0
  13. package/kits/libtasker/kitdef.js +125 -0
  14. package/package.json +1 -1
  15. package/scripts/update-bin.js +0 -0
  16. package/src/core/executor.js +7 -4
  17. package/src/core/lexer.js +2 -2
  18. package/src/index.js +0 -0
  19. package/novac/LICENSE +0 -21
  20. package/novac/README.md +0 -1823
  21. package/novac/bin/novac +0 -950
  22. package/novac/bin/nvc +0 -522
  23. package/novac/bin/nvml +0 -542
  24. package/novac/demo.nv +0 -245
  25. package/novac/demo_builtins.nv +0 -209
  26. package/novac/demo_http.nv +0 -62
  27. package/novac/examples/bf.nv +0 -69
  28. package/novac/examples/math.nv +0 -21
  29. package/novac/kits/kitai/kitdef.js +0 -2185
  30. package/novac/kits/kitansi/kitdef.js +0 -1402
  31. package/novac/kits/kitformat/kitdef.js +0 -1485
  32. package/novac/kits/kitgps/kitdef.js +0 -1862
  33. package/novac/kits/kitlibfs/kitdef.js +0 -231
  34. package/novac/kits/kitlibproc/kitdef.js +0 -78
  35. package/novac/kits/kitmatrix/ex.js +0 -19
  36. package/novac/kits/kitmatrix/kitdef.js +0 -960
  37. package/novac/kits/kitmpatch/kitdef.js +0 -906
  38. package/novac/kits/kitnovacweb/README.md +0 -1572
  39. package/novac/kits/kitnovacweb/demo.nv +0 -12
  40. package/novac/kits/kitnovacweb/demo.nvml +0 -71
  41. package/novac/kits/kitnovacweb/index.nova +0 -12
  42. package/novac/kits/kitnovacweb/kitdef.js +0 -692
  43. package/novac/kits/kitnovacweb/nova.kit.json +0 -8
  44. package/novac/kits/kitnovacweb/nvml/executor.js +0 -739
  45. package/novac/kits/kitnovacweb/nvml/index.js +0 -67
  46. package/novac/kits/kitnovacweb/nvml/lexer.js +0 -263
  47. package/novac/kits/kitnovacweb/nvml/parser.js +0 -508
  48. package/novac/kits/kitnovacweb/nvml/renderer.js +0 -924
  49. package/novac/kits/kitparse/kitdef.js +0 -1688
  50. package/novac/kits/kitregex++/kitdef.js +0 -1353
  51. package/novac/kits/kitrequire/kitdef.js +0 -1599
  52. package/novac/kits/kitx11/kitdef.js +0 -1
  53. package/novac/kits/kitx11/kitx11.js +0 -2472
  54. package/novac/kits/kitx11/kitx11_conn.js +0 -948
  55. package/novac/kits/kitx11/kitx11_worker.js +0 -121
  56. package/novac/kits/libtea/tf.js +0 -2691
  57. package/novac/kits/libterm/ex.js +0 -285
  58. package/novac/kits/libterm/kitdef.js +0 -1927
  59. package/novac/node_modules/chalk/license +0 -9
  60. package/novac/node_modules/chalk/package.json +0 -83
  61. package/novac/node_modules/chalk/readme.md +0 -297
  62. package/novac/node_modules/chalk/source/index.d.ts +0 -325
  63. package/novac/node_modules/chalk/source/index.js +0 -225
  64. package/novac/node_modules/chalk/source/utilities.js +0 -33
  65. package/novac/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +0 -236
  66. package/novac/node_modules/chalk/source/vendor/ansi-styles/index.js +0 -223
  67. package/novac/node_modules/chalk/source/vendor/supports-color/browser.d.ts +0 -1
  68. package/novac/node_modules/chalk/source/vendor/supports-color/browser.js +0 -34
  69. package/novac/node_modules/chalk/source/vendor/supports-color/index.d.ts +0 -55
  70. package/novac/node_modules/chalk/source/vendor/supports-color/index.js +0 -190
  71. package/novac/node_modules/commander/LICENSE +0 -22
  72. package/novac/node_modules/commander/Readme.md +0 -1176
  73. package/novac/node_modules/commander/esm.mjs +0 -16
  74. package/novac/node_modules/commander/index.js +0 -24
  75. package/novac/node_modules/commander/lib/argument.js +0 -150
  76. package/novac/node_modules/commander/lib/command.js +0 -2777
  77. package/novac/node_modules/commander/lib/error.js +0 -39
  78. package/novac/node_modules/commander/lib/help.js +0 -747
  79. package/novac/node_modules/commander/lib/option.js +0 -380
  80. package/novac/node_modules/commander/lib/suggestSimilar.js +0 -101
  81. package/novac/node_modules/commander/package-support.json +0 -19
  82. package/novac/node_modules/commander/package.json +0 -82
  83. package/novac/node_modules/commander/typings/esm.d.mts +0 -3
  84. package/novac/node_modules/commander/typings/index.d.ts +0 -1113
  85. package/novac/node_modules/node-addon-api/LICENSE.md +0 -9
  86. package/novac/node_modules/node-addon-api/README.md +0 -95
  87. package/novac/node_modules/node-addon-api/common.gypi +0 -21
  88. package/novac/node_modules/node-addon-api/except.gypi +0 -25
  89. package/novac/node_modules/node-addon-api/index.js +0 -14
  90. package/novac/node_modules/node-addon-api/napi-inl.deprecated.h +0 -186
  91. package/novac/node_modules/node-addon-api/napi-inl.h +0 -7165
  92. package/novac/node_modules/node-addon-api/napi.h +0 -3364
  93. package/novac/node_modules/node-addon-api/node_addon_api.gyp +0 -42
  94. package/novac/node_modules/node-addon-api/node_api.gyp +0 -9
  95. package/novac/node_modules/node-addon-api/noexcept.gypi +0 -26
  96. package/novac/node_modules/node-addon-api/nothing.c +0 -0
  97. package/novac/node_modules/node-addon-api/package-support.json +0 -21
  98. package/novac/node_modules/node-addon-api/package.json +0 -480
  99. package/novac/node_modules/node-addon-api/tools/README.md +0 -73
  100. package/novac/node_modules/node-addon-api/tools/check-napi.js +0 -99
  101. package/novac/node_modules/node-addon-api/tools/clang-format.js +0 -71
  102. package/novac/node_modules/node-addon-api/tools/conversion.js +0 -301
  103. package/novac/node_modules/serialize-javascript/LICENSE +0 -27
  104. package/novac/node_modules/serialize-javascript/README.md +0 -149
  105. package/novac/node_modules/serialize-javascript/index.js +0 -297
  106. package/novac/node_modules/serialize-javascript/package.json +0 -33
  107. package/novac/package.json +0 -27
  108. package/novac/scripts/update-bin.js +0 -24
  109. package/novac/src/core/bstd.js +0 -1035
  110. package/novac/src/core/config.js +0 -155
  111. package/novac/src/core/describe.js +0 -187
  112. package/novac/src/core/emitter.js +0 -499
  113. package/novac/src/core/error.js +0 -86
  114. package/novac/src/core/executor.js +0 -5606
  115. package/novac/src/core/formatter.js +0 -686
  116. package/novac/src/core/lexer.js +0 -1026
  117. package/novac/src/core/nova_builtins.js +0 -717
  118. package/novac/src/core/nova_thread_worker.js +0 -166
  119. package/novac/src/core/parser.js +0 -2181
  120. package/novac/src/core/types.js +0 -112
  121. package/novac/src/index.js +0 -28
  122. package/novac/src/runtime/stdlib.js +0 -244
@@ -1,508 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * NVML Parser v2
5
- *
6
- * Grammar:
7
- *
8
- * program → topNode*
9
- *
10
- * topNode → at_block | element_node
11
- *
12
- * at_block → '@config' '[' kv_list ']'
13
- * | '@visual' '[' block* ']'
14
- * | '@ss' '[' block* ']'
15
- * | '@component' IDENT '[' block* ']'
16
- * | '@state' '[' kv_list ']'
17
- * | '@computed' '[' named_fn_list ']'
18
- * | '@effect' '[' effect_list ']'
19
- * | '@route' STRING '[' block* ']'
20
- * | '@slot' IDENT? '[' block* ']'
21
- * | '@use' IDENT (',' IDENT)*
22
- *
23
- * element_node → ELEMENT ('=' value)? ('[' element_body ']')?
24
- *
25
- * element_body → (property | child | parent_ref)*
26
- *
27
- * property → IDENT '=' value
28
- * | IDENT (boolean)
29
- * | IDENT '->' IDENT (one-way binding: prop -> signal)
30
- * | IDENT '<->' IDENT (two-way binding: prop <-> signal)
31
- * | IDENT '~' IDENT (transition: prop ~ transitionName)
32
- * | '[..]' '::' IDENT ('=' value | '[' sub_body ']' | ε)
33
- *
34
- * child → element_node | at_block | text_node | conditional_child
35
- *
36
- * conditional_child → '?' IDENT element_node (render element only if signal is truthy)
37
- *
38
- * value → STRING | NUMBER | IDENT | '#' IDENT | pipe_list
39
- */
40
-
41
- const { Lexer, TT } = require('./lexer');
42
-
43
- class ParseError extends Error {
44
- constructor(msg, token) {
45
- super(`[NVML Parser ${token ? token.line + ':' + token.col : '?'}] ${msg}`);
46
- this.name = 'ParseError';
47
- this.token = token;
48
- }
49
- }
50
-
51
- const N = {
52
- program: (body) => ({ kind: 'program', body }),
53
- atConfig: (pairs) => ({ kind: 'at_config', pairs }),
54
- atBlock: (name, arg, body) => ({ kind: 'at_block', name, arg, body }),
55
- atState: (pairs) => ({ kind: 'at_state', pairs }),
56
- atComputed: (entries) => ({ kind: 'at_computed', entries }),
57
- atEffect: (entries) => ({ kind: 'at_effect', entries }),
58
- atLang: (name, pairs) => ({ kind: 'at_lang', name, pairs }),
59
- atRoute: (path, body) => ({ kind: 'at_route', path, body }),
60
- atSlot: (name, body) => ({ kind: 'at_slot', name, body }),
61
- atUse: (names) => ({ kind: 'at_use', names }),
62
- element: (tag, textValue, props, body,
63
- bindings, transitions, cond) => ({ kind: 'element', tag, textValue, props, body, bindings, transitions, cond }),
64
- property: (key, value) => ({ kind: 'property', key, value }),
65
- binding: (prop, signal, twoWay) => ({ kind: 'binding', prop, signal, twoWay }),
66
- transition: (prop, name) => ({ kind: 'transition', prop, name }),
67
- parentProp: (prop, value, subBody) => ({ kind: 'parent_prop', prop, value, subBody }),
68
- signalRef: (name) => ({ kind: 'signal_ref', name }),
69
- value: (v) => ({ kind: 'value', value: v }),
70
- pipeList: (items) => ({ kind: 'pipe_list', items }),
71
- textNode: (value) => ({ kind: 'text_node', value }),
72
- conditional: (signal, child) => ({ kind: 'conditional', signal, child }),
73
- cssDecl: (key, value) => ({ kind: 'css_decl', key, value }),
74
- eachBlock: (signal, itemVar, indexVar, body)=> ({ kind: 'each_block', signal, itemVar, indexVar, body }),
75
- slotOutlet: (name) => ({ kind: 'slot_outlet', name }),
76
- componentUse: (name, props, slots) => ({ kind: 'component_use', name, props, slots }),
77
- };
78
-
79
- class Parser {
80
- constructor(source) {
81
- this.tokens = new Lexer(source).tokenize();
82
- this.current = 0;
83
- }
84
-
85
- peek() { return this.tokens[this.current]; }
86
- previous() { return this.tokens[this.current - 1]; }
87
- isAtEnd() { return this.peek().type === TT.EOF; }
88
-
89
- advance() { if (!this.isAtEnd()) this.current++; return this.previous(); }
90
-
91
- check(type, value = null) {
92
- if (this.isAtEnd()) return false;
93
- const t = this.peek();
94
- return t.type === type && (value === null || t.value === value);
95
- }
96
-
97
- match(...specs) {
98
- for (const spec of specs) {
99
- const [type, value = null] = Array.isArray(spec) ? spec : [spec];
100
- if (this.check(type, value)) { this.advance(); return true; }
101
- }
102
- return false;
103
- }
104
-
105
- consume(type, value, msg) {
106
- if (value !== null && value !== undefined && this.check(type, value)) return this.advance();
107
- if ((value === null || value === undefined) && this.check(type)) return this.advance();
108
- throw new ParseError(msg || `Expected ${type}${value ? ' ' + value : ''}`, this.peek());
109
- }
110
-
111
- error(msg) { throw new ParseError(msg, this.peek()); }
112
-
113
- // ── entry ──────────────────────────────────────────────────────
114
-
115
- parse() {
116
- const body = [];
117
- while (!this.isAtEnd()) body.push(this.parseTopNode());
118
- return N.program(body);
119
- }
120
-
121
- parseTopNode() {
122
- const t = this.peek();
123
- if (t.type === TT.AT_IDENT) return this.parseAtBlock();
124
- if (t.type === TT.ELEMENT) return this.parseElement();
125
- this.error(`Unexpected token '${t.value}' (${t.type}) at top level`);
126
- }
127
-
128
- // ── @blocks ────────────────────────────────────────────────────
129
-
130
- parseAtBlock() {
131
- const name = this.advance().value; // consume AT_IDENT
132
-
133
- switch (name) {
134
- case 'config':
135
- this.consume(TT.LBRACKET, null, "Expected '[' after @config");
136
- const configPairs = this.parseKVList();
137
- this.consume(TT.RBRACKET, null, "Expected ']' to close @config");
138
- return N.atConfig(configPairs);
139
-
140
- case 'state':
141
- this.consume(TT.LBRACKET, null, "Expected '[' after @state");
142
- const statePairs = this.parseKVList();
143
- this.consume(TT.RBRACKET, null, "Expected ']' to close @state");
144
- return N.atState(statePairs);
145
-
146
- case 'computed':
147
- this.consume(TT.LBRACKET, null, "Expected '[' after @computed");
148
- const computedEntries = this.parseNamedCodeList();
149
- this.consume(TT.RBRACKET, null, "Expected ']' to close @computed");
150
- return N.atComputed(computedEntries);
151
-
152
- case 'effect':
153
- this.consume(TT.LBRACKET, null, "Expected '[' after @effect");
154
- const effectEntries = this.parseEffectList();
155
- this.consume(TT.RBRACKET, null, "Expected ']' to close @effect");
156
- return N.atEffect(effectEntries);
157
-
158
- case 'component': {
159
- const compName = this.consume(TT.IDENT, null, "Expected component name after @component").value;
160
- this.consume(TT.LBRACKET, null, "Expected '[' after @component name");
161
- const compBody = this.parseBlockList();
162
- this.consume(TT.RBRACKET, null, "Expected ']' to close @component");
163
- return N.atComponent(compName, compBody);
164
- }
165
-
166
- case 'lang': {
167
- // @lang {langName} [ runtime_language="nv", config="", src="PATH", code=(...) ]
168
- const langName = this.consume(TT.IDENT, null, 'Expected language name after @lang').value;
169
- this.consume(TT.LBRACKET, null, "Expected '[' after @lang name");
170
- const langPairs = this._parseLangKVList();
171
- this.consume(TT.RBRACKET, null, "Expected ']' to close @lang");
172
- return N.atLang(langName, langPairs);
173
- }
174
-
175
- case 'route': {
176
- const routePath = this.parseValue(); // STRING or IDENT
177
- this.consume(TT.LBRACKET, null, "Expected '[' after @route path");
178
- const routeBody = this.parseBlockList();
179
- this.consume(TT.RBRACKET, null, "Expected ']' to close @route");
180
- return N.atRoute(routePath, routeBody);
181
- }
182
-
183
- case 'slot': {
184
- let slotName = 'default';
185
- if (this.check(TT.IDENT)) slotName = this.advance().value;
186
- this.consume(TT.LBRACKET, null, "Expected '[' after @slot");
187
- const slotBody = this.parseBlockList();
188
- this.consume(TT.RBRACKET, null, "Expected ']' to close @slot");
189
- return N.atSlot(slotName, slotBody);
190
- }
191
-
192
- case 'use': {
193
- const useNames = [];
194
- useNames.push(this.consume(TT.IDENT, null, "Expected component name after @use").value);
195
- while (this.match(TT.COMMA)) {
196
- useNames.push(this.consume(TT.IDENT, null, "Expected component name").value);
197
- }
198
- return N.atUse(useNames);
199
- }
200
-
201
- default: {
202
- // @visual, @ss, or unknown custom at-block
203
- this.consume(TT.LBRACKET, null, `Expected '[' after @${name}`);
204
- const body = this.parseBlockList();
205
- this.consume(TT.RBRACKET, null, `Expected ']' to close @${name}`);
206
- return N.atBlock(name, null, body);
207
- }
208
- }
209
- }
210
-
211
- parseBlockList() {
212
- const body = [];
213
- while (!this.isAtEnd() && !this.check(TT.RBRACKET)) {
214
- if (this.check(TT.AT_IDENT)) { body.push(this.parseAtBlock()); continue; }
215
- if (this.check(TT.ELEMENT)) { body.push(this.parseElement()); continue; }
216
- if (this.check(TT.DOTDOT)) { body.push(this.parseParentPropInline()); continue; }
217
- if (this.check(TT.STRING)) { body.push(N.textNode(this.advance().value)); continue; }
218
- if (this.check(TT.COMMA)) { this.advance(); continue; }
219
- break;
220
- }
221
- return body;
222
- }
223
-
224
- // ── kv list (flat key=value pairs) ────────────────────────────
225
-
226
- parseKVList() {
227
- const pairs = {};
228
- while (!this.isAtEnd() && !this.check(TT.RBRACKET)) {
229
- if (this.match(TT.COMMA)) continue;
230
- if (!this.check(TT.IDENT)) break;
231
- const key = this.advance().value;
232
- this.consume(TT.EQ, null, `Expected '=' after key '${key}'`);
233
- pairs[key] = this.parseValue();
234
- this.match(TT.COMMA);
235
- }
236
- return pairs;
237
- }
238
-
239
- // ── @lang kv list — like parseKVList but also accepts bare string values ──
240
- // Handles: runtime_language="nv" config="" src="PATH" code=(...)
241
- _parseLangKVList() {
242
- const pairs = {};
243
- while (!this.isAtEnd() && !this.check(TT.RBRACKET)) {
244
- if (this.match(TT.COMMA)) continue;
245
- if (!this.check(TT.IDENT)) break;
246
- const key = this.advance().value;
247
- if (this.match(TT.EQ)) {
248
- pairs[key] = this.parseValue();
249
- } else {
250
- pairs[key] = N.value(true);
251
- }
252
- this.match(TT.COMMA);
253
- }
254
- return pairs;
255
- }
256
-
257
-
258
-
259
- parseNamedCodeList() {
260
- const entries = [];
261
- while (!this.isAtEnd() && !this.check(TT.RBRACKET)) {
262
- if (this.match(TT.COMMA)) continue;
263
- if (!this.check(TT.IDENT)) break;
264
- const name = this.advance().value;
265
- this.consume(TT.EQ, null, `Expected '=' after computed name '${name}'`);
266
- const code = this.parseValue();
267
- entries.push({ name, code });
268
- this.match(TT.COMMA);
269
- }
270
- return entries;
271
- }
272
-
273
- // ── @effect entries: deps -> (nova code) ──────────────────────
274
-
275
- parseEffectList() {
276
- const entries = [];
277
- while (!this.isAtEnd() && !this.check(TT.RBRACKET)) {
278
- if (this.match(TT.COMMA)) continue;
279
- // deps: [signalA, signalB] or * (always) — stored as array of names
280
- const deps = [];
281
- if (this.check(TT.LBRACKET)) {
282
- this.advance();
283
- while (!this.isAtEnd() && !this.check(TT.RBRACKET)) {
284
- if (this.check(TT.IDENT)) deps.push(this.advance().value);
285
- this.match(TT.COMMA);
286
- }
287
- this.consume(TT.RBRACKET, null, "Expected ']' to close effect deps");
288
- } else if (this.check(TT.IDENT, '*')) {
289
- this.advance(); // wildcard — run on any signal change
290
- deps.push('*');
291
- } else if (this.check(TT.IDENT)) {
292
- deps.push(this.advance().value);
293
- }
294
- this.consume(TT.ARROW, null, "Expected '->' after effect deps");
295
- const code = this.parseValue();
296
- entries.push({ deps, code });
297
- this.match(TT.COMMA);
298
- }
299
- return entries;
300
- }
301
-
302
- // ── element ────────────────────────────────────────────────────
303
-
304
- parseElement() {
305
- const tag = this.advance().value;
306
-
307
- let textValue = null;
308
- if (this.match(TT.EQ)) textValue = this.parseValue();
309
-
310
- let props = {};
311
- let body = [];
312
- let bindings = [];
313
- let transitions= [];
314
- let cond = null;
315
-
316
- if (this.match(TT.LBRACKET)) {
317
- const parsed = this.parseElementBody();
318
- props = parsed.props;
319
- body = parsed.body;
320
- bindings = parsed.bindings;
321
- transitions = parsed.transitions;
322
- this.consume(TT.RBRACKET, null, `Expected ']' to close element {${tag}}`);
323
- }
324
-
325
- return N.element(tag, textValue, props, body, bindings, transitions, cond);
326
- }
327
-
328
- parseElementBody() {
329
- const props = {}, body = [], bindings = [], transitions = [];
330
-
331
- while (!this.isAtEnd() && !this.check(TT.RBRACKET)) {
332
- const t = this.peek();
333
-
334
- // [..] parent-ref
335
- if (t.type === TT.DOTDOT) {
336
- const pp = this.parseParentPropInline();
337
- if (pp.subBody !== null) props[pp.prop] = { __subBody: pp.subBody };
338
- else props[pp.prop] = pp.value;
339
- continue;
340
- }
341
-
342
- // conditional child: ? signalName {element}
343
- if (t.type === TT.QUESTION) {
344
- this.advance();
345
- const sigName = this.consume(TT.IDENT, null, "Expected signal name after '?'").value;
346
- const child = this.parseElement();
347
- child.cond = sigName;
348
- body.push(child);
349
- continue;
350
- }
351
-
352
- // each block: @each signalName as itemVar (index: indexVar) [ body ]
353
- if (t.type === TT.AT_IDENT && t.value === 'each') {
354
- body.push(this.parseEachBlock());
355
- continue;
356
- }
357
-
358
- // slot outlet: @slot name
359
- if (t.type === TT.AT_IDENT && t.value === 'slot') {
360
- this.advance();
361
- const slotName = this.check(TT.IDENT) ? this.advance().value : 'default';
362
- body.push(N.slotOutlet(slotName));
363
- continue;
364
- }
365
-
366
- // child element
367
- if (t.type === TT.ELEMENT) { body.push(this.parseElement()); continue; }
368
-
369
- // child @block
370
- if (t.type === TT.AT_IDENT) { body.push(this.parseAtBlock()); continue; }
371
-
372
- // bare text node
373
- if (t.type === TT.STRING) { body.push(N.textNode(this.advance().value)); continue; }
374
-
375
- // property
376
- if (t.type === TT.IDENT) {
377
- const key = this.advance().value;
378
-
379
- // one-way binding: prop -> signalName
380
- if (this.match(TT.ARROW)) {
381
- const sig = this.consume(TT.IDENT, null, "Expected signal name after '->'").value;
382
- bindings.push(N.binding(key, sig, false));
383
- this.match(TT.COMMA);
384
- continue;
385
- }
386
-
387
- // two-way binding: prop <-> signalName
388
- if (this.match(TT.DARROW)) {
389
- const sig = this.consume(TT.IDENT, null, "Expected signal name after '<->'").value;
390
- bindings.push(N.binding(key, sig, true));
391
- this.match(TT.COMMA);
392
- continue;
393
- }
394
-
395
- // transition hint: prop ~ transitionName
396
- if (this.match(TT.TILDE)) {
397
- const tname = this.consume(TT.IDENT, null, "Expected transition name after '~'").value;
398
- transitions.push(N.transition(key, tname));
399
- this.match(TT.COMMA);
400
- continue;
401
- }
402
-
403
- if (this.match(TT.EQ)) {
404
- props[key] = this.parseValue();
405
- } else {
406
- props[key] = N.value(true); // boolean flag
407
- }
408
- this.match(TT.COMMA);
409
- continue;
410
- }
411
-
412
- // signal ref as prop value: #signal (shorthand for text binding)
413
- if (t.type === TT.HASH) {
414
- this.advance();
415
- body.push(N.signalRef(t.value));
416
- continue;
417
- }
418
-
419
- if (t.type === TT.NUMBER) { this.advance(); this.match(TT.COMMA); continue; }
420
- if (t.type === TT.COMMA) { this.advance(); continue; }
421
-
422
- this.error(`Unexpected token '${t.value}' (${t.type}) inside element body`);
423
- }
424
-
425
- return { props, body, bindings, transitions };
426
- }
427
-
428
- // ── @each signalName as item (index: i) [ body ] ──────────────
429
-
430
- parseEachBlock() {
431
- this.advance(); // consume 'each' AT_IDENT
432
- const signal = this.consume(TT.IDENT, null, "Expected signal name after @each").value;
433
- // 'as' keyword
434
- if (this.check(TT.IDENT, 'as')) this.advance();
435
- const itemVar = this.consume(TT.IDENT, null, "Expected item variable after 'as'").value;
436
- let indexVar = null;
437
- // optional (index: i)
438
- if (this.check(TT.STRING) || (this.check(TT.IDENT, 'index'))) {
439
- this.advance(); // skip
440
- }
441
- // (index: varName)
442
- if (this.check(TT.LBRACKET) || this.check(TT.LPAREN)) {
443
- // simple: check for index keyword in next ident
444
- }
445
- this.consume(TT.LBRACKET, null, "Expected '[' after @each header");
446
- const body = this.parseBlockList();
447
- this.consume(TT.RBRACKET, null, "Expected ']' to close @each block");
448
- return N.eachBlock(signal, itemVar, indexVar, body);
449
- }
450
-
451
- // ── [..] property ──────────────────────────────────────────────
452
-
453
- parseParentPropInline() {
454
- this.advance(); // consume DOTDOT
455
- this.consume(TT.COLONCOLON, null, "Expected '::' after '[..]'");
456
- const prop = this.consume(TT.IDENT, null, "Expected property name after '::'").value;
457
-
458
- if (this.match(TT.LBRACKET)) {
459
- const subBody = [];
460
- while (!this.isAtEnd() && !this.check(TT.RBRACKET)) {
461
- if (this.check(TT.ELEMENT) || this.check(TT.AT_IDENT) || this.check(TT.DOTDOT)) {
462
- subBody.push(this.parseTopNode());
463
- } else if (this.check(TT.IDENT)) {
464
- const key = this.advance().value;
465
- if (this.match(TT.EQ)) {
466
- subBody.push(N.cssDecl(key, this.parseValue()));
467
- } else {
468
- subBody.push(N.cssDecl(key, N.value(true)));
469
- }
470
- this.match(TT.COMMA);
471
- } else if (this.check(TT.COMMA)) {
472
- this.advance();
473
- } else { break; }
474
- }
475
- this.consume(TT.RBRACKET, null, `Expected ']' to close [..]::${prop} block`);
476
- return N.parentProp(prop, null, subBody);
477
- }
478
-
479
- if (this.match(TT.EQ)) return N.parentProp(prop, this.parseValue(), null);
480
- return N.parentProp(prop, N.value(true), null);
481
- }
482
-
483
- // ── value ──────────────────────────────────────────────────────
484
-
485
- parseValue() {
486
- const t = this.peek();
487
- if (t.type === TT.PIPE) return this.parsePipeList();
488
- if (t.type === TT.HASH) { this.advance(); return N.signalRef(t.value); }
489
- if (t.type === TT.STRING) { this.advance(); return N.value(t.value); }
490
- if (t.type === TT.NUMBER) { this.advance(); return N.value(t.value); }
491
- if (t.type === TT.IDENT) { this.advance(); return N.value(t.value); }
492
- this.error(`Expected a value, got '${t.value}' (${t.type})`);
493
- }
494
-
495
- parsePipeList() {
496
- this.consume(TT.PIPE, null, "Expected '|'");
497
- const items = [];
498
- while (!this.isAtEnd() && !this.check(TT.PIPE)) {
499
- const t = this.peek();
500
- if (t.type === TT.IDENT || t.type === TT.STRING) items.push(this.advance().value);
501
- this.match(TT.COMMA);
502
- }
503
- this.consume(TT.PIPE, null, "Expected closing '|'");
504
- return N.pipeList(items);
505
- }
506
- }
507
-
508
- module.exports = { Parser, ParseError, N };