rip-lang 3.7.4 → 3.8.8
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 +97 -1
- package/README.md +42 -34
- package/docs/RIP-INTERNALS.md +2 -4
- package/docs/RIP-LANG.md +150 -3
- package/docs/RIP-TYPES.md +1 -2
- package/docs/demo.html +342 -0
- package/docs/dist/rip-ui.min.js +516 -0
- package/docs/dist/rip-ui.min.js.br +0 -0
- package/docs/dist/rip.browser.js +285 -406
- package/docs/dist/rip.browser.min.js +166 -204
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/dist/ui.js +956 -0
- package/docs/dist/ui.min.js +2 -0
- package/docs/dist/ui.min.js.br +0 -0
- package/docs/dist/ui.rip +957 -0
- package/docs/dist/ui.rip.br +0 -0
- package/docs/examples.rip +180 -0
- package/docs/index.html +3 -1599
- package/docs/playground-app.html +1022 -0
- package/docs/playground-js.html +1645 -0
- package/docs/playground-rip-ui.html +1419 -0
- package/docs/playground-rip.html +1450 -0
- package/docs/rip-fav.svg +5 -0
- package/package.json +3 -3
- package/scripts/serve.js +3 -2
- package/src/browser.js +21 -5
- package/src/compiler.js +148 -221
- package/src/components.js +100 -95
- package/src/grammar/README.md +234 -0
- package/src/grammar/lunar.rip +2412 -0
- package/src/grammar/solar.rip +18 -4
- package/src/lexer.js +53 -24
- package/src/parser-rd.js +3242 -0
- package/src/parser.js +6 -5
- package/src/repl.js +24 -5
- package/docs/NOTES.md +0 -93
- package/docs/RIP-GUIDE.md +0 -698
- package/docs/RIP-REACTIVITY.md +0 -311
package/src/grammar/solar.rip
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
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'
|
|
13
14
|
|
|
14
15
|
VERSION = '1.5.0'
|
|
15
16
|
|
|
@@ -89,9 +90,11 @@ class Generator
|
|
|
89
90
|
# Build parser
|
|
90
91
|
@timing '💥 Total time', =>
|
|
91
92
|
@timing 'processGrammar' , => @processGrammar grammar # Process grammar rules
|
|
92
|
-
|
|
93
|
+
unless @options.rd
|
|
94
|
+
@timing 'buildLRAutomaton' , => @buildLRAutomaton() # Build LR(0) automaton
|
|
93
95
|
@timing 'processLookaheads', => @processLookaheads() # Compute FIRST/FOLLOW and assign lookaheads
|
|
94
|
-
|
|
96
|
+
unless @options.rd
|
|
97
|
+
@timing 'buildParseTable' , => @buildParseTable() # Build parse table with default actions
|
|
95
98
|
|
|
96
99
|
# ============================================================================
|
|
97
100
|
# Helper Functions
|
|
@@ -470,6 +473,7 @@ class Generator
|
|
|
470
473
|
|
|
471
474
|
# Assign FOLLOW sets to reduction items
|
|
472
475
|
_assignItemLookaheads!: ->
|
|
476
|
+
return unless @states
|
|
473
477
|
for state in @states
|
|
474
478
|
for item as state.reductions
|
|
475
479
|
follows = @types[item.rule.type]?.follows
|
|
@@ -653,6 +657,8 @@ class Generator
|
|
|
653
657
|
# Decoder reconstructs [{key: val}, ...] at runtime
|
|
654
658
|
"(()=>{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})()"
|
|
655
659
|
|
|
660
|
+
# Recursive descent generation — installed by Lunar (see lunar.rip)
|
|
661
|
+
|
|
656
662
|
# ============================================================================
|
|
657
663
|
# Runtime Parser
|
|
658
664
|
# ============================================================================
|
|
@@ -767,6 +773,8 @@ class Generator
|
|
|
767
773
|
# Exports
|
|
768
774
|
# ==============================================================================
|
|
769
775
|
|
|
776
|
+
installLunar Generator
|
|
777
|
+
|
|
770
778
|
export { Generator }
|
|
771
779
|
|
|
772
780
|
# ==============================================================================
|
|
@@ -798,6 +806,7 @@ if isRunAsScript
|
|
|
798
806
|
-v, --version Show version
|
|
799
807
|
-i, --info Show grammar information
|
|
800
808
|
-s, --sexpr Show grammar as s-expression
|
|
809
|
+
-r, --rd Generate recursive descent parser (parser-rd.js)
|
|
801
810
|
-c, --conflicts Show conflict details (use with --info)
|
|
802
811
|
-o, --output <file> Output file (default: parser.js)
|
|
803
812
|
|
|
@@ -835,7 +844,7 @@ if isRunAsScript
|
|
|
835
844
|
console.log " Resolution: #{conflict.resolution} (by default)"
|
|
836
845
|
|
|
837
846
|
# Parse command line
|
|
838
|
-
options = {help: false, version: false, info: false, sexpr: false, conflicts: false, output: 'parser.js'}
|
|
847
|
+
options = {help: false, version: false, info: false, sexpr: false, conflicts: false, rd: false, output: 'parser.js'}
|
|
839
848
|
grammarFile = null
|
|
840
849
|
i = 0
|
|
841
850
|
|
|
@@ -846,6 +855,7 @@ if isRunAsScript
|
|
|
846
855
|
when '-v', '--version' then options.version = true
|
|
847
856
|
when '-i', '--info' then options.info = true
|
|
848
857
|
when '-s', '--sexpr' then options.sexpr = true
|
|
858
|
+
when '-r', '--rd' then options.rd = true
|
|
849
859
|
when '-c', '--conflicts' then options.conflicts = true
|
|
850
860
|
when '-o', '--output' then options.output = process.argv[++i + 2]
|
|
851
861
|
else grammarFile = arg unless arg.startsWith('-')
|
|
@@ -906,7 +916,11 @@ if isRunAsScript
|
|
|
906
916
|
if options.info
|
|
907
917
|
showStats generator
|
|
908
918
|
else
|
|
909
|
-
|
|
919
|
+
if options.rd
|
|
920
|
+
options.output = 'parser-rd.js' if options.output is 'parser.js'
|
|
921
|
+
parserCode = generator.generateRD()
|
|
922
|
+
else
|
|
923
|
+
parserCode = generator.generate()
|
|
910
924
|
fs.writeFileSync options.output, parserCode
|
|
911
925
|
console.log "\nParser generated: #{options.output}"
|
|
912
926
|
|
package/src/lexer.js
CHANGED
|
@@ -220,7 +220,7 @@ let NEWLINE_RE = /^(?:\n[^\n\S]*)+/;
|
|
|
220
220
|
let COMMENT_RE = /^(\s*)###([^#][\s\S]*?)(?:###([^\n\S]*)|###$)|^((?:\s*#(?!##[^#]).*)+)/;
|
|
221
221
|
let CODE_RE = /^[-=]>/;
|
|
222
222
|
let REACTIVE_RE = /^(?:~[=>]|=!)/;
|
|
223
|
-
let STRING_START_RE = /^(?:'''|"""|'|")/;
|
|
223
|
+
let STRING_START_RE = /^(?:'''\\|"""\\|'''|"""|'|")/;
|
|
224
224
|
let STRING_SINGLE_RE = /^(?:[^\\']|\\[\s\S])*/;
|
|
225
225
|
let STRING_DOUBLE_RE = /^(?:[^\\"#$]|\\[\s\S]|\#(?!\{)|\$(?!\{))*/;
|
|
226
226
|
let HEREDOC_SINGLE_RE = /^(?:[^\\']|\\[\s\S]|'(?!''))*/;
|
|
@@ -627,10 +627,19 @@ export class Lexer {
|
|
|
627
627
|
// --------------------------------------------------------------------------
|
|
628
628
|
|
|
629
629
|
commentToken() {
|
|
630
|
+
// In render blocks, #word is an element ID, not a comment
|
|
631
|
+
if (this.inRenderBlock) {
|
|
632
|
+
if (/^#[a-zA-Z_]/.test(this.chunk)) {
|
|
633
|
+
let prev = this.prev();
|
|
634
|
+
if (prev && (prev[0] === 'IDENTIFIER' || prev[0] === 'PROPERTY'))
|
|
635
|
+
return 0; // after a tag (div#main) → let # become a token for rewriter
|
|
636
|
+
let m = /^#([a-zA-Z_][\w-]*)/.exec(this.chunk);
|
|
637
|
+
if (m) { this.emit('IDENTIFIER', 'div#' + m[1]); return m[0].length; }
|
|
638
|
+
}
|
|
639
|
+
if (/^\s+#[a-zA-Z_]/.test(this.chunk)) return 0; // let lineToken handle indentation first
|
|
640
|
+
}
|
|
630
641
|
let match = COMMENT_RE.exec(this.chunk);
|
|
631
642
|
if (!match) return 0;
|
|
632
|
-
// For now, consume the comment and discard it
|
|
633
|
-
// TODO: attach comments to adjacent tokens for source map support
|
|
634
643
|
return match[0].length;
|
|
635
644
|
}
|
|
636
645
|
|
|
@@ -779,6 +788,8 @@ export class Lexer {
|
|
|
779
788
|
if (!m) return 0;
|
|
780
789
|
|
|
781
790
|
let quote = m[0];
|
|
791
|
+
let raw = quote.length > 1 && quote.endsWith('\\');
|
|
792
|
+
let baseQuote = raw ? quote.slice(0, -1) : quote;
|
|
782
793
|
let prev = this.prev();
|
|
783
794
|
|
|
784
795
|
// Tag 'from' in import/export context
|
|
@@ -787,24 +798,24 @@ export class Lexer {
|
|
|
787
798
|
}
|
|
788
799
|
|
|
789
800
|
let regex;
|
|
790
|
-
switch (
|
|
801
|
+
switch (baseQuote) {
|
|
791
802
|
case "'": regex = STRING_SINGLE_RE; break;
|
|
792
803
|
case '"': regex = STRING_DOUBLE_RE; break;
|
|
793
804
|
case "'''": regex = HEREDOC_SINGLE_RE; break;
|
|
794
805
|
case '"""': regex = HEREDOC_DOUBLE_RE; break;
|
|
795
806
|
}
|
|
796
807
|
|
|
797
|
-
let {tokens: parts, index: end} = this.matchWithInterpolations(regex, quote);
|
|
798
|
-
let heredoc =
|
|
808
|
+
let {tokens: parts, index: end} = this.matchWithInterpolations(regex, quote, baseQuote);
|
|
809
|
+
let heredoc = baseQuote.length === 3;
|
|
799
810
|
|
|
800
811
|
// Heredoc indent processing
|
|
801
812
|
let indent = null;
|
|
802
813
|
if (heredoc) {
|
|
803
|
-
indent = this.processHeredocIndent(end,
|
|
814
|
+
indent = this.processHeredocIndent(end, baseQuote, parts);
|
|
804
815
|
}
|
|
805
816
|
|
|
806
817
|
// Merge interpolation tokens into the stream
|
|
807
|
-
this.mergeInterpolationTokens(parts, {quote, indent, endOffset: end});
|
|
818
|
+
this.mergeInterpolationTokens(parts, {quote: baseQuote, indent, endOffset: end, raw});
|
|
808
819
|
|
|
809
820
|
return end;
|
|
810
821
|
}
|
|
@@ -910,7 +921,7 @@ export class Lexer {
|
|
|
910
921
|
}
|
|
911
922
|
|
|
912
923
|
// Merge NEOSTRING/TOKENS into the real token stream
|
|
913
|
-
mergeInterpolationTokens(tokens, {quote, indent, endOffset}) {
|
|
924
|
+
mergeInterpolationTokens(tokens, {quote, indent, endOffset, raw}) {
|
|
914
925
|
if (tokens.length > 1) {
|
|
915
926
|
this.emit('STRING_START', '(', {len: quote?.length || 0, data: {quote}});
|
|
916
927
|
}
|
|
@@ -939,6 +950,12 @@ export class Lexer {
|
|
|
939
950
|
processed = processed.replace(/\n[^\S\n]*$/, '');
|
|
940
951
|
}
|
|
941
952
|
|
|
953
|
+
// Raw heredocs ('''\, """\): escape only recognized JS escape sequences
|
|
954
|
+
// so \n \t \u etc. stay literal, but \s \w \d pass through unchanged
|
|
955
|
+
if (raw) {
|
|
956
|
+
processed = processed.replace(/\\([nrtbfv0\\'"`xu])/g, '\\\\$1');
|
|
957
|
+
}
|
|
958
|
+
|
|
942
959
|
this.emit('STRING', `"${processed}"`, {len: val.length, data: {quote}});
|
|
943
960
|
}
|
|
944
961
|
}
|
|
@@ -996,8 +1013,8 @@ export class Lexer {
|
|
|
996
1013
|
let end = index + flags.length;
|
|
997
1014
|
|
|
998
1015
|
if (parts.length === 1 || !parts.some(p => p[0] === 'TOKENS')) {
|
|
999
|
-
// Simple heregex (no interpolations)
|
|
1000
|
-
let body = parts[0]?.[1] || '';
|
|
1016
|
+
// Simple heregex (no interpolations) — escape unescaped / for regex literal
|
|
1017
|
+
let body = (parts[0]?.[1] || '').replace(/(?<!\\)\//g, '\\/');
|
|
1001
1018
|
this.emit('REGEX', `/${body}/${flags}`, {len: end, data: {delimiter: '///', heregex: {flags}}});
|
|
1002
1019
|
} else {
|
|
1003
1020
|
// Complex heregex with interpolations
|
|
@@ -1398,7 +1415,7 @@ export class Lexer {
|
|
|
1398
1415
|
return isHtmlTag(name) || isComponent(name);
|
|
1399
1416
|
};
|
|
1400
1417
|
|
|
1401
|
-
let
|
|
1418
|
+
let startsWithTag = (tokens, i) => {
|
|
1402
1419
|
let j = i;
|
|
1403
1420
|
while (j > 0) {
|
|
1404
1421
|
let pt = tokens[j - 1][0];
|
|
@@ -1407,7 +1424,7 @@ export class Lexer {
|
|
|
1407
1424
|
}
|
|
1408
1425
|
j--;
|
|
1409
1426
|
}
|
|
1410
|
-
return tokens[j] && tokens[j][0] === 'IDENTIFIER' &&
|
|
1427
|
+
return tokens[j] && tokens[j][0] === 'IDENTIFIER' && isTemplateTag(tokens[j][1]);
|
|
1411
1428
|
};
|
|
1412
1429
|
|
|
1413
1430
|
this.scanTokens(function(token, i, tokens) {
|
|
@@ -1499,7 +1516,7 @@ export class Lexer {
|
|
|
1499
1516
|
if (tag === 'IDENTIFIER' || tag === 'PROPERTY') {
|
|
1500
1517
|
let next = tokens[i + 1];
|
|
1501
1518
|
let nextNext = tokens[i + 2];
|
|
1502
|
-
if (next && next[0] === '#' && nextNext && nextNext[0] === 'PROPERTY') {
|
|
1519
|
+
if (next && next[0] === '#' && nextNext && (nextNext[0] === 'PROPERTY' || nextNext[0] === 'IDENTIFIER')) {
|
|
1503
1520
|
token[1] = token[1] + '#' + nextNext[1];
|
|
1504
1521
|
if (nextNext.spaced) token.spaced = true;
|
|
1505
1522
|
tokens.splice(i + 1, 2);
|
|
@@ -1585,14 +1602,16 @@ export class Lexer {
|
|
|
1585
1602
|
}
|
|
1586
1603
|
|
|
1587
1604
|
let isTemplateElement = false;
|
|
1605
|
+
let prevTag = i > 0 ? tokens[i - 1][0] : null;
|
|
1606
|
+
let isAfterControlFlow = prevTag === 'IF' || prevTag === 'UNLESS' || prevTag === 'WHILE' || prevTag === 'UNTIL' || prevTag === 'WHEN';
|
|
1588
1607
|
|
|
1589
|
-
if (tag === 'IDENTIFIER' && isTemplateTag(token[1])) {
|
|
1608
|
+
if (tag === 'IDENTIFIER' && isTemplateTag(token[1]) && !isAfterControlFlow) {
|
|
1590
1609
|
isTemplateElement = true;
|
|
1591
1610
|
} else if (tag === 'PROPERTY' || tag === 'STRING' || tag === 'CALL_END' || tag === ')') {
|
|
1592
|
-
isTemplateElement =
|
|
1611
|
+
isTemplateElement = startsWithTag(tokens, i);
|
|
1593
1612
|
}
|
|
1594
1613
|
else if (tag === 'IDENTIFIER' && i > 1 && tokens[i - 1][0] === '...') {
|
|
1595
|
-
if (
|
|
1614
|
+
if (startsWithTag(tokens, i)) {
|
|
1596
1615
|
let commaToken = gen(',', ',', token);
|
|
1597
1616
|
let arrowToken = gen('->', '->', token);
|
|
1598
1617
|
arrowToken.newLine = true;
|
|
@@ -1602,13 +1621,23 @@ export class Lexer {
|
|
|
1602
1621
|
}
|
|
1603
1622
|
|
|
1604
1623
|
if (isTemplateElement) {
|
|
1605
|
-
let
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1624
|
+
let isClassOrIdTail = tag === 'PROPERTY' && i > 0 && (tokens[i - 1][0] === '.' || tokens[i - 1][0] === '#');
|
|
1625
|
+
if ((tag === 'IDENTIFIER' && isTemplateTag(token[1])) || isClassOrIdTail) {
|
|
1626
|
+
// Bare tag or tag.class/tag#id (no other args): inject CALL_START -> and manage CALL_END
|
|
1627
|
+
let callStartToken = gen('CALL_START', '(', token);
|
|
1628
|
+
let arrowToken = gen('->', '->', token);
|
|
1629
|
+
arrowToken.newLine = true;
|
|
1630
|
+
tokens.splice(i + 1, 0, callStartToken, arrowToken);
|
|
1631
|
+
pendingCallEnds.push(currentIndent + 1);
|
|
1632
|
+
return 3;
|
|
1633
|
+
} else {
|
|
1634
|
+
// Tag with args: inject , -> (call wrapping handled by addImplicitBracesAndParens)
|
|
1635
|
+
let commaToken = gen(',', ',', token);
|
|
1636
|
+
let arrowToken = gen('->', '->', token);
|
|
1637
|
+
arrowToken.newLine = true;
|
|
1638
|
+
tokens.splice(i + 1, 0, commaToken, arrowToken);
|
|
1639
|
+
return 3;
|
|
1640
|
+
}
|
|
1612
1641
|
}
|
|
1613
1642
|
}
|
|
1614
1643
|
|