rip-lang 3.6.1 → 3.7.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/CHANGELOG.md +27 -1
- package/README.md +42 -17
- package/docs/NOTES.md +93 -0
- package/docs/RIP-GUIDE.md +39 -1
- package/docs/RIP-INTERNALS.md +9 -9
- package/docs/RIP-LANG.md +213 -2
- package/docs/RIP-TYPES.md +98 -0
- package/docs/dist/rip.browser.js +976 -508
- package/docs/dist/rip.browser.min.js +215 -215
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/index.html +52 -31
- package/package.json +3 -3
- package/src/compiler.js +238 -151
- package/src/components.js +167 -169
- package/src/grammar/grammar.rip +12 -2
- package/src/lexer.js +77 -5
- package/src/parser.js +123 -120
- package/src/repl.js +4 -128
- package/src/types.js +416 -35
package/src/compiler.js
CHANGED
|
@@ -206,7 +206,9 @@ export class CodeGenerator {
|
|
|
206
206
|
'continue-if': 'generateContinueIf',
|
|
207
207
|
'?': 'generateExistential',
|
|
208
208
|
'?:': 'generateTernary',
|
|
209
|
+
'|>': 'generatePipe',
|
|
209
210
|
'loop': 'generateLoop',
|
|
211
|
+
'loop-n': 'generateLoopN',
|
|
210
212
|
'await': 'generateAwait',
|
|
211
213
|
'yield': 'generateYield',
|
|
212
214
|
'yield-from': 'generateYieldFrom',
|
|
@@ -362,9 +364,9 @@ export class CodeGenerator {
|
|
|
362
364
|
if (typeof target === 'string' || target instanceof String) {
|
|
363
365
|
let varName = str(target);
|
|
364
366
|
if (!this.reactiveVars?.has(varName)) this.programVars.add(varName);
|
|
365
|
-
} else if (
|
|
367
|
+
} else if (this.is(target, 'array')) {
|
|
366
368
|
this.collectVarsFromArray(target, this.programVars);
|
|
367
|
-
} else if (
|
|
369
|
+
} else if (this.is(target, 'object')) {
|
|
368
370
|
this.collectVarsFromObject(target, this.programVars);
|
|
369
371
|
}
|
|
370
372
|
this.collectProgramVariables(value);
|
|
@@ -392,13 +394,13 @@ export class CodeGenerator {
|
|
|
392
394
|
this.collectProgramVariables(rest[0]);
|
|
393
395
|
if (rest.length >= 2 && Array.isArray(rest[1]) && rest[1].length === 2 && rest[1][0] !== 'block') {
|
|
394
396
|
let [param, catchBlock] = rest[1];
|
|
395
|
-
if (param &&
|
|
397
|
+
if (param && this.is(param, 'object')) {
|
|
396
398
|
param.slice(1).forEach(pair => {
|
|
397
399
|
if (Array.isArray(pair) && pair.length === 2 && typeof pair[1] === 'string') {
|
|
398
400
|
this.programVars.add(pair[1]);
|
|
399
401
|
}
|
|
400
402
|
});
|
|
401
|
-
} else if (param &&
|
|
403
|
+
} else if (param && this.is(param, 'array')) {
|
|
402
404
|
param.slice(1).forEach(item => {
|
|
403
405
|
if (typeof item === 'string') this.programVars.add(item);
|
|
404
406
|
});
|
|
@@ -425,8 +427,8 @@ export class CodeGenerator {
|
|
|
425
427
|
if (CodeGenerator.ASSIGNMENT_OPS.has(head)) {
|
|
426
428
|
let [target, value] = rest;
|
|
427
429
|
if (typeof target === 'string') vars.add(target);
|
|
428
|
-
else if (
|
|
429
|
-
else if (
|
|
430
|
+
else if (this.is(target, 'array')) this.collectVarsFromArray(target, vars);
|
|
431
|
+
else if (this.is(target, 'object')) this.collectVarsFromObject(target, vars);
|
|
430
432
|
collect(value);
|
|
431
433
|
return;
|
|
432
434
|
}
|
|
@@ -435,11 +437,11 @@ export class CodeGenerator {
|
|
|
435
437
|
collect(rest[0]);
|
|
436
438
|
if (rest.length >= 2 && Array.isArray(rest[1]) && rest[1].length === 2 && rest[1][0] !== 'block') {
|
|
437
439
|
let [param, catchBlock] = rest[1];
|
|
438
|
-
if (param &&
|
|
440
|
+
if (param && this.is(param, 'object')) {
|
|
439
441
|
param.slice(1).forEach(pair => {
|
|
440
442
|
if (Array.isArray(pair) && pair.length === 2 && typeof pair[1] === 'string') vars.add(pair[1]);
|
|
441
443
|
});
|
|
442
|
-
} else if (param &&
|
|
444
|
+
} else if (param && this.is(param, 'array')) {
|
|
443
445
|
param.slice(1).forEach(item => { if (typeof item === 'string') vars.add(item); });
|
|
444
446
|
}
|
|
445
447
|
collect(catchBlock);
|
|
@@ -603,7 +605,7 @@ export class CodeGenerator {
|
|
|
603
605
|
let [obj, prop] = head.slice(1);
|
|
604
606
|
let objCode = this.generate(obj, 'value');
|
|
605
607
|
let needsParens = CodeGenerator.NUMBER_LITERAL_RE.test(objCode) ||
|
|
606
|
-
(
|
|
608
|
+
((this.is(obj, 'object') || this.is(obj, 'await') || this.is(obj, 'yield')));
|
|
607
609
|
let base = needsParens ? `(${objCode})` : objCode;
|
|
608
610
|
calleeCode = `${base}.${str(prop)}`;
|
|
609
611
|
} else {
|
|
@@ -638,9 +640,9 @@ export class CodeGenerator {
|
|
|
638
640
|
let blockStmts = ['def', 'class', 'if', 'unless', 'for-in', 'for-of', 'for-as', 'while', 'until', 'loop', 'switch', 'try'];
|
|
639
641
|
let statementsCode = other.map((stmt, index) => {
|
|
640
642
|
let isSingle = other.length === 1 && imports.length === 0 && exports.length === 0;
|
|
641
|
-
let isObj =
|
|
643
|
+
let isObj = this.is(stmt, 'object');
|
|
642
644
|
let isObjComp = isObj && stmt.length === 2 && Array.isArray(stmt[1]) && Array.isArray(stmt[1][1]) && stmt[1][1][0] === 'comprehension';
|
|
643
|
-
let isAlreadyExpr =
|
|
645
|
+
let isAlreadyExpr = (this.is(stmt, 'comprehension') || this.is(stmt, 'object-comprehension') || this.is(stmt, 'do-iife'));
|
|
644
646
|
let hasNoVars = this.programVars.size === 0;
|
|
645
647
|
let needsParens = isSingle && isObj && hasNoVars && !isAlreadyExpr && !isObjComp;
|
|
646
648
|
let isLast = index === other.length - 1;
|
|
@@ -672,24 +674,28 @@ export class CodeGenerator {
|
|
|
672
674
|
needsBlank = true;
|
|
673
675
|
}
|
|
674
676
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
if (
|
|
678
|
-
code += 'const
|
|
679
|
-
code += '
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
677
|
+
let skip = this.options.skipPreamble;
|
|
678
|
+
|
|
679
|
+
if (!skip) {
|
|
680
|
+
if (this.helpers.has('slice')) { code += 'const slice = [].slice;\n'; needsBlank = true; }
|
|
681
|
+
if (this.helpers.has('modulo')) { code += 'const modulo = (n, d) => { n = +n; d = +d; return (n % d + d) % d; };\n'; needsBlank = true; }
|
|
682
|
+
if (this.helpers.has('toSearchable')) {
|
|
683
|
+
code += 'const toSearchable = (v, allowNewlines) => {\n';
|
|
684
|
+
code += ' if (typeof v === "string") return !allowNewlines && /[\\n\\r]/.test(v) ? null : v;\n';
|
|
685
|
+
code += ' if (v == null) return "";\n';
|
|
686
|
+
code += ' if (typeof v === "number" || typeof v === "bigint" || typeof v === "boolean") return String(v);\n';
|
|
687
|
+
code += ' if (typeof v === "symbol") return v.description || "";\n';
|
|
688
|
+
code += ' if (v instanceof Uint8Array || v instanceof ArrayBuffer) {\n';
|
|
689
|
+
code += ' return new TextDecoder().decode(v instanceof Uint8Array ? v : new Uint8Array(v));\n';
|
|
690
|
+
code += ' }\n';
|
|
691
|
+
code += ' if (Array.isArray(v)) return v.join(",");\n';
|
|
692
|
+
code += ' if (typeof v.toString === "function" && v.toString !== Object.prototype.toString) {\n';
|
|
693
|
+
code += ' try { return v.toString(); } catch { return ""; }\n';
|
|
694
|
+
code += ' }\n';
|
|
695
|
+
code += ' return "";\n';
|
|
696
|
+
code += '};\n';
|
|
697
|
+
needsBlank = true;
|
|
698
|
+
}
|
|
693
699
|
}
|
|
694
700
|
|
|
695
701
|
// Generate exports code early so component/reactivity flags are set before runtime checks
|
|
@@ -698,7 +704,7 @@ export class CodeGenerator {
|
|
|
698
704
|
exportsCode = '\n' + exports.map(s => this.addSemicolon(s, this.generate(s, 'statement'))).join('\n');
|
|
699
705
|
}
|
|
700
706
|
|
|
701
|
-
if (this.usesReactivity && !
|
|
707
|
+
if (this.usesReactivity && !skip) {
|
|
702
708
|
if (typeof globalThis !== 'undefined' && globalThis.__rip) {
|
|
703
709
|
code += 'const { __state, __computed, __effect, __batch, __readonly, __setErrorHandler, __handleError, __catchErrors } = globalThis.__rip;\n';
|
|
704
710
|
} else {
|
|
@@ -707,7 +713,7 @@ export class CodeGenerator {
|
|
|
707
713
|
needsBlank = true;
|
|
708
714
|
}
|
|
709
715
|
|
|
710
|
-
if (this.usesTemplates && !
|
|
716
|
+
if (this.usesTemplates && !skip) {
|
|
711
717
|
if (typeof globalThis !== 'undefined' && globalThis.__ripComponent) {
|
|
712
718
|
code += 'const { isSignal, __pushComponent, __popComponent, setContext, getContext, hasContext, __cx__ } = globalThis.__ripComponent;\n';
|
|
713
719
|
} else {
|
|
@@ -716,7 +722,7 @@ export class CodeGenerator {
|
|
|
716
722
|
needsBlank = true;
|
|
717
723
|
}
|
|
718
724
|
|
|
719
|
-
if (this.dataSection !== null && this.dataSection !== undefined) {
|
|
725
|
+
if (this.dataSection !== null && this.dataSection !== undefined && !skip) {
|
|
720
726
|
code += 'var DATA;\n_setDataSection();\n';
|
|
721
727
|
needsBlank = true;
|
|
722
728
|
}
|
|
@@ -741,6 +747,24 @@ export class CodeGenerator {
|
|
|
741
747
|
return `(${op}${this.generate(rest[0], 'value')})`;
|
|
742
748
|
}
|
|
743
749
|
let [left, right] = rest;
|
|
750
|
+
// String repeat: "str" * n → "str".repeat(n)
|
|
751
|
+
if (op === '*') {
|
|
752
|
+
let leftStr = left?.valueOf?.() ?? left;
|
|
753
|
+
if (typeof leftStr === 'string' && /^["']/.test(leftStr)) {
|
|
754
|
+
return `${this.generate(left, 'value')}.repeat(${this.generate(right, 'value')})`;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
// Chained comparisons: (< (< a b) c) → ((a < b) && (b < c))
|
|
758
|
+
let COMPARE_OPS = new Set(['<', '>', '<=', '>=']);
|
|
759
|
+
if (COMPARE_OPS.has(op) && Array.isArray(left)) {
|
|
760
|
+
let leftOp = left[0]?.valueOf?.() ?? left[0];
|
|
761
|
+
if (COMPARE_OPS.has(leftOp)) {
|
|
762
|
+
let a = this.generate(left[1], 'value');
|
|
763
|
+
let b = this.generate(left[2], 'value');
|
|
764
|
+
let c = this.generate(right, 'value');
|
|
765
|
+
return `((${a} ${leftOp} ${b}) && (${b} ${op} ${c}))`;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
744
768
|
if (op === '!?') {
|
|
745
769
|
let l = this.generate(left, 'value'), r = this.generate(right, 'value');
|
|
746
770
|
return `(${l} !== undefined ? ${l} : ${r})`;
|
|
@@ -776,7 +800,7 @@ export class CodeGenerator {
|
|
|
776
800
|
let op = head === '?=' ? '??=' : head;
|
|
777
801
|
|
|
778
802
|
// Validate: no sigils in assignment targets (except void function syntax)
|
|
779
|
-
let isFnValue =
|
|
803
|
+
let isFnValue = (this.is(value, '->') || this.is(value, '=>') || this.is(value, 'def'));
|
|
780
804
|
if (target instanceof String && meta(target, 'await') !== undefined && !isFnValue) {
|
|
781
805
|
let sigil = meta(target, 'await') === true ? '!' : '&';
|
|
782
806
|
throw new Error(`Cannot use ${sigil} sigil in variable declaration '${str(target)}'.`);
|
|
@@ -787,8 +811,8 @@ export class CodeGenerator {
|
|
|
787
811
|
}
|
|
788
812
|
|
|
789
813
|
// Empty destructuring — just evaluate RHS
|
|
790
|
-
let isEmptyArr =
|
|
791
|
-
let isEmptyObj =
|
|
814
|
+
let isEmptyArr = this.is(target, 'array', 0);
|
|
815
|
+
let isEmptyObj = this.is(target, 'object', 0);
|
|
792
816
|
if (isEmptyArr || isEmptyObj) {
|
|
793
817
|
let v = this.generate(value, 'value');
|
|
794
818
|
return (isEmptyObj && context === 'statement') ? `(${v})` : v;
|
|
@@ -817,8 +841,8 @@ export class CodeGenerator {
|
|
|
817
841
|
}
|
|
818
842
|
|
|
819
843
|
// Middle/leading rest in array destructuring
|
|
820
|
-
if (
|
|
821
|
-
let restIdx = target.slice(1).findIndex(el => (
|
|
844
|
+
if (this.is(target, 'array')) {
|
|
845
|
+
let restIdx = target.slice(1).findIndex(el => (this.is(el, '...')) || el === '...');
|
|
822
846
|
if (restIdx !== -1 && restIdx < target.length - 2) {
|
|
823
847
|
let elements = target.slice(1);
|
|
824
848
|
let afterRest = elements.slice(restIdx + 1);
|
|
@@ -832,10 +856,10 @@ export class CodeGenerator {
|
|
|
832
856
|
elements.forEach(el => {
|
|
833
857
|
if (el === ',' || el === '...') return;
|
|
834
858
|
if (typeof el === 'string') this.programVars.add(el);
|
|
835
|
-
else if (
|
|
859
|
+
else if (this.is(el, '...') && typeof el[1] === 'string') this.programVars.add(el[1]);
|
|
836
860
|
});
|
|
837
861
|
let restEl = elements[restIdx];
|
|
838
|
-
let restVar =
|
|
862
|
+
let restVar = this.is(restEl, '...') ? restEl[1] : null;
|
|
839
863
|
let stmts = [];
|
|
840
864
|
if (beforePattern) stmts.push(`[${beforePattern}] = ${valueCode}`);
|
|
841
865
|
if (restVar) stmts.push(`[...${restVar}] = ${valueCode}.slice(${restIdx}, -${afterCount})`);
|
|
@@ -849,7 +873,7 @@ export class CodeGenerator {
|
|
|
849
873
|
if (context === 'statement' && head === '=' && Array.isArray(value) &&
|
|
850
874
|
(value[0] === '||' || value[0] === '&&') && value.length === 3) {
|
|
851
875
|
let [binOp, left, right] = value;
|
|
852
|
-
if (
|
|
876
|
+
if ((this.is(right, 'unless') || this.is(right, 'if')) && right.length === 3) {
|
|
853
877
|
let [condType, condition, wrappedValue] = right;
|
|
854
878
|
let unwrapped = Array.isArray(wrappedValue) && wrappedValue.length === 1 ? wrappedValue[0] : wrappedValue;
|
|
855
879
|
let fullValue = [binOp, left, unwrapped];
|
|
@@ -889,11 +913,11 @@ export class CodeGenerator {
|
|
|
889
913
|
}
|
|
890
914
|
|
|
891
915
|
let valueCode = this.generate(value, 'value');
|
|
892
|
-
let isObjLit =
|
|
916
|
+
let isObjLit = this.is(value, 'object');
|
|
893
917
|
if (!isObjLit) valueCode = this.unwrap(valueCode);
|
|
894
918
|
|
|
895
919
|
let needsParensVal = context === 'value';
|
|
896
|
-
let needsParensObj = context === 'statement' &&
|
|
920
|
+
let needsParensObj = context === 'statement' && this.is(target, 'object');
|
|
897
921
|
if (needsParensVal || needsParensObj) return `(${targetCode} ${op} ${valueCode})`;
|
|
898
922
|
return `${targetCode} ${op} ${valueCode}`;
|
|
899
923
|
}
|
|
@@ -908,7 +932,7 @@ export class CodeGenerator {
|
|
|
908
932
|
let objCode = this.generate(obj, 'value');
|
|
909
933
|
this.suppressReactiveUnwrap = false;
|
|
910
934
|
let needsParens = CodeGenerator.NUMBER_LITERAL_RE.test(objCode) ||
|
|
911
|
-
(
|
|
935
|
+
((this.is(obj, 'object') || this.is(obj, 'await') || this.is(obj, 'yield')));
|
|
912
936
|
let base = needsParens ? `(${objCode})` : objCode;
|
|
913
937
|
if (meta(prop, 'await') === true) return `await ${base}.${str(prop)}()`;
|
|
914
938
|
if (meta(prop, 'predicate')) return `(${base}.${str(prop)} != null)`;
|
|
@@ -932,27 +956,41 @@ export class CodeGenerator {
|
|
|
932
956
|
|
|
933
957
|
generateIndexAccess(head, rest) {
|
|
934
958
|
let [arr, index] = rest;
|
|
935
|
-
if (
|
|
959
|
+
if ((this.is(index, '..') || this.is(index, '...'))) {
|
|
936
960
|
let isIncl = index[0] === '..';
|
|
937
961
|
let arrCode = this.generate(arr, 'value');
|
|
938
962
|
let [start, end] = index.slice(1);
|
|
939
963
|
if (start === null && end === null) return `${arrCode}.slice()`;
|
|
940
964
|
if (start === null) {
|
|
941
|
-
if (isIncl && this.
|
|
965
|
+
if (isIncl && this.is(end, '-', 1) && (str(end[1]) ?? end[1]) == 1) return `${arrCode}.slice(0)`;
|
|
942
966
|
let e = this.generate(end, 'value');
|
|
943
967
|
return isIncl ? `${arrCode}.slice(0, +${e} + 1 || 9e9)` : `${arrCode}.slice(0, ${e})`;
|
|
944
968
|
}
|
|
945
969
|
if (end === null) return `${arrCode}.slice(${this.generate(start, 'value')})`;
|
|
946
970
|
let s = this.generate(start, 'value');
|
|
947
|
-
if (isIncl && this.
|
|
971
|
+
if (isIncl && this.is(end, '-', 1) && (str(end[1]) ?? end[1]) == 1) return `${arrCode}.slice(${s})`;
|
|
948
972
|
let e = this.generate(end, 'value');
|
|
949
973
|
return isIncl ? `${arrCode}.slice(${s}, +${e} + 1 || 9e9)` : `${arrCode}.slice(${s}, ${e})`;
|
|
950
974
|
}
|
|
975
|
+
// Negative literal index: arr[-1] → arr.at(-1)
|
|
976
|
+
if (this.is(index, '-', 1)) {
|
|
977
|
+
let n = str(index[1]) ?? index[1];
|
|
978
|
+
if (typeof n === 'number' || (typeof n === 'string' && /^\d+$/.test(n))) {
|
|
979
|
+
return `${this.generate(arr, 'value')}.at(-${n})`;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
951
982
|
return `${this.generate(arr, 'value')}[${this.unwrap(this.generate(index, 'value'))}]`;
|
|
952
983
|
}
|
|
953
984
|
|
|
954
985
|
generateOptIndex(head, rest) {
|
|
955
986
|
let [arr, index] = rest;
|
|
987
|
+
// Negative literal index: arr?[-1] → arr?.at(-1)
|
|
988
|
+
if (this.is(index, '-', 1)) {
|
|
989
|
+
let n = str(index[1]) ?? index[1];
|
|
990
|
+
if (typeof n === 'number' || (typeof n === 'string' && /^\d+$/.test(n))) {
|
|
991
|
+
return `${this.generate(arr, 'value')}?.at(-${n})`;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
956
994
|
return `${this.generate(arr, 'value')}?.[${this.generate(index, 'value')}]`;
|
|
957
995
|
}
|
|
958
996
|
|
|
@@ -978,6 +1016,7 @@ export class CodeGenerator {
|
|
|
978
1016
|
|
|
979
1017
|
generateThinArrow(head, rest, context, sexpr) {
|
|
980
1018
|
let [params, body] = rest;
|
|
1019
|
+
if ((!params || (Array.isArray(params) && params.length === 0)) && this.containsIt(body)) params = ['it'];
|
|
981
1020
|
let sideEffectOnly = this.nextFunctionIsVoid || false;
|
|
982
1021
|
this.nextFunctionIsVoid = false;
|
|
983
1022
|
let paramList = this.generateParamList(params);
|
|
@@ -990,6 +1029,7 @@ export class CodeGenerator {
|
|
|
990
1029
|
|
|
991
1030
|
generateFatArrow(head, rest, context, sexpr) {
|
|
992
1031
|
let [params, body] = rest;
|
|
1032
|
+
if ((!params || (Array.isArray(params) && params.length === 0)) && this.containsIt(body)) params = ['it'];
|
|
993
1033
|
let sideEffectOnly = this.nextFunctionIsVoid || false;
|
|
994
1034
|
this.nextFunctionIsVoid = false;
|
|
995
1035
|
let paramList = this.generateParamList(params);
|
|
@@ -1001,7 +1041,7 @@ export class CodeGenerator {
|
|
|
1001
1041
|
let prefix = isAsync ? 'async ' : '';
|
|
1002
1042
|
|
|
1003
1043
|
if (!sideEffectOnly) {
|
|
1004
|
-
if (
|
|
1044
|
+
if (this.is(body, 'block') && body.length === 2) {
|
|
1005
1045
|
let expr = body[1];
|
|
1006
1046
|
if (!Array.isArray(expr) || expr[0] !== 'return') {
|
|
1007
1047
|
return `${prefix}${paramSyntax} => ${this.generate(expr, 'value')}`;
|
|
@@ -1021,19 +1061,19 @@ export class CodeGenerator {
|
|
|
1021
1061
|
let [expr] = rest;
|
|
1022
1062
|
if (this.sideEffectOnly) return 'return';
|
|
1023
1063
|
|
|
1024
|
-
if (
|
|
1064
|
+
if (this.is(expr, 'unless')) {
|
|
1025
1065
|
let [, condition, body] = expr;
|
|
1026
1066
|
let val = Array.isArray(body) && body.length === 1 ? body[0] : body;
|
|
1027
1067
|
return `if (!${this.generate(condition, 'value')}) return ${this.generate(val, 'value')}`;
|
|
1028
1068
|
}
|
|
1029
|
-
if (
|
|
1069
|
+
if (this.is(expr, 'if')) {
|
|
1030
1070
|
let [, condition, body, ...elseParts] = expr;
|
|
1031
1071
|
if (elseParts.length === 0) {
|
|
1032
1072
|
let val = Array.isArray(body) && body.length === 1 ? body[0] : body;
|
|
1033
1073
|
return `if (${this.generate(condition, 'value')}) return ${this.generate(val, 'value')}`;
|
|
1034
1074
|
}
|
|
1035
1075
|
}
|
|
1036
|
-
if (
|
|
1076
|
+
if (this.is(expr, 'new') && Array.isArray(expr[1]) && expr[1][0] === 'unless') {
|
|
1037
1077
|
let [, unlessNode] = expr;
|
|
1038
1078
|
let [, condition, body] = unlessNode;
|
|
1039
1079
|
let val = Array.isArray(body) && body.length === 1 ? body[0] : body;
|
|
@@ -1073,10 +1113,10 @@ export class CodeGenerator {
|
|
|
1073
1113
|
let [target, body] = rest;
|
|
1074
1114
|
this.usesReactivity = true;
|
|
1075
1115
|
let bodyCode;
|
|
1076
|
-
if (
|
|
1116
|
+
if (this.is(body, 'block')) {
|
|
1077
1117
|
let stmts = this.withIndent(() => this.formatStatements(body.slice(1)));
|
|
1078
1118
|
bodyCode = `{\n${stmts.join('\n')}\n${this.indent()}}`;
|
|
1079
|
-
} else if (
|
|
1119
|
+
} else if ((this.is(body, '->') || this.is(body, '=>'))) {
|
|
1080
1120
|
let fnCode = this.generate(body, 'value');
|
|
1081
1121
|
if (target) return `const ${str(target) ?? this.generate(target, 'value')} = __effect(${fnCode})`;
|
|
1082
1122
|
return `__effect(${fnCode})`;
|
|
@@ -1101,15 +1141,49 @@ export class CodeGenerator {
|
|
|
1101
1141
|
return `(${this.generate(rest[0], 'value')} != null)`;
|
|
1102
1142
|
}
|
|
1103
1143
|
|
|
1104
|
-
generateTernary(head, rest) {
|
|
1144
|
+
generateTernary(head, rest, context) {
|
|
1105
1145
|
let [cond, then_, else_] = rest;
|
|
1146
|
+
|
|
1147
|
+
// Hoist assignment: (cond ? (x = a) : b) → x = (cond ? a : b)
|
|
1148
|
+
// Enables: x = "admin" if cond else "member" without parens
|
|
1149
|
+
let thenHead = then_?.[0]?.valueOf?.() ?? then_?.[0];
|
|
1150
|
+
if (thenHead === '=' && Array.isArray(then_)) {
|
|
1151
|
+
let target = this.generate(then_[1], 'value');
|
|
1152
|
+
let thenVal = this.generate(then_[2], 'value');
|
|
1153
|
+
let elseVal = this.generate(else_, 'value');
|
|
1154
|
+
return `${target} = (${this.unwrap(this.generate(cond, 'value'))} ? ${thenVal} : ${elseVal})`;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1106
1157
|
return `(${this.unwrap(this.generate(cond, 'value'))} ? ${this.generate(then_, 'value')} : ${this.generate(else_, 'value')})`;
|
|
1107
1158
|
}
|
|
1108
1159
|
|
|
1160
|
+
generatePipe(head, rest) {
|
|
1161
|
+
let [left, right] = rest;
|
|
1162
|
+
let leftCode = this.generate(left, 'value');
|
|
1163
|
+
// Detect function calls: [fn, ...args] where fn is an identifier or accessor
|
|
1164
|
+
if (Array.isArray(right) && right.length > 1) {
|
|
1165
|
+
let fn = right[0];
|
|
1166
|
+
let isCall = Array.isArray(fn) || (typeof fn === 'string' && /^[a-zA-Z_$]/.test(fn));
|
|
1167
|
+
if (isCall) {
|
|
1168
|
+
let fnCode = this.generate(fn, 'value');
|
|
1169
|
+
let args = right.slice(1).map(a => this.generate(a, 'value'));
|
|
1170
|
+
return `${fnCode}(${leftCode}, ${args.join(', ')})`;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
// Simple reference or property access — call with left as sole arg
|
|
1174
|
+
return `${this.generate(right, 'value')}(${leftCode})`;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1109
1177
|
generateLoop(head, rest) {
|
|
1110
1178
|
return `while (true) ${this.generateLoopBody(rest[0])}`;
|
|
1111
1179
|
}
|
|
1112
1180
|
|
|
1181
|
+
generateLoopN(head, rest) {
|
|
1182
|
+
let [count, body] = rest;
|
|
1183
|
+
let n = this.generate(count, 'value');
|
|
1184
|
+
return `for (let _i = 0; _i < ${n}; _i++) ${this.generateLoopBody(body)}`;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1113
1187
|
generateAwait(head, rest) { return `await ${this.generate(rest[0], 'value')}`; }
|
|
1114
1188
|
|
|
1115
1189
|
generateYield(head, rest) {
|
|
@@ -1154,7 +1228,7 @@ export class CodeGenerator {
|
|
|
1154
1228
|
let varsArray = Array.isArray(vars) ? vars : [vars];
|
|
1155
1229
|
let noVar = varsArray.length === 0;
|
|
1156
1230
|
let [itemVar, indexVar] = noVar ? ['_i', null] : varsArray;
|
|
1157
|
-
let itemVarPattern = (
|
|
1231
|
+
let itemVarPattern = ((this.is(itemVar, 'array') || this.is(itemVar, 'object')))
|
|
1158
1232
|
? this.generateDestructuringPattern(itemVar) : itemVar;
|
|
1159
1233
|
|
|
1160
1234
|
// Stepped iteration
|
|
@@ -1162,7 +1236,7 @@ export class CodeGenerator {
|
|
|
1162
1236
|
let iterCode = this.generate(iterable, 'value');
|
|
1163
1237
|
let idxName = indexVar || '_i';
|
|
1164
1238
|
let stepCode = this.generate(step, 'value');
|
|
1165
|
-
let isNeg = this.
|
|
1239
|
+
let isNeg = this.is(step, '-', 1);
|
|
1166
1240
|
let isMinus1 = isNeg && (step[1] === '1' || step[1] === 1 || str(step[1]) === '1');
|
|
1167
1241
|
let isPlus1 = !isNeg && (step === '1' || step === 1 || str(step) === '1');
|
|
1168
1242
|
|
|
@@ -1172,7 +1246,7 @@ export class CodeGenerator {
|
|
|
1172
1246
|
else if (isNeg) loopHeader = `for (let ${idxName} = ${iterCode}.length - 1; ${idxName} >= 0; ${idxName} += ${stepCode}) `;
|
|
1173
1247
|
else loopHeader = `for (let ${idxName} = 0; ${idxName} < ${iterCode}.length; ${idxName} += ${stepCode}) `;
|
|
1174
1248
|
|
|
1175
|
-
if (
|
|
1249
|
+
if (this.is(body, 'block')) {
|
|
1176
1250
|
let stmts = body.slice(1);
|
|
1177
1251
|
this.indentLevel++;
|
|
1178
1252
|
let lines = [];
|
|
@@ -1204,7 +1278,7 @@ export class CodeGenerator {
|
|
|
1204
1278
|
if (indexVar) {
|
|
1205
1279
|
let iterCode = this.generate(iterable, 'value');
|
|
1206
1280
|
let code = `for (let ${indexVar} = 0; ${indexVar} < ${iterCode}.length; ${indexVar}++) `;
|
|
1207
|
-
if (
|
|
1281
|
+
if (this.is(body, 'block')) {
|
|
1208
1282
|
code += '{\n';
|
|
1209
1283
|
this.indentLevel++;
|
|
1210
1284
|
code += this.indent() + `const ${itemVarPattern} = ${iterCode}[${indexVar}];\n`;
|
|
@@ -1234,7 +1308,7 @@ export class CodeGenerator {
|
|
|
1234
1308
|
let isExcl = iterHead === '...';
|
|
1235
1309
|
let [start, end] = iterable.slice(1);
|
|
1236
1310
|
let isSimple = (e) => typeof e === 'number' || typeof e === 'string' && !e.includes('(') ||
|
|
1237
|
-
(e instanceof String && !str(e).includes('(')) || (
|
|
1311
|
+
(e instanceof String && !str(e).includes('(')) || (this.is(e, '.'));
|
|
1238
1312
|
if (isSimple(start) && isSimple(end)) {
|
|
1239
1313
|
let s = this.generate(start, 'value'), e = this.generate(end, 'value');
|
|
1240
1314
|
let cmp = isExcl ? '<' : '<=';
|
|
@@ -1258,7 +1332,7 @@ export class CodeGenerator {
|
|
|
1258
1332
|
let code = `for (const ${keyVar} in ${objCode}) `;
|
|
1259
1333
|
|
|
1260
1334
|
if (own && !valueVar && !guard) {
|
|
1261
|
-
if (
|
|
1335
|
+
if (this.is(body, 'block')) {
|
|
1262
1336
|
this.indentLevel++;
|
|
1263
1337
|
let stmts = [`if (!Object.hasOwn(${objCode}, ${keyVar})) continue;`, ...body.slice(1).map(s => this.addSemicolon(s, this.generate(s, 'statement')))];
|
|
1264
1338
|
this.indentLevel--;
|
|
@@ -1268,7 +1342,7 @@ export class CodeGenerator {
|
|
|
1268
1342
|
}
|
|
1269
1343
|
|
|
1270
1344
|
if (valueVar) {
|
|
1271
|
-
if (
|
|
1345
|
+
if (this.is(body, 'block')) {
|
|
1272
1346
|
let stmts = body.slice(1);
|
|
1273
1347
|
this.indentLevel++;
|
|
1274
1348
|
let lines = [];
|
|
@@ -1304,15 +1378,15 @@ export class CodeGenerator {
|
|
|
1304
1378
|
let iterable = rest[1], isAwait = rest[2], guard = rest[3], body = rest[4];
|
|
1305
1379
|
|
|
1306
1380
|
let needsTempVar = false, destructStmts = [];
|
|
1307
|
-
if (
|
|
1381
|
+
if (this.is(firstVar, 'array')) {
|
|
1308
1382
|
let elements = firstVar.slice(1);
|
|
1309
|
-
let restIdx = elements.findIndex(el => (
|
|
1383
|
+
let restIdx = elements.findIndex(el => (this.is(el, '...')) || el === '...');
|
|
1310
1384
|
if (restIdx !== -1 && restIdx < elements.length - 1) {
|
|
1311
1385
|
needsTempVar = true;
|
|
1312
1386
|
let afterRest = elements.slice(restIdx + 1), afterCount = afterRest.length;
|
|
1313
1387
|
let beforeRest = elements.slice(0, restIdx);
|
|
1314
1388
|
let restEl = elements[restIdx];
|
|
1315
|
-
let restVar =
|
|
1389
|
+
let restVar = this.is(restEl, '...') ? restEl[1] : '_rest';
|
|
1316
1390
|
let beforePattern = beforeRest.map(el => el === ',' ? '' : typeof el === 'string' ? el : this.generate(el, 'value')).join(', ');
|
|
1317
1391
|
let firstPattern = beforePattern ? `${beforePattern}, ...${restVar}` : `...${restVar}`;
|
|
1318
1392
|
let afterPattern = afterRest.map(el => el === ',' ? '' : typeof el === 'string' ? el : this.generate(el, 'value')).join(', ');
|
|
@@ -1322,7 +1396,7 @@ export class CodeGenerator {
|
|
|
1322
1396
|
elements.forEach(el => {
|
|
1323
1397
|
if (el === ',' || el === '...') return;
|
|
1324
1398
|
if (typeof el === 'string') this.programVars.add(el);
|
|
1325
|
-
else if (
|
|
1399
|
+
else if (this.is(el, '...') && typeof el[1] === 'string') this.programVars.add(el[1]);
|
|
1326
1400
|
});
|
|
1327
1401
|
}
|
|
1328
1402
|
}
|
|
@@ -1331,7 +1405,7 @@ export class CodeGenerator {
|
|
|
1331
1405
|
let awaitKw = isAwait ? 'await ' : '';
|
|
1332
1406
|
let itemVarPattern;
|
|
1333
1407
|
if (needsTempVar) itemVarPattern = '_item';
|
|
1334
|
-
else if (
|
|
1408
|
+
else if ((this.is(firstVar, 'array') || this.is(firstVar, 'object')))
|
|
1335
1409
|
itemVarPattern = this.generateDestructuringPattern(firstVar);
|
|
1336
1410
|
else itemVarPattern = firstVar;
|
|
1337
1411
|
|
|
@@ -1410,7 +1484,7 @@ export class CodeGenerator {
|
|
|
1410
1484
|
let [key, container] = rest;
|
|
1411
1485
|
let keyCode = this.generate(key, 'value');
|
|
1412
1486
|
let isNeg = meta(sexpr[0], 'invert');
|
|
1413
|
-
if (
|
|
1487
|
+
if (this.is(container, 'object')) {
|
|
1414
1488
|
let result = `(${keyCode} in ${this.generate(container, 'value')})`;
|
|
1415
1489
|
return isNeg ? `(!${result})` : result;
|
|
1416
1490
|
}
|
|
@@ -1438,7 +1512,7 @@ export class CodeGenerator {
|
|
|
1438
1512
|
|
|
1439
1513
|
generateNew(head, rest) {
|
|
1440
1514
|
let [call] = rest;
|
|
1441
|
-
if (
|
|
1515
|
+
if ((this.is(call, '.') || this.is(call, '?.'))) {
|
|
1442
1516
|
let [accType, target, prop] = call;
|
|
1443
1517
|
if (Array.isArray(target) && !target[0].startsWith) {
|
|
1444
1518
|
return `(${this.generate(['new', target], 'value')}).${prop}`;
|
|
@@ -1479,7 +1553,7 @@ export class CodeGenerator {
|
|
|
1479
1553
|
let codes = elements.map(el => {
|
|
1480
1554
|
if (el === ',') return '';
|
|
1481
1555
|
if (el === '...') return '';
|
|
1482
|
-
if (
|
|
1556
|
+
if (this.is(el, '...')) return `...${this.generate(el[1], 'value')}`;
|
|
1483
1557
|
return this.generate(el, 'value');
|
|
1484
1558
|
}).join(', ');
|
|
1485
1559
|
return hasTrailingElision ? `[${codes},]` : `[${codes}]`;
|
|
@@ -1494,11 +1568,11 @@ export class CodeGenerator {
|
|
|
1494
1568
|
}
|
|
1495
1569
|
|
|
1496
1570
|
let codes = pairs.map(pair => {
|
|
1497
|
-
if (
|
|
1571
|
+
if (this.is(pair, '...')) return `...${this.generate(pair[1], 'value')}`;
|
|
1498
1572
|
let [key, value, operator] = pair;
|
|
1499
1573
|
let keyCode;
|
|
1500
|
-
if (
|
|
1501
|
-
else if (
|
|
1574
|
+
if (this.is(key, 'dynamicKey')) keyCode = `[${this.generate(key[1], 'value')}]`;
|
|
1575
|
+
else if (this.is(key, 'str')) keyCode = `[${this.generate(key, 'value')}]`;
|
|
1502
1576
|
else keyCode = this.generate(key, 'value');
|
|
1503
1577
|
let valCode = this.generate(value, 'value');
|
|
1504
1578
|
if (operator === '=') return `${keyCode} = ${valCode}`;
|
|
@@ -1533,22 +1607,22 @@ export class CodeGenerator {
|
|
|
1533
1607
|
let needsReturns = context === 'value';
|
|
1534
1608
|
let tryCode = 'try ';
|
|
1535
1609
|
let tryBlock = rest[0];
|
|
1536
|
-
tryCode += (needsReturns &&
|
|
1610
|
+
tryCode += (needsReturns && this.is(tryBlock, 'block'))
|
|
1537
1611
|
? this.generateBlockWithReturns(tryBlock) : this.generate(tryBlock, 'statement');
|
|
1538
1612
|
|
|
1539
1613
|
if (rest.length >= 2 && Array.isArray(rest[1]) && rest[1].length === 2 && rest[1][0] !== 'block') {
|
|
1540
1614
|
let [param, catchBlock] = rest[1];
|
|
1541
1615
|
tryCode += ' catch';
|
|
1542
|
-
if (param &&
|
|
1616
|
+
if (param && (this.is(param, 'object') || this.is(param, 'array'))) {
|
|
1543
1617
|
tryCode += ' (error)';
|
|
1544
1618
|
let destructStmt = `(${this.generate(param, 'value')} = error)`;
|
|
1545
|
-
catchBlock =
|
|
1619
|
+
catchBlock = this.is(catchBlock, 'block')
|
|
1546
1620
|
? ['block', destructStmt, ...catchBlock.slice(1)]
|
|
1547
1621
|
: ['block', destructStmt, catchBlock];
|
|
1548
1622
|
} else if (param) {
|
|
1549
1623
|
tryCode += ` (${param})`;
|
|
1550
1624
|
}
|
|
1551
|
-
tryCode += ' ' + ((needsReturns &&
|
|
1625
|
+
tryCode += ' ' + ((needsReturns && this.is(catchBlock, 'block'))
|
|
1552
1626
|
? this.generateBlockWithReturns(catchBlock) : this.generate(catchBlock, 'statement'));
|
|
1553
1627
|
} else if (rest.length === 2) {
|
|
1554
1628
|
tryCode += ' finally ' + this.generate(rest[1], 'statement');
|
|
@@ -1672,7 +1746,7 @@ export class CodeGenerator {
|
|
|
1672
1746
|
let va = Array.isArray(vars) ? vars : [vars];
|
|
1673
1747
|
let noVar = va.length === 0;
|
|
1674
1748
|
let [itemVar, indexVar] = noVar ? ['_i', null] : va;
|
|
1675
|
-
let ivp = (
|
|
1749
|
+
let ivp = ((this.is(itemVar, 'array') || this.is(itemVar, 'object')))
|
|
1676
1750
|
? this.generateDestructuringPattern(itemVar) : itemVar;
|
|
1677
1751
|
|
|
1678
1752
|
if (step && step !== null) {
|
|
@@ -1687,7 +1761,7 @@ export class CodeGenerator {
|
|
|
1687
1761
|
this.indentLevel++;
|
|
1688
1762
|
} else {
|
|
1689
1763
|
let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
|
|
1690
|
-
let isNeg = this.
|
|
1764
|
+
let isNeg = this.is(step, '-', 1);
|
|
1691
1765
|
code += isNeg
|
|
1692
1766
|
? this.indent() + `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN} += ${stc}) {\n`
|
|
1693
1767
|
: this.indent() + `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN} += ${stc}) {\n`;
|
|
@@ -1707,7 +1781,7 @@ export class CodeGenerator {
|
|
|
1707
1781
|
let own = stepOrOwn;
|
|
1708
1782
|
let va = Array.isArray(vars) ? vars : [vars];
|
|
1709
1783
|
let [kv, vv] = va;
|
|
1710
|
-
let kvp = (
|
|
1784
|
+
let kvp = ((this.is(kv, 'array') || this.is(kv, 'object')))
|
|
1711
1785
|
? this.generateDestructuringPattern(kv) : kv;
|
|
1712
1786
|
let oc = this.generate(iterable, 'value');
|
|
1713
1787
|
code += this.indent() + `for (const ${kvp} in ${oc}) {\n`;
|
|
@@ -1718,7 +1792,7 @@ export class CodeGenerator {
|
|
|
1718
1792
|
let isAwait = iter[3];
|
|
1719
1793
|
let va = Array.isArray(vars) ? vars : [vars];
|
|
1720
1794
|
let [fv] = va;
|
|
1721
|
-
let ivp = (
|
|
1795
|
+
let ivp = ((this.is(fv, 'array') || this.is(fv, 'object')))
|
|
1722
1796
|
? this.generateDestructuringPattern(fv) : fv;
|
|
1723
1797
|
code += this.indent() + `for ${isAwait ? 'await ' : ''}(const ${ivp} of ${this.generate(iterable, 'value')}) {\n`;
|
|
1724
1798
|
this.indentLevel++;
|
|
@@ -1739,7 +1813,7 @@ export class CodeGenerator {
|
|
|
1739
1813
|
};
|
|
1740
1814
|
|
|
1741
1815
|
let loopStmts = ['for-in', 'for-of', 'for-as', 'while', 'until', 'loop'];
|
|
1742
|
-
if (
|
|
1816
|
+
if (this.is(expr, 'block')) {
|
|
1743
1817
|
for (let i = 0; i < expr.length - 1; i++) {
|
|
1744
1818
|
let s = expr[i + 1], isLast = i === expr.length - 2;
|
|
1745
1819
|
if (!isLast || hasCtrl(s)) {
|
|
@@ -1818,24 +1892,24 @@ export class CodeGenerator {
|
|
|
1818
1892
|
// First pass: identify bound methods
|
|
1819
1893
|
let boundMethods = [];
|
|
1820
1894
|
for (let [mk, mv] of members) {
|
|
1821
|
-
let isStatic = this.
|
|
1822
|
-
let isComputed = this.
|
|
1895
|
+
let isStatic = this.is(mk, '.') && mk[1] === 'this';
|
|
1896
|
+
let isComputed = this.is(mk, 'computed');
|
|
1823
1897
|
let mName = this.extractMemberName(mk);
|
|
1824
|
-
if (this.
|
|
1898
|
+
if (this.is(mv, '=>') && !isStatic && !isComputed && mName !== 'constructor') boundMethods.push(mName);
|
|
1825
1899
|
}
|
|
1826
1900
|
|
|
1827
1901
|
// Second pass: generate members
|
|
1828
1902
|
for (let [mk, mv] of members) {
|
|
1829
|
-
let isStatic = this.
|
|
1830
|
-
let isComputed = this.
|
|
1903
|
+
let isStatic = this.is(mk, '.') && mk[1] === 'this';
|
|
1904
|
+
let isComputed = this.is(mk, 'computed');
|
|
1831
1905
|
let mName = this.extractMemberName(mk);
|
|
1832
|
-
if (
|
|
1906
|
+
if ((this.is(mv, '->') || this.is(mv, '=>'))) {
|
|
1833
1907
|
let [, params, body] = mv;
|
|
1834
1908
|
let hasAwait = this.containsAwait(body), hasYield = this.containsYield(body);
|
|
1835
1909
|
let cleanParams = params, autoAssign = [];
|
|
1836
1910
|
if (mName === 'constructor') {
|
|
1837
1911
|
cleanParams = params.map(p => {
|
|
1838
|
-
if (
|
|
1912
|
+
if (this.is(p, '.') && p[1] === 'this') { autoAssign.push(`this.${p[2]} = ${p[2]}`); return p[2]; }
|
|
1839
1913
|
return p;
|
|
1840
1914
|
});
|
|
1841
1915
|
for (let bm of boundMethods) autoAssign.unshift(`this.${bm} = this.${bm}.bind(this)`);
|
|
@@ -1859,8 +1933,8 @@ export class CodeGenerator {
|
|
|
1859
1933
|
let additionalStmts = bodyStmts.slice(1);
|
|
1860
1934
|
this.indentLevel++;
|
|
1861
1935
|
for (let [mk, mv] of members) {
|
|
1862
|
-
let isStatic = this.
|
|
1863
|
-
if (
|
|
1936
|
+
let isStatic = this.is(mk, '.') && mk[1] === 'this', mName = this.extractMemberName(mk);
|
|
1937
|
+
if ((this.is(mv, '->') || this.is(mv, '=>'))) {
|
|
1864
1938
|
let [, params, body] = mv;
|
|
1865
1939
|
let pList = this.generateParamList(params);
|
|
1866
1940
|
let prefix = (isStatic ? 'static ' : '') + (this.containsAwait(body) ? 'async ' : '') + (this.containsYield(body) ? '*' : '');
|
|
@@ -1876,9 +1950,9 @@ export class CodeGenerator {
|
|
|
1876
1950
|
}
|
|
1877
1951
|
}
|
|
1878
1952
|
for (let stmt of additionalStmts) {
|
|
1879
|
-
if (
|
|
1953
|
+
if (this.is(stmt, 'class')) {
|
|
1880
1954
|
let [, nestedName, parent, ...nestedBody] = stmt;
|
|
1881
|
-
if (
|
|
1955
|
+
if (this.is(nestedName, '.') && nestedName[1] === 'this') {
|
|
1882
1956
|
code += this.indent() + `static ${nestedName[2]} = ${this.generate(['class', null, parent, ...nestedBody], 'value')};\n`;
|
|
1883
1957
|
}
|
|
1884
1958
|
} else {
|
|
@@ -1889,7 +1963,7 @@ export class CodeGenerator {
|
|
|
1889
1963
|
} else {
|
|
1890
1964
|
this.indentLevel++;
|
|
1891
1965
|
for (let stmt of bodyStmts) {
|
|
1892
|
-
if (
|
|
1966
|
+
if (this.is(stmt, '=') && Array.isArray(stmt[1]) && stmt[1][0] === '.' && stmt[1][1] === 'this') {
|
|
1893
1967
|
code += this.indent() + `static ${stmt[1][2]} = ${this.generate(stmt[2], 'value')};\n`;
|
|
1894
1968
|
} else {
|
|
1895
1969
|
code += this.indent() + this.generate(stmt, 'statement') + ';\n';
|
|
@@ -1944,13 +2018,13 @@ export class CodeGenerator {
|
|
|
1944
2018
|
generateExport(head, rest) {
|
|
1945
2019
|
let [decl] = rest;
|
|
1946
2020
|
if (Array.isArray(decl) && decl.every(i => typeof i === 'string')) return `export { ${decl.join(', ')} }`;
|
|
1947
|
-
if (
|
|
2021
|
+
if (this.is(decl, '=')) return `export const ${decl[1]} = ${this.generate(decl[2], 'value')}`;
|
|
1948
2022
|
return `export ${this.generate(decl, 'statement')}`;
|
|
1949
2023
|
}
|
|
1950
2024
|
|
|
1951
2025
|
generateExportDefault(head, rest) {
|
|
1952
2026
|
let [expr] = rest;
|
|
1953
|
-
if (
|
|
2027
|
+
if (this.is(expr, '=')) {
|
|
1954
2028
|
return `const ${expr[1]} = ${this.generate(expr[2], 'value')};\nexport default ${expr[1]}`;
|
|
1955
2029
|
}
|
|
1956
2030
|
return `export default ${this.generate(expr, 'statement')}`;
|
|
@@ -2042,14 +2116,14 @@ export class CodeGenerator {
|
|
|
2042
2116
|
generateDestructuringPattern(pattern) { return this.formatParam(pattern); }
|
|
2043
2117
|
|
|
2044
2118
|
generateParamList(params) {
|
|
2045
|
-
let expIdx = params.findIndex(p =>
|
|
2119
|
+
let expIdx = params.findIndex(p => this.is(p, 'expansion'));
|
|
2046
2120
|
if (expIdx !== -1) {
|
|
2047
2121
|
let before = params.slice(0, expIdx), after = params.slice(expIdx + 1);
|
|
2048
2122
|
let regular = before.map(p => this.formatParam(p)).join(', ');
|
|
2049
2123
|
this.expansionAfterParams = after;
|
|
2050
2124
|
return regular ? `${regular}, ..._rest` : '..._rest';
|
|
2051
2125
|
}
|
|
2052
|
-
let restIdx = params.findIndex(p =>
|
|
2126
|
+
let restIdx = params.findIndex(p => this.is(p, 'rest'));
|
|
2053
2127
|
if (restIdx !== -1 && restIdx < params.length - 1) {
|
|
2054
2128
|
let before = params.slice(0, restIdx), restP = params[restIdx], after = params.slice(restIdx + 1);
|
|
2055
2129
|
let beforeP = before.map(p => this.formatParam(p));
|
|
@@ -2064,24 +2138,24 @@ export class CodeGenerator {
|
|
|
2064
2138
|
formatParam(param) {
|
|
2065
2139
|
if (typeof param === 'string') return param;
|
|
2066
2140
|
if (param instanceof String) return param.valueOf();
|
|
2067
|
-
if (
|
|
2068
|
-
if (
|
|
2069
|
-
if (
|
|
2070
|
-
if (
|
|
2141
|
+
if (this.is(param, 'rest')) return `...${param[1]}`;
|
|
2142
|
+
if (this.is(param, 'default')) return `${param[1]} = ${this.generate(param[2], 'value')}`;
|
|
2143
|
+
if (this.is(param, '.') && param[1] === 'this') return param[2];
|
|
2144
|
+
if (this.is(param, 'array')) {
|
|
2071
2145
|
let els = param.slice(1).map(el => {
|
|
2072
2146
|
if (el === ',') return '';
|
|
2073
2147
|
if (el === '...') return '';
|
|
2074
|
-
if (
|
|
2075
|
-
if (
|
|
2148
|
+
if (this.is(el, '...')) return `...${el[1]}`;
|
|
2149
|
+
if (this.is(el, '=') && typeof el[1] === 'string') return `${el[1]} = ${this.generate(el[2], 'value')}`;
|
|
2076
2150
|
if (typeof el === 'string') return el;
|
|
2077
2151
|
return this.formatParam(el);
|
|
2078
2152
|
});
|
|
2079
2153
|
return `[${els.join(', ')}]`;
|
|
2080
2154
|
}
|
|
2081
|
-
if (
|
|
2155
|
+
if (this.is(param, 'object')) {
|
|
2082
2156
|
let pairs = param.slice(1).map(pair => {
|
|
2083
|
-
if (
|
|
2084
|
-
if (
|
|
2157
|
+
if (this.is(pair, '...')) return `...${pair[1]}`;
|
|
2158
|
+
if (this.is(pair, 'default')) return `${pair[1]} = ${this.generate(pair[2], 'value')}`;
|
|
2085
2159
|
let [key, value] = pair;
|
|
2086
2160
|
if (key === value) return key;
|
|
2087
2161
|
return `${key}: ${value}`;
|
|
@@ -2116,7 +2190,7 @@ export class CodeGenerator {
|
|
|
2116
2190
|
let noRetStmts = ['return', 'throw', 'break', 'continue'];
|
|
2117
2191
|
let loopStmts = ['for-in', 'for-of', 'for-as', 'while', 'until', 'loop'];
|
|
2118
2192
|
|
|
2119
|
-
if (
|
|
2193
|
+
if (this.is(body, 'block')) {
|
|
2120
2194
|
let statements = this.unwrapBlock(body);
|
|
2121
2195
|
|
|
2122
2196
|
if (hasExpansionParams && this.expansionAfterParams?.length > 0) {
|
|
@@ -2133,7 +2207,7 @@ export class CodeGenerator {
|
|
|
2133
2207
|
let afterCount = afterParams.length;
|
|
2134
2208
|
let extr = [];
|
|
2135
2209
|
afterParams.forEach((p, i) => {
|
|
2136
|
-
let pn = typeof p === 'string' ? p : (
|
|
2210
|
+
let pn = typeof p === 'string' ? p : (this.is(p, 'default')) ? p[1] : JSON.stringify(p);
|
|
2137
2211
|
extr.push(`const ${pn} = ${restName}[${restName}.length - ${afterCount - i}]`);
|
|
2138
2212
|
});
|
|
2139
2213
|
if (afterCount > 0) extr.push(`${restName} = ${restName}.slice(0, -${afterCount})`);
|
|
@@ -2161,7 +2235,7 @@ export class CodeGenerator {
|
|
|
2161
2235
|
|
|
2162
2236
|
if (!isConstructor && !sideEffectOnly && isLast && (h === 'if' || h === 'unless')) {
|
|
2163
2237
|
let [cond, thenB, ...elseB] = stmt.slice(1);
|
|
2164
|
-
let hasMulti = (b) =>
|
|
2238
|
+
let hasMulti = (b) => this.is(b, 'block') && b.length > 2;
|
|
2165
2239
|
if (hasMulti(thenB) || elseB.some(hasMulti)) {
|
|
2166
2240
|
code += this.generateIfElseWithEarlyReturns(stmt);
|
|
2167
2241
|
return;
|
|
@@ -2252,7 +2326,7 @@ export class CodeGenerator {
|
|
|
2252
2326
|
if (body[0] === 'block' || Array.isArray(body[0])) {
|
|
2253
2327
|
let stmts = body[0] === 'block' ? body.slice(1) : body;
|
|
2254
2328
|
let lines = this.withIndent(() => stmts.map(s => {
|
|
2255
|
-
if (
|
|
2329
|
+
if (this.is(s, 'comprehension')) {
|
|
2256
2330
|
let [, expr, iters, guards] = s;
|
|
2257
2331
|
return this.indent() + this.generateComprehensionAsLoop(expr, iters, guards);
|
|
2258
2332
|
}
|
|
@@ -2289,7 +2363,7 @@ export class CodeGenerator {
|
|
|
2289
2363
|
generateComprehensionWithTarget(expr, iterators, guards, targetVar) {
|
|
2290
2364
|
let code = '';
|
|
2291
2365
|
code += this.indent() + `${targetVar} = [];\n`;
|
|
2292
|
-
let unwrappedExpr = (
|
|
2366
|
+
let unwrappedExpr = (this.is(expr, 'block') && expr.length === 2) ? expr[1] : expr;
|
|
2293
2367
|
|
|
2294
2368
|
if (iterators.length === 1) {
|
|
2295
2369
|
let [iterType, vars, iterable, stepOrOwn] = iterators[0];
|
|
@@ -2298,7 +2372,7 @@ export class CodeGenerator {
|
|
|
2298
2372
|
let va = Array.isArray(vars) ? vars : [vars];
|
|
2299
2373
|
let noVar = va.length === 0;
|
|
2300
2374
|
let [itemVar, indexVar] = noVar ? ['_i', null] : va;
|
|
2301
|
-
let ivp = (
|
|
2375
|
+
let ivp = ((this.is(itemVar, 'array') || this.is(itemVar, 'object')))
|
|
2302
2376
|
? this.generateDestructuringPattern(itemVar) : itemVar;
|
|
2303
2377
|
|
|
2304
2378
|
if (step && step !== null) {
|
|
@@ -2311,7 +2385,7 @@ export class CodeGenerator {
|
|
|
2311
2385
|
code += this.indent() + `for (let ${ivp} = ${this.generate(s, 'value')}; ${ivp} ${isExcl ? '<' : '<='} ${this.generate(e, 'value')}; ${ivp} += ${this.generate(step, 'value')}) {\n`;
|
|
2312
2386
|
} else {
|
|
2313
2387
|
let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
|
|
2314
|
-
let isNeg = this.
|
|
2388
|
+
let isNeg = this.is(step, '-', 1);
|
|
2315
2389
|
code += isNeg
|
|
2316
2390
|
? this.indent() + `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN} += ${stc}) {\n`
|
|
2317
2391
|
: this.indent() + `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN} += ${stc}) {\n`;
|
|
@@ -2346,7 +2420,7 @@ export class CodeGenerator {
|
|
|
2346
2420
|
let va = Array.isArray(vars) ? vars : [vars];
|
|
2347
2421
|
let noVar = va.length === 0;
|
|
2348
2422
|
let [itemVar, indexVar] = noVar ? ['_i', null] : va;
|
|
2349
|
-
let ivp = (
|
|
2423
|
+
let ivp = ((this.is(itemVar, 'array') || this.is(itemVar, 'object')))
|
|
2350
2424
|
? this.generateDestructuringPattern(itemVar) : itemVar;
|
|
2351
2425
|
|
|
2352
2426
|
if (step && step !== null) {
|
|
@@ -2359,7 +2433,7 @@ export class CodeGenerator {
|
|
|
2359
2433
|
code += `for (let ${ivp} = ${this.generate(s, 'value')}; ${ivp} ${isExcl ? '<' : '<='} ${this.generate(e, 'value')}; ${ivp} += ${this.generate(step, 'value')}) `;
|
|
2360
2434
|
} else {
|
|
2361
2435
|
let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
|
|
2362
|
-
let isNeg = this.
|
|
2436
|
+
let isNeg = this.is(step, '-', 1);
|
|
2363
2437
|
let isMinus1 = isNeg && (step[1] === '1' || step[1] === 1 || str(step[1]) === '1');
|
|
2364
2438
|
let isPlus1 = !isNeg && (step === '1' || step === 1 || str(step) === '1');
|
|
2365
2439
|
if (isMinus1) code += `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN}--) `;
|
|
@@ -2427,7 +2501,7 @@ export class CodeGenerator {
|
|
|
2427
2501
|
if (iterType === 'for-as') {
|
|
2428
2502
|
let va = Array.isArray(vars) ? vars : [vars];
|
|
2429
2503
|
let [fv] = va;
|
|
2430
|
-
let ivp = (
|
|
2504
|
+
let ivp = ((this.is(fv, 'array') || this.is(fv, 'object')))
|
|
2431
2505
|
? this.generateDestructuringPattern(fv) : fv;
|
|
2432
2506
|
code += `for (const ${ivp} of ${this.generate(iterable, 'value')}) `;
|
|
2433
2507
|
if (guards?.length) {
|
|
@@ -2509,7 +2583,7 @@ export class CodeGenerator {
|
|
|
2509
2583
|
code += this.indent() + '}';
|
|
2510
2584
|
for (let branch of elseBranches) {
|
|
2511
2585
|
code += ' else ';
|
|
2512
|
-
if (
|
|
2586
|
+
if (this.is(branch, 'if')) {
|
|
2513
2587
|
let [, nc, nt, ...ne] = branch;
|
|
2514
2588
|
code += `if (${this.generate(nc, 'value')}) {\n`;
|
|
2515
2589
|
code += this.withIndent(() => this.generateBranchWithReturn(nt));
|
|
@@ -2538,8 +2612,8 @@ export class CodeGenerator {
|
|
|
2538
2612
|
}
|
|
2539
2613
|
|
|
2540
2614
|
generateIfAsExpression(condition, thenBranch, elseBranches) {
|
|
2541
|
-
let needsIIFE = this.
|
|
2542
|
-
elseBranches.some(b => this.
|
|
2615
|
+
let needsIIFE = this.is(thenBranch, 'block') && thenBranch.length > 2 || this.hasStatementInBranch(thenBranch) ||
|
|
2616
|
+
elseBranches.some(b => this.is(b, 'block') && b.length > 2 || this.hasStatementInBranch(b) || this.hasNestedMultiStatement(b));
|
|
2543
2617
|
if (needsIIFE) {
|
|
2544
2618
|
let hasAwait = this.containsAwait(condition) || this.containsAwait(thenBranch) || elseBranches.some(b => this.containsAwait(b));
|
|
2545
2619
|
let code = `${hasAwait ? 'await ' : ''}(${hasAwait ? 'async ' : ''}() => { `;
|
|
@@ -2547,13 +2621,13 @@ export class CodeGenerator {
|
|
|
2547
2621
|
code += this.generateBlockWithReturns(thenBranch);
|
|
2548
2622
|
for (let branch of elseBranches) {
|
|
2549
2623
|
code += ' else ';
|
|
2550
|
-
if (
|
|
2624
|
+
if (this.is(branch, 'if')) {
|
|
2551
2625
|
let [_, nc, nt, ...ne] = branch;
|
|
2552
2626
|
code += `if (${this.generate(nc, 'value')}) `;
|
|
2553
2627
|
code += this.generateBlockWithReturns(nt);
|
|
2554
2628
|
for (let nb of ne) {
|
|
2555
2629
|
code += ' else ';
|
|
2556
|
-
if (
|
|
2630
|
+
if (this.is(nb, 'if')) {
|
|
2557
2631
|
let [__, nnc, nnt, ...nne] = nb;
|
|
2558
2632
|
code += `if (${this.generate(nnc, 'value')}) `;
|
|
2559
2633
|
code += this.generateBlockWithReturns(nnt);
|
|
@@ -2571,7 +2645,7 @@ export class CodeGenerator {
|
|
|
2571
2645
|
let thenExpr = this.extractExpression(this.unwrapIfBranch(thenBranch));
|
|
2572
2646
|
let elseExpr = this.buildTernaryChain(elseBranches);
|
|
2573
2647
|
let condCode = this.generate(condition, 'value');
|
|
2574
|
-
if (
|
|
2648
|
+
if ((this.is(condition, 'yield') || this.is(condition, 'await'))) condCode = `(${condCode})`;
|
|
2575
2649
|
return `(${condCode} ? ${thenExpr} : ${elseExpr})`;
|
|
2576
2650
|
}
|
|
2577
2651
|
|
|
@@ -2588,7 +2662,7 @@ export class CodeGenerator {
|
|
|
2588
2662
|
if (hasFlow) {
|
|
2589
2663
|
for (let s of this.unwrapBlock(body)) code += this.indent() + this.generate(s, 'statement') + ';\n';
|
|
2590
2664
|
} else if (context === 'value') {
|
|
2591
|
-
if (
|
|
2665
|
+
if (this.is(body, 'block') && body.length > 2) {
|
|
2592
2666
|
let stmts = body.slice(1);
|
|
2593
2667
|
for (let i = 0; i < stmts.length; i++) {
|
|
2594
2668
|
if (i === stmts.length - 1) code += this.indent() + `return ${this.generate(stmts[i], 'value')};\n`;
|
|
@@ -2598,7 +2672,7 @@ export class CodeGenerator {
|
|
|
2598
2672
|
code += this.indent() + `return ${this.extractExpression(body)};\n`;
|
|
2599
2673
|
}
|
|
2600
2674
|
} else {
|
|
2601
|
-
if (
|
|
2675
|
+
if (this.is(body, 'block') && body.length > 1) {
|
|
2602
2676
|
for (let s of body.slice(1)) code += this.indent() + this.generate(s, 'statement') + ';\n';
|
|
2603
2677
|
} else {
|
|
2604
2678
|
code += this.indent() + this.generate(body, 'statement') + ';\n';
|
|
@@ -2702,14 +2776,17 @@ export class CodeGenerator {
|
|
|
2702
2776
|
return result;
|
|
2703
2777
|
}
|
|
2704
2778
|
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2779
|
+
// S-expression pattern match: is(node, op, arity?) → args or null
|
|
2780
|
+
// is(node, '-', 1) on ["-", 5] → [5]
|
|
2781
|
+
// is(node, '[]', 2) on ["[]", a, b] → [a, b]
|
|
2782
|
+
// is(node, 'block') on ["block", ...] → [...]
|
|
2783
|
+
// is("x", '-', 1) → null
|
|
2784
|
+
is(node, op, arity) {
|
|
2785
|
+
if (!Array.isArray(node)) return null;
|
|
2786
|
+
if ((str(node[0]) ?? node[0]) !== op) return null;
|
|
2787
|
+
let args = node.slice(1);
|
|
2788
|
+
if (arity != null && args.length !== arity) return null;
|
|
2789
|
+
return args;
|
|
2713
2790
|
}
|
|
2714
2791
|
|
|
2715
2792
|
unwrap(code) {
|
|
@@ -2772,13 +2849,11 @@ export class CodeGenerator {
|
|
|
2772
2849
|
return false;
|
|
2773
2850
|
}
|
|
2774
2851
|
|
|
2775
|
-
isMultiStatementBlock(branch) { return Array.isArray(branch) && branch[0] === 'block' && branch.length > 2; }
|
|
2776
|
-
|
|
2777
2852
|
hasNestedMultiStatement(branch) {
|
|
2778
2853
|
if (!Array.isArray(branch)) return false;
|
|
2779
2854
|
if (branch[0] === 'if') {
|
|
2780
2855
|
let [_, cond, then_, ...elseB] = branch;
|
|
2781
|
-
return this.
|
|
2856
|
+
return this.is(then_, 'block') && then_.length > 2 || elseB.some(b => this.hasNestedMultiStatement(b));
|
|
2782
2857
|
}
|
|
2783
2858
|
return false;
|
|
2784
2859
|
}
|
|
@@ -2787,7 +2862,7 @@ export class CodeGenerator {
|
|
|
2787
2862
|
if (branches.length === 0) return 'undefined';
|
|
2788
2863
|
if (branches.length === 1) return this.extractExpression(this.unwrapIfBranch(branches[0]));
|
|
2789
2864
|
let first = branches[0];
|
|
2790
|
-
if (
|
|
2865
|
+
if (this.is(first, 'if')) {
|
|
2791
2866
|
let [_, cond, then_, ...rest] = first;
|
|
2792
2867
|
let thenPart = this.extractExpression(this.unwrapIfBranch(then_));
|
|
2793
2868
|
let elsePart = this.buildTernaryChain([...rest, ...branches.slice(1)]);
|
|
@@ -2886,13 +2961,22 @@ export class CodeGenerator {
|
|
|
2886
2961
|
return `'${finalPath}'` + assertion;
|
|
2887
2962
|
}
|
|
2888
2963
|
|
|
2964
|
+
containsIt(sexpr) {
|
|
2965
|
+
if (!sexpr) return false;
|
|
2966
|
+
if (sexpr === 'it' || (sexpr instanceof String && str(sexpr) === 'it')) return true;
|
|
2967
|
+
if (typeof sexpr !== 'object') return false;
|
|
2968
|
+
if (this.is(sexpr, 'def') || this.is(sexpr, '->') || this.is(sexpr, '=>')) return false;
|
|
2969
|
+
if (Array.isArray(sexpr)) return sexpr.some(item => this.containsIt(item));
|
|
2970
|
+
return false;
|
|
2971
|
+
}
|
|
2972
|
+
|
|
2889
2973
|
containsAwait(sexpr) {
|
|
2890
2974
|
if (!sexpr) return false;
|
|
2891
2975
|
if (sexpr instanceof String && meta(sexpr, 'await') === true) return true;
|
|
2892
2976
|
if (typeof sexpr !== 'object') return false;
|
|
2893
|
-
if (
|
|
2894
|
-
if (
|
|
2895
|
-
if (
|
|
2977
|
+
if (this.is(sexpr, 'await')) return true;
|
|
2978
|
+
if (this.is(sexpr, 'for-as') && sexpr[3] === true) return true;
|
|
2979
|
+
if ((this.is(sexpr, 'def') || this.is(sexpr, '->') || this.is(sexpr, '=>') || this.is(sexpr, 'class'))) return false;
|
|
2896
2980
|
if (Array.isArray(sexpr)) return sexpr.some(item => this.containsAwait(item));
|
|
2897
2981
|
return false;
|
|
2898
2982
|
}
|
|
@@ -2900,21 +2984,18 @@ export class CodeGenerator {
|
|
|
2900
2984
|
containsYield(sexpr) {
|
|
2901
2985
|
if (!sexpr) return false;
|
|
2902
2986
|
if (typeof sexpr !== 'object') return false;
|
|
2903
|
-
if (
|
|
2904
|
-
if (
|
|
2987
|
+
if ((this.is(sexpr, 'yield') || this.is(sexpr, 'yield-from'))) return true;
|
|
2988
|
+
if ((this.is(sexpr, 'def') || this.is(sexpr, '->') || this.is(sexpr, '=>') || this.is(sexpr, 'class'))) return false;
|
|
2905
2989
|
if (Array.isArray(sexpr)) return sexpr.some(item => this.containsYield(item));
|
|
2906
2990
|
return false;
|
|
2907
2991
|
}
|
|
2908
2992
|
|
|
2909
2993
|
// Class helpers
|
|
2910
|
-
isStaticMember(mk) { return Array.isArray(mk) && mk[0] === '.' && mk[1] === 'this'; }
|
|
2911
|
-
isComputedMember(mk) { return Array.isArray(mk) && mk[0] === 'computed'; }
|
|
2912
2994
|
extractMemberName(mk) {
|
|
2913
|
-
if (this.
|
|
2914
|
-
if (this.
|
|
2995
|
+
if (this.is(mk, '.') && mk[1] === 'this') return mk[2];
|
|
2996
|
+
if (this.is(mk, 'computed')) return `[${this.generate(mk[1], 'value')}]`;
|
|
2915
2997
|
return mk;
|
|
2916
2998
|
}
|
|
2917
|
-
isBoundMethod(mv) { return Array.isArray(mv) && mv[0] === '=>'; }
|
|
2918
2999
|
|
|
2919
3000
|
// ---------------------------------------------------------------------------
|
|
2920
3001
|
// Reactive Runtime (injected inline when reactive operators are used)
|
|
@@ -3166,13 +3247,14 @@ export class Compiler {
|
|
|
3166
3247
|
console.log();
|
|
3167
3248
|
}
|
|
3168
3249
|
|
|
3169
|
-
//
|
|
3250
|
+
// Save annotated tokens for deferred .d.ts emission (after parsing)
|
|
3170
3251
|
let dts = null;
|
|
3252
|
+
let typeTokens = null;
|
|
3171
3253
|
if (this.options.types === 'emit' || this.options.types === 'check' || this.options.types === true) {
|
|
3172
|
-
|
|
3254
|
+
typeTokens = [...tokens];
|
|
3173
3255
|
}
|
|
3174
3256
|
|
|
3175
|
-
//
|
|
3257
|
+
// Remove TYPE_DECL markers — the parser doesn't know about them
|
|
3176
3258
|
tokens = tokens.filter(t => t[0] !== 'TYPE_DECL');
|
|
3177
3259
|
|
|
3178
3260
|
// Strip leading terminators that may result from removed type declarations
|
|
@@ -3180,8 +3262,9 @@ export class Compiler {
|
|
|
3180
3262
|
tokens.shift();
|
|
3181
3263
|
}
|
|
3182
3264
|
|
|
3183
|
-
// If only terminators remain (type-only source), return early
|
|
3265
|
+
// If only terminators remain (type-only source), emit types and return early
|
|
3184
3266
|
if (tokens.every(t => t[0] === 'TERMINATOR')) {
|
|
3267
|
+
if (typeTokens) dts = emitTypes(typeTokens, ['program']);
|
|
3185
3268
|
return { tokens, sexpr: ['program'], code: '', dts, data: dataSection, reactiveVars: {} };
|
|
3186
3269
|
}
|
|
3187
3270
|
|
|
@@ -3229,8 +3312,7 @@ export class Compiler {
|
|
|
3229
3312
|
|
|
3230
3313
|
let generator = new CodeGenerator({
|
|
3231
3314
|
dataSection,
|
|
3232
|
-
|
|
3233
|
-
skipComponentRuntime: this.options.skipComponentRuntime,
|
|
3315
|
+
skipPreamble: this.options.skipPreamble,
|
|
3234
3316
|
reactiveVars: this.options.reactiveVars,
|
|
3235
3317
|
sourceMap,
|
|
3236
3318
|
});
|
|
@@ -3245,6 +3327,11 @@ export class Compiler {
|
|
|
3245
3327
|
code += `\n//# sourceMappingURL=${this.options.filename}.js.map`;
|
|
3246
3328
|
}
|
|
3247
3329
|
|
|
3330
|
+
// Step 5: Emit .d.ts from annotated tokens + parsed s-expression
|
|
3331
|
+
if (typeTokens) {
|
|
3332
|
+
dts = emitTypes(typeTokens, sexpr);
|
|
3333
|
+
}
|
|
3334
|
+
|
|
3248
3335
|
return { tokens, sexpr, code, dts, map, reverseMap, data: dataSection, reactiveVars: generator.reactiveVars };
|
|
3249
3336
|
}
|
|
3250
3337
|
|