rip-lang 3.14.4 → 3.15.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 +9 -5
- package/bin/rip +5 -0
- package/docs/AGENTS.md +1 -1
- package/docs/RIP-LANG.md +17 -5
- package/docs/RIP-SCHEMA.md +4 -4
- package/docs/demo/README.md +43 -0
- package/docs/demo/components/_layout.rip +28 -0
- package/docs/demo/components/about.rip +36 -0
- package/docs/demo/components/card.rip +10 -0
- package/docs/demo/components/counter.rip +33 -0
- package/docs/demo/components/index.rip +30 -0
- package/docs/demo/components/todos.rip +48 -0
- package/docs/demo/css/styles.css +472 -0
- package/docs/dist/rip.js +3331 -4741
- package/docs/dist/rip.min.js +270 -683
- package/docs/dist/rip.min.js.br +0 -0
- package/docs/example/index.json +6 -6
- package/docs/extensions/duckdb/index.html +7 -5
- package/docs/extensions/duckdb/manifest.json +1 -1
- package/docs/extensions/duckdb/v1.5.2/linux_amd64/ripdb.duckdb_extension.gz +0 -0
- package/docs/extensions/duckdb/v1.5.2/osx_arm64/ripdb.duckdb_extension.gz +0 -0
- package/package.json +11 -3
- package/src/AGENTS.md +105 -9
- package/src/{ui.rip → app.rip} +24 -2
- package/src/browser.js +154 -37
- package/src/compiler.js +108 -32
- package/src/grammar/grammar.rip +1 -1
- package/src/grammar/solar.rip +0 -1
- package/src/lexer.js +25 -3
- package/src/parser.js +4 -4
- package/src/typecheck.js +3 -2
- package/src/types-emit.js +1021 -0
- package/src/types.js +11 -1035
- package/src/schema.js +0 -3389
package/src/compiler.js
CHANGED
|
@@ -11,8 +11,12 @@
|
|
|
11
11
|
import { Lexer } from './lexer.js';
|
|
12
12
|
import { parser } from './parser.js';
|
|
13
13
|
import { installComponentSupport } from './components.js';
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
// Type emission is CLI/editor-only. types-emit.js registers itself via
|
|
15
|
+
// setTypesEmitter() at module load. The browser never imports types-emit,
|
|
16
|
+
// so _typesEmitter stays null and .d.ts output is silently skipped.
|
|
17
|
+
let _typesEmitter = null;
|
|
18
|
+
export function setTypesEmitter(fn) { _typesEmitter = fn; }
|
|
19
|
+
import { installSchemaSupport } from '@rip-lang/schema';
|
|
16
20
|
import { SourceMapGenerator } from './sourcemaps.js';
|
|
17
21
|
import { RipError, toRipError } from './error.js';
|
|
18
22
|
|
|
@@ -1472,7 +1476,7 @@ export class CodeEmitter {
|
|
|
1472
1476
|
let stmts = body.slice(1);
|
|
1473
1477
|
this.indentLevel++;
|
|
1474
1478
|
let lines = [];
|
|
1475
|
-
if (!noVar) lines.push(
|
|
1479
|
+
if (!noVar) lines.push(`let ${itemVarPattern} = ${iterCode}[${idxName}];`);
|
|
1476
1480
|
if (guard) {
|
|
1477
1481
|
lines.push(`if (${this.emit(guard, 'value')}) {`);
|
|
1478
1482
|
this.indentLevel++;
|
|
@@ -1492,8 +1496,8 @@ export class CodeEmitter {
|
|
|
1492
1496
|
: loopHeader + `{ ${this.emit(body, 'statement')}; }`;
|
|
1493
1497
|
}
|
|
1494
1498
|
return guard
|
|
1495
|
-
? loopHeader + `{ ${itemVarPattern} = ${iterCode}[${idxName}]; if (${this.emit(guard, 'value')}) ${this.emit(body, 'statement')}; }`
|
|
1496
|
-
: loopHeader + `{ ${itemVarPattern} = ${iterCode}[${idxName}]; ${this.emit(body, 'statement')}; }`;
|
|
1499
|
+
? loopHeader + `{ let ${itemVarPattern} = ${iterCode}[${idxName}]; if (${this.emit(guard, 'value')}) ${this.emit(body, 'statement')}; }`
|
|
1500
|
+
: loopHeader + `{ let ${itemVarPattern} = ${iterCode}[${idxName}]; ${this.emit(body, 'statement')}; }`;
|
|
1497
1501
|
}
|
|
1498
1502
|
|
|
1499
1503
|
// Index variable → traditional for loop
|
|
@@ -1503,7 +1507,7 @@ export class CodeEmitter {
|
|
|
1503
1507
|
if (this.is(body, 'block')) {
|
|
1504
1508
|
code += '{\n';
|
|
1505
1509
|
this.indentLevel++;
|
|
1506
|
-
code += this.indent() +
|
|
1510
|
+
code += this.indent() + `let ${itemVarPattern} = ${iterCode}[${indexVar}];\n`;
|
|
1507
1511
|
if (guard) {
|
|
1508
1512
|
code += this.indent() + `if (${this.unwrap(this.emit(guard, 'value'))}) {\n`;
|
|
1509
1513
|
this.indentLevel++;
|
|
@@ -1517,8 +1521,8 @@ export class CodeEmitter {
|
|
|
1517
1521
|
code += this.indent() + '}';
|
|
1518
1522
|
} else {
|
|
1519
1523
|
code += guard
|
|
1520
|
-
? `{ ${itemVarPattern} = ${iterCode}[${indexVar}]; if (${this.unwrap(this.emit(guard, 'value'))}) ${this.emit(body, 'statement')}; }`
|
|
1521
|
-
: `{ ${itemVarPattern} = ${iterCode}[${indexVar}]; ${this.emit(body, 'statement')}; }`;
|
|
1524
|
+
? `{ let ${itemVarPattern} = ${iterCode}[${indexVar}]; if (${this.unwrap(this.emit(guard, 'value'))}) ${this.emit(body, 'statement')}; }`
|
|
1525
|
+
: `{ let ${itemVarPattern} = ${iterCode}[${indexVar}]; ${this.emit(body, 'statement')}; }`;
|
|
1522
1526
|
}
|
|
1523
1527
|
return code;
|
|
1524
1528
|
}
|
|
@@ -1542,8 +1546,7 @@ export class CodeEmitter {
|
|
|
1542
1546
|
}
|
|
1543
1547
|
|
|
1544
1548
|
// Default: for-of
|
|
1545
|
-
let
|
|
1546
|
-
let code = `for (${bind}${itemVarPattern} of ${this.emit(iterable, 'value')}) `;
|
|
1549
|
+
let code = `for (let ${itemVarPattern} of ${this.emit(iterable, 'value')}) `;
|
|
1547
1550
|
code += guard ? this.emitLoopBodyWithGuard(body, guard) : this.emitLoopBody(body);
|
|
1548
1551
|
return code;
|
|
1549
1552
|
}
|
|
@@ -1558,7 +1561,7 @@ export class CodeEmitter {
|
|
|
1558
1561
|
|
|
1559
1562
|
let [keyVar, valueVar] = Array.isArray(vars) ? vars : [vars];
|
|
1560
1563
|
let objCode = this.emit(obj, 'value');
|
|
1561
|
-
let code = `for (${keyVar} in ${objCode}) `;
|
|
1564
|
+
let code = `for (let ${keyVar} in ${objCode}) `;
|
|
1562
1565
|
|
|
1563
1566
|
if (own && !valueVar && !guard) {
|
|
1564
1567
|
if (this.is(body, 'block')) {
|
|
@@ -1576,7 +1579,7 @@ export class CodeEmitter {
|
|
|
1576
1579
|
this.indentLevel++;
|
|
1577
1580
|
let lines = [];
|
|
1578
1581
|
if (own) lines.push(`if (!Object.hasOwn(${objCode}, ${keyVar})) continue;`);
|
|
1579
|
-
lines.push(
|
|
1582
|
+
lines.push(`let ${valueVar} = ${objCode}[${keyVar}];`);
|
|
1580
1583
|
if (guard) {
|
|
1581
1584
|
lines.push(`if (${this.emit(guard, 'value')}) {`);
|
|
1582
1585
|
this.indentLevel++;
|
|
@@ -1591,7 +1594,7 @@ export class CodeEmitter {
|
|
|
1591
1594
|
}
|
|
1592
1595
|
let inline = '';
|
|
1593
1596
|
if (own) inline += `if (!Object.hasOwn(${objCode}, ${keyVar})) continue; `;
|
|
1594
|
-
inline +=
|
|
1597
|
+
inline += `let ${valueVar} = ${objCode}[${keyVar}]; `;
|
|
1595
1598
|
if (guard) inline += `if (${this.emit(guard, 'value')}) `;
|
|
1596
1599
|
inline += `${this.emit(body, 'statement')};`;
|
|
1597
1600
|
return code + `{ ${inline} }`;
|
|
@@ -1638,7 +1641,7 @@ export class CodeEmitter {
|
|
|
1638
1641
|
itemVarPattern = this.emitDestructuringPattern(firstVar);
|
|
1639
1642
|
else itemVarPattern = firstVar;
|
|
1640
1643
|
|
|
1641
|
-
let code = `for ${awaitKw}(${itemVarPattern} of ${iterCode}) `;
|
|
1644
|
+
let code = `for ${awaitKw}(let ${itemVarPattern} of ${iterCode}) `;
|
|
1642
1645
|
|
|
1643
1646
|
if (needsTempVar && destructStmts.length > 0) {
|
|
1644
1647
|
let stmts = this.unwrapBlock(body);
|
|
@@ -2012,17 +2015,16 @@ export class CodeEmitter {
|
|
|
2012
2015
|
let header = isNeg
|
|
2013
2016
|
? `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${update})`
|
|
2014
2017
|
: `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${update})`;
|
|
2015
|
-
return { header, setup: noVar ? null :
|
|
2018
|
+
return { header, setup: noVar ? null : `let ${ivp} = ${ic}[${idxN}];` };
|
|
2016
2019
|
}
|
|
2017
2020
|
if (indexVar) {
|
|
2018
2021
|
let ic = this.emit(iterable, 'value');
|
|
2019
2022
|
return {
|
|
2020
2023
|
header: `for (let ${indexVar} = 0; ${indexVar} < ${ic}.length; ${indexVar}++)`,
|
|
2021
|
-
setup:
|
|
2024
|
+
setup: `let ${ivp} = ${ic}[${indexVar}];`,
|
|
2022
2025
|
};
|
|
2023
2026
|
}
|
|
2024
|
-
|
|
2025
|
-
return { header: `for (${bind}${ivp} of ${this.emit(iterable, 'value')})`, setup: null };
|
|
2027
|
+
return { header: `for (let ${ivp} of ${this.emit(iterable, 'value')})`, setup: null };
|
|
2026
2028
|
}
|
|
2027
2029
|
|
|
2028
2030
|
// Shared: parse a for-of (object) iterator and return { header, own, vv, oc, kvp }.
|
|
@@ -2032,7 +2034,7 @@ export class CodeEmitter {
|
|
|
2032
2034
|
let kvp = (this.is(kv, 'array') || this.is(kv, 'object'))
|
|
2033
2035
|
? this.emitDestructuringPattern(kv) : kv;
|
|
2034
2036
|
let oc = this.emit(iterable, 'value');
|
|
2035
|
-
return { header: `for (${kvp} in ${oc})`, own, vv, oc, kvp };
|
|
2037
|
+
return { header: `for (let ${kvp} in ${oc})`, own, vv, oc, kvp };
|
|
2036
2038
|
}
|
|
2037
2039
|
|
|
2038
2040
|
// Shared: parse a for-as (iterator) spec and return { header }.
|
|
@@ -2041,13 +2043,28 @@ export class CodeEmitter {
|
|
|
2041
2043
|
let [fv] = va;
|
|
2042
2044
|
let ivp = (this.is(fv, 'array') || this.is(fv, 'object'))
|
|
2043
2045
|
? this.emitDestructuringPattern(fv) : fv;
|
|
2044
|
-
return { header: `for ${isAwait ? 'await ' : ''}(${ivp} of ${this.emit(iterable, 'value')})` };
|
|
2046
|
+
return { header: `for ${isAwait ? 'await ' : ''}(let ${ivp} of ${this.emit(iterable, 'value')})` };
|
|
2045
2047
|
}
|
|
2046
2048
|
|
|
2047
2049
|
emitComprehension(head, rest, context) {
|
|
2048
2050
|
let [expr, iterators, guards] = rest;
|
|
2049
2051
|
if (context === 'statement') return this.emitComprehensionAsLoop(expr, iterators, guards);
|
|
2050
|
-
if (this.comprehensionTarget)
|
|
2052
|
+
if (this.comprehensionTarget) {
|
|
2053
|
+
// Consume-and-clear: the auto-return-loop logic sets comprehensionTarget
|
|
2054
|
+
// expecting ONE consumer (the direct value-context comprehension being
|
|
2055
|
+
// routed to the named target). Without clearing here, nested
|
|
2056
|
+
// comprehensions inside this comprehension's body (call args, RHS
|
|
2057
|
+
// expressions) would inherit the target and skip their own IIFE,
|
|
2058
|
+
// producing malformed JS or wrong semantics. The body's own emit calls
|
|
2059
|
+
// see comprehensionTarget = null and correctly produce IIFEs.
|
|
2060
|
+
let target = this.comprehensionTarget;
|
|
2061
|
+
this.comprehensionTarget = null;
|
|
2062
|
+
try {
|
|
2063
|
+
return this.emitComprehensionWithTarget(expr, iterators, guards, target);
|
|
2064
|
+
} finally {
|
|
2065
|
+
this.comprehensionTarget = target;
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2051
2068
|
|
|
2052
2069
|
// Enclosed: expr, iterators (iterable expressions), guards
|
|
2053
2070
|
let hasAwait = this.containsAwait(expr) || iterators.some(i => this.containsAwait(i)) || guards.some(g => this.containsAwait(g));
|
|
@@ -2068,7 +2085,7 @@ export class CodeEmitter {
|
|
|
2068
2085
|
code += this.indent() + header + ' {\n';
|
|
2069
2086
|
this.indentLevel++;
|
|
2070
2087
|
if (own) code += this.indent() + `if (!Object.hasOwn(${oc}, ${kvp})) continue;\n`;
|
|
2071
|
-
if (vv) code += this.indent() +
|
|
2088
|
+
if (vv) code += this.indent() + `let ${vv} = ${oc}[${kvp}];\n`;
|
|
2072
2089
|
} else if (iterType === 'for-as') {
|
|
2073
2090
|
let { header } = this._forAsHeader(vars, iterable, iter[3]);
|
|
2074
2091
|
code += this.indent() + header + ' {\n';
|
|
@@ -2132,10 +2149,10 @@ export class CodeEmitter {
|
|
|
2132
2149
|
if (iterType === 'for-of') {
|
|
2133
2150
|
let [kv, vv] = vars;
|
|
2134
2151
|
let oc = this.emit(iterable, 'value');
|
|
2135
|
-
code += this.indent() + `for (${kv} in ${oc}) {\n`;
|
|
2152
|
+
code += this.indent() + `for (let ${kv} in ${oc}) {\n`;
|
|
2136
2153
|
this.indentLevel++;
|
|
2137
2154
|
if (own) code += this.indent() + `if (!Object.hasOwn(${oc}, ${kv})) continue;\n`;
|
|
2138
|
-
if (vv) code += this.indent() +
|
|
2155
|
+
if (vv) code += this.indent() + `let ${vv} = ${oc}[${kv}];\n`;
|
|
2139
2156
|
}
|
|
2140
2157
|
}
|
|
2141
2158
|
for (let guard of guards) { code += this.indent() + `if (${this.emit(guard, 'value')}) {\n`; this.indentLevel++; }
|
|
@@ -2616,6 +2633,21 @@ export class CodeEmitter {
|
|
|
2616
2633
|
code += this.indent() + this.addSemicolon(stmt, this.emit(stmt, 'statement')) + '\n';
|
|
2617
2634
|
return;
|
|
2618
2635
|
}
|
|
2636
|
+
// Auto-return-loop: only for-in/for-of/for-as auto-collect into
|
|
2637
|
+
// _result, because emitForIn/emitForOf/emitForAs at value-context
|
|
2638
|
+
// wrap themselves in a comprehension that consumes
|
|
2639
|
+
// comprehensionTarget. `loop` and `while` have no such wrapping —
|
|
2640
|
+
// emitLoop/emitWhile just emit `while(...) { body }` and any
|
|
2641
|
+
// comprehensionTarget set here would LEAK into nested
|
|
2642
|
+
// expression-context comprehensions inside the body (causing
|
|
2643
|
+
// them to be routed to the wrong target and skip their own
|
|
2644
|
+
// IIFE). Loops with explicit `return X` inside their body
|
|
2645
|
+
// already work correctly; emit them as plain statements.
|
|
2646
|
+
let isCollectibleLoop = h === 'for-in' || h === 'for-of' || h === 'for-as';
|
|
2647
|
+
if (!isCollectibleLoop) {
|
|
2648
|
+
code += this.indent() + this.addSemicolon(stmt, this.emit(stmt, 'statement')) + '\n';
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2619
2651
|
code += this.indent() + 'const _result = [];\n';
|
|
2620
2652
|
this.comprehensionTarget = '_result';
|
|
2621
2653
|
let saved = this._skipCompTargetInit;
|
|
@@ -2792,7 +2824,7 @@ export class CodeEmitter {
|
|
|
2792
2824
|
code += this.indent() + header + ' {\n';
|
|
2793
2825
|
this.indentLevel++;
|
|
2794
2826
|
if (own) code += this.indent() + `if (!Object.hasOwn(${oc}, ${kvp})) continue;\n`;
|
|
2795
|
-
if (vv) code += this.indent() +
|
|
2827
|
+
if (vv) code += this.indent() + `let ${vv} = ${oc}[${kvp}];\n`;
|
|
2796
2828
|
if (guards?.length > 0) { code += this.indent() + `if (${guards.map(g => this.emit(g, 'value')).join(' && ')}) {\n`; this.indentLevel++; }
|
|
2797
2829
|
emitBody();
|
|
2798
2830
|
if (guards?.length > 0) { this.indentLevel--; code += this.indent() + '}\n'; }
|
|
@@ -2864,7 +2896,7 @@ export class CodeEmitter {
|
|
|
2864
2896
|
code += header + ' {\n';
|
|
2865
2897
|
this.indentLevel++;
|
|
2866
2898
|
if (own) code += this.indent() + `if (!Object.hasOwn(${oc}, ${kvp})) continue;\n`;
|
|
2867
|
-
if (vv) code += this.indent() +
|
|
2899
|
+
if (vv) code += this.indent() + `let ${vv} = ${oc}[${kvp}];\n`;
|
|
2868
2900
|
emitBody();
|
|
2869
2901
|
this.indentLevel--;
|
|
2870
2902
|
code += this.indent() + '}';
|
|
@@ -3691,7 +3723,7 @@ export class Compiler {
|
|
|
3691
3723
|
|
|
3692
3724
|
// If only terminators remain (type-only source), emit types and return early
|
|
3693
3725
|
if (tokens.every(t => t[0] === 'TERMINATOR')) {
|
|
3694
|
-
if (typeTokens) dts =
|
|
3726
|
+
if (typeTokens && _typesEmitter) dts = _typesEmitter(typeTokens, ['program'], source);
|
|
3695
3727
|
return { tokens, sexpr: ['program'], code: '', dts, data: dataSection, reactiveVars: {} };
|
|
3696
3728
|
}
|
|
3697
3729
|
|
|
@@ -3761,6 +3793,11 @@ export class Compiler {
|
|
|
3761
3793
|
skipDataPart: this.options.skipDataPart,
|
|
3762
3794
|
stubComponents: this.options.stubComponents,
|
|
3763
3795
|
reactiveVars: this.options.reactiveVars,
|
|
3796
|
+
// Schema runtime mode: 'browser' / 'validate' / 'server' / 'migration'.
|
|
3797
|
+
// Default 'migration' covers the common case (CLI, server, tests) where
|
|
3798
|
+
// the user might call any schema feature including .toSQL(). The browser
|
|
3799
|
+
// bundle build script overrides to 'browser' for size reduction.
|
|
3800
|
+
schemaMode: this.options.schemaMode,
|
|
3764
3801
|
sourceMap,
|
|
3765
3802
|
});
|
|
3766
3803
|
let code = generator.compile(sexpr);
|
|
@@ -3768,15 +3805,28 @@ export class Compiler {
|
|
|
3768
3805
|
let map = sourceMap ? sourceMap.toJSON() : null;
|
|
3769
3806
|
let reverseMap = sourceMap ? sourceMap.toReverseMap() : null;
|
|
3770
3807
|
if (map && this.options.sourceMap === 'inline') {
|
|
3771
|
-
|
|
3808
|
+
// map is already a JSON string (sourceMaps.toJSON() stringifies). UTF-8
|
|
3809
|
+
// safe encode: btoa() only handles Latin-1, so pre-encode non-ASCII via
|
|
3810
|
+
// TextEncoder before base64 in browsers. Bun's Buffer handles utf-8
|
|
3811
|
+
// directly. Source files containing emoji, em-dashes, accented chars,
|
|
3812
|
+
// etc. would otherwise break with `Failed to execute 'btoa'`.
|
|
3813
|
+
let b64;
|
|
3814
|
+
if (typeof Buffer !== 'undefined') {
|
|
3815
|
+
b64 = Buffer.from(map, 'utf8').toString('base64');
|
|
3816
|
+
} else {
|
|
3817
|
+
const bytes = new TextEncoder().encode(map);
|
|
3818
|
+
let bin = '';
|
|
3819
|
+
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
|
|
3820
|
+
b64 = btoa(bin);
|
|
3821
|
+
}
|
|
3772
3822
|
code += `\n//# sourceMappingURL=data:application/json;base64,${b64}`;
|
|
3773
3823
|
} else if (map && this.options.filename) {
|
|
3774
3824
|
code += `\n//# sourceMappingURL=${this.options.filename}.js.map`;
|
|
3775
3825
|
}
|
|
3776
3826
|
|
|
3777
3827
|
// Step 5: Emit .d.ts from annotated tokens + parsed s-expression
|
|
3778
|
-
if (typeTokens) {
|
|
3779
|
-
dts =
|
|
3828
|
+
if (typeTokens && _typesEmitter) {
|
|
3829
|
+
dts = _typesEmitter(typeTokens, sexpr, source);
|
|
3780
3830
|
}
|
|
3781
3831
|
|
|
3782
3832
|
return { tokens, sexpr, code, dts, map, reverseMap, data: dataSection, reactiveVars: generator.reactiveVars };
|
|
@@ -3793,10 +3843,36 @@ export class Compiler {
|
|
|
3793
3843
|
installComponentSupport(CodeEmitter, Lexer);
|
|
3794
3844
|
|
|
3795
3845
|
// =============================================================================
|
|
3796
|
-
//
|
|
3846
|
+
// Enum Codegen (CodeEmitter method)
|
|
3797
3847
|
// =============================================================================
|
|
3848
|
+
// `enum` blocks compile to a runtime JavaScript object that maps both
|
|
3849
|
+
// forward (key → value) and reverse (value → key). This is real codegen,
|
|
3850
|
+
// not type machinery, so it lives with the rest of the emitter dispatch.
|
|
3851
|
+
|
|
3852
|
+
CodeEmitter.prototype.emitEnum = function emitEnum(head, rest, context) {
|
|
3853
|
+
let [name, body] = rest;
|
|
3854
|
+
let enumName = name?.valueOf?.() ?? name;
|
|
3855
|
+
|
|
3856
|
+
let pairs = [];
|
|
3857
|
+
if (Array.isArray(body)) {
|
|
3858
|
+
let items = body[0] === 'block' ? body.slice(1) : [body];
|
|
3859
|
+
for (let item of items) {
|
|
3860
|
+
if (Array.isArray(item)) {
|
|
3861
|
+
if (item[0]?.valueOf?.() === '=') {
|
|
3862
|
+
let key = item[1]?.valueOf?.() ?? item[1];
|
|
3863
|
+
let val = item[2]?.valueOf?.() ?? item[2];
|
|
3864
|
+
pairs.push([key, val]);
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
if (pairs.length === 0) return `const ${enumName} = {}`;
|
|
3798
3871
|
|
|
3799
|
-
|
|
3872
|
+
let forward = pairs.map(([k, v]) => `${k}: ${v}`).join(', ');
|
|
3873
|
+
let reverse = pairs.map(([k, v]) => `${v}: "${k}"`).join(', ');
|
|
3874
|
+
return `const ${enumName} = {${forward}, ${reverse}}`;
|
|
3875
|
+
};
|
|
3800
3876
|
|
|
3801
3877
|
// =============================================================================
|
|
3802
3878
|
// Schema Support (prototype installation)
|
package/src/grammar/grammar.rip
CHANGED
|
@@ -808,7 +808,7 @@ grammar =
|
|
|
808
808
|
# Schema
|
|
809
809
|
# ============================================================================
|
|
810
810
|
# `schema [:kind] INDENT ... OUTDENT` is parsed entirely by the schema
|
|
811
|
-
# sub-parser at lexer-rewrite time (src/schema.js). The rewriter emits
|
|
811
|
+
# sub-parser at lexer-rewrite time (packages/schema/src/schema.js). The rewriter emits
|
|
812
812
|
# a synthetic SCHEMA_BODY token whose .data carries the full descriptor
|
|
813
813
|
# (kind, entries, per-entry loc). The main grammar only sees these two
|
|
814
814
|
# terminals, which keeps schema body syntax (`name! type`, `@directive`,
|
package/src/grammar/solar.rip
CHANGED
package/src/lexer.js
CHANGED
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
// ==========================================================================
|
|
41
41
|
|
|
42
42
|
import { installTypeSupport } from './types.js';
|
|
43
|
-
import { installSchemaSupport } from '
|
|
43
|
+
import { installSchemaSupport } from '@rip-lang/schema';
|
|
44
44
|
|
|
45
45
|
// ==========================================================================
|
|
46
46
|
// Token Category Sets
|
|
@@ -174,6 +174,15 @@ let TAGGABLE = new Set(['IDENTIFIER', 'PROPERTY', ')', 'CALL_END', ']', 'INDEX_E
|
|
|
174
174
|
// Control flow tokens that don't end implicit calls/objects
|
|
175
175
|
let CONTROL_IN_IMPLICIT = new Set(['IF', 'TRY', 'FINALLY', 'CATCH', 'CLASS', 'SWITCH', 'COMPONENT', 'FOR']);
|
|
176
176
|
|
|
177
|
+
// Tokens that complete an expression value. Used to detect postfix-position
|
|
178
|
+
// for keywords that exist in both prefix and postfix forms (FOR has no
|
|
179
|
+
// dedicated POST_FOR token like POST_IF, so we infer it from context).
|
|
180
|
+
let VALUE_END_TAGS = new Set([
|
|
181
|
+
'IDENTIFIER', 'PROPERTY', 'NUMBER', 'STRING', 'STRING_END', 'REGEX', 'REGEX_END',
|
|
182
|
+
')', 'CALL_END', ']', 'INDEX_END', '}', 'MAP_END', 'PICK_END',
|
|
183
|
+
'BOOL', 'NULL', 'UNDEFINED', 'INFINITY', 'NAN', 'SUPER', 'THIS', '@', 'SYMBOL',
|
|
184
|
+
]);
|
|
185
|
+
|
|
177
186
|
// Single-liner keywords that get implicit INDENT/OUTDENT
|
|
178
187
|
let SINGLE_LINERS = new Set(['ELSE', '->', '=>', 'TRY', 'FINALLY', 'THEN']);
|
|
179
188
|
|
|
@@ -1782,8 +1791,21 @@ export class Lexer {
|
|
|
1782
1791
|
i += 1;
|
|
1783
1792
|
};
|
|
1784
1793
|
|
|
1785
|
-
// Don't end implicit on INDENT for control flow inside implicit
|
|
1786
|
-
|
|
1794
|
+
// Don't end implicit on INDENT for control flow inside implicit.
|
|
1795
|
+
//
|
|
1796
|
+
// Special case: FOR is the only entry in CONTROL_IN_IMPLICIT that can
|
|
1797
|
+
// appear in postfix position (no POST_FOR token exists, unlike
|
|
1798
|
+
// POST_IF / POST_UNLESS). When FOR follows a value-completing token
|
|
1799
|
+
// on the same line (e.g. `addSymbol s for s in xs`), it's a postfix
|
|
1800
|
+
// comprehension that should END the implicit call so the comprehension
|
|
1801
|
+
// wraps the call rather than becoming the call's argument. Detect
|
|
1802
|
+
// postfix FOR by: not at the start of a new line, AND the previous
|
|
1803
|
+
// token is a value-completing token (IDENTIFIER, ), ], literal, etc.).
|
|
1804
|
+
// Prefix FOR (after `:`, `=`, `->`, comma, etc.) keeps the existing
|
|
1805
|
+
// CONTROL behaviour. Falling through lets the IMPLICIT_END handler
|
|
1806
|
+
// below close the implicit call.
|
|
1807
|
+
let isPostfixFor = tag === 'FOR' && !token.newLine && VALUE_END_TAGS.has(prevTag);
|
|
1808
|
+
if ((inImplicitCall() || inImplicitObject()) && CONTROL_IN_IMPLICIT.has(tag) && !isPostfixFor) {
|
|
1787
1809
|
stack.push(['CONTROL', i, {ours: true}]);
|
|
1788
1810
|
return forward(1);
|
|
1789
1811
|
}
|
package/src/parser.js
CHANGED
|
@@ -256,16 +256,16 @@ const parserInstance = {
|
|
|
256
256
|
}
|
|
257
257
|
},
|
|
258
258
|
parse(input) {
|
|
259
|
-
let EOF, TERROR, action, errStr, expected, len, lex, lexer, loc, locs, newState, p, parseTable, preErrorSymbol, r, recovering, rv, sharedState, state, stk, symbol, tokenLen, tokenLine, tokenLoc, tokenText, vals;
|
|
259
|
+
let EOF, TERROR, action, errStr, expected, k, len, lex, lexer, loc, locs, newState, p, parseTable, preErrorSymbol, r, recovering, rv, sharedState, state, stk, symbol, tokenLen, tokenLine, tokenLoc, tokenText, v, vals;
|
|
260
260
|
[stk, vals, locs] = [[0], [null], []];
|
|
261
261
|
[parseTable, tokenText, tokenLine, tokenLen, recovering] = [this.parseTable, "", 0, 0, 0];
|
|
262
262
|
[TERROR, EOF] = [2, 1];
|
|
263
263
|
lexer = Object.create(this.lexer);
|
|
264
264
|
sharedState = { ctx: {} };
|
|
265
|
-
for (
|
|
265
|
+
for (let k in this.ctx) {
|
|
266
266
|
if (!Object.hasOwn(this.ctx, k))
|
|
267
267
|
continue;
|
|
268
|
-
|
|
268
|
+
let v = this.ctx[k];
|
|
269
269
|
sharedState.ctx[k] = v;
|
|
270
270
|
}
|
|
271
271
|
lexer.setInput(input, sharedState.ctx);
|
|
@@ -294,7 +294,7 @@ const parserInstance = {
|
|
|
294
294
|
if (!recovering)
|
|
295
295
|
expected = (() => {
|
|
296
296
|
const result = [];
|
|
297
|
-
for (
|
|
297
|
+
for (let p in parseTable[state]) {
|
|
298
298
|
if (!Object.hasOwn(parseTable[state], p))
|
|
299
299
|
continue;
|
|
300
300
|
if (this.tokenNames[p] && p > TERROR)
|
package/src/typecheck.js
CHANGED
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
// type errors mapped back to Rip source positions.
|
|
12
12
|
|
|
13
13
|
import { Compiler, getStdlibCode } from './compiler.js';
|
|
14
|
-
import { INTRINSIC_TYPE_DECLS, INTRINSIC_FN_DECL, ARIA_TYPE_DECLS, SIGNAL_INTERFACE, SIGNAL_FN, COMPUTED_INTERFACE, COMPUTED_FN, EFFECT_FN } from './types.js';
|
|
15
|
-
import { hasSchemas } from '
|
|
14
|
+
import { INTRINSIC_TYPE_DECLS, INTRINSIC_FN_DECL, ARIA_TYPE_DECLS, SIGNAL_INTERFACE, SIGNAL_FN, COMPUTED_INTERFACE, COMPUTED_FN, EFFECT_FN } from './types-emit.js';
|
|
15
|
+
import { hasSchemas } from '@rip-lang/schema';
|
|
16
|
+
import '@rip-lang/schema/loader-server'; // registers full schema runtime provider
|
|
16
17
|
import { createRequire } from 'module';
|
|
17
18
|
import { readFileSync, existsSync, readdirSync } from 'fs';
|
|
18
19
|
import { resolve, relative, dirname } from 'path';
|