rip-lang 3.16.0 → 3.16.2
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 +3 -4
- package/bin/rip +201 -18
- package/bin/rip-schema +175 -0
- package/docs/AGENTS.md +1 -1
- package/docs/RIP-APP.md +200 -19
- package/docs/RIP-DUCKDB.md +64 -1
- package/docs/RIP-INTRO.md +4 -4
- package/docs/RIP-LANG.md +36 -38
- package/docs/RIP-SCHEMA.md +1204 -364
- package/docs/RIP-TYPES.md +74 -103
- package/docs/demo/README.md +4 -3
- package/docs/dist/rip.js +4195 -966
- package/docs/dist/rip.min.js +1161 -284
- package/docs/dist/rip.min.js.br +0 -0
- package/docs/example/index.json +7 -7
- package/docs/example/index.json.br +0 -0
- package/docs/extensions/vscode/print/print-1.0.14.vsix +0 -0
- package/docs/extensions/vscode/print/print-latest.vsix +0 -0
- package/docs/extensions/vscode/rip/index.html +2 -1
- package/docs/extensions/vscode/rip/rip-0.5.15.vsix +0 -0
- package/docs/extensions/vscode/rip/rip-latest.vsix +0 -0
- package/docs/extensions/vscode/rip/vscode-rip-0.6.0.vsix +0 -0
- package/docs/index.html +1 -1
- package/docs/ui/bundle.json +55 -55
- package/docs/ui/bundle.json.br +0 -0
- package/docs/ui/hljs-rip.js +1 -1
- package/docs/ui/index.html +1 -1
- package/package.json +15 -7
- package/rip-loader.js +59 -2
- package/src/AGENTS.md +43 -12
- package/src/browser.js +52 -11
- package/src/compiler.js +538 -80
- package/src/components.js +488 -48
- package/src/dts.js +80 -50
- package/src/grammar/README.md +29 -170
- package/src/grammar/grammar.rip +17 -12
- package/src/grammar/solar.rip +4 -17
- package/src/lexer.js +82 -32
- package/src/parser.js +229 -229
- package/src/schema/dts.js +328 -54
- package/src/schema/loader-server.js +2 -1
- package/src/schema/runtime-browser-stubs.js +20 -9
- package/src/schema/runtime-ddl.js +161 -44
- package/src/schema/runtime-migrate.js +681 -0
- package/src/schema/runtime-orm.js +698 -54
- package/src/schema/runtime-validate.js +808 -24
- package/src/schema/runtime.generated.js +2395 -135
- package/src/schema/schema.js +1054 -94
- package/src/typecheck.js +1610 -127
- package/src/types.js +90 -6
- package/src/grammar/lunar.rip +0 -2412
- /package/docs/demo/{components → routes}/_layout.rip +0 -0
- /package/docs/demo/{components → routes}/about.rip +0 -0
- /package/docs/demo/{components → routes}/card.rip +0 -0
- /package/docs/demo/{components → routes}/counter.rip +0 -0
- /package/docs/demo/{components → routes}/index.rip +0 -0
- /package/docs/demo/{components → routes}/todos.rip +0 -0
package/src/grammar/solar.rip
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
import * as fs from 'fs'
|
|
11
11
|
import * as path from 'path'
|
|
12
12
|
import { fileURLToPath, pathToFileURL } from 'url'
|
|
13
|
-
import { install as installLunar } from './lunar.rip'
|
|
14
13
|
|
|
15
14
|
VERSION = '1.5.0'
|
|
16
15
|
|
|
@@ -90,11 +89,9 @@ class Generator
|
|
|
90
89
|
# Build parser
|
|
91
90
|
@timing '💥 Total time', =>
|
|
92
91
|
@timing 'processGrammar' , => @processGrammar grammar # Process grammar rules
|
|
93
|
-
|
|
94
|
-
@timing 'buildLRAutomaton' , => @buildLRAutomaton() # Build LR(0) automaton
|
|
92
|
+
@timing 'buildLRAutomaton' , => @buildLRAutomaton() # Build LR(0) automaton
|
|
95
93
|
@timing 'processLookaheads', => @processLookaheads() # Compute FIRST/FOLLOW and assign lookaheads
|
|
96
|
-
|
|
97
|
-
@timing 'buildParseTable' , => @buildParseTable() # Build parse table with default actions
|
|
94
|
+
@timing 'buildParseTable' , => @buildParseTable() # Build parse table with default actions
|
|
98
95
|
|
|
99
96
|
# ============================================================================
|
|
100
97
|
# Helper Functions
|
|
@@ -656,8 +653,6 @@ class Generator
|
|
|
656
653
|
# Decoder reconstructs [{key: val}, ...] at runtime
|
|
657
654
|
"(()=>{let d=[#{data.join ','}],t=[],p=0,n,o,k,a;while(p<d.length){n=d[p++];o={};k=0;a=[];while(n--)k+=d[p++],a.push(k);for(k of a)o[k]=d[p++];t.push(o)}return t})()"
|
|
658
655
|
|
|
659
|
-
# Recursive descent generation — installed by Lunar (see lunar.rip)
|
|
660
|
-
|
|
661
656
|
# ============================================================================
|
|
662
657
|
# Runtime Parser
|
|
663
658
|
# ============================================================================
|
|
@@ -772,8 +767,6 @@ class Generator
|
|
|
772
767
|
# Exports
|
|
773
768
|
# ==============================================================================
|
|
774
769
|
|
|
775
|
-
installLunar Generator
|
|
776
|
-
|
|
777
770
|
export { Generator }
|
|
778
771
|
|
|
779
772
|
# ==============================================================================
|
|
@@ -805,7 +798,6 @@ if isRunAsScript
|
|
|
805
798
|
-v, --version Show version
|
|
806
799
|
-i, --info Show grammar information
|
|
807
800
|
-s, --sexpr Show grammar as s-expression
|
|
808
|
-
-r, --rd Generate recursive descent parser (parser-rd.js)
|
|
809
801
|
-c, --conflicts Show conflict details (use with --info)
|
|
810
802
|
-o, --output <file> Output file (default: parser.js)
|
|
811
803
|
|
|
@@ -843,7 +835,7 @@ if isRunAsScript
|
|
|
843
835
|
console.log " Resolution: #{conflict.resolution} (by default)"
|
|
844
836
|
|
|
845
837
|
# Parse command line
|
|
846
|
-
options = {help: false, version: false, info: false, sexpr: false, conflicts: false,
|
|
838
|
+
options = {help: false, version: false, info: false, sexpr: false, conflicts: false, output: 'parser.js'}
|
|
847
839
|
grammarFile = null
|
|
848
840
|
i = 0
|
|
849
841
|
|
|
@@ -854,7 +846,6 @@ if isRunAsScript
|
|
|
854
846
|
when '-v', '--version' then options.version = true
|
|
855
847
|
when '-i', '--info' then options.info = true
|
|
856
848
|
when '-s', '--sexpr' then options.sexpr = true
|
|
857
|
-
when '-r', '--rd' then options.rd = true
|
|
858
849
|
when '-c', '--conflicts' then options.conflicts = true
|
|
859
850
|
when '-o', '--output' then options.output = process.argv[++i + 2]
|
|
860
851
|
else grammarFile = arg unless arg.startsWith('-')
|
|
@@ -915,11 +906,7 @@ if isRunAsScript
|
|
|
915
906
|
if options.info
|
|
916
907
|
showStats generator
|
|
917
908
|
else
|
|
918
|
-
|
|
919
|
-
options.output = 'parser-rd.js' if options.output is 'parser.js'
|
|
920
|
-
parserCode = generator.generateRD()
|
|
921
|
-
else
|
|
922
|
-
parserCode = generator.generate()
|
|
909
|
+
parserCode = generator.generate()
|
|
923
910
|
fs.writeFileSync options.output, parserCode
|
|
924
911
|
console.log "\nParser generated: #{options.output}"
|
|
925
912
|
|
package/src/lexer.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
// Design principles:
|
|
9
9
|
// - Every token carries .pre (whitespace count before it)
|
|
10
|
-
// - Every token carries .data (metadata:
|
|
10
|
+
// - Every token carries .data (metadata: bang, optional, quote, etc.)
|
|
11
11
|
// - Every token carries .loc (location: row, col, len)
|
|
12
12
|
// - Indentation is derived from .pre, not tracked during lexing
|
|
13
13
|
// - Token categories use Sets for O(1) membership tests
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
// token.newLine — true if preceded by a newline
|
|
24
24
|
//
|
|
25
25
|
// Identifier suffixes:
|
|
26
|
-
// ! —
|
|
27
|
-
// ? —
|
|
26
|
+
// ! — bang: fetch!() → await fetch() (call site) | foo! = -> → void (definition)
|
|
27
|
+
// ? — optional: empty? → (empty != null) (existence check / optional marker)
|
|
28
28
|
//
|
|
29
29
|
// The 9 tokenizer methods (in priority order):
|
|
30
30
|
// 1. identifier — variables, keywords, properties, ! and ? suffixes
|
|
@@ -129,7 +129,7 @@ let IMPLICIT_UNSPACED_CALL = new Set(['+', '-']);
|
|
|
129
129
|
// Tokens that end an implicit call
|
|
130
130
|
let IMPLICIT_END = new Set([
|
|
131
131
|
'POST_IF', 'POST_UNLESS', 'FOR', 'WHILE', 'UNTIL',
|
|
132
|
-
'WHEN', 'BY', 'LOOP', 'TERMINATOR', '||', '&&',
|
|
132
|
+
'WHEN', 'BY', 'LOOP', 'TERMINATOR', '||', '&&',
|
|
133
133
|
]);
|
|
134
134
|
|
|
135
135
|
// Tokens that trigger implicit comma insertion before arrows
|
|
@@ -246,12 +246,12 @@ let UNARY_MATH = new Set(['!', '~']);
|
|
|
246
246
|
// Regex Patterns
|
|
247
247
|
// ==========================================================================
|
|
248
248
|
|
|
249
|
-
// Identifier: word chars + optional trailing ! (
|
|
249
|
+
// Identifier: word chars + optional trailing ! (bang) or ? (optional)
|
|
250
250
|
// The ? suffix is only captured when NOT followed by . ? ! [ ( to avoid
|
|
251
251
|
// conflict with ?. (optional chaining), ?? (nullish), ?! (presence), ?.( and ?.[
|
|
252
252
|
let IDENTIFIER_RE = /^(?!\d)((?:(?!\s)[$\w\x7f-\uffff])+(?:!|[?](?![.?![(]))?)([^\n\S]*:(?![=:]))?/;
|
|
253
253
|
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;
|
|
254
|
-
let OPERATOR_RE = /^(
|
|
254
|
+
let OPERATOR_RE = /^(?:<=>|<~|::|\*>|[-=]>|~>|~=|:=|=!|===|!==|\?\!|\?\?|=~|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>*\/%])\2=?|\?\.?|\.{2,3})/;
|
|
255
255
|
let WHITESPACE_RE = /^[^\n\S]+/;
|
|
256
256
|
let NEWLINE_RE = /^(?:\n[^\n\S]*)+/;
|
|
257
257
|
let COMMENT_RE = /^(\s*)###([^#][\s\S]*?)(?:###([^\n\S]*)|###$)|^((?:\s*#(?!##[^#]).*)+)/;
|
|
@@ -519,8 +519,10 @@ export class Lexer {
|
|
|
519
519
|
// Handles: variables, keywords, properties, aliases
|
|
520
520
|
//
|
|
521
521
|
// Suffix operators on identifiers:
|
|
522
|
-
// ! →
|
|
523
|
-
//
|
|
522
|
+
// ! → bang: a neutral "trailing !" flag. Resolved by context downstream —
|
|
523
|
+
// dammit/await at a call site (fetch!() → await fetch()), or the void
|
|
524
|
+
// marker at a function definition (foo! = -> / def foo! → no return).
|
|
525
|
+
// ? → optional (existence): empty? → (empty != null)
|
|
524
526
|
//
|
|
525
527
|
// The ? suffix is captured by IDENTIFIER_RE only when NOT followed by
|
|
526
528
|
// . ? [ ( — so x?.y (optional chaining) and x?? (nullish coalescing)
|
|
@@ -655,16 +657,20 @@ export class Lexer {
|
|
|
655
657
|
}
|
|
656
658
|
}
|
|
657
659
|
|
|
658
|
-
// ---
|
|
660
|
+
// --- Bang: trailing ! → context-resolved (await at call site / void at def) ---
|
|
659
661
|
if (id.length > 1 && id.endsWith('!')) {
|
|
660
|
-
data.
|
|
662
|
+
data.bang = true;
|
|
661
663
|
id = id.slice(0, -1);
|
|
662
664
|
}
|
|
663
665
|
|
|
664
|
-
// ---
|
|
665
|
-
//
|
|
666
|
+
// --- Optional marker: trailing ? ---
|
|
667
|
+
// Identifier-position: existence check (`empty?` → `(empty != null)`).
|
|
668
|
+
// Property-name position: optional prop / type field (`@label?:: T`,
|
|
669
|
+
// `{ x?:: T }`, `def f(x?:: T)`). The flag is consumed by the type/
|
|
670
|
+
// component/schema emitters; the runtime semantics for `name?` as an
|
|
671
|
+
// existence check are unchanged.
|
|
666
672
|
if (id.length > 1 && id.endsWith('?')) {
|
|
667
|
-
data.
|
|
673
|
+
data.optional = true;
|
|
668
674
|
id = id.slice(0, -1);
|
|
669
675
|
}
|
|
670
676
|
|
|
@@ -742,18 +748,6 @@ export class Lexer {
|
|
|
742
748
|
}
|
|
743
749
|
if (/^\s+#[a-zA-Z_]/.test(this.chunk)) return 0; // let lineToken handle indentation first
|
|
744
750
|
}
|
|
745
|
-
// Schema field modifier: `#` adjacent (unspaced) to an identifier acts
|
|
746
|
-
// as the unique-marker inside schema bodies (e.g. `email!# email`).
|
|
747
|
-
// Return 0 so literalToken emits a standalone `#` token; rewriteSchema
|
|
748
|
-
// absorbs it. Outside schema bodies the `#` token is harmless because
|
|
749
|
-
// nothing else in the grammar accepts `IDENTIFIER #` without a space.
|
|
750
|
-
if (this.chunk[0] === '#') {
|
|
751
|
-
let prev = this.prev();
|
|
752
|
-
if (prev && !prev.spaced && !prev.newLine &&
|
|
753
|
-
(prev[0] === 'IDENTIFIER' || prev[0] === 'PROPERTY')) {
|
|
754
|
-
return 0;
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
751
|
let match = COMMENT_RE.exec(this.chunk);
|
|
758
752
|
if (!match) return 0;
|
|
759
753
|
return match[0].length;
|
|
@@ -1382,8 +1376,6 @@ export class Lexer {
|
|
|
1382
1376
|
this.inTypeAnnotation = false;
|
|
1383
1377
|
tag = 'TERMINATOR';
|
|
1384
1378
|
}
|
|
1385
|
-
// Pipe operator
|
|
1386
|
-
else if (val === '|>') tag = 'PIPE';
|
|
1387
1379
|
// Type operators
|
|
1388
1380
|
else if (val === '::' && /^[a-zA-Z_$]/.test(this.chunk[2] || '')) {
|
|
1389
1381
|
// Prototype access: String::trim → String.prototype.trim
|
|
@@ -1397,6 +1389,7 @@ export class Lexer {
|
|
|
1397
1389
|
else if (val === '~=') tag = 'COMPUTED_ASSIGN';
|
|
1398
1390
|
else if (val === ':=') tag = 'REACTIVE_ASSIGN';
|
|
1399
1391
|
else if (val === '<=>') tag = 'BIND';
|
|
1392
|
+
else if (val === '<~') { tag = 'GATE'; this.inTypeAnnotation = false; }
|
|
1400
1393
|
else if (val === '~>') { tag = 'EFFECT'; this.inTypeAnnotation = false; }
|
|
1401
1394
|
else if (val === '=!') { tag = 'READONLY_ASSIGN'; this.inTypeAnnotation = false; }
|
|
1402
1395
|
// Merge assignment: *>config = {a: 1} → Object.assign(config, {a: 1})
|
|
@@ -1519,9 +1512,42 @@ export class Lexer {
|
|
|
1519
1512
|
|
|
1520
1513
|
// Walk back to tag parameters for arrow functions
|
|
1521
1514
|
tagParameters() {
|
|
1522
|
-
|
|
1515
|
+
let closeIdx = this.tokens.length - 1;
|
|
1516
|
+
if (this.tokens[closeIdx]?.[0] !== ')') {
|
|
1517
|
+
// Maybe a return-type annotation sits between `)` and the arrow:
|
|
1518
|
+
// `(x:: T):: R ->`. Scan backward over the type expression (balanced
|
|
1519
|
+
// brackets) looking for the trailing `TYPE_ANNOTATION` whose previous
|
|
1520
|
+
// token is `)`. If found, treat that `)` as the param-list close.
|
|
1521
|
+
let n = this.tokens.length;
|
|
1522
|
+
let depth = 0;
|
|
1523
|
+
let j = n - 1;
|
|
1524
|
+
let found = -1;
|
|
1525
|
+
while (j >= 0) {
|
|
1526
|
+
let tk = this.tokens[j];
|
|
1527
|
+
let tg = tk[0];
|
|
1528
|
+
if (tg === ')' || tg === ']' || tg === '}' || tg === 'CALL_END' ||
|
|
1529
|
+
tg === 'PARAM_END' || tg === 'INDEX_END' ||
|
|
1530
|
+
(tg === 'COMPARE' && tk[1] === '>')) {
|
|
1531
|
+
depth++;
|
|
1532
|
+
} else if (tg === '(' || tg === '[' || tg === '{' || tg === 'CALL_START' ||
|
|
1533
|
+
tg === 'PARAM_START' || tg === 'INDEX_START' ||
|
|
1534
|
+
(tg === 'COMPARE' && tk[1] === '<')) {
|
|
1535
|
+
depth--;
|
|
1536
|
+
} else if (depth === 0) {
|
|
1537
|
+
if (tg === 'TYPE_ANNOTATION') {
|
|
1538
|
+
if (j > 0 && this.tokens[j - 1][0] === ')') found = j - 1;
|
|
1539
|
+
break;
|
|
1540
|
+
}
|
|
1541
|
+
if (tg === 'TERMINATOR' || tg === 'INDENT' || tg === 'OUTDENT' ||
|
|
1542
|
+
tg === '=' || tg === '->' || tg === '=>') break;
|
|
1543
|
+
}
|
|
1544
|
+
j--;
|
|
1545
|
+
}
|
|
1546
|
+
if (found < 0) return this.tagDoIife();
|
|
1547
|
+
closeIdx = found;
|
|
1548
|
+
}
|
|
1523
1549
|
|
|
1524
|
-
let i =
|
|
1550
|
+
let i = closeIdx;
|
|
1525
1551
|
let stack = [];
|
|
1526
1552
|
this.tokens[i][0] = 'PARAM_END';
|
|
1527
1553
|
|
|
@@ -1536,7 +1562,7 @@ export class Lexer {
|
|
|
1536
1562
|
tok[0] = 'PARAM_START';
|
|
1537
1563
|
return this.tagDoIife(i - 1);
|
|
1538
1564
|
} else {
|
|
1539
|
-
this.tokens[
|
|
1565
|
+
this.tokens[closeIdx][0] = 'CALL_END';
|
|
1540
1566
|
return;
|
|
1541
1567
|
}
|
|
1542
1568
|
}
|
|
@@ -1562,10 +1588,14 @@ export class Lexer {
|
|
|
1562
1588
|
this.closeMergeAssignments();
|
|
1563
1589
|
this.closeOpenCalls();
|
|
1564
1590
|
this.closeOpenIndexes();
|
|
1591
|
+
// rewriteTypes must run BEFORE normalizeLines: otherwise a type-arrow
|
|
1592
|
+
// `=>` inside `(...) => T` gets treated as a single-liner function and
|
|
1593
|
+
// wrapped in spurious INDENT/OUTDENT, which then derails the type
|
|
1594
|
+
// collector (it slurps the whole assignment body as part of the type).
|
|
1595
|
+
this.rewriteTypes();
|
|
1565
1596
|
this.normalizeLines();
|
|
1566
1597
|
this.rewriteRender?.();
|
|
1567
1598
|
this.rewriteSchema?.();
|
|
1568
|
-
this.rewriteTypes();
|
|
1569
1599
|
this.tagPostfixConditionals();
|
|
1570
1600
|
this.rewriteTaggedTemplates();
|
|
1571
1601
|
this.addImplicitBracesAndParens();
|
|
@@ -2020,12 +2050,32 @@ export class Lexer {
|
|
|
2020
2050
|
} else if (t && t !== 'TERMINATOR' && t !== 'OUTDENT' && t !== ',') j++;
|
|
2021
2051
|
logicalKeep = tokens[j]?.[0] === ',';
|
|
2022
2052
|
}
|
|
2053
|
+
// A postfix conditional on the FIRST property line of a multiline
|
|
2054
|
+
// implicit object must bind to the property's value, not close the
|
|
2055
|
+
// object — otherwise `a: 1 if c` followed by more properties splits
|
|
2056
|
+
// the object into separate arguments. Later property lines already
|
|
2057
|
+
// bind to the value (the sameLine flag is false by then), so only
|
|
2058
|
+
// first-line conditionals need the lookahead: keep the object open
|
|
2059
|
+
// when the property list continues after this line.
|
|
2060
|
+
let objectContinues = (j) => {
|
|
2061
|
+
for (let d = 0; j < tokens.length; j++) {
|
|
2062
|
+
let t = tokens[j][0];
|
|
2063
|
+
if (t === '(' || t === '[' || t === '{' || t === 'CALL_START' || t === 'INDEX_START' || t === 'INDENT') d++;
|
|
2064
|
+
else if (t === ')' || t === ']' || t === '}' || t === 'CALL_END' || t === 'INDEX_END' || t === 'OUTDENT') {
|
|
2065
|
+
if (d === 0) return false;
|
|
2066
|
+
d--;
|
|
2067
|
+
}
|
|
2068
|
+
else if (t === 'TERMINATOR' && d === 0) return this.looksObjectish(j + 1);
|
|
2069
|
+
}
|
|
2070
|
+
return false;
|
|
2071
|
+
};
|
|
2023
2072
|
if ((IMPLICIT_END.has(tag) && !logicalKeep) || (CALL_CLOSERS.has(tag) && newLine)) {
|
|
2024
2073
|
while (isImplicit(stackTop())) {
|
|
2025
2074
|
let [stackTag, , {sameLine, startsLine}] = stackTop();
|
|
2026
2075
|
if (inImplicitCall() && prevTag !== ',') {
|
|
2027
2076
|
endImplicitCall();
|
|
2028
|
-
} else if (inImplicitObject() && !isLogicalOp && sameLine && tag !== 'TERMINATOR' && prevTag !== ':'
|
|
2077
|
+
} else if (inImplicitObject() && !isLogicalOp && sameLine && tag !== 'TERMINATOR' && prevTag !== ':' &&
|
|
2078
|
+
!((tag === 'POST_IF' || tag === 'POST_UNLESS') && objectContinues(i + 1))) {
|
|
2029
2079
|
endImplicitObject();
|
|
2030
2080
|
} else if (inImplicitObject() && tag === 'TERMINATOR' && prevTag !== ',' && !(startsLine && this.looksObjectish(i + 1))) {
|
|
2031
2081
|
endImplicitObject();
|