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.
Binary file
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.13.129",
3
+ "version": "3.13.130",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
package/src/AGENTS.md CHANGED
@@ -53,6 +53,7 @@ Complete node reference:
53
53
  // Data Structures
54
54
  ['array', ...elements]
55
55
  ['object', ...pairs] // pairs: [key, value]
56
+ ['map-literal', ...pairs] // *{ } → new Map([[key, value], ...])
56
57
  ['...', expr] // Spread (prefix only)
57
58
 
58
59
  // Operators
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 vars = Array.from(this.programVars).sort().join(', ');
715
- if (needsBlank) code += '\n';
716
- code += `let ${vars};\n`;
717
- needsBlank = true;
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' && isTemplateTag(tokens[j][1]);
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 ['object', ...sexpr.slice(1).map(pair => {
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(name: string, detail?: any): void {}');
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(props${propsOpt}: ${propsType}) {}`);
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 => this.formatParam(p)).join(', ') : '';
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 !== 'object' && head !== 'switch' && (TEMPLATE_TAGS.has(head.split(/[.#]/)[0]) ||
1068
- (/^[a-z]/.test(head) && node.length > 1 && Array.isArray(node[1]) && (node[1][0] === '->' || node[1][0] === '=>')))) {
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;
@@ -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
- if ((stackTag === '{' || (stackTag === 'INDENT' && stackNext?.[0] === '{' && !isImplicit(stackNext))) &&
1702
- (startsLine || this.tokens[s - 1]?.[0] === ',' || this.tokens[s - 1]?.[0] === '{' || this.tokens[s]?.[0] === '{')) {
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
  }