@hyperfixi/core 3.1.1 → 3.3.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 +28 -36
- package/dist/ast/command-slots.d.ts +1 -1
- package/dist/ast/nodes.d.ts +2 -0
- package/dist/ast-utils/index.cjs +193 -48
- package/dist/ast-utils/index.mjs +193 -48
- package/dist/ast-utils/interchange/types.d.ts +19 -1
- package/dist/behaviors/index.cjs +19 -0
- package/dist/behaviors/index.mjs +19 -0
- package/dist/bundle-generator/index.cjs +36 -17
- package/dist/bundle-generator/index.mjs +36 -17
- package/dist/bundle-generator/parser-templates.d.ts +1 -1
- package/dist/chunks/bridge-CI9SOl8u.js +2 -0
- package/dist/chunks/browser-modular-7CpI5VF2.js +2 -0
- package/dist/chunks/index-BwXDEK0m.js +2 -0
- package/dist/chunks/index-DQi2o8Lk.js +2 -0
- package/dist/chunks/index-uCOQ8Qn0.js +2 -0
- package/dist/commands/animation/transition.d.ts +12 -6
- package/dist/commands/helpers/loop-executor.d.ts +2 -2
- package/dist/commands/helpers/numeric-target-parser.d.ts +1 -1
- package/dist/commands/helpers/variable-access.d.ts +5 -2
- package/dist/commands/helpers/write-target.d.ts +5 -0
- package/dist/commands/index.cjs +938 -425
- package/dist/commands/index.mjs +938 -425
- package/dist/commands/templates/render.d.ts +4 -4
- package/dist/commands/utility/beep.d.ts +0 -3
- package/dist/expressions/index.cjs +7 -13
- package/dist/expressions/index.mjs +7 -13
- package/dist/htmx/i18n-hooks.d.ts +4 -1
- package/dist/hyperfixi-browser-classic-i18n.js +1 -1
- package/dist/hyperfixi-browser.js +1 -1
- package/dist/hyperfixi-classic-i18n.js +1 -1
- package/dist/hyperfixi-hx-v4.js +1 -1
- package/dist/hyperfixi-hx.js +1 -1
- package/dist/hyperfixi-hybrid-complete.js +1 -1
- package/dist/hyperfixi-hybrid-hx.js +1 -1
- package/dist/hyperfixi-multilingual.js +1 -1
- package/dist/hyperfixi.js +1 -1
- package/dist/hyperfixi.mjs +1 -1
- package/dist/index.cjs +1149 -485
- package/dist/index.min.js +1 -1
- package/dist/index.mjs +1149 -485
- package/dist/lib/swap-executor.d.ts +1 -1
- package/dist/lokascript-browser-classic-i18n.js +1 -1
- package/dist/lokascript-browser.js +1 -1
- package/dist/lokascript-hybrid-complete.js +1 -1
- package/dist/lokascript-hybrid-hx.js +1 -1
- package/dist/lokascript-multilingual.js +1 -1
- package/dist/metadata.cjs +9 -9
- package/dist/metadata.d.ts +1 -1
- package/dist/metadata.mjs +9 -9
- package/dist/parser/command-grammar.d.ts +1 -0
- package/dist/parser/full-parser.cjs +396 -153
- package/dist/parser/full-parser.mjs +396 -153
- package/dist/parser/helpers/parsing-helpers.d.ts +3 -0
- package/dist/parser/hybrid/index.cjs +3 -0
- package/dist/parser/hybrid/index.mjs +3 -0
- package/dist/parser/hybrid/parser-core.cjs +3 -0
- package/dist/parser/hybrid/parser-core.mjs +3 -0
- package/dist/parser/hybrid-parser.cjs +3 -0
- package/dist/parser/hybrid-parser.mjs +3 -0
- package/dist/parser/parser-types.d.ts +2 -0
- package/dist/parser/parser.d.ts +9 -0
- package/dist/parser/pratt-parser.d.ts +2 -0
- package/dist/registry/index.cjs +535 -210
- package/dist/registry/index.mjs +535 -210
- package/dist/utils/beep.d.ts +8 -0
- package/dist/version.d.ts +1 -1
- package/package.json +21 -18
- package/vocab/htmx/ar.js +22 -20
- package/vocab/htmx/bn.js +21 -5
- package/vocab/htmx/de.js +33 -10
- package/vocab/htmx/en.js +4 -1
- package/vocab/htmx/es.js +28 -9
- package/vocab/htmx/fr.js +28 -8
- package/vocab/htmx/he.js +22 -1
- package/vocab/htmx/hi.js +28 -7
- package/vocab/htmx/id.js +29 -10
- package/vocab/htmx/it.js +26 -5
- package/vocab/htmx/ja.js +36 -11
- package/vocab/htmx/ko.js +27 -6
- package/vocab/htmx/ms.js +24 -1
- package/vocab/htmx/pl.js +27 -7
- package/vocab/htmx/pt.js +30 -9
- package/vocab/htmx/qu.js +32 -9
- package/vocab/htmx/ru.js +28 -6
- package/vocab/htmx/sw.js +28 -8
- package/vocab/htmx/th.js +19 -1
- package/vocab/htmx/tl.js +24 -1
- package/vocab/htmx/tr.js +34 -8
- package/vocab/htmx/uk.js +28 -6
- package/vocab/htmx/vi.js +27 -19
- package/vocab/htmx/zh.js +28 -9
- package/dist/chunks/bridge-D7qrHhV0.js +0 -2
- package/dist/chunks/browser-modular-C4LFxfoS.js +0 -2
- package/dist/chunks/index-C7lqD4mo.js +0 -2
- package/dist/chunks/index-CAPhqA6k.js +0 -2
- package/dist/chunks/index-CLdRyZaP.js +0 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @hyperfixi/core
|
|
2
2
|
|
|
3
3
|
An experimental hyperscript engine that provides fast parsing, command execution, and comprehensive error handling for web applications. Built with TypeScript-first design.
|
|
4
4
|
|
|
@@ -7,7 +7,7 @@ An experimental hyperscript engine that provides fast parsing, command execution
|
|
|
7
7
|
- 🎯 **\_hyperscript Compatible** - Tested via gallery examples, bundle compatibility matrix, and command/expression browser tests
|
|
8
8
|
- 🚀 **High Performance** - Optimized tokenizer and parser for large expressions
|
|
9
9
|
- 🔧 **TypeScript First** - Complete type safety with comprehensive type definitions
|
|
10
|
-
- 🧪 **Thoroughly Tested** -
|
|
10
|
+
- 🧪 **Thoroughly Tested** - Vitest unit suites plus Playwright browser tests
|
|
11
11
|
- 🌊 **Complete Command System** - All major commands implemented (PUT, SET, ADD, SHOW/HIDE, etc.)
|
|
12
12
|
- ⚡ **HTML Integration** - Automatic `_=""` attribute processing and event binding
|
|
13
13
|
- 🛡️ **Error Recovery** - Graceful handling of syntax errors with helpful guidance
|
|
@@ -15,26 +15,26 @@ An experimental hyperscript engine that provides fast parsing, command execution
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
npm install @
|
|
18
|
+
npm install @hyperfixi/core
|
|
19
19
|
# or
|
|
20
|
-
yarn add @
|
|
20
|
+
yarn add @hyperfixi/core
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## Quick Start
|
|
24
24
|
|
|
25
25
|
```typescript
|
|
26
|
-
import { hyperscript } from '@
|
|
26
|
+
import { hyperscript } from '@hyperfixi/core';
|
|
27
27
|
|
|
28
28
|
// Simple expression evaluation
|
|
29
|
-
const result = await hyperscript.
|
|
29
|
+
const result = await hyperscript.eval('5 + 3 * 2'); // Returns 11
|
|
30
30
|
|
|
31
31
|
// DOM manipulation with commands
|
|
32
32
|
const button = document.getElementById('myButton');
|
|
33
33
|
const context = hyperscript.createContext(button);
|
|
34
34
|
|
|
35
|
-
await hyperscript.
|
|
36
|
-
await hyperscript.
|
|
37
|
-
await hyperscript.
|
|
35
|
+
await hyperscript.eval('hide me', context); // Hides the button
|
|
36
|
+
await hyperscript.eval('put "Hello World" into my innerHTML', context);
|
|
37
|
+
await hyperscript.eval('set my className to "active"', context);
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
### HTML Integration (Automatic)
|
|
@@ -52,18 +52,18 @@ await hyperscript.run('set my className to "active"', context);
|
|
|
52
52
|
|
|
53
53
|
## Debugging
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
HyperFixi includes a built-in debug control API for troubleshooting compilation and execution issues.
|
|
56
56
|
|
|
57
57
|
### Enable Debug Logging
|
|
58
58
|
|
|
59
59
|
```javascript
|
|
60
60
|
// In browser console
|
|
61
|
-
|
|
61
|
+
hyperfixi.debugControl.enable(); // Enable detailed logging
|
|
62
62
|
// Reload page to see logs
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
hyperfixi.debugControl.disable(); // Disable logging
|
|
65
|
+
hyperfixi.debugControl.isEnabled(); // Check if enabled
|
|
66
|
+
hyperfixi.debugControl.status(); // Get detailed status
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
Debug settings persist across page reloads via localStorage. Logs include:
|
|
@@ -94,13 +94,13 @@ that fell back to its English rendering — reports.
|
|
|
94
94
|
|
|
95
95
|
## Runtime Hooks
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
HyperFixi provides a hooks system for observing and intercepting command execution:
|
|
98
98
|
|
|
99
99
|
```javascript
|
|
100
|
-
import {
|
|
100
|
+
import { hyperscript } from '@hyperfixi/core';
|
|
101
101
|
|
|
102
102
|
// Create hooks for logging, analytics, or debugging
|
|
103
|
-
const hooks =
|
|
103
|
+
const hooks = {
|
|
104
104
|
beforeExecute: ctx => {
|
|
105
105
|
console.log(`Executing: ${ctx.commandName}`);
|
|
106
106
|
},
|
|
@@ -115,34 +115,26 @@ const hooks = createHooks({
|
|
|
115
115
|
// Return true to skip command execution
|
|
116
116
|
return name === 'disabled-command';
|
|
117
117
|
},
|
|
118
|
-
}
|
|
118
|
+
};
|
|
119
119
|
|
|
120
120
|
// Register hooks with the runtime
|
|
121
|
-
|
|
121
|
+
hyperscript.registerHooks('my-hooks', hooks);
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
-
Built-in hook utilities:
|
|
125
|
-
|
|
126
|
-
- `loggingHooks()` - Pre-configured debug logging hooks
|
|
127
|
-
- `createTimingHooks()` - Performance timing hooks
|
|
128
|
-
|
|
129
124
|
## Cleanup & Memory Management
|
|
130
125
|
|
|
131
126
|
The runtime automatically tracks event listeners and observers for cleanup when elements are removed from the DOM. You can also manually trigger cleanup:
|
|
132
127
|
|
|
133
128
|
```javascript
|
|
134
|
-
// Clean up
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// Clean up element and all descendants
|
|
138
|
-
lokascript.cleanupTree(containerElement);
|
|
129
|
+
// Clean up an element and all its descendants
|
|
130
|
+
hyperscript.cleanup(containerElement);
|
|
139
131
|
|
|
140
132
|
// Get cleanup statistics
|
|
141
|
-
const stats =
|
|
133
|
+
const stats = hyperscript.getDefaultRuntime().getCleanupStats();
|
|
142
134
|
// { elementsTracked: 5, listeners: 12, observers: 2, ... }
|
|
143
135
|
|
|
144
136
|
// Full runtime shutdown
|
|
145
|
-
|
|
137
|
+
hyperscript.getDefaultRuntime().destroy();
|
|
146
138
|
```
|
|
147
139
|
|
|
148
140
|
## API Reference
|
|
@@ -153,12 +145,12 @@ For complete API documentation, see [API.md](./docs/API.md).
|
|
|
153
145
|
|
|
154
146
|
- `hyperscript.compile(code)` - Compile hyperscript to AST
|
|
155
147
|
- `hyperscript.execute(ast, context)` - Execute compiled AST
|
|
156
|
-
- `hyperscript.
|
|
148
|
+
- `hyperscript.eval(code, context)` - Compile and execute in one step
|
|
157
149
|
- `hyperscript.createContext(element)` - Create execution context
|
|
158
|
-
- `evalHyperScript(code, context)` - \_hyperscript compatibility API
|
|
150
|
+
- `evalHyperScript(code, context)` - \_hyperscript compatibility API (a global in the `hyperfixi.js` browser bundle)
|
|
159
151
|
- `hyperscript.registerHooks(name, hooks)` - Register runtime hooks
|
|
160
|
-
- `hyperscript.cleanup(element)` - Clean up element
|
|
161
|
-
- `hyperscript.destroy()` - Full runtime shutdown
|
|
152
|
+
- `hyperscript.cleanup(element)` - Clean up an element and its descendants
|
|
153
|
+
- `hyperscript.getDefaultRuntime().destroy()` - Full runtime shutdown
|
|
162
154
|
|
|
163
155
|
## Supported Features
|
|
164
156
|
|
|
@@ -193,7 +185,7 @@ See [EXAMPLES.md](./docs/EXAMPLES.md) for comprehensive usage examples.
|
|
|
193
185
|
|
|
194
186
|
## Compatibility Testing
|
|
195
187
|
|
|
196
|
-
This package includes compatibility tests that validate
|
|
188
|
+
This package includes compatibility tests that validate HyperFixi against the official \_hyperscript library:
|
|
197
189
|
|
|
198
190
|
```bash
|
|
199
191
|
# Run compatibility tests with official hyperscript test suite
|
|
@@ -58,7 +58,7 @@ export declare const COMMAND_SLOTS: {
|
|
|
58
58
|
readonly throw: readonly [];
|
|
59
59
|
readonly toggle: readonly ["as", "between", "for", "from", "on", "until"];
|
|
60
60
|
readonly unless: readonly [];
|
|
61
|
-
readonly transition: readonly ["on", "over", "to", "with"];
|
|
61
|
+
readonly transition: readonly ["from", "on", "over", "pairs", "to", "using", "with"];
|
|
62
62
|
readonly trigger: readonly ["on", "to", "with"];
|
|
63
63
|
readonly wait: readonly [];
|
|
64
64
|
};
|
package/dist/ast/nodes.d.ts
CHANGED
|
@@ -60,6 +60,7 @@ export interface CallExpressionNode extends BaseNode {
|
|
|
60
60
|
readonly callee: Expr;
|
|
61
61
|
readonly arguments: Expr[];
|
|
62
62
|
readonly isConstructor?: boolean;
|
|
63
|
+
readonly closestTo?: Expr;
|
|
63
64
|
}
|
|
64
65
|
export interface MemberExpressionNode extends BaseNode {
|
|
65
66
|
readonly type: 'memberExpression';
|
|
@@ -182,6 +183,7 @@ export interface EventHandlerNode extends BaseNode {
|
|
|
182
183
|
readonly events?: string[];
|
|
183
184
|
readonly condition?: Expr;
|
|
184
185
|
readonly target?: string;
|
|
186
|
+
readonly targetExpression?: Expr;
|
|
185
187
|
readonly selector?: string;
|
|
186
188
|
readonly args?: string[];
|
|
187
189
|
readonly attributeName?: string;
|
package/dist/ast-utils/index.cjs
CHANGED
|
@@ -2254,6 +2254,14 @@ function convertNode(node, infer) {
|
|
|
2254
2254
|
...pos(node),
|
|
2255
2255
|
};
|
|
2256
2256
|
case 'identifier':
|
|
2257
|
+
if (node.scope === 'element' || node.scope === 'local' || node.scope === 'global') {
|
|
2258
|
+
return {
|
|
2259
|
+
type: 'variable',
|
|
2260
|
+
name: (node.name ?? ''),
|
|
2261
|
+
scope: node.scope,
|
|
2262
|
+
...pos(node),
|
|
2263
|
+
};
|
|
2264
|
+
}
|
|
2257
2265
|
return {
|
|
2258
2266
|
type: 'identifier',
|
|
2259
2267
|
value: (node.name ?? node.value ?? ''),
|
|
@@ -2276,6 +2284,15 @@ function convertNode(node, infer) {
|
|
|
2276
2284
|
operand: convertNode(node.argument ?? node.operand, infer),
|
|
2277
2285
|
...pos(node),
|
|
2278
2286
|
};
|
|
2287
|
+
case 'typeCheckExpression':
|
|
2288
|
+
return {
|
|
2289
|
+
type: 'typeCheck',
|
|
2290
|
+
value: convertNode(node.value, infer),
|
|
2291
|
+
typeName: String(node.typeName),
|
|
2292
|
+
nullOk: node.nullOk !== false,
|
|
2293
|
+
negated: node.negated === true,
|
|
2294
|
+
...pos(node),
|
|
2295
|
+
};
|
|
2279
2296
|
case 'timeExpression':
|
|
2280
2297
|
return { type: 'literal', value: node.value, ...pos(node) };
|
|
2281
2298
|
case 'templateLiteral':
|
|
@@ -2287,6 +2304,28 @@ function convertNode(node, infer) {
|
|
|
2287
2304
|
scope: (node.scope ?? 'local'),
|
|
2288
2305
|
...pos(node),
|
|
2289
2306
|
};
|
|
2307
|
+
case 'arrayLiteral':
|
|
2308
|
+
return {
|
|
2309
|
+
type: 'array',
|
|
2310
|
+
elements: (node.elements ?? []).map(element => convertNode(element, infer)),
|
|
2311
|
+
...pos(node),
|
|
2312
|
+
};
|
|
2313
|
+
case 'attributeAccess':
|
|
2314
|
+
return {
|
|
2315
|
+
type: 'possessive',
|
|
2316
|
+
object: { type: 'identifier', value: 'me' },
|
|
2317
|
+
property: `@${node.attributeName}`,
|
|
2318
|
+
...pos(node),
|
|
2319
|
+
};
|
|
2320
|
+
case 'propertyOfExpression': {
|
|
2321
|
+
const property = node.property;
|
|
2322
|
+
return {
|
|
2323
|
+
type: 'possessive',
|
|
2324
|
+
object: convertNode(node.target, infer),
|
|
2325
|
+
property: (property?.name ?? property?.value ?? ''),
|
|
2326
|
+
...pos(node),
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2290
2329
|
case 'htmlSelector':
|
|
2291
2330
|
return {
|
|
2292
2331
|
type: 'selector',
|
|
@@ -2338,6 +2377,21 @@ function convertCommand$1(node, infer) {
|
|
|
2338
2377
|
if (name === 'repeat') {
|
|
2339
2378
|
return convertRepeatCommand(node, infer);
|
|
2340
2379
|
}
|
|
2380
|
+
const original = node.originalCommand;
|
|
2381
|
+
const sum = node.modifiers?.to;
|
|
2382
|
+
const counted = node.args?.[0];
|
|
2383
|
+
if (name === 'set' &&
|
|
2384
|
+
(original === 'increment' || original === 'decrement') &&
|
|
2385
|
+
sum?.type === 'binaryExpression' &&
|
|
2386
|
+
counted) {
|
|
2387
|
+
return convertCommand$1({
|
|
2388
|
+
...node,
|
|
2389
|
+
name: original,
|
|
2390
|
+
args: [counted],
|
|
2391
|
+
modifiers: { by: sum.right },
|
|
2392
|
+
originalCommand: undefined,
|
|
2393
|
+
}, infer);
|
|
2394
|
+
}
|
|
2341
2395
|
const args = (node.args ?? []).map(arg => convertNode(arg, infer));
|
|
2342
2396
|
const target = node.target ? convertNode(node.target, infer) : undefined;
|
|
2343
2397
|
const modifiers = node.modifiers
|
|
@@ -2386,6 +2440,64 @@ function convertIfCommand(node, infer) {
|
|
|
2386
2440
|
};
|
|
2387
2441
|
}
|
|
2388
2442
|
function convertRepeatCommand(node, infer) {
|
|
2443
|
+
const slots = (node.modifiers ?? {});
|
|
2444
|
+
const form = slotText(slots.loopType);
|
|
2445
|
+
if (form === undefined)
|
|
2446
|
+
return convertPositionalRepeat(node, infer);
|
|
2447
|
+
const [bodyBlock, elseBlock] = (node.args ?? []);
|
|
2448
|
+
const body = extractBlockCommands(bodyBlock, infer);
|
|
2449
|
+
const indexName = slotText(slots.index);
|
|
2450
|
+
const shared = {
|
|
2451
|
+
...(indexName ? { indexName } : {}),
|
|
2452
|
+
...(elseBlock ? { elseBody: extractBlockCommands(elseBlock, infer) } : {}),
|
|
2453
|
+
...pos(node),
|
|
2454
|
+
};
|
|
2455
|
+
const operand = (slot) => (slot ? convertNode(slot, infer) : undefined);
|
|
2456
|
+
switch (form) {
|
|
2457
|
+
case 'for':
|
|
2458
|
+
return {
|
|
2459
|
+
type: 'foreach',
|
|
2460
|
+
itemName: slotText(slots.for) ?? 'it',
|
|
2461
|
+
collection: operand(slots.in) ?? { type: 'literal', value: null },
|
|
2462
|
+
body,
|
|
2463
|
+
...shared,
|
|
2464
|
+
};
|
|
2465
|
+
case 'times':
|
|
2466
|
+
return { type: 'repeat', count: operand(slots.times), body, ...shared };
|
|
2467
|
+
case 'while':
|
|
2468
|
+
case 'until': {
|
|
2469
|
+
const test = operand(slots[form]) ?? { type: 'literal', value: form === 'until' };
|
|
2470
|
+
return {
|
|
2471
|
+
type: 'while',
|
|
2472
|
+
condition: form === 'until' ? { type: 'unary', operator: 'not', operand: test } : test,
|
|
2473
|
+
...(slots.bottomTested?.value === true ? { bottomTested: true } : {}),
|
|
2474
|
+
body,
|
|
2475
|
+
...shared,
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
case 'until-event': {
|
|
2479
|
+
const event = slotText(slots.event);
|
|
2480
|
+
if (!event) {
|
|
2481
|
+
return { type: 'error', message: "'repeat until event' has no event name", ...pos(node) };
|
|
2482
|
+
}
|
|
2483
|
+
const target = operand(slots.from);
|
|
2484
|
+
return {
|
|
2485
|
+
type: 'repeat',
|
|
2486
|
+
untilEvent: event,
|
|
2487
|
+
...(target ? { untilEventTarget: target } : {}),
|
|
2488
|
+
body,
|
|
2489
|
+
...shared,
|
|
2490
|
+
};
|
|
2491
|
+
}
|
|
2492
|
+
default:
|
|
2493
|
+
return { type: 'repeat', body, ...shared };
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
function slotText(slot) {
|
|
2497
|
+
const text = slot?.value ?? slot?.name;
|
|
2498
|
+
return typeof text === 'string' ? text : undefined;
|
|
2499
|
+
}
|
|
2500
|
+
function convertPositionalRepeat(node, infer) {
|
|
2389
2501
|
const args = (node.args ?? []);
|
|
2390
2502
|
if (args.length === 0) {
|
|
2391
2503
|
return { type: 'repeat', body: [], ...pos(node) };
|
|
@@ -2702,6 +2814,15 @@ function toCoreAST(node) {
|
|
|
2702
2814
|
prefix: true,
|
|
2703
2815
|
...nodePos(node),
|
|
2704
2816
|
};
|
|
2817
|
+
case 'typeCheck':
|
|
2818
|
+
return {
|
|
2819
|
+
type: 'typeCheckExpression',
|
|
2820
|
+
value: toCoreAST(node.value),
|
|
2821
|
+
typeName: node.typeName,
|
|
2822
|
+
nullOk: node.nullOk,
|
|
2823
|
+
negated: node.negated,
|
|
2824
|
+
...nodePos(node),
|
|
2825
|
+
};
|
|
2705
2826
|
case 'member':
|
|
2706
2827
|
return {
|
|
2707
2828
|
type: 'memberExpression',
|
|
@@ -2712,6 +2833,12 @@ function toCoreAST(node) {
|
|
|
2712
2833
|
computed: node.computed ?? false,
|
|
2713
2834
|
...nodePos(node),
|
|
2714
2835
|
};
|
|
2836
|
+
case 'array':
|
|
2837
|
+
return {
|
|
2838
|
+
type: 'arrayLiteral',
|
|
2839
|
+
elements: node.elements.map(element => toCoreAST(element)),
|
|
2840
|
+
...nodePos(node),
|
|
2841
|
+
};
|
|
2715
2842
|
case 'possessive':
|
|
2716
2843
|
return {
|
|
2717
2844
|
type: 'possessiveExpression',
|
|
@@ -2774,72 +2901,85 @@ function convertCommand(node) {
|
|
|
2774
2901
|
}
|
|
2775
2902
|
return result;
|
|
2776
2903
|
}
|
|
2904
|
+
function block(nodes) {
|
|
2905
|
+
return { type: 'block', commands: nodes.map(n => toCoreAST(n)), ...POS };
|
|
2906
|
+
}
|
|
2777
2907
|
function convertIf(node) {
|
|
2778
|
-
const
|
|
2908
|
+
const [elseIf, ...laterElseIfs] = node.elseIfBranches ?? [];
|
|
2909
|
+
const elseBranch = elseIf
|
|
2910
|
+
? [
|
|
2911
|
+
{
|
|
2912
|
+
type: 'if',
|
|
2913
|
+
condition: elseIf.condition,
|
|
2914
|
+
thenBranch: elseIf.body,
|
|
2915
|
+
elseIfBranches: laterElseIfs,
|
|
2916
|
+
...(node.elseBranch ? { elseBranch: node.elseBranch } : {}),
|
|
2917
|
+
},
|
|
2918
|
+
]
|
|
2919
|
+
: node.elseBranch;
|
|
2920
|
+
return {
|
|
2779
2921
|
type: 'command',
|
|
2780
2922
|
name: 'if',
|
|
2781
2923
|
isBlocking: true,
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2924
|
+
args: [
|
|
2925
|
+
toCoreAST(node.condition),
|
|
2926
|
+
block(node.thenBranch),
|
|
2927
|
+
...(elseBranch ? [block(elseBranch)] : []),
|
|
2928
|
+
],
|
|
2785
2929
|
...nodePos(node),
|
|
2786
2930
|
};
|
|
2787
|
-
if (node.elseBranch) {
|
|
2788
|
-
result.elseBranch = node.elseBranch.map(n => toCoreAST(n));
|
|
2789
|
-
}
|
|
2790
|
-
return result;
|
|
2791
2931
|
}
|
|
2792
|
-
function
|
|
2793
|
-
const
|
|
2932
|
+
function loopCommand(node, form, operands) {
|
|
2933
|
+
const modifiers = { loopType: slotString(form) };
|
|
2934
|
+
for (const [slot, value] of Object.entries(operands)) {
|
|
2935
|
+
if (value)
|
|
2936
|
+
modifiers[slot] = value;
|
|
2937
|
+
}
|
|
2938
|
+
if (node.indexName)
|
|
2939
|
+
modifiers.index = slotString(node.indexName);
|
|
2940
|
+
return {
|
|
2794
2941
|
type: 'command',
|
|
2795
2942
|
name: 'repeat',
|
|
2796
2943
|
isBlocking: true,
|
|
2797
|
-
|
|
2798
|
-
|
|
2944
|
+
args: [block(node.body), ...(node.elseBody ? [block(node.elseBody)] : [])],
|
|
2945
|
+
modifiers,
|
|
2799
2946
|
...nodePos(node),
|
|
2800
2947
|
};
|
|
2948
|
+
}
|
|
2949
|
+
function slotString(value) {
|
|
2950
|
+
return { type: 'string', value, ...POS };
|
|
2951
|
+
}
|
|
2952
|
+
function convertRepeat(node) {
|
|
2801
2953
|
if (node.count !== undefined) {
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2954
|
+
const times = typeof node.count === 'number'
|
|
2955
|
+
? { type: 'literal', value: node.count, raw: String(node.count), ...POS }
|
|
2956
|
+
: toCoreAST(node.count);
|
|
2957
|
+
return loopCommand(node, 'times', { times });
|
|
2958
|
+
}
|
|
2959
|
+
if (node.whileCondition !== undefined) {
|
|
2960
|
+
return loopCommand(node, 'while', { while: toCoreAST(node.whileCondition) });
|
|
2961
|
+
}
|
|
2962
|
+
if (node.untilEvent !== undefined) {
|
|
2963
|
+
return loopCommand(node, 'until-event', {
|
|
2964
|
+
event: slotString(node.untilEvent),
|
|
2965
|
+
from: node.untilEventTarget ? toCoreAST(node.untilEventTarget) : undefined,
|
|
2966
|
+
});
|
|
2815
2967
|
}
|
|
2816
|
-
return
|
|
2968
|
+
return loopCommand(node, 'forever', {});
|
|
2817
2969
|
}
|
|
2818
2970
|
function convertForEach(node) {
|
|
2819
|
-
return {
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
loopVariant: 'for',
|
|
2824
|
-
itemName: node.itemName,
|
|
2825
|
-
...(node.indexName ? { indexName: node.indexName } : {}),
|
|
2826
|
-
collection: toCoreAST(node.collection),
|
|
2827
|
-
body: node.body.map(n => toCoreAST(n)),
|
|
2828
|
-
args: [],
|
|
2829
|
-
...nodePos(node),
|
|
2830
|
-
};
|
|
2971
|
+
return loopCommand(node, 'for', {
|
|
2972
|
+
for: slotString(node.itemName),
|
|
2973
|
+
in: toCoreAST(node.collection),
|
|
2974
|
+
});
|
|
2831
2975
|
}
|
|
2832
2976
|
function convertWhile(node) {
|
|
2833
|
-
return {
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
body: node.body.map(n => toCoreAST(n)),
|
|
2840
|
-
args: [],
|
|
2841
|
-
...nodePos(node),
|
|
2842
|
-
};
|
|
2977
|
+
return loopCommand(node, 'while', {
|
|
2978
|
+
while: toCoreAST(node.condition),
|
|
2979
|
+
bottomTested: node.bottomTested
|
|
2980
|
+
? { type: 'literal', value: true, raw: 'true', ...POS }
|
|
2981
|
+
: undefined,
|
|
2982
|
+
});
|
|
2843
2983
|
}
|
|
2844
2984
|
function flattenEventModifiers(modifiers) {
|
|
2845
2985
|
if (!modifiers)
|
|
@@ -3243,6 +3383,9 @@ function getChildren(node) {
|
|
|
3243
3383
|
case 'unary':
|
|
3244
3384
|
children.push(node.operand);
|
|
3245
3385
|
break;
|
|
3386
|
+
case 'typeCheck':
|
|
3387
|
+
children.push(node.value);
|
|
3388
|
+
break;
|
|
3246
3389
|
case 'member': {
|
|
3247
3390
|
const m = node;
|
|
3248
3391
|
children.push(m.object);
|
|
@@ -3380,6 +3523,8 @@ function friendlyTypeName(node) {
|
|
|
3380
3523
|
return `Binary Expression (${node.operator})`;
|
|
3381
3524
|
case 'unary':
|
|
3382
3525
|
return `Unary Expression (${node.operator})`;
|
|
3526
|
+
case 'typeCheck':
|
|
3527
|
+
return `Type Check (${node.typeName})`;
|
|
3383
3528
|
case 'literal':
|
|
3384
3529
|
return 'Literal';
|
|
3385
3530
|
case 'identifier':
|