rip-lang 3.13.129 → 3.13.130
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 +6 -6
- package/docs/RIP-LANG.md +12 -0
- package/docs/dist/rip.js +469 -364
- package/docs/dist/rip.min.js +151 -149
- package/docs/dist/rip.min.js.br +0 -0
- package/docs/ui/hljs-rip.js +7 -0
- package/package.json +1 -1
- package/src/AGENTS.md +1 -0
- package/src/compiler.js +36 -4
- package/src/components.js +17 -8
- package/src/grammar/grammar.rip +40 -0
- package/src/lexer.js +31 -8
- package/src/parser.js +223 -221
- package/src/sourcemap-utils.js +46 -2
- package/src/typecheck.js +39 -9
- package/src/types.js +5 -5
package/docs/dist/rip.min.js.br
CHANGED
|
Binary file
|
package/docs/ui/hljs-rip.js
CHANGED
|
@@ -163,6 +163,12 @@ export default function(hljs) {
|
|
|
163
163
|
],
|
|
164
164
|
};
|
|
165
165
|
|
|
166
|
+
const MAP_LITERAL = {
|
|
167
|
+
className: 'operator',
|
|
168
|
+
begin: /\*(?=\{)/,
|
|
169
|
+
relevance: 5,
|
|
170
|
+
};
|
|
171
|
+
|
|
166
172
|
const OPERATORS = {
|
|
167
173
|
className: 'operator',
|
|
168
174
|
begin: /\|>|::|:=|~=|~>|<=>|\.=|=!|!\?|\?!|=~|\?\?=|\?\?|\?\.|\.\.\.|\.\.|=>|->|\*\*|\/\/|%%|===|!==|==|!=|<=|>=|&&|\|\||[+\-*\/%&|^~<>=!?]/,
|
|
@@ -199,6 +205,7 @@ export default function(hljs) {
|
|
|
199
205
|
INSTANCE_VAR,
|
|
200
206
|
SIGIL_ATTR,
|
|
201
207
|
TYPE_KEYWORDS,
|
|
208
|
+
MAP_LITERAL,
|
|
202
209
|
OPERATORS,
|
|
203
210
|
{ // inline JS (backtick)
|
|
204
211
|
className: 'string',
|
package/package.json
CHANGED
package/src/AGENTS.md
CHANGED
package/src/compiler.js
CHANGED
|
@@ -40,6 +40,8 @@ let STMT_ONLY = new Set([
|
|
|
40
40
|
'while', 'until', 'loop', 'switch', 'try', 'throw',
|
|
41
41
|
]);
|
|
42
42
|
|
|
43
|
+
let MAP_LITERAL_KEYS = new Set(['true', 'false', 'null', 'undefined', 'Infinity', 'NaN']);
|
|
44
|
+
|
|
43
45
|
function isInline(arr) {
|
|
44
46
|
if (!Array.isArray(arr) || arr.length === 0) return false;
|
|
45
47
|
let head = arr[0]?.valueOf?.() ?? arr[0];
|
|
@@ -183,6 +185,7 @@ export class CodeGenerator {
|
|
|
183
185
|
// Data structures
|
|
184
186
|
'array': 'generateArray',
|
|
185
187
|
'object': 'generateObject',
|
|
188
|
+
'map-literal': 'generateMap',
|
|
186
189
|
'block': 'generateBlock',
|
|
187
190
|
|
|
188
191
|
// Property access
|
|
@@ -711,10 +714,19 @@ export class CodeGenerator {
|
|
|
711
714
|
}
|
|
712
715
|
|
|
713
716
|
if (this.programVars.size > 0) {
|
|
714
|
-
let
|
|
715
|
-
if (
|
|
716
|
-
|
|
717
|
-
|
|
717
|
+
let hasUnderscore = this.programVars.has('_');
|
|
718
|
+
if (hasUnderscore) this.programVars.delete('_');
|
|
719
|
+
if (this.programVars.size > 0) {
|
|
720
|
+
let vars = Array.from(this.programVars).sort().join(', ');
|
|
721
|
+
if (needsBlank) code += '\n';
|
|
722
|
+
code += `let ${vars};\n`;
|
|
723
|
+
needsBlank = true;
|
|
724
|
+
}
|
|
725
|
+
if (hasUnderscore) {
|
|
726
|
+
if (needsBlank) code += '\n';
|
|
727
|
+
code += `var _;\n`;
|
|
728
|
+
needsBlank = true;
|
|
729
|
+
}
|
|
718
730
|
}
|
|
719
731
|
|
|
720
732
|
let skip = this.options.skipPreamble;
|
|
@@ -1679,6 +1691,26 @@ export class CodeGenerator {
|
|
|
1679
1691
|
return `{${codes}}`;
|
|
1680
1692
|
}
|
|
1681
1693
|
|
|
1694
|
+
generateMap(head, pairs, context) {
|
|
1695
|
+
if (pairs.length === 0) return 'new Map()';
|
|
1696
|
+
let entries = pairs.map(pair => {
|
|
1697
|
+
if (this.is(pair, '...')) return `...${this.generate(pair[1], 'value')}`;
|
|
1698
|
+
let [, key, value] = pair;
|
|
1699
|
+
let keyCode;
|
|
1700
|
+
if (Array.isArray(key)) {
|
|
1701
|
+
keyCode = this.generate(key, 'value');
|
|
1702
|
+
} else {
|
|
1703
|
+
let k = str(key) ?? key;
|
|
1704
|
+
let isIdentifier = !k.startsWith('"') && !k.startsWith("'") && !k.startsWith('/') &&
|
|
1705
|
+
!CodeGenerator.NUMBER_START_RE.test(k) && !MAP_LITERAL_KEYS.has(k);
|
|
1706
|
+
keyCode = isIdentifier ? `"${k}"` : this.generate(key, 'value');
|
|
1707
|
+
}
|
|
1708
|
+
let valCode = this.generate(value, 'value');
|
|
1709
|
+
return `[${keyCode}, ${valCode}]`;
|
|
1710
|
+
}).join(', ');
|
|
1711
|
+
return `new Map([${entries}])`;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1682
1714
|
generateBlock(head, statements, context) {
|
|
1683
1715
|
if (context === 'statement') {
|
|
1684
1716
|
let stmts = this.withIndent(() => this.formatStatements(statements));
|
package/src/components.js
CHANGED
|
@@ -193,7 +193,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
193
193
|
}
|
|
194
194
|
j--;
|
|
195
195
|
}
|
|
196
|
-
return tokens[j] && tokens[j][0] === 'IDENTIFIER' &&
|
|
196
|
+
return tokens[j] && tokens[j][0] === 'IDENTIFIER' &&
|
|
197
|
+
(isTemplateTag(tokens[j][1]) ||
|
|
198
|
+
(j === 0 || tokens[j - 1][0] === 'INDENT' || tokens[j - 1][0] === 'TERMINATOR' || tokens[j - 1][0] === 'RENDER'));
|
|
197
199
|
};
|
|
198
200
|
|
|
199
201
|
this.scanTokens(function(token, i, tokens) {
|
|
@@ -621,8 +623,11 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
621
623
|
}
|
|
622
624
|
|
|
623
625
|
// Object literals: transform values but leave bare string keys untouched
|
|
624
|
-
if (sexpr[0] === 'object') {
|
|
625
|
-
return [
|
|
626
|
+
if (sexpr[0] === 'object' || sexpr[0] === 'map-literal') {
|
|
627
|
+
return [sexpr[0], ...sexpr.slice(1).map(pair => {
|
|
628
|
+
if (Array.isArray(pair) && pair[0] === '...') {
|
|
629
|
+
return ['...', this.transformComponentMembers(pair[1], localScope)];
|
|
630
|
+
}
|
|
626
631
|
if (Array.isArray(pair) && pair.length >= 2) {
|
|
627
632
|
let key = pair[1];
|
|
628
633
|
let newKey = Array.isArray(key) ? this.transformComponentMembers(key, localScope) : key;
|
|
@@ -800,7 +805,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
800
805
|
const sl = [];
|
|
801
806
|
sl.push('class {');
|
|
802
807
|
sl.push(' declare _root: Element | null;');
|
|
803
|
-
sl.push(' emit(
|
|
808
|
+
sl.push(' emit(_name: string, _detail?: any): void {}');
|
|
804
809
|
|
|
805
810
|
// Constructor — typed props for public state/readonly (matches DTS)
|
|
806
811
|
const propEntries = [];
|
|
@@ -822,7 +827,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
822
827
|
const propsOpt = hasRequired ? '' : '?';
|
|
823
828
|
let propsType = propEntries.length > 0 ? `{${propEntries.join('; ')}}` : '{}';
|
|
824
829
|
if (inheritsTag) propsType += ` & __RipProps<'${inheritsTag}'>`;
|
|
825
|
-
sl.push(` constructor(
|
|
830
|
+
sl.push(` constructor(_props${propsOpt}: ${propsType}) {}`);
|
|
826
831
|
}
|
|
827
832
|
|
|
828
833
|
// Infer type from literal initializer when no explicit annotation
|
|
@@ -931,7 +936,11 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
931
936
|
if (Array.isArray(func) && (func[0] === '->' || func[0] === '=>')) {
|
|
932
937
|
let [, params, methodBody] = func;
|
|
933
938
|
if ((!params || (Array.isArray(params) && params.length === 0)) && this.containsIt(methodBody)) params = ['it'];
|
|
934
|
-
let paramStr = Array.isArray(params) ? params.map(p =>
|
|
939
|
+
let paramStr = Array.isArray(params) ? params.map(p => {
|
|
940
|
+
let base = this.formatParam(p);
|
|
941
|
+
if (p?.type) base += `: ${p.type}`;
|
|
942
|
+
return base;
|
|
943
|
+
}).join(', ') : '';
|
|
935
944
|
// Inject event type on untyped first param when method is bound to an event
|
|
936
945
|
const boundEvent = eventMethodTypes.get(name);
|
|
937
946
|
if (boundEvent && Array.isArray(params) && params.length > 0) {
|
|
@@ -1064,8 +1073,8 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1064
1073
|
constructions.push(` };`);
|
|
1065
1074
|
}
|
|
1066
1075
|
}
|
|
1067
|
-
} else if (typeof head === 'string' && head
|
|
1068
|
-
(/^[a-z]
|
|
1076
|
+
} else if (typeof head === 'string' && !CodeGenerator.GENERATORS[head] && (TEMPLATE_TAGS.has(head.split(/[.#]/)[0]) ||
|
|
1077
|
+
(/^[a-z][\w-]*$/.test(head) && node.length > 1))) {
|
|
1069
1078
|
const tagName = head.split(/[.#]/)[0];
|
|
1070
1079
|
const iProps = extractIntrinsicProps(node.slice(1));
|
|
1071
1080
|
const tagLine = node.loc?.r;
|
package/src/grammar/grammar.rip
CHANGED
|
@@ -110,6 +110,7 @@ grammar =
|
|
|
110
110
|
o 'This'
|
|
111
111
|
o 'Super'
|
|
112
112
|
o 'MetaProperty'
|
|
113
|
+
o 'MapLiteral'
|
|
113
114
|
]
|
|
114
115
|
|
|
115
116
|
# Immediate values — numbers, strings, booleans, etc.
|
|
@@ -328,6 +329,45 @@ grammar =
|
|
|
328
329
|
o 'ObjSpreadExpr INDEX_START INDENT Expression OUTDENT INDEX_END', '["[]", 1, 4]'
|
|
329
330
|
]
|
|
330
331
|
|
|
332
|
+
# ============================================================================
|
|
333
|
+
# Map Literals — *{ key: value } → new Map([[key, value]])
|
|
334
|
+
# ============================================================================
|
|
335
|
+
|
|
336
|
+
MapLiteral: [
|
|
337
|
+
o 'MAP_START MAP_END' , '["map-literal"]'
|
|
338
|
+
o 'MAP_START MapAssignList OptComma MAP_END' , '["map-literal", ...2]'
|
|
339
|
+
]
|
|
340
|
+
|
|
341
|
+
MapAssignList: [
|
|
342
|
+
o '' , '[]'
|
|
343
|
+
o 'MapAssignObj' , '[1]'
|
|
344
|
+
o 'MapAssignList , MapAssignObj' , '[...1, 3]'
|
|
345
|
+
o 'MapAssignList OptComma TERMINATOR MapAssignObj' , '[...1, 4]'
|
|
346
|
+
o 'MapAssignList OptComma INDENT MapAssignList OptComma OUTDENT' , '[...1, ...4]'
|
|
347
|
+
]
|
|
348
|
+
|
|
349
|
+
MapAssignObj: [
|
|
350
|
+
o 'MapAssignable : Expression' , '[":", 1, 3]'
|
|
351
|
+
o 'MapAssignable : INDENT Expression OUTDENT' , '[":", 1, 4]'
|
|
352
|
+
o '... Expression' , '["...", 2]'
|
|
353
|
+
]
|
|
354
|
+
|
|
355
|
+
MapAssignable: [
|
|
356
|
+
o 'Identifier'
|
|
357
|
+
o 'Property'
|
|
358
|
+
o 'ThisProperty'
|
|
359
|
+
o 'AlphaNumeric'
|
|
360
|
+
o 'Regex'
|
|
361
|
+
o 'BOOL'
|
|
362
|
+
o 'NULL' , '"null"'
|
|
363
|
+
o 'UNDEFINED' , '"undefined"'
|
|
364
|
+
o 'INFINITY'
|
|
365
|
+
o 'NAN'
|
|
366
|
+
o 'Array'
|
|
367
|
+
o 'Object'
|
|
368
|
+
o 'Parenthetical'
|
|
369
|
+
]
|
|
370
|
+
|
|
331
371
|
# ============================================================================
|
|
332
372
|
# Array Literals
|
|
333
373
|
# ============================================================================
|
package/src/lexer.js
CHANGED
|
@@ -106,7 +106,7 @@ let CALLABLE = new Set([
|
|
|
106
106
|
let INDEXABLE = new Set([
|
|
107
107
|
...CALLABLE,
|
|
108
108
|
'NUMBER', 'INFINITY', 'NAN', 'STRING', 'STRING_END',
|
|
109
|
-
'REGEX', 'REGEX_END', 'BOOL', 'NULL', 'UNDEFINED', '}',
|
|
109
|
+
'REGEX', 'REGEX_END', 'BOOL', 'NULL', 'UNDEFINED', '}', 'MAP_END',
|
|
110
110
|
]);
|
|
111
111
|
|
|
112
112
|
// Tokens that can follow IMPLICIT_FUNC to start an implicit call
|
|
@@ -117,7 +117,7 @@ let IMPLICIT_CALL = new Set([
|
|
|
117
117
|
'THIS', 'DYNAMIC_IMPORT', 'IMPORT_META', 'NEW_TARGET',
|
|
118
118
|
'UNDEFINED', 'NULL', 'BOOL', 'UNARY', 'DO', 'DO_IIFE',
|
|
119
119
|
'YIELD', 'AWAIT', 'UNARY_MATH', 'SUPER', 'THROW',
|
|
120
|
-
'@', '->', '=>', '[', '(', '{', '--', '++',
|
|
120
|
+
'@', '->', '=>', '[', '(', '{', 'MAP_START', '--', '++',
|
|
121
121
|
]);
|
|
122
122
|
|
|
123
123
|
// Tokens that can start an implicit call (unspaced, like +/-)
|
|
@@ -132,12 +132,12 @@ let IMPLICIT_END = new Set([
|
|
|
132
132
|
// Tokens that trigger implicit comma insertion before arrows
|
|
133
133
|
let IMPLICIT_COMMA_BEFORE_ARROW = new Set([
|
|
134
134
|
'STRING', 'STRING_END', 'REGEX', 'REGEX_END', 'NUMBER',
|
|
135
|
-
'BOOL', 'NULL', 'UNDEFINED', 'INFINITY', 'NAN', ']', '}',
|
|
135
|
+
'BOOL', 'NULL', 'UNDEFINED', 'INFINITY', 'NAN', ']', '}', 'MAP_END',
|
|
136
136
|
]);
|
|
137
137
|
|
|
138
138
|
// Tokens that start/end balanced pairs
|
|
139
|
-
let EXPRESSION_START = new Set(['(', '[', '{', 'INDENT', 'CALL_START', 'PARAM_START', 'INDEX_START', 'STRING_START', 'INTERPOLATION_START', 'REGEX_START']);
|
|
140
|
-
let EXPRESSION_END = new Set([')', ']', '}', 'OUTDENT', 'CALL_END', 'PARAM_END', 'INDEX_END', 'STRING_END', 'INTERPOLATION_END', 'REGEX_END']);
|
|
139
|
+
let EXPRESSION_START = new Set(['(', '[', '{', 'MAP_START', 'INDENT', 'CALL_START', 'PARAM_START', 'INDEX_START', 'STRING_START', 'INTERPOLATION_START', 'REGEX_START']);
|
|
140
|
+
let EXPRESSION_END = new Set([')', ']', '}', 'MAP_END', 'OUTDENT', 'CALL_END', 'PARAM_END', 'INDEX_END', 'STRING_END', 'INTERPOLATION_END', 'REGEX_END']);
|
|
141
141
|
|
|
142
142
|
// Balanced pair inverses
|
|
143
143
|
let INVERSES = {
|
|
@@ -151,6 +151,7 @@ let INVERSES = {
|
|
|
151
151
|
'STRING_START': 'STRING_END', 'STRING_END': 'STRING_START',
|
|
152
152
|
'INTERPOLATION_START': 'INTERPOLATION_END', 'INTERPOLATION_END': 'INTERPOLATION_START',
|
|
153
153
|
'REGEX_START': 'REGEX_END', 'REGEX_END': 'REGEX_START',
|
|
154
|
+
'MAP_START': 'MAP_END', 'MAP_END': 'MAP_START',
|
|
154
155
|
};
|
|
155
156
|
|
|
156
157
|
// Tokens that close a clause (for normalizeLines)
|
|
@@ -1369,6 +1370,7 @@ export class Lexer {
|
|
|
1369
1370
|
rewrite(tokens) {
|
|
1370
1371
|
this.tokens = tokens;
|
|
1371
1372
|
this.removeLeadingNewlines();
|
|
1373
|
+
this.rewriteMapLiterals();
|
|
1372
1374
|
this.closeMergeAssignments();
|
|
1373
1375
|
this.closeOpenCalls();
|
|
1374
1376
|
this.closeOpenIndexes();
|
|
@@ -1390,6 +1392,26 @@ export class Lexer {
|
|
|
1390
1392
|
if (i > 0) this.tokens.splice(0, i);
|
|
1391
1393
|
}
|
|
1392
1394
|
|
|
1395
|
+
rewriteMapLiterals() {
|
|
1396
|
+
let tokens = this.tokens;
|
|
1397
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
1398
|
+
if (tokens[i][0] !== 'MATH' || tokens[i][1] !== '*') continue;
|
|
1399
|
+
let next = tokens[i + 1];
|
|
1400
|
+
if (!next || next[0] !== '{') continue;
|
|
1401
|
+
if (tokens[i].spaced || tokens[i].newLine) continue;
|
|
1402
|
+
let prev = tokens[i - 1];
|
|
1403
|
+
if (prev && INDEXABLE.has(prev[0])) continue;
|
|
1404
|
+
tokens.splice(i, 1);
|
|
1405
|
+
tokens[i][0] = 'MAP_START';
|
|
1406
|
+
let depth = 1;
|
|
1407
|
+
for (let j = i + 1; j < tokens.length && depth > 0; j++) {
|
|
1408
|
+
let tag = tokens[j][0];
|
|
1409
|
+
if (tag === '{' || tag === 'MAP_START') depth++;
|
|
1410
|
+
if (tag === '}') { depth--; if (depth === 0) tokens[j][0] = 'MAP_END'; }
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1393
1415
|
closeOpenCalls() {
|
|
1394
1416
|
this.scanTokens((token, i) => {
|
|
1395
1417
|
if (token[0] === 'CALL_START') {
|
|
@@ -1694,12 +1716,13 @@ export class Lexer {
|
|
|
1694
1716
|
|
|
1695
1717
|
let startsLine = s <= 0 || LINE_BREAK.has(this.tokens[s - 1]?.[0]) || this.tokens[s - 1]?.newLine;
|
|
1696
1718
|
|
|
1697
|
-
// Check if we're continuing an existing object
|
|
1719
|
+
// Check if we're continuing an existing object or map literal
|
|
1698
1720
|
if (stackTop()) {
|
|
1699
1721
|
let [stackTag, stackIdx] = stackTop();
|
|
1700
1722
|
let stackNext = stack[stack.length - 2];
|
|
1701
|
-
|
|
1702
|
-
|
|
1723
|
+
let isBrace = (t) => t === '{' || t === 'MAP_START';
|
|
1724
|
+
if ((isBrace(stackTag) || (stackTag === 'INDENT' && isBrace(stackNext?.[0]) && !isImplicit(stackNext))) &&
|
|
1725
|
+
(startsLine || this.tokens[s - 1]?.[0] === ',' || isBrace(this.tokens[s - 1]?.[0]) || isBrace(this.tokens[s]?.[0]))) {
|
|
1703
1726
|
return forward(1);
|
|
1704
1727
|
}
|
|
1705
1728
|
}
|