rip-lang 3.8.10 → 3.9.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 +48 -0
- package/README.md +14 -14
- package/docs/RIP-LANG.md +10 -1
- package/docs/dist/rip-ui.min.js +169 -169
- package/docs/dist/rip-ui.min.js.br +0 -0
- package/docs/dist/rip.browser.min.js +163 -163
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/example/index.html +34 -0
- package/docs/example/index.json +14 -0
- package/docs/index.html +1475 -3
- package/docs/sierpinski.html +114 -0
- package/package.json +3 -3
- package/src/browser.js +18 -9
- package/src/compiler.js +4 -1
- package/src/components.js +415 -20
- package/src/grammar/grammar.rip +2 -2
- package/src/grammar/parser.js +360 -0
- package/src/lexer.js +12 -275
- package/src/parser.js +5 -6
- package/docs/demo.html +0 -342
- package/docs/dist/rip.browser.js +0 -8023
- package/docs/dist/ui.js +0 -962
- package/docs/dist/ui.min.js +0 -2
- package/docs/dist/ui.min.js.br +0 -0
- package/docs/dist/ui.rip +0 -957
- package/docs/dist/ui.rip.br +0 -0
- package/docs/playground-app.html +0 -1022
- package/docs/playground-js.html +0 -1645
- package/docs/playground-rip-ui.html +0 -1419
- package/docs/playground-rip.html +0 -1450
- package/src/parser-rd.js +0 -3242
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Sierpinski Triangle — Rip UI</title>
|
|
7
|
+
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
|
|
8
|
+
<style>
|
|
9
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
10
|
+
body { background: #0a0a0a; color: white; font-family: 'JetBrains Mono', monospace; overflow: hidden; }
|
|
11
|
+
.scene { position: relative; width: 100vw; height: 100vh; }
|
|
12
|
+
.stats { position: fixed; top: 20px; left: 24px; z-index: 10; }
|
|
13
|
+
.stats-title { font-size: 18px; font-weight: bold; }
|
|
14
|
+
.stats-line { font-size: 13px; color: rgba(255,255,255,0.45); margin-top: 3px; }
|
|
15
|
+
.stats-note { font-size: 11px; color: rgba(255,255,255,0.25); margin-top: 8px; max-width: 280px; line-height: 1.5; }
|
|
16
|
+
.source-toggle { display: inline-block; margin-top: 10px; padding: 4px 10px; background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.15); border-radius: 4px; color: rgba(255,255,255,0.5); font-family: inherit; font-size: 12px; cursor: pointer; transition: all 0.2s; }
|
|
17
|
+
.source-toggle:hover { color: white; border-color: rgba(255,255,255,0.4); background: rgba(255,255,255,0.12); }
|
|
18
|
+
.source-panel { position: fixed; top: 0; right: 0; bottom: 0; width: min(520px, 90vw); z-index: 100; background: rgba(10,10,10,0.92); backdrop-filter: blur(12px); border-left: 1px solid rgba(255,255,255,0.1); transform: translateX(100%); transition: transform 0.3s ease; overflow-y: auto; }
|
|
19
|
+
.source-panel.open { transform: translateX(0); }
|
|
20
|
+
.source-header { display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; border-bottom: 1px solid rgba(255,255,255,0.08); }
|
|
21
|
+
.source-label { font-size: 13px; font-weight: bold; color: rgba(255,255,255,0.6); letter-spacing: 0.05em; }
|
|
22
|
+
.source-close { background: none; border: none; color: rgba(255,255,255,0.4); font-size: 20px; cursor: pointer; padding: 4px 8px; border-radius: 4px; font-family: inherit; transition: all 0.2s; }
|
|
23
|
+
.source-close:hover { color: white; background: rgba(255,255,255,0.1); }
|
|
24
|
+
.source-code { padding: 16px 20px; font-size: 13px; line-height: 1.7; color: rgba(255,255,255,0.85); white-space: pre; overflow-x: auto; }
|
|
25
|
+
.container { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(var(--scale, 1)); width: 1000px; height: 870px; transform-origin: center center; }
|
|
26
|
+
.dot {
|
|
27
|
+
position: absolute; width: 24px; height: 24px; border-radius: 50%;
|
|
28
|
+
display: flex; align-items: center; justify-content: center;
|
|
29
|
+
font-size: 10px; font-weight: bold; color: rgba(255,255,255,0.9);
|
|
30
|
+
user-select: none; cursor: default; transition: transform 0.1s;
|
|
31
|
+
}
|
|
32
|
+
.dot:hover { transform: scale(2.5); z-index: 1; }
|
|
33
|
+
</style>
|
|
34
|
+
<script type="module" src="https://shreeve.github.io/rip-lang/dist/rip-ui.min.js"></script>
|
|
35
|
+
<script>
|
|
36
|
+
function updateScale() {
|
|
37
|
+
var s = Math.min(window.innerWidth / 1350, window.innerHeight / 1250, 1);
|
|
38
|
+
document.documentElement.style.setProperty('--scale', s);
|
|
39
|
+
}
|
|
40
|
+
updateScale();
|
|
41
|
+
window.addEventListener('resize', updateScale);
|
|
42
|
+
</script>
|
|
43
|
+
</head>
|
|
44
|
+
<body>
|
|
45
|
+
<div class="source-panel" id="source-panel">
|
|
46
|
+
<div class="source-header">
|
|
47
|
+
<span class="source-label">SOURCE — 48 LINES OF RIP</span>
|
|
48
|
+
<button class="source-close" onclick="document.getElementById('source-panel').classList.remove('open')">×</button>
|
|
49
|
+
</div>
|
|
50
|
+
<pre class="source-code" id="source-code"></pre>
|
|
51
|
+
</div>
|
|
52
|
+
<script type="text/rip">
|
|
53
|
+
# Sierpinski Triangle — 729 reactive dots at 60fps
|
|
54
|
+
# No virtual DOM. No tree diffing. Just fine-grained reactivity.
|
|
55
|
+
|
|
56
|
+
App = component
|
|
57
|
+
elapsed := 0
|
|
58
|
+
fps := 0
|
|
59
|
+
|
|
60
|
+
dots := do ->
|
|
61
|
+
result = []
|
|
62
|
+
build = (x, y, s) ->
|
|
63
|
+
if s <= 25
|
|
64
|
+
result.push { x, y, id: result.length }
|
|
65
|
+
else
|
|
66
|
+
build x , y - s / 4, s / 2
|
|
67
|
+
build x - s / 2, y + s / 4, s / 2
|
|
68
|
+
build x + s / 2, y + s / 4, s / 2
|
|
69
|
+
build 500, 435, 1000
|
|
70
|
+
result
|
|
71
|
+
|
|
72
|
+
mounted: ->
|
|
73
|
+
start = performance.now()
|
|
74
|
+
frames = 0
|
|
75
|
+
last = start
|
|
76
|
+
tick = ->
|
|
77
|
+
now = performance.now()
|
|
78
|
+
elapsed = (now - start) / 1000
|
|
79
|
+
frames++
|
|
80
|
+
if now - last >= 1000
|
|
81
|
+
fps = frames
|
|
82
|
+
frames = 0
|
|
83
|
+
last = now
|
|
84
|
+
requestAnimationFrame tick
|
|
85
|
+
requestAnimationFrame tick
|
|
86
|
+
|
|
87
|
+
render
|
|
88
|
+
.scene
|
|
89
|
+
.stats
|
|
90
|
+
.stats-title "Sierpinski Triangle"
|
|
91
|
+
.stats-line dots.length + " dots · " + dots.length * 2 + " reactive bindings"
|
|
92
|
+
.stats-line fps + " fps"
|
|
93
|
+
.stats-note "Each dot updates independently via fine-grained reactivity"
|
|
94
|
+
button.source-toggle @click: (-> document.getElementById('source-panel').classList.toggle('open')), "View Source"
|
|
95
|
+
|
|
96
|
+
.container
|
|
97
|
+
for dot in dots
|
|
98
|
+
.dot style: "left:" + (dot.x - 12) + "px;top:" + (dot.y - 12) + "px;background:hsl(" + ((dot.id / 729 * 360 + elapsed * 40) % 360) + " 70% 50%)"
|
|
99
|
+
Math.floor(elapsed % 10) + 1
|
|
100
|
+
|
|
101
|
+
App.new().mount document.body
|
|
102
|
+
|
|
103
|
+
# Populate source panel with this page's own Rip source
|
|
104
|
+
ripScript = document.querySelector('script[type="text/rip"]')
|
|
105
|
+
lines = ripScript.textContent.split("\n")
|
|
106
|
+
while lines.length and lines[0].trim() is ''
|
|
107
|
+
lines.shift()
|
|
108
|
+
cutoff = lines.findIndex((l) -> l.includes('Populate source panel'))
|
|
109
|
+
lines = lines.slice(0, cutoff).filter((l, i, a) -> i < a.length - 1 or l.trim() isnt '')
|
|
110
|
+
sourceEl = document.getElementById('source-code')
|
|
111
|
+
sourceEl.textContent = lines.join("\n")
|
|
112
|
+
</script>
|
|
113
|
+
</body>
|
|
114
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rip-lang",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.1",
|
|
4
4
|
"description": "A modern language that compiles to JavaScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/compiler.js",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"author": "Steve Shreeve <steve.shreeve@gmail.com>",
|
|
68
68
|
"license": "MIT",
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@rip-lang/api": "
|
|
71
|
-
"@rip-lang/ui": "
|
|
70
|
+
"@rip-lang/api": "1.1.5",
|
|
71
|
+
"@rip-lang/ui": "0.2.0"
|
|
72
72
|
}
|
|
73
73
|
}
|
package/src/browser.js
CHANGED
|
@@ -71,8 +71,14 @@ export { processRipScripts };
|
|
|
71
71
|
* Import a .rip file as an ES module
|
|
72
72
|
* Fetches the URL, compiles Rip→JS, dynamically imports via Blob URL
|
|
73
73
|
* Usage: const { launch } = await importRip('/ui.rip')
|
|
74
|
+
*
|
|
75
|
+
* Pre-compiled modules can be registered on importRip.modules to skip fetching.
|
|
76
|
+
* The rip-ui bundle uses this to embed ui.rip without a server round-trip.
|
|
74
77
|
*/
|
|
75
78
|
export async function importRip(url) {
|
|
79
|
+
for (const [key, mod] of Object.entries(importRip.modules)) {
|
|
80
|
+
if (url.includes(key)) return mod;
|
|
81
|
+
}
|
|
76
82
|
const source = await fetch(url).then(r => {
|
|
77
83
|
if (!r.ok) throw new Error(`importRip: ${url} (${r.status})`);
|
|
78
84
|
return r.text();
|
|
@@ -86,6 +92,7 @@ export async function importRip(url) {
|
|
|
86
92
|
URL.revokeObjectURL(blobUrl);
|
|
87
93
|
}
|
|
88
94
|
}
|
|
95
|
+
importRip.modules = {};
|
|
89
96
|
|
|
90
97
|
/**
|
|
91
98
|
* Browser Console REPL
|
|
@@ -128,14 +135,16 @@ if (typeof globalThis !== 'undefined') {
|
|
|
128
135
|
globalThis.__ripExports = { compile, compileToJS, formatSExpr, VERSION, BUILD_DATE, getReactiveRuntime, getComponentRuntime };
|
|
129
136
|
}
|
|
130
137
|
|
|
131
|
-
// Auto-process
|
|
132
|
-
//
|
|
138
|
+
// Auto-process <script type="text/rip"> blocks.
|
|
139
|
+
// Deferred via queueMicrotask so bundled entry code (e.g. rip-ui.min.js registering
|
|
140
|
+
// importRip.modules) runs before script processing begins.
|
|
133
141
|
if (typeof document !== 'undefined') {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
globalThis.__ripScriptsReady = new Promise(resolve => {
|
|
143
|
+
const run = () => processRipScripts().then(resolve);
|
|
144
|
+
if (document.readyState === 'loading') {
|
|
145
|
+
document.addEventListener('DOMContentLoaded', () => queueMicrotask(run));
|
|
146
|
+
} else {
|
|
147
|
+
queueMicrotask(run);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
141
150
|
}
|
package/src/compiler.js
CHANGED
|
@@ -1094,6 +1094,9 @@ export class CodeGenerator {
|
|
|
1094
1094
|
if (!this.reactiveVars) this.reactiveVars = new Set();
|
|
1095
1095
|
let varName = str(name) ?? name;
|
|
1096
1096
|
this.reactiveVars.add(varName);
|
|
1097
|
+
if (this.is(expr, 'block') && expr.length > 2) {
|
|
1098
|
+
return `const ${varName} = __computed(() => ${this.generateFunctionBody(expr)})`;
|
|
1099
|
+
}
|
|
1097
1100
|
return `const ${varName} = __computed(() => ${this.generate(expr, 'value')})`;
|
|
1098
1101
|
}
|
|
1099
1102
|
|
|
@@ -3253,7 +3256,7 @@ export class Compiler {
|
|
|
3253
3256
|
// Component Support (prototype installation)
|
|
3254
3257
|
// =============================================================================
|
|
3255
3258
|
|
|
3256
|
-
installComponentSupport(CodeGenerator);
|
|
3259
|
+
installComponentSupport(CodeGenerator, Lexer);
|
|
3257
3260
|
|
|
3258
3261
|
// =============================================================================
|
|
3259
3262
|
// Type Support (enum generator)
|