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.
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="demo">Live Demo</button>
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, '&lt;').replace(/>/g, '&gt;') + 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
- document.getElementById('repl-input').focus();
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 (['demo', 'compiler', 'repl'].includes(hash)) {
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 demo)
606
+ // Initialize from URL hash on page load (default to compiler)
758
607
  const initialTab = window.location.hash.slice(1);
759
- if (['demo', 'compiler', 'repl'].includes(initialTab)) {
608
+ if (['compiler', 'repl'].includes(initialTab)) {
760
609
  switchToTab(initialTab);
761
610
  } else {
762
- // No hash - demo is default, run it
763
- setTimeout(() => runDemo(), 50);
611
+ // No hash - compiler is default
612
+ compileCode();
764
613
  }
765
614
 
766
- // Show page now that everything is initialized (prevents render flicker)
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 Implementation
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, '&lt;').replace(/>/g, '&gt;') + 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.5.1",
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',