rip-lang 3.13.26 → 3.13.28
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 +25 -24
- package/bin/rip +29 -18
- package/docs/RIP-LANG.md +259 -4
- package/docs/RIP-TYPES.md +1 -1
- package/docs/dist/rip.js +414 -269
- package/docs/dist/rip.min.js +359 -178
- package/docs/dist/rip.min.js.br +0 -0
- package/package.json +1 -1
- package/src/app.rip +1 -1
- package/src/compiler.js +28 -2
- package/src/components.js +25 -13
- package/src/grammar/grammar.rip +6 -3
- package/src/lexer.js +15 -10
- package/src/parser.js +28 -27
- package/docs/RIP-INTERNALS.md +0 -593
- package/docs/WEB-FRAMEWORKS.md +0 -486
- package/docs/WEB-STYLING.md +0 -701
package/docs/dist/rip.min.js.br
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/app.rip
CHANGED
|
@@ -757,7 +757,7 @@ export createRenderer = (opts = {}) ->
|
|
|
757
757
|
inst.mount wrapper
|
|
758
758
|
layoutInstances.push inst
|
|
759
759
|
|
|
760
|
-
slot = wrapper.querySelector('
|
|
760
|
+
slot = wrapper.querySelector('slot') or wrapper
|
|
761
761
|
mp = slot
|
|
762
762
|
|
|
763
763
|
currentLayouts = [...layoutFiles]
|
package/src/compiler.js
CHANGED
|
@@ -205,6 +205,7 @@ export class CodeGenerator {
|
|
|
205
205
|
'continue': 'generateContinue',
|
|
206
206
|
'?': 'generateExistential',
|
|
207
207
|
'defined': 'generateDefined',
|
|
208
|
+
'presence': 'generatePresence',
|
|
208
209
|
'?:': 'generateTernary',
|
|
209
210
|
'|>': 'generatePipe',
|
|
210
211
|
'loop': 'generateLoop',
|
|
@@ -941,17 +942,38 @@ export class CodeGenerator {
|
|
|
941
942
|
let isIncl = index[0] === '..';
|
|
942
943
|
let arrCode = this.generate(arr, 'value');
|
|
943
944
|
let [start, end] = index.slice(1);
|
|
945
|
+
|
|
946
|
+
// Detect compile-time numeric literals (positive, negative, String objects)
|
|
947
|
+
let numericLiteral = (node) => {
|
|
948
|
+
if (node === null) return null;
|
|
949
|
+
let v = str(node) ?? node;
|
|
950
|
+
if (typeof v === 'number') return v;
|
|
951
|
+
if ((typeof v === 'string') && /^\d+$/.test(v)) return +v;
|
|
952
|
+
if (Array.isArray(node) && node[0] === '-' && node.length === 2) {
|
|
953
|
+
let inner = str(node[1]) ?? node[1];
|
|
954
|
+
if (typeof inner === 'number') return -inner;
|
|
955
|
+
if ((typeof inner === 'string') && /^\d+$/.test(inner)) return -inner;
|
|
956
|
+
}
|
|
957
|
+
return null;
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
let inclEnd = (s, e, endNode) => {
|
|
961
|
+
let n = numericLiteral(endNode);
|
|
962
|
+
if (n !== null && n !== -1) return `${arrCode}.slice(${s}, ${n + 1})`;
|
|
963
|
+
return `${arrCode}.slice(${s}, +${e} + 1 || 9e9)`;
|
|
964
|
+
};
|
|
965
|
+
|
|
944
966
|
if (start === null && end === null) return `${arrCode}.slice()`;
|
|
945
967
|
if (start === null) {
|
|
946
968
|
if (isIncl && this.is(end, '-', 1) && (str(end[1]) ?? end[1]) == 1) return `${arrCode}.slice(0)`;
|
|
947
969
|
let e = this.generate(end, 'value');
|
|
948
|
-
return isIncl ?
|
|
970
|
+
return isIncl ? inclEnd('0', e, end) : `${arrCode}.slice(0, ${e})`;
|
|
949
971
|
}
|
|
950
972
|
if (end === null) return `${arrCode}.slice(${this.generate(start, 'value')})`;
|
|
951
973
|
let s = this.generate(start, 'value');
|
|
952
974
|
if (isIncl && this.is(end, '-', 1) && (str(end[1]) ?? end[1]) == 1) return `${arrCode}.slice(${s})`;
|
|
953
975
|
let e = this.generate(end, 'value');
|
|
954
|
-
return isIncl ?
|
|
976
|
+
return isIncl ? inclEnd(s, e, end) : `${arrCode}.slice(${s}, ${e})`;
|
|
955
977
|
}
|
|
956
978
|
// Negative literal index: arr[-1] → arr.at(-1)
|
|
957
979
|
if (this.is(index, '-', 1)) {
|
|
@@ -1124,6 +1146,10 @@ export class CodeGenerator {
|
|
|
1124
1146
|
return `(${this.generate(rest[0], 'value')} !== undefined)`;
|
|
1125
1147
|
}
|
|
1126
1148
|
|
|
1149
|
+
generatePresence(head, rest) {
|
|
1150
|
+
return `(${this.generate(rest[0], 'value')} ? true : undefined)`;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1127
1153
|
generateTernary(head, rest, context) {
|
|
1128
1154
|
let [cond, then_, else_] = rest;
|
|
1129
1155
|
|
package/src/components.js
CHANGED
|
@@ -289,9 +289,14 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
289
289
|
let prevTag = prevToken ? prevToken[0] : null;
|
|
290
290
|
if (prevTag === 'INDENT' || prevTag === 'TERMINATOR') {
|
|
291
291
|
if (nextToken && nextToken[0] === 'PROPERTY') {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
292
|
+
// Check if property is followed by : — if so, it's an attribute
|
|
293
|
+
// (. foo: bar → div foo: bar), not a class (. foo → div.foo)
|
|
294
|
+
let nextNext = i + 2 < tokens.length ? tokens[i + 2] : null;
|
|
295
|
+
if (!nextNext || nextNext[0] !== ':') {
|
|
296
|
+
let divToken = gen('IDENTIFIER', 'div', token);
|
|
297
|
+
tokens.splice(i, 0, divToken);
|
|
298
|
+
return 2;
|
|
299
|
+
}
|
|
295
300
|
}
|
|
296
301
|
// Skip .('classes') — handled by dynamic classes handler below
|
|
297
302
|
if (!nextToken || nextToken[0] !== '(') {
|
|
@@ -1041,19 +1046,26 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1041
1046
|
this.generateAttributes(elVar, arg);
|
|
1042
1047
|
}
|
|
1043
1048
|
else if (typeof arg === 'string' || arg instanceof String) {
|
|
1044
|
-
const textVar = this.newTextVar();
|
|
1045
1049
|
const val = arg.valueOf();
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
this.
|
|
1051
|
-
} else if (this.componentMembers && this.componentMembers.has(val)) {
|
|
1052
|
-
this._createLines.push(`${textVar} = document.createTextNode(String(${this._self}.${val}));`);
|
|
1050
|
+
// Template tag appearing as a string arg (e.g., slot after multi-line attrs)
|
|
1051
|
+
const [tagPart, idPart] = val.split('#');
|
|
1052
|
+
if (this.isHtmlTag(tagPart || 'div') || this.isComponent(val)) {
|
|
1053
|
+
const childVar = this.generateNode(arg);
|
|
1054
|
+
this._createLines.push(`${elVar}.appendChild(${childVar});`);
|
|
1053
1055
|
} else {
|
|
1054
|
-
|
|
1056
|
+
const textVar = this.newTextVar();
|
|
1057
|
+
if (val.startsWith('"') || val.startsWith("'") || val.startsWith('`')) {
|
|
1058
|
+
this._createLines.push(`${textVar} = document.createTextNode(${val});`);
|
|
1059
|
+
} else if (this.reactiveMembers && this.reactiveMembers.has(val)) {
|
|
1060
|
+
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
1061
|
+
this._pushEffect(`${textVar}.data = ${this._self}.${val}.value;`);
|
|
1062
|
+
} else if (this.componentMembers && this.componentMembers.has(val)) {
|
|
1063
|
+
this._createLines.push(`${textVar} = document.createTextNode(String(${this._self}.${val}));`);
|
|
1064
|
+
} else {
|
|
1065
|
+
this._createLines.push(`${textVar} = document.createTextNode(${this.generateInComponent(arg, 'value')});`);
|
|
1066
|
+
}
|
|
1067
|
+
this._createLines.push(`${elVar}.appendChild(${textVar});`);
|
|
1055
1068
|
}
|
|
1056
|
-
this._createLines.push(`${elVar}.appendChild(${textVar});`);
|
|
1057
1069
|
}
|
|
1058
1070
|
else if (arg) {
|
|
1059
1071
|
const childVar = this.generateNode(arg);
|
package/src/grammar/grammar.rip
CHANGED
|
@@ -853,6 +853,9 @@ grammar =
|
|
|
853
853
|
# Postfix defined check: expr!? → (expr !== undefined)
|
|
854
854
|
o 'Value DEFINED' , '["defined", 1]'
|
|
855
855
|
|
|
856
|
+
# Postfix presence check: expr?! → (expr ? true : undefined) — Houdini operator
|
|
857
|
+
o 'Value PRESENCE' , '["presence", 1]'
|
|
858
|
+
|
|
856
859
|
# Await
|
|
857
860
|
o 'AWAIT Expression' , '["await", 2]'
|
|
858
861
|
o 'AWAIT INDENT Object OUTDENT' , '["await", 3]'
|
|
@@ -895,7 +898,7 @@ grammar =
|
|
|
895
898
|
o 'Expression RELATION Expression', '[2, 1, 3]' # in, of, instanceof
|
|
896
899
|
|
|
897
900
|
# Ternary
|
|
898
|
-
o 'Expression
|
|
901
|
+
o 'Expression TERNARY Expression : Expression', '["?:", 1, 3, 5]'
|
|
899
902
|
|
|
900
903
|
# Compound assignment
|
|
901
904
|
o 'SimpleAssignable COMPOUND_ASSIGN Expression' , '[2, 1, 3]'
|
|
@@ -916,7 +919,7 @@ operators = """
|
|
|
916
919
|
right DO_IIFE
|
|
917
920
|
left . ?.
|
|
918
921
|
left CALL_START CALL_END
|
|
919
|
-
nonassoc ++ -- ? DEFINED
|
|
922
|
+
nonassoc ++ -- ? DEFINED PRESENCE
|
|
920
923
|
right UNARY DO
|
|
921
924
|
right AWAIT
|
|
922
925
|
right **
|
|
@@ -932,7 +935,7 @@ operators = """
|
|
|
932
935
|
left &&
|
|
933
936
|
left ||
|
|
934
937
|
left PIPE
|
|
935
|
-
right
|
|
938
|
+
right TERNARY
|
|
936
939
|
nonassoc INDENT OUTDENT
|
|
937
940
|
right YIELD
|
|
938
941
|
right = : COMPOUND_ASSIGN RETURN THROW EXTENDS
|
package/src/lexer.js
CHANGED
|
@@ -180,7 +180,7 @@ let CALL_CLOSERS = new Set(['.', '?.']);
|
|
|
180
180
|
let UNFINISHED = new Set([
|
|
181
181
|
'\\', '.', '?.', 'UNARY', 'DO', 'DO_IIFE',
|
|
182
182
|
'MATH', 'UNARY_MATH', '+', '-', '**', 'SHIFT', 'RELATION',
|
|
183
|
-
'COMPARE', '&', '^', '|', '&&', '||', '
|
|
183
|
+
'COMPARE', '&', '^', '|', '&&', '||', 'TERNARY', 'EXTENDS',
|
|
184
184
|
]);
|
|
185
185
|
|
|
186
186
|
// Tokens that are not followed by regex (division context)
|
|
@@ -209,12 +209,12 @@ let UNARY_MATH = new Set(['!', '~']);
|
|
|
209
209
|
// ==========================================================================
|
|
210
210
|
|
|
211
211
|
// Identifier: word chars + optional trailing ! (await) or ? (predicate)
|
|
212
|
-
// The ? suffix is only captured when NOT followed by . ? [ ( to avoid
|
|
213
|
-
// conflict with ?. (optional chaining), ?? (nullish), ?.( and ?.[
|
|
212
|
+
// The ? suffix is only captured when NOT followed by . ? ! [ ( to avoid
|
|
213
|
+
// conflict with ?. (optional chaining), ?? (nullish), ?! (presence), ?.( and ?.[
|
|
214
214
|
// The ! suffix is NOT captured when followed by ? to preserve !? as operator
|
|
215
|
-
let IDENTIFIER_RE = /^(?!\d)((?:(?!\s)[$\w\x7f-\uffff])+(?:!(?!\?)|[?](?![
|
|
215
|
+
let IDENTIFIER_RE = /^(?!\d)((?:(?!\s)[$\w\x7f-\uffff])+(?:!(?!\?)|[?](?![.?![(]))?)([^\n\S]*:(?![=:]))?/;
|
|
216
216
|
let NUMBER_RE = /^0b[01](?:_?[01])*n?|^0o[0-7](?:_?[0-7])*n?|^0x[\da-f](?:_?[\da-f])*n?|^\d+(?:_\d+)*n|^(?:\d+(?:_\d+)*)?\.?\d+(?:_\d+)*(?:e[+-]?\d+(?:_\d+)*)?/i;
|
|
217
|
-
let OPERATOR_RE = /^(?:<=>|::=|::|[-=]
|
|
217
|
+
let OPERATOR_RE = /^(?:<=>|::=|::|[-=]>|~>|~=|:=|=!|===|!==|!\?|\?\!|\?\?|=~|\|>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>*\/%])\2=?|\?\.?|\.{2,3})/;
|
|
218
218
|
let WHITESPACE_RE = /^[^\n\S]+/;
|
|
219
219
|
let NEWLINE_RE = /^(?:\n[^\n\S]*)+/;
|
|
220
220
|
let COMMENT_RE = /^(\s*)###([^#][\s\S]*?)(?:###([^\n\S]*)|###$)|^((?:\s*#(?!##[^#]).*)+)/;
|
|
@@ -500,7 +500,7 @@ export class Lexer {
|
|
|
500
500
|
let prev = this.prev();
|
|
501
501
|
|
|
502
502
|
// Don't treat colon as property when in ternary context
|
|
503
|
-
if (colon && prev && prev[0] === '
|
|
503
|
+
if (colon && prev && prev[0] === 'TERNARY') colon = null;
|
|
504
504
|
|
|
505
505
|
// Property vs identifier
|
|
506
506
|
if (colon || (prev && (prev[0] === '.' || prev[0] === '?.' || (!prev.spaced && prev[0] === '@')))) {
|
|
@@ -634,7 +634,10 @@ export class Lexer {
|
|
|
634
634
|
if (prev && (prev[0] === 'IDENTIFIER' || prev[0] === 'PROPERTY'))
|
|
635
635
|
return 0; // after a tag (div#main) → let # become a token for rewriter
|
|
636
636
|
let m = /^#([a-zA-Z_][\w-]*)/.exec(this.chunk);
|
|
637
|
-
if (m) {
|
|
637
|
+
if (m) {
|
|
638
|
+
this.emit('IDENTIFIER', m[1] === 'content' ? 'slot' : 'div#' + m[1]);
|
|
639
|
+
return m[0].length;
|
|
640
|
+
}
|
|
638
641
|
}
|
|
639
642
|
if (/^\s+#[a-zA-Z_]/.test(this.chunk)) return 0; // let lineToken handle indentation first
|
|
640
643
|
}
|
|
@@ -1225,10 +1228,12 @@ export class Lexer {
|
|
|
1225
1228
|
else if (COMPOUND_ASSIGN.has(val)) tag = 'COMPOUND_ASSIGN';
|
|
1226
1229
|
else if (UNARY_MATH.has(val)) tag = 'UNARY_MATH';
|
|
1227
1230
|
else if (SHIFT.has(val)) tag = 'SHIFT';
|
|
1228
|
-
// Spaced ? →
|
|
1229
|
-
else if (val === '?' && prev?.spaced) tag = '
|
|
1231
|
+
// Spaced ? → TERNARY (ternary)
|
|
1232
|
+
else if (val === '?' && prev?.spaced) tag = 'TERNARY';
|
|
1230
1233
|
// Unspaced !? → DEFINED (postfix defined check: v!? → v !== undefined)
|
|
1231
1234
|
else if (val === '!?' && prev && !prev.spaced) tag = 'DEFINED';
|
|
1235
|
+
// Unspaced ?! → PRESENCE (Houdini: v?! → v ? true : undefined)
|
|
1236
|
+
else if (val === '?!' && prev && !prev.spaced) tag = 'PRESENCE';
|
|
1232
1237
|
// ?[ and ?( without dot → treat as optional chaining (?.)
|
|
1233
1238
|
else if (val === '?' && (this.chunk[1] === '[' || this.chunk[1] === '(')) tag = '?.';
|
|
1234
1239
|
// Call/index context (ES6 optional chaining only)
|
|
@@ -1529,7 +1534,7 @@ export class Lexer {
|
|
|
1529
1534
|
}
|
|
1530
1535
|
|
|
1531
1536
|
// Track ternary
|
|
1532
|
-
if (tag === '
|
|
1537
|
+
if (tag === 'TERNARY') inTernary = true;
|
|
1533
1538
|
|
|
1534
1539
|
// Implicit objects start at ':'
|
|
1535
1540
|
if (tag === ':') {
|