rip-lang 2.5.1 → 2.7.1
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 +33 -10
- package/README.md +133 -289
- package/docs/BROWSER.md +8 -11
- package/docs/GUIDE.md +101 -923
- package/docs/INTERNALS.md +1 -1
- package/docs/PHILOSOPHY.md +22 -77
- package/docs/REACTIVITY.md +288 -0
- package/docs/WHY-YES-RIP.md +39 -177
- package/docs/dist/rip.browser.js +605 -2453
- package/docs/dist/rip.browser.min.js +283 -359
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/repl.html +94 -437
- package/package.json +4 -1
- package/scripts/serve.js +2 -0
- package/src/compiler.js +73 -2160
- package/src/grammar/grammar.rip +22 -57
- package/src/lexer.js +11 -333
- package/src/parser.js +220 -223
- package/src/repl.js +202 -128
- package/src/tags.js +0 -62
|
Binary file
|
package/docs/repl.html
CHANGED
|
@@ -108,10 +108,6 @@
|
|
|
108
108
|
background: #1e1e1e;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
.tab[data-tab="demo"].active {
|
|
112
|
-
border-bottom-color: #e94560;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
111
|
.content {
|
|
116
112
|
flex: 1;
|
|
117
113
|
display: flex;
|
|
@@ -328,139 +324,6 @@
|
|
|
328
324
|
}
|
|
329
325
|
|
|
330
326
|
|
|
331
|
-
/* Demo Pane Styles */
|
|
332
|
-
.demo-output {
|
|
333
|
-
flex: 1;
|
|
334
|
-
min-height: 0;
|
|
335
|
-
padding: 20px;
|
|
336
|
-
background: #1a1a2e;
|
|
337
|
-
overflow: auto;
|
|
338
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
.demo-output h2 {
|
|
342
|
-
color: #00d4ff;
|
|
343
|
-
font-size: 1.2rem;
|
|
344
|
-
margin-bottom: 1rem;
|
|
345
|
-
padding-bottom: 0.5rem;
|
|
346
|
-
border-bottom: 1px solid #3e3e42;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
.demo-output .demo-section {
|
|
350
|
-
margin-bottom: 1.5rem;
|
|
351
|
-
padding: 1rem;
|
|
352
|
-
background: #0f3460;
|
|
353
|
-
border-radius: 8px;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
.demo-output .counter {
|
|
357
|
-
display: flex;
|
|
358
|
-
align-items: center;
|
|
359
|
-
gap: 1rem;
|
|
360
|
-
justify-content: center;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
.demo-output button {
|
|
364
|
-
background: #e94560;
|
|
365
|
-
color: white;
|
|
366
|
-
border: none;
|
|
367
|
-
padding: 10px 20px;
|
|
368
|
-
font-size: 1.2rem;
|
|
369
|
-
border-radius: 5px;
|
|
370
|
-
cursor: pointer;
|
|
371
|
-
min-width: 50px;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
.demo-output button:hover {
|
|
375
|
-
background: #ff6b6b;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
.demo-output .value {
|
|
379
|
-
font-size: 2rem;
|
|
380
|
-
font-weight: bold;
|
|
381
|
-
color: #ff6b6b;
|
|
382
|
-
min-width: 60px;
|
|
383
|
-
text-align: center;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
.demo-output input[type="text"],
|
|
387
|
-
.demo-output input[type="number"] {
|
|
388
|
-
background: #16213e;
|
|
389
|
-
border: 1px solid #3e3e42;
|
|
390
|
-
color: #d4d4d4;
|
|
391
|
-
padding: 8px 12px;
|
|
392
|
-
border-radius: 4px;
|
|
393
|
-
font-size: 14px;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
.demo-output .greeting {
|
|
397
|
-
font-size: 1.5rem;
|
|
398
|
-
color: #4ec9b0;
|
|
399
|
-
margin-top: 0.5rem;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
.demo-output .derived {
|
|
403
|
-
margin-top: 1rem;
|
|
404
|
-
color: #858585;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
.demo-output .derived span {
|
|
408
|
-
color: #ce9178;
|
|
409
|
-
font-weight: bold;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/* Component-generated elements */
|
|
413
|
-
.demo-output .counter {
|
|
414
|
-
display: flex;
|
|
415
|
-
align-items: center;
|
|
416
|
-
gap: 1rem;
|
|
417
|
-
justify-content: center;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
.demo-output .greeter input {
|
|
421
|
-
margin-bottom: 0.5rem;
|
|
422
|
-
width: 200px;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
.demo-output .greeter .name {
|
|
426
|
-
color: #4ec9b0;
|
|
427
|
-
font-weight: bold;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
.demo-output .calculator .row {
|
|
431
|
-
margin: 0.5rem 0;
|
|
432
|
-
display: flex;
|
|
433
|
-
align-items: center;
|
|
434
|
-
gap: 0.5rem;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
.demo-output .calculator input {
|
|
438
|
-
width: 80px;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
.demo-output .calculator .result {
|
|
442
|
-
color: #ce9178;
|
|
443
|
-
font-weight: bold;
|
|
444
|
-
font-size: 1.2rem;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
.demo-error {
|
|
448
|
-
background: #3c1618;
|
|
449
|
-
color: #f48771;
|
|
450
|
-
padding: 1rem;
|
|
451
|
-
border-radius: 4px;
|
|
452
|
-
font-family: monospace;
|
|
453
|
-
white-space: pre-wrap;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
.run-btn-primary {
|
|
457
|
-
background: #e94560 !important;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
.run-btn-primary:hover {
|
|
461
|
-
background: #ff6b6b !important;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
327
|
/* Scrollbar styling */
|
|
465
328
|
::-webkit-scrollbar {
|
|
466
329
|
width: 10px;
|
|
@@ -527,8 +390,7 @@
|
|
|
527
390
|
</div>
|
|
528
391
|
|
|
529
392
|
<div class="tabs">
|
|
530
|
-
<button class="tab active" data-tab="
|
|
531
|
-
<button class="tab" data-tab="compiler">Live Compiler</button>
|
|
393
|
+
<button class="tab active" data-tab="compiler">Live Compiler</button>
|
|
532
394
|
<button class="tab" data-tab="repl">REPL Console</button>
|
|
533
395
|
</div>
|
|
534
396
|
|
|
@@ -549,102 +411,8 @@
|
|
|
549
411
|
</div>
|
|
550
412
|
</div>
|
|
551
413
|
|
|
552
|
-
<!-- Demo Pane -->
|
|
553
|
-
<div class="pane active" id="demo-pane">
|
|
554
|
-
<div class="compiler-container">
|
|
555
|
-
<div class="editor-pane" id="demo-editor-left">
|
|
556
|
-
<div class="pane-header">
|
|
557
|
-
<span>Rip Source</span>
|
|
558
|
-
<div class="pane-header-buttons">
|
|
559
|
-
<button id="demo-reset-btn" title="Reset to default demo">Reset</button>
|
|
560
|
-
<button class="run-btn-primary" id="demo-run-btn" title="Run the demo (Cmd+Enter)">Run</button>
|
|
561
|
-
</div>
|
|
562
|
-
</div>
|
|
563
|
-
<textarea class="editor" id="demo-editor" spellcheck="false"># ============================================
|
|
564
|
-
# Rip Components & Templates Demo
|
|
565
|
-
# ============================================
|
|
566
|
-
|
|
567
|
-
# A counter component with Pug-style template syntax
|
|
568
|
-
# A counter component with Pug-style template syntax
|
|
569
|
-
component Counter
|
|
570
|
-
@name = "Hello" # prop with default
|
|
571
|
-
@initial = 0 # prop with default
|
|
572
|
-
count := @initial
|
|
573
|
-
dozen ~= count * 12
|
|
574
|
-
inc: -> count += 1
|
|
575
|
-
dec: -> count -= 1
|
|
576
|
-
render
|
|
577
|
-
.counter
|
|
578
|
-
i "#{@name}, did you know that"
|
|
579
|
-
button @click: @dec, "−"
|
|
580
|
-
span.value count
|
|
581
|
-
button @click: @inc, "+"
|
|
582
|
-
i "dozen is #{dozen} items?"
|
|
583
|
-
|
|
584
|
-
Counter.new(name: "Señor", initial: 5).mount "#counter-mount"
|
|
585
|
-
|
|
586
|
-
# ============================================
|
|
587
|
-
# Component with Two-Way Binding
|
|
588
|
-
# ============================================
|
|
589
|
-
|
|
590
|
-
component Greeter
|
|
591
|
-
name = "World"
|
|
592
|
-
render
|
|
593
|
-
.greeter
|
|
594
|
-
input value <=> name
|
|
595
|
-
.greeting
|
|
596
|
-
span "Hello, "
|
|
597
|
-
span.name name
|
|
598
|
-
span "!"
|
|
599
|
-
|
|
600
|
-
Greeter.new().mount "#greeter-mount"
|
|
601
|
-
|
|
602
|
-
# ============================================
|
|
603
|
-
# Derived Values (~= always equal)
|
|
604
|
-
# ============================================
|
|
605
|
-
|
|
606
|
-
component Calculator
|
|
607
|
-
num := 5
|
|
608
|
-
doubled ~= num * 2
|
|
609
|
-
squared ~= num * num
|
|
610
|
-
render
|
|
611
|
-
.calculator
|
|
612
|
-
.row
|
|
613
|
-
label "Number: "
|
|
614
|
-
input type: "number", value <=> num
|
|
615
|
-
.row
|
|
616
|
-
span "Doubled: "
|
|
617
|
-
span.result doubled
|
|
618
|
-
.row
|
|
619
|
-
span "Squared: "
|
|
620
|
-
span.result squared
|
|
621
|
-
|
|
622
|
-
Calculator.new().mount "#calc-mount"
|
|
623
|
-
</textarea>
|
|
624
|
-
</div>
|
|
625
|
-
|
|
626
|
-
<div class="resizer" id="demo-resizer"></div>
|
|
627
|
-
|
|
628
|
-
<div class="editor-pane" id="demo-editor-right">
|
|
629
|
-
<div class="pane-header">
|
|
630
|
-
<span>Live Output</span>
|
|
631
|
-
</div>
|
|
632
|
-
<div class="demo-output" id="demo-output">
|
|
633
|
-
<h2>Counter Component</h2>
|
|
634
|
-
<div class="demo-section" id="counter-mount"></div>
|
|
635
|
-
|
|
636
|
-
<h2>Greeter with Two-Way Binding</h2>
|
|
637
|
-
<div class="demo-section" id="greeter-mount"></div>
|
|
638
|
-
|
|
639
|
-
<h2>Calculator with Derived Values</h2>
|
|
640
|
-
<div class="demo-section" id="calc-mount"></div>
|
|
641
|
-
</div>
|
|
642
|
-
</div>
|
|
643
|
-
</div>
|
|
644
|
-
</div>
|
|
645
|
-
|
|
646
414
|
<!-- Compiler Pane -->
|
|
647
|
-
<div class="pane" id="compiler-pane">
|
|
415
|
+
<div class="pane active" id="compiler-pane">
|
|
648
416
|
<div class="compiler-container">
|
|
649
417
|
<div class="editor-pane" id="editor-left">
|
|
650
418
|
<div class="pane-header">
|
|
@@ -715,6 +483,90 @@ console.log "Domain:", domain</textarea>
|
|
|
715
483
|
document.querySelector('.subtitle').textContent =
|
|
716
484
|
`Interactive environment • Version ${VERSION} • Built ${localBuildDate}`;
|
|
717
485
|
|
|
486
|
+
// ========================================================================
|
|
487
|
+
// DOM Element References (must be before functions that use them)
|
|
488
|
+
// ========================================================================
|
|
489
|
+
|
|
490
|
+
const editor = document.getElementById('editor');
|
|
491
|
+
const output = document.getElementById('output');
|
|
492
|
+
const showSexpBtn = document.getElementById('show-sexp');
|
|
493
|
+
const showTokensBtn = document.getElementById('show-tokens');
|
|
494
|
+
const replOutput = document.getElementById('repl-output');
|
|
495
|
+
const replInput = document.getElementById('repl-input');
|
|
496
|
+
const promptSpan = document.getElementById('prompt');
|
|
497
|
+
const resizer = document.getElementById('resizer');
|
|
498
|
+
const leftPane = document.getElementById('editor-left');
|
|
499
|
+
const rightPane = document.getElementById('editor-right');
|
|
500
|
+
|
|
501
|
+
// ========================================================================
|
|
502
|
+
// Compiler Implementation (must be before tab switching)
|
|
503
|
+
// ========================================================================
|
|
504
|
+
|
|
505
|
+
// Track toggle state
|
|
506
|
+
let showSexp = false;
|
|
507
|
+
let showTokens = false;
|
|
508
|
+
|
|
509
|
+
// Strip utility helper functions for cleaner display
|
|
510
|
+
function stripHelpers(code) {
|
|
511
|
+
// Remove helper function definitions (slice, modulo, toSearchable)
|
|
512
|
+
code = code.replace(/^const slice = \[\]\.slice;\n/m, '');
|
|
513
|
+
code = code.replace(/^const modulo = \(n, d\) => \{[^}]+\};\n/m, '');
|
|
514
|
+
code = code.replace(/^const toSearchable = \(v, allowNewlines\) => \{[\s\S]+?\n\};\n/m, '');
|
|
515
|
+
|
|
516
|
+
// Remove empty lines at the start
|
|
517
|
+
code = code.replace(/^\n+/, '');
|
|
518
|
+
|
|
519
|
+
return code;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Simple syntax highlighter for JavaScript
|
|
523
|
+
function highlightJS(code) {
|
|
524
|
+
return code
|
|
525
|
+
// Keywords
|
|
526
|
+
.replace(/\\b(def|function|async|const|let|var|if|else|return|while|for|class|extends|super|new|this|try|catch|finally|throw|switch|case|default|break|continue)\\b/g,
|
|
527
|
+
'<span class="hl-keyword">$1</span>')
|
|
528
|
+
// Strings
|
|
529
|
+
.replace(/("(?:[^"\\\\]|\\\\.)*"|'(?:[^'\\\\]|\\\\.)*'|`(?:[^`\\\\]|\\\\.)*`)/g,
|
|
530
|
+
'<span class="hl-string">$1</span>')
|
|
531
|
+
// Numbers
|
|
532
|
+
.replace(/\\b(\\d+\\.?\\d*|0x[0-9a-fA-F]+|0b[01]+|0o[0-7]+)\\b/g,
|
|
533
|
+
'<span class="hl-number">$1</span>')
|
|
534
|
+
// Comments
|
|
535
|
+
.replace(/(\/\/.*$|\/\*[\s\S]*?\*\/)/gm,
|
|
536
|
+
'<span class="hl-comment">$1</span>')
|
|
537
|
+
// Function names (simple: word before parenthesis)
|
|
538
|
+
.replace(/\b([a-zA-Z_$][a-zA-Z0-9_$]*)(?=\s*\()/g,
|
|
539
|
+
'<span class="hl-function">$1</span>');
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function compileCode() {
|
|
543
|
+
try {
|
|
544
|
+
const source = editor.value;
|
|
545
|
+
const result = compile(source);
|
|
546
|
+
|
|
547
|
+
let outputText = '';
|
|
548
|
+
|
|
549
|
+
if (showTokens) {
|
|
550
|
+
result.tokens.forEach(t => {
|
|
551
|
+
outputText += `${t[0].padEnd(12)} ${JSON.stringify(t[1])}\n`;
|
|
552
|
+
});
|
|
553
|
+
outputText += '\n';
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (showSexp) {
|
|
557
|
+
outputText += window.toSexpr ? window.toSexpr(result.sexpr, 0, true) : JSON.stringify(result.sexpr, null, 1);
|
|
558
|
+
outputText += '\n\n';
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// JavaScript output with syntax highlighting (strip helpers for cleaner display)
|
|
562
|
+
const cleanCode = stripHelpers(result.code);
|
|
563
|
+
const highlightedCode = highlightJS(cleanCode);
|
|
564
|
+
output.innerHTML = outputText.replace(/</g, '<').replace(/>/g, '>') + highlightedCode;
|
|
565
|
+
} catch (error) {
|
|
566
|
+
output.textContent = `Error: ${error.message}`;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
718
570
|
// ========================================================================
|
|
719
571
|
// Tab Switching
|
|
720
572
|
// ========================================================================
|
|
@@ -733,10 +585,7 @@ console.log "Domain:", domain</textarea>
|
|
|
733
585
|
|
|
734
586
|
// Focus appropriate input and run setup
|
|
735
587
|
if (tabName === 'repl') {
|
|
736
|
-
|
|
737
|
-
} else if (tabName === 'demo') {
|
|
738
|
-
// Auto-run demo when switching to demo tab
|
|
739
|
-
setTimeout(() => runDemo(), 50);
|
|
588
|
+
replInput.focus();
|
|
740
589
|
} else if (tabName === 'compiler') {
|
|
741
590
|
compileCode(); // Update compiler output
|
|
742
591
|
}
|
|
@@ -749,31 +598,27 @@ console.log "Domain:", domain</textarea>
|
|
|
749
598
|
// Handle browser back/forward
|
|
750
599
|
window.addEventListener('hashchange', () => {
|
|
751
600
|
const hash = window.location.hash.slice(1);
|
|
752
|
-
if (['
|
|
601
|
+
if (['compiler', 'repl'].includes(hash)) {
|
|
753
602
|
switchToTab(hash);
|
|
754
603
|
}
|
|
755
604
|
});
|
|
756
605
|
|
|
757
|
-
// Initialize from URL hash on page load (default to
|
|
606
|
+
// Initialize from URL hash on page load (default to compiler)
|
|
758
607
|
const initialTab = window.location.hash.slice(1);
|
|
759
|
-
if (['
|
|
608
|
+
if (['compiler', 'repl'].includes(initialTab)) {
|
|
760
609
|
switchToTab(initialTab);
|
|
761
610
|
} else {
|
|
762
|
-
// No hash -
|
|
763
|
-
|
|
611
|
+
// No hash - compiler is default
|
|
612
|
+
compileCode();
|
|
764
613
|
}
|
|
765
614
|
|
|
766
|
-
// Show page now that everything is initialized (prevents
|
|
615
|
+
// Show page now that everything is initialized (prevents flicker)
|
|
767
616
|
document.body.classList.add('ready');
|
|
768
617
|
|
|
769
618
|
// ========================================================================
|
|
770
619
|
// Resizable Panes (Live Compiler)
|
|
771
620
|
// ========================================================================
|
|
772
621
|
|
|
773
|
-
const resizer = document.getElementById('resizer');
|
|
774
|
-
const leftPane = document.getElementById('editor-left');
|
|
775
|
-
const rightPane = document.getElementById('editor-right');
|
|
776
|
-
|
|
777
622
|
if (resizer && leftPane && rightPane) {
|
|
778
623
|
let isResizing = false;
|
|
779
624
|
|
|
@@ -814,9 +659,6 @@ console.log "Domain:", domain</textarea>
|
|
|
814
659
|
let replHistory = [];
|
|
815
660
|
let historyIndex = -1;
|
|
816
661
|
let replBuffer = '';
|
|
817
|
-
const replOutput = document.getElementById('repl-output');
|
|
818
|
-
const replInput = document.getElementById('repl-input');
|
|
819
|
-
const promptSpan = document.getElementById('prompt');
|
|
820
662
|
|
|
821
663
|
// Create isolated iframe context for REPL (like vm.createContext in Node)
|
|
822
664
|
const iframe = document.createElement('iframe');
|
|
@@ -1018,79 +860,9 @@ console.log "Domain:", domain</textarea>
|
|
|
1018
860
|
});
|
|
1019
861
|
|
|
1020
862
|
// ========================================================================
|
|
1021
|
-
// Compiler
|
|
863
|
+
// Compiler Event Handlers
|
|
1022
864
|
// ========================================================================
|
|
1023
865
|
|
|
1024
|
-
const editor = document.getElementById('editor');
|
|
1025
|
-
const output = document.getElementById('output');
|
|
1026
|
-
const showSexpBtn = document.getElementById('show-sexp');
|
|
1027
|
-
const showTokensBtn = document.getElementById('show-tokens');
|
|
1028
|
-
|
|
1029
|
-
// Track toggle state
|
|
1030
|
-
let showSexp = false;
|
|
1031
|
-
let showTokens = false;
|
|
1032
|
-
|
|
1033
|
-
// Strip utility helper functions for cleaner display
|
|
1034
|
-
function stripHelpers(code) {
|
|
1035
|
-
// Remove helper function definitions (slice, modulo, toSearchable)
|
|
1036
|
-
code = code.replace(/^const slice = \[\]\.slice;\n/m, '');
|
|
1037
|
-
code = code.replace(/^const modulo = \(n, d\) => \{[^}]+\};\n/m, '');
|
|
1038
|
-
code = code.replace(/^const toSearchable = \(v, allowNewlines\) => \{[\s\S]+?\n\};\n/m, '');
|
|
1039
|
-
|
|
1040
|
-
// Remove empty lines at the start
|
|
1041
|
-
code = code.replace(/^\n+/, '');
|
|
1042
|
-
|
|
1043
|
-
return code;
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
// Simple syntax highlighter for JavaScript
|
|
1047
|
-
function highlightJS(code) {
|
|
1048
|
-
return code
|
|
1049
|
-
// Keywords
|
|
1050
|
-
.replace(/\\b(def|function|async|const|let|var|if|else|return|while|for|class|extends|super|new|this|try|catch|finally|throw|switch|case|default|break|continue)\\b/g,
|
|
1051
|
-
'<span class="hl-keyword">$1</span>')
|
|
1052
|
-
// Strings
|
|
1053
|
-
.replace(/("(?:[^"\\\\]|\\\\.)*"|'(?:[^'\\\\]|\\\\.)*'|`(?:[^`\\\\]|\\\\.)*`)/g,
|
|
1054
|
-
'<span class="hl-string">$1</span>')
|
|
1055
|
-
// Numbers
|
|
1056
|
-
.replace(/\\b(\\d+\\.?\\d*|0x[0-9a-fA-F]+|0b[01]+|0o[0-7]+)\\b/g,
|
|
1057
|
-
'<span class="hl-number">$1</span>')
|
|
1058
|
-
// Comments
|
|
1059
|
-
.replace(/(\/\/.*$|\/\*[\s\S]*?\*\/)/gm,
|
|
1060
|
-
'<span class="hl-comment">$1</span>')
|
|
1061
|
-
// Function names (simple: word before parenthesis)
|
|
1062
|
-
.replace(/\b([a-zA-Z_$][a-zA-Z0-9_$]*)(?=\s*\()/g,
|
|
1063
|
-
'<span class="hl-function">$1</span>');
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
function compileCode() {
|
|
1067
|
-
try {
|
|
1068
|
-
const source = editor.value;
|
|
1069
|
-
const result = compile(source);
|
|
1070
|
-
|
|
1071
|
-
let outputText = '';
|
|
1072
|
-
|
|
1073
|
-
if (showTokens) {
|
|
1074
|
-
result.tokens.forEach(t => {
|
|
1075
|
-
outputText += `${t[0].padEnd(12)} ${JSON.stringify(t[1])}\n`;
|
|
1076
|
-
});
|
|
1077
|
-
outputText += '\n';
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
if (showSexp) {
|
|
1081
|
-
outputText += window.toSexpr ? window.toSexpr(result.sexpr, 0, true) : JSON.stringify(result.sexpr, null, 1);
|
|
1082
|
-
outputText += '\n\n';
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
// JavaScript output with syntax highlighting (strip helpers for cleaner display)
|
|
1086
|
-
const cleanCode = stripHelpers(result.code);
|
|
1087
|
-
const highlightedCode = highlightJS(cleanCode);
|
|
1088
|
-
output.innerHTML = outputText.replace(/</g, '<').replace(/>/g, '>') + highlightedCode;
|
|
1089
|
-
} catch (error) {
|
|
1090
|
-
output.textContent = `Error: ${error.message}`;
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
866
|
// Auto-compile on changes
|
|
1095
867
|
editor.addEventListener('input', compileCode);
|
|
1096
868
|
|
|
@@ -1153,121 +925,6 @@ console.log "Domain:", domain</textarea>
|
|
|
1153
925
|
|
|
1154
926
|
// Initial compile
|
|
1155
927
|
compileCode();
|
|
1156
|
-
|
|
1157
|
-
// ========================================================================
|
|
1158
|
-
// Live Demo Implementation
|
|
1159
|
-
// ========================================================================
|
|
1160
|
-
|
|
1161
|
-
const demoEditor = document.getElementById('demo-editor');
|
|
1162
|
-
const demoOutput = document.getElementById('demo-output');
|
|
1163
|
-
const demoRunBtn = document.getElementById('demo-run-btn');
|
|
1164
|
-
const demoResetBtn = document.getElementById('demo-reset-btn');
|
|
1165
|
-
const demoResizer = document.getElementById('demo-resizer');
|
|
1166
|
-
const demoLeftPane = document.getElementById('demo-editor-left');
|
|
1167
|
-
const demoRightPane = document.getElementById('demo-editor-right');
|
|
1168
|
-
|
|
1169
|
-
// Store default demo code
|
|
1170
|
-
const defaultDemoCode = demoEditor.value;
|
|
1171
|
-
|
|
1172
|
-
// Store default demo HTML
|
|
1173
|
-
const defaultDemoHTML = demoOutput.innerHTML;
|
|
1174
|
-
|
|
1175
|
-
// Demo resizer
|
|
1176
|
-
if (demoResizer && demoLeftPane && demoRightPane) {
|
|
1177
|
-
let isDemoResizing = false;
|
|
1178
|
-
|
|
1179
|
-
demoResizer.addEventListener('mousedown', (e) => {
|
|
1180
|
-
isDemoResizing = true;
|
|
1181
|
-
document.body.style.cursor = 'col-resize';
|
|
1182
|
-
e.preventDefault();
|
|
1183
|
-
});
|
|
1184
|
-
|
|
1185
|
-
document.addEventListener('mousemove', (e) => {
|
|
1186
|
-
if (!isDemoResizing) return;
|
|
1187
|
-
|
|
1188
|
-
const container = demoResizer.parentElement;
|
|
1189
|
-
const containerRect = container.getBoundingClientRect();
|
|
1190
|
-
const offsetX = e.clientX - containerRect.left;
|
|
1191
|
-
const containerWidth = containerRect.width;
|
|
1192
|
-
|
|
1193
|
-
let leftPercent = (offsetX / containerWidth) * 100;
|
|
1194
|
-
leftPercent = Math.max(20, Math.min(80, leftPercent));
|
|
1195
|
-
|
|
1196
|
-
demoLeftPane.style.flexBasis = `${leftPercent}%`;
|
|
1197
|
-
demoRightPane.style.flexBasis = `${100 - leftPercent}%`;
|
|
1198
|
-
});
|
|
1199
|
-
|
|
1200
|
-
document.addEventListener('mouseup', () => {
|
|
1201
|
-
if (isDemoResizing) {
|
|
1202
|
-
isDemoResizing = false;
|
|
1203
|
-
document.body.style.cursor = 'default';
|
|
1204
|
-
}
|
|
1205
|
-
});
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
// Create isolated iframe for demo execution
|
|
1209
|
-
let demoFrame = null;
|
|
1210
|
-
|
|
1211
|
-
function runDemo() {
|
|
1212
|
-
try {
|
|
1213
|
-
const source = demoEditor.value;
|
|
1214
|
-
const result = compile(source);
|
|
1215
|
-
let js = result.code;
|
|
1216
|
-
|
|
1217
|
-
// Reset the demo output HTML
|
|
1218
|
-
demoOutput.innerHTML = defaultDemoHTML;
|
|
1219
|
-
|
|
1220
|
-
// Remove any existing demo iframe
|
|
1221
|
-
if (demoFrame) {
|
|
1222
|
-
demoFrame.remove();
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
// Create fresh iframe for isolated execution
|
|
1226
|
-
demoFrame = document.createElement('iframe');
|
|
1227
|
-
demoFrame.style.display = 'none';
|
|
1228
|
-
document.body.appendChild(demoFrame);
|
|
1229
|
-
const demoContext = demoFrame.contentWindow;
|
|
1230
|
-
|
|
1231
|
-
// Create a proxy document that redirects to demoOutput
|
|
1232
|
-
const proxyDocument = {
|
|
1233
|
-
getElementById: (id) => demoOutput.querySelector(`#${id}`),
|
|
1234
|
-
querySelector: (sel) => demoOutput.querySelector(sel),
|
|
1235
|
-
querySelectorAll: (sel) => demoOutput.querySelectorAll(sel),
|
|
1236
|
-
createElement: (tag) => document.createElement(tag),
|
|
1237
|
-
createTextNode: (text) => document.createTextNode(text)
|
|
1238
|
-
};
|
|
1239
|
-
|
|
1240
|
-
// Wrap the code to use our proxy document
|
|
1241
|
-
const wrappedJs = `
|
|
1242
|
-
const document = arguments[0];
|
|
1243
|
-
const console = arguments[1];
|
|
1244
|
-
${js}
|
|
1245
|
-
`;
|
|
1246
|
-
|
|
1247
|
-
// Execute with injected document proxy
|
|
1248
|
-
new Function(wrappedJs).call(demoContext, proxyDocument, console);
|
|
1249
|
-
|
|
1250
|
-
} catch (error) {
|
|
1251
|
-
demoOutput.innerHTML = `<div class="demo-error">Error: ${error.message}</div>`;
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
// Run button
|
|
1256
|
-
demoRunBtn.addEventListener('click', runDemo);
|
|
1257
|
-
|
|
1258
|
-
// Reset button
|
|
1259
|
-
demoResetBtn.addEventListener('click', () => {
|
|
1260
|
-
demoEditor.value = defaultDemoCode;
|
|
1261
|
-
runDemo();
|
|
1262
|
-
});
|
|
1263
|
-
|
|
1264
|
-
// Keyboard shortcut for demo (Cmd/Ctrl+Enter when demo tab is active)
|
|
1265
|
-
demoEditor.addEventListener('keydown', (e) => {
|
|
1266
|
-
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
|
|
1267
|
-
e.preventDefault();
|
|
1268
|
-
runDemo();
|
|
1269
|
-
}
|
|
1270
|
-
});
|
|
1271
928
|
</script>
|
|
1272
929
|
</body>
|
|
1273
930
|
</html>
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rip-lang",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"description": "A modern language that compiles to JavaScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/compiler.js",
|
|
7
|
+
"workspaces": [
|
|
8
|
+
"packages/*"
|
|
9
|
+
],
|
|
7
10
|
"browser": "docs/dist/rip.browser.js",
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
package/scripts/serve.js
CHANGED
|
@@ -18,6 +18,8 @@ const MIME_TYPES = {
|
|
|
18
18
|
'.js': 'application/javascript; charset=utf-8',
|
|
19
19
|
'.css': 'text/css; charset=utf-8',
|
|
20
20
|
'.json': 'application/json',
|
|
21
|
+
'.txt': 'text/plain; charset=utf-8',
|
|
22
|
+
'.md': 'text/plain; charset=utf-8',
|
|
21
23
|
'.png': 'image/png',
|
|
22
24
|
'.jpg': 'image/jpeg',
|
|
23
25
|
'.svg': 'image/svg+xml',
|