rip-lang 3.13.92 → 3.13.94
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 +2 -2
- package/README.md +3 -3
- package/bin/rip +11 -1
- package/docs/AGENTS.md +43 -0
- package/docs/RIP-LANG.md +3 -3
- package/docs/RIP-TYPES.md +72 -91
- package/docs/charts.html +15 -15
- package/docs/dist/rip.js +142 -38
- package/docs/dist/rip.min.js +174 -174
- package/docs/dist/rip.min.js.br +0 -0
- package/docs/index.html +2 -2
- package/package.json +1 -1
- package/src/AGENTS.md +456 -0
- package/src/lexer.js +1 -2
- package/src/typecheck.js +188 -6
- package/src/types.js +63 -38
- package/src/ui.rip +65 -0
- package/docs/ui/accordion.rip +0 -113
- package/docs/ui/alert-dialog.rip +0 -96
- package/docs/ui/autocomplete.rip +0 -141
- package/docs/ui/avatar.rip +0 -37
- package/docs/ui/badge.rip +0 -15
- package/docs/ui/breadcrumb.rip +0 -46
- package/docs/ui/button-group.rip +0 -26
- package/docs/ui/button.rip +0 -23
- package/docs/ui/card.rip +0 -25
- package/docs/ui/carousel.rip +0 -110
- package/docs/ui/checkbox-group.rip +0 -65
- package/docs/ui/checkbox.rip +0 -33
- package/docs/ui/collapsible.rip +0 -50
- package/docs/ui/combobox.rip +0 -155
- package/docs/ui/context-menu.rip +0 -105
- package/docs/ui/date-picker.rip +0 -214
- package/docs/ui/dialog.rip +0 -107
- package/docs/ui/drawer.rip +0 -79
- package/docs/ui/editable-value.rip +0 -80
- package/docs/ui/field.rip +0 -53
- package/docs/ui/fieldset.rip +0 -22
- package/docs/ui/form.rip +0 -39
- package/docs/ui/grid.rip +0 -901
- package/docs/ui/hljs-rip.js +0 -209
- package/docs/ui/index.css +0 -1772
- package/docs/ui/index.html +0 -2433
- package/docs/ui/input-group.rip +0 -28
- package/docs/ui/input.rip +0 -36
- package/docs/ui/label.rip +0 -16
- package/docs/ui/menu.rip +0 -162
- package/docs/ui/menubar.rip +0 -155
- package/docs/ui/meter.rip +0 -36
- package/docs/ui/multi-select.rip +0 -158
- package/docs/ui/native-select.rip +0 -32
- package/docs/ui/nav-menu.rip +0 -129
- package/docs/ui/number-field.rip +0 -162
- package/docs/ui/otp-field.rip +0 -89
- package/docs/ui/pagination.rip +0 -123
- package/docs/ui/popover.rip +0 -143
- package/docs/ui/preview-card.rip +0 -73
- package/docs/ui/progress.rip +0 -25
- package/docs/ui/radio-group.rip +0 -67
- package/docs/ui/resizable.rip +0 -123
- package/docs/ui/scroll-area.rip +0 -145
- package/docs/ui/select.rip +0 -184
- package/docs/ui/separator.rip +0 -17
- package/docs/ui/skeleton.rip +0 -22
- package/docs/ui/slider.rip +0 -165
- package/docs/ui/spinner.rip +0 -17
- package/docs/ui/table.rip +0 -27
- package/docs/ui/tabs.rip +0 -124
- package/docs/ui/textarea.rip +0 -48
- package/docs/ui/toast.rip +0 -87
- package/docs/ui/toggle-group.rip +0 -78
- package/docs/ui/toggle.rip +0 -24
- package/docs/ui/toolbar.rip +0 -46
- package/docs/ui/tooltip.rip +0 -115
package/docs/dist/rip.min.js.br
CHANGED
|
Binary file
|
package/docs/index.html
CHANGED
|
@@ -634,7 +634,7 @@
|
|
|
634
634
|
typeKeywords: ['number', 'string', 'boolean', 'void', 'any', 'never', 'unknown', 'object', 'symbol', 'bigint'],
|
|
635
635
|
|
|
636
636
|
operators: [
|
|
637
|
-
'|>', '
|
|
637
|
+
'|>', '::', ':=', '~=', '~>', '<=>', '=!', '!?', '=~', '??', '?.', '...',
|
|
638
638
|
'..', '**', '//', '%%', '++', '--', '&&', '||', '===', '!==', '==', '!=',
|
|
639
639
|
'<=', '>=', '=>', '->', '+=', '-=', '*=', '/=', '%=', '**=', '&&=', '||=',
|
|
640
640
|
'??=', '<<=', '>>=', '>>>=', '&=', '|=', '^=', '>>>', '>>', '<<',
|
|
@@ -695,7 +695,7 @@
|
|
|
695
695
|
[/\d[\d_]*(?:\.[\d][\d_]*)?(?:[eE][+-]?\d+)?n?/, 'number'],
|
|
696
696
|
|
|
697
697
|
// Operators (multi-char first)
|
|
698
|
-
[
|
|
698
|
+
[/\btype\b/, 'keyword'],
|
|
699
699
|
[/::(?!=)/, 'keyword.operator.type'],
|
|
700
700
|
[/~=|:=|~>|<=>/, 'keyword.operator.reactive'],
|
|
701
701
|
[/=!/, 'keyword.operator.readonly'],
|
package/package.json
CHANGED
package/src/AGENTS.md
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
# Compiler Subsystem — Agent Guide
|
|
2
|
+
|
|
3
|
+
This covers `compiler.js`, `lexer.js`, `components.js`, `browser.js`, `types.js`, `typecheck.js`, and the `grammar/` directory.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## S-Expression Patterns
|
|
8
|
+
|
|
9
|
+
Common patterns:
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
["=", "x", 42]
|
|
13
|
+
["+", left, right]
|
|
14
|
+
["def", "name", params, body]
|
|
15
|
+
["->", params, body]
|
|
16
|
+
["if", condition, then, else]
|
|
17
|
+
["state", name, expr]
|
|
18
|
+
["computed", name, expr]
|
|
19
|
+
["enum", name, body]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Complete node reference:
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
// Top Level
|
|
26
|
+
['program', ...statements]
|
|
27
|
+
|
|
28
|
+
// Variables & Assignment
|
|
29
|
+
['=', target, value]
|
|
30
|
+
['+=', target, value] // Also: -=, *=, /=, %=, **=
|
|
31
|
+
['&&=', target, value] ['||=', target, value]
|
|
32
|
+
['?=', target, value] ['??=', target, value]
|
|
33
|
+
|
|
34
|
+
// Functions
|
|
35
|
+
['def', name, params, body]
|
|
36
|
+
['->', params, body] // Thin arrow
|
|
37
|
+
['=>', params, body] // Fat arrow
|
|
38
|
+
// Params: 'name', ['rest', 'name'], ['default', 'name', expr],
|
|
39
|
+
// ['expansion'], ['object', ...], ['array', ...]
|
|
40
|
+
|
|
41
|
+
// Calls & Property Access
|
|
42
|
+
[callee, ...args] // Function call
|
|
43
|
+
['await', expr]
|
|
44
|
+
['.', obj, 'prop']
|
|
45
|
+
['?.', obj, 'prop']
|
|
46
|
+
['[]', arr, index]
|
|
47
|
+
['optindex', arr, index] // arr?.[index]
|
|
48
|
+
['optcall', fn, ...args] // fn?.(args)
|
|
49
|
+
['new', constructorExpr]
|
|
50
|
+
['super', ...args]
|
|
51
|
+
['tagged-template', tag, str]
|
|
52
|
+
|
|
53
|
+
// Data Structures
|
|
54
|
+
['array', ...elements]
|
|
55
|
+
['object', ...pairs] // pairs: [key, value]
|
|
56
|
+
['...', expr] // Spread (prefix only)
|
|
57
|
+
|
|
58
|
+
// Operators
|
|
59
|
+
['+', left, right] ['-', left, right] ['*', left, right]
|
|
60
|
+
['/', left, right] ['%', left, right] ['**', left, right]
|
|
61
|
+
['==', left, right] ['!=', left, right] // == compiles to ===
|
|
62
|
+
['<', left, right] ['<=', left, right]
|
|
63
|
+
['>', left, right] ['>=', left, right]
|
|
64
|
+
['&&', left, right] ['||', left, right] ['??', left, right]
|
|
65
|
+
['!', expr] ['~', expr] ['typeof', expr]
|
|
66
|
+
['delete', expr] ['instanceof', expr, type]
|
|
67
|
+
['?', expr] // Existence check (x?)
|
|
68
|
+
['defined', expr] // Defined check (x!?)
|
|
69
|
+
['presence', expr] // Presence check (x?!) — Houdini operator
|
|
70
|
+
['++', expr, isPostfix] ['--', expr, isPostfix]
|
|
71
|
+
|
|
72
|
+
// Control Flow
|
|
73
|
+
['if', condition, thenBlock, elseBlock?]
|
|
74
|
+
['unless', condition, body]
|
|
75
|
+
['?:', condition, thenExpr, elseExpr]
|
|
76
|
+
['switch', discriminant, cases, defaultCase?]
|
|
77
|
+
|
|
78
|
+
// Loops
|
|
79
|
+
['for-in', vars, iterable, step?, guard?, body]
|
|
80
|
+
['for-of', vars, object, guard?, body]
|
|
81
|
+
['for-as', vars, iterable, async?, guard?, body] // for await
|
|
82
|
+
['while', condition, body] ['until', condition, body] ['loop', body]
|
|
83
|
+
['break'] ['continue'] ['break-if', condition] ['continue-if', condition]
|
|
84
|
+
|
|
85
|
+
// Comprehensions
|
|
86
|
+
['comprehension', expr, iterators, guards]
|
|
87
|
+
['object-comprehension', keyExpr, valueExpr, iterators, guards]
|
|
88
|
+
|
|
89
|
+
// Exceptions
|
|
90
|
+
['try', tryBlock, [catchParam, catchBlock]?, finallyBlock?]
|
|
91
|
+
['throw', expr]
|
|
92
|
+
|
|
93
|
+
// Classes & Types
|
|
94
|
+
['class', name, parent?, ...members]
|
|
95
|
+
['enum', name, body]
|
|
96
|
+
|
|
97
|
+
// Ranges
|
|
98
|
+
['..', from, to] // Inclusive
|
|
99
|
+
['...', from, to] // Exclusive
|
|
100
|
+
|
|
101
|
+
// Blocks & Modules
|
|
102
|
+
['block', ...statements]
|
|
103
|
+
['do-iife', expr]
|
|
104
|
+
['import', specifiers, source]
|
|
105
|
+
['export', statement] ['export-default', expr]
|
|
106
|
+
['export-all', source] ['export-from', specifiers, source]
|
|
107
|
+
|
|
108
|
+
// Reactivity
|
|
109
|
+
['state', name, expr] // :=
|
|
110
|
+
['computed', name, expr] // ~=
|
|
111
|
+
['effect', name, expr] // ~>
|
|
112
|
+
['readonly', name, expr] // =!
|
|
113
|
+
|
|
114
|
+
// Components
|
|
115
|
+
['component', null, body]
|
|
116
|
+
['render', body]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Lexer Token Format
|
|
120
|
+
|
|
121
|
+
Tokens are `[tag, val]` arrays with extra properties:
|
|
122
|
+
|
|
123
|
+
- `.pre` — whitespace count before token
|
|
124
|
+
- `.data` — metadata like `{ await, predicate, quote, invert, parsedValue }`
|
|
125
|
+
- `.loc` — `{ r, c, n }`
|
|
126
|
+
- `.spaced` — sugar for `.pre > 0`
|
|
127
|
+
- `.newLine` — whether preceded by newline
|
|
128
|
+
|
|
129
|
+
Identifier suffixes:
|
|
130
|
+
|
|
131
|
+
- `!` sets `.data.await = true`
|
|
132
|
+
- `?` sets `.data.predicate = true`
|
|
133
|
+
- `as!` in loops emits `FORASAWAIT` for `for await`
|
|
134
|
+
|
|
135
|
+
## Context-Aware Generation
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
generate(sexpr, context = 'statement')
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- Value context: emit an expression result
|
|
142
|
+
- Statement context: emit statements without preserving a result
|
|
143
|
+
|
|
144
|
+
Comprehensions are the canonical example — value context becomes an IIFE with array building, statement context becomes a plain loop.
|
|
145
|
+
|
|
146
|
+
## Dispatch Table
|
|
147
|
+
|
|
148
|
+
All node types dispatch through `GENERATORS` for O(1) lookup. To change a feature:
|
|
149
|
+
|
|
150
|
+
1. Inspect the s-expression with `echo 'code' | ./bin/rip -s`
|
|
151
|
+
2. Search `GENERATORS` in `src/compiler.js`
|
|
152
|
+
3. Edit the matching generator method
|
|
153
|
+
4. Run `bun run test`
|
|
154
|
+
|
|
155
|
+
For grammar work:
|
|
156
|
+
|
|
157
|
+
1. edit `src/grammar/grammar.rip`
|
|
158
|
+
2. run `bun run parser`
|
|
159
|
+
3. verify the new parse shape with `./bin/rip -s`
|
|
160
|
+
4. update codegen or lexer handling if the new node shape requires it
|
|
161
|
+
|
|
162
|
+
## Heredocs
|
|
163
|
+
|
|
164
|
+
Heredocs (`'''` and `"""`) use a **closing-delimiter-as-left-margin** rule. The column position of the closing delimiter defines how much leading whitespace to strip from each content line:
|
|
165
|
+
|
|
166
|
+
```coffee
|
|
167
|
+
# Closing delimiter at same indent as content — fully left-aligned output
|
|
168
|
+
contents = """
|
|
169
|
+
username=#{user}
|
|
170
|
+
password=#{pass}
|
|
171
|
+
"""
|
|
172
|
+
# Result: "username=...\npassword=..."
|
|
173
|
+
|
|
174
|
+
# Closing delimiter 2 columns left of content — 2 spaces retained
|
|
175
|
+
html = '''
|
|
176
|
+
<div>
|
|
177
|
+
<p>Hello</p>
|
|
178
|
+
</div>
|
|
179
|
+
'''
|
|
180
|
+
# Result: " <div>\n <p>Hello</p>\n </div>"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Raw heredocs (`'''\` and `"""\`) keep recognized escape sequences literal (`\n`, `\t`, `\u`, `\x`, `\\`). Non-JS escapes like `\s`, `\w`, and `\d` already pass through unchanged in both modes.
|
|
184
|
+
|
|
185
|
+
## Block Unwrapping
|
|
186
|
+
|
|
187
|
+
Parser bodies are commonly wrapped in `['block', ...]`. Always unwrap before operating on statements:
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
if (Array.isArray(body) && body[0] === 'block') {
|
|
191
|
+
const statements = body.slice(1)
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Component Internals
|
|
198
|
+
|
|
199
|
+
The component system is a compiler sidecar. `installComponentSupport(CodeGenerator, Lexer)` adds methods to both prototypes.
|
|
200
|
+
|
|
201
|
+
### Render Rewriter
|
|
202
|
+
|
|
203
|
+
`rewriteRender()` runs after `normalizeLines` and before `tagPostfixConditionals`.
|
|
204
|
+
|
|
205
|
+
Inside `render` blocks it rewrites template syntax into function-call syntax:
|
|
206
|
+
|
|
207
|
+
```coffee
|
|
208
|
+
div
|
|
209
|
+
span "hello"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
becomes roughly:
|
|
213
|
+
|
|
214
|
+
```coffee
|
|
215
|
+
div(->
|
|
216
|
+
span("hello"))
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Tag class patterns:
|
|
220
|
+
|
|
221
|
+
```coffee
|
|
222
|
+
div.card
|
|
223
|
+
.card
|
|
224
|
+
div.card.active
|
|
225
|
+
.("flex-1 p-4")
|
|
226
|
+
.card.("flex-1 p-4")
|
|
227
|
+
.card.primary.("flex", x)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Key mechanisms:
|
|
231
|
+
|
|
232
|
+
- `startsWithTag` — backward scan to decide whether a line starts a template tag
|
|
233
|
+
- `pendingCallEnds` — indent stack for injected `CALL_START` / `CALL_END`
|
|
234
|
+
- `fromThen` skip — inline postfix `if ... then ... else ...` expressions are never template nesting
|
|
235
|
+
- `$open: true` becomes `"data-open": true`
|
|
236
|
+
|
|
237
|
+
### Component Codegen
|
|
238
|
+
|
|
239
|
+
Key entry points:
|
|
240
|
+
|
|
241
|
+
- `buildRender` — initializes counters and create/setup line arrays
|
|
242
|
+
- `generateNode` — dispatch for elements, text, conditionals, loops, components
|
|
243
|
+
- `generateConditional` — emits conditional block factories
|
|
244
|
+
- `generateTemplateLoop` — emits `__reconcile(...)`
|
|
245
|
+
- `emitBlockFactory` — shared factory emitter used by conditionals and loops
|
|
246
|
+
|
|
247
|
+
### Factory Mode
|
|
248
|
+
|
|
249
|
+
Block factories need locals and `ctx.member` references instead of `this._elN` and `this.member`.
|
|
250
|
+
|
|
251
|
+
- `_factoryMode` — emit locals like `_el0` and `ctx.member`
|
|
252
|
+
- `_self` — returns `'this'` or `'ctx'`
|
|
253
|
+
- `_factoryVars` — variables that need local `let` declarations
|
|
254
|
+
- `_fragChildren` — fragment-to-children tracking for removals
|
|
255
|
+
- `_pushEffect(body)` — emits `__effect(...)` or `disposers.push(__effect(...))`
|
|
256
|
+
- `_loopVarStack` — threads loop variables through nested factories
|
|
257
|
+
|
|
258
|
+
Factory mode is entered in `generateConditionBranch` and `generateTemplateLoop` via save/restore of `[_createLines, _setupLines, _factoryMode, _factoryVars]`.
|
|
259
|
+
|
|
260
|
+
### Auto-Wired Event Handlers
|
|
261
|
+
|
|
262
|
+
Methods named `onClick`, `onKeydown`, `onMouseenter`, etc. automatically bind to the root DOM element.
|
|
263
|
+
|
|
264
|
+
- detection: methods matching `/^on[A-Z]/` that are not lifecycle hooks
|
|
265
|
+
- root only: the first generated HTML tag can claim auto-wiring
|
|
266
|
+
- explicit override: `@click: handler` on the root suppresses auto-wire for that event
|
|
267
|
+
- lifecycle exclusion: `onError` is not auto-wired
|
|
268
|
+
|
|
269
|
+
```coffee
|
|
270
|
+
export Checkbox = component
|
|
271
|
+
@checked := false
|
|
272
|
+
onClick: -> @checked = not @checked
|
|
273
|
+
onKeydown: (e) ->
|
|
274
|
+
@onClick() if e.key in ['Enter', ' ']
|
|
275
|
+
render
|
|
276
|
+
button role: 'checkbox', aria-checked: !!@checked
|
|
277
|
+
slot
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### List Reconciliation
|
|
281
|
+
|
|
282
|
+
Loop rendering uses runtime `__reconcile` instead of inlined diff logic.
|
|
283
|
+
|
|
284
|
+
Phases:
|
|
285
|
+
|
|
286
|
+
1. creation batch via `DocumentFragment`
|
|
287
|
+
2. prefix scan
|
|
288
|
+
3. suffix scan
|
|
289
|
+
4. fast paths for pure insert/delete
|
|
290
|
+
5. LIS for minimal moves on true reorders
|
|
291
|
+
|
|
292
|
+
Compile-time optimizations:
|
|
293
|
+
|
|
294
|
+
- static blocks: `_s: true` skips patch calls when possible
|
|
295
|
+
- array-based `state.blocks[]`
|
|
296
|
+
- `state.keys = items.slice()` for default item-as-key behavior
|
|
297
|
+
|
|
298
|
+
### Error Boundaries
|
|
299
|
+
|
|
300
|
+
`onError` walks the `_parent` chain.
|
|
301
|
+
|
|
302
|
+
- constructor wraps `_init`
|
|
303
|
+
- mount wraps `_create`, `_setup`, and `mounted`
|
|
304
|
+
- child setup and mount calls are wrapped at codegen time
|
|
305
|
+
- `__handleComponentError(error, component)` finds the nearest handler or rethrows
|
|
306
|
+
|
|
307
|
+
### Transitions
|
|
308
|
+
|
|
309
|
+
Syntax:
|
|
310
|
+
|
|
311
|
+
```coffee
|
|
312
|
+
div ~fade
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Pipeline:
|
|
316
|
+
|
|
317
|
+
- rewriter converts the tilde form into `__transition__`
|
|
318
|
+
- `generateAttributes` emits `this._t = "fade"`
|
|
319
|
+
- conditionals check `_t` for async leave / enter
|
|
320
|
+
- runtime `__transition(el, name, dir, done)` performs the CSS class dance
|
|
321
|
+
|
|
322
|
+
Built-in presets: `fade`, `slide`, `scale`, `blur`, `fly`
|
|
323
|
+
|
|
324
|
+
Custom transitions follow the `{name}-enter-from`, `{name}-enter-active`, `{name}-leave-to` convention.
|
|
325
|
+
|
|
326
|
+
### Component Testing
|
|
327
|
+
|
|
328
|
+
Component tests live in `test/rip/components.rip`.
|
|
329
|
+
|
|
330
|
+
- use `code` tests with `{ skipPreamble: true, skipRuntimes: true }` for codegen assertions
|
|
331
|
+
- use `test` tests for runtime behavior like state, methods, reconciliation, and error boundaries
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Browser Runtime (`browser.js`)
|
|
336
|
+
|
|
337
|
+
The browser bundle is an IIFE loaded with `<script defer>`, not `type="module"`, so `file://` demos still work without CORS issues.
|
|
338
|
+
|
|
339
|
+
### Loading Flow
|
|
340
|
+
|
|
341
|
+
`processRipScripts()` runs on `DOMContentLoaded` and:
|
|
342
|
+
|
|
343
|
+
1. collects `data-src` URLs from the runtime script
|
|
344
|
+
2. collects all `<script type="text/rip">` tags
|
|
345
|
+
3. fetches `.rip` sources and JSON bundles
|
|
346
|
+
4. expands bundles and merges bundle data into `data-state`
|
|
347
|
+
5. compiles each source with `{ skipRuntimes: true, skipExports: true }`
|
|
348
|
+
6. appends `Component.mount(target)` when `data-mount` is present
|
|
349
|
+
7. executes everything as one shared async IIFE
|
|
350
|
+
|
|
351
|
+
### Compiler Options
|
|
352
|
+
|
|
353
|
+
- `skipExports` — suppresses `export` in codegen; `export` makes components visible across tags as plain `const` declarations in the shared scope
|
|
354
|
+
- `skipRuntimes` — skips re-emitting reactive/component runtimes and uses `var` helpers for safe concatenation
|
|
355
|
+
|
|
356
|
+
### Browser Helpers
|
|
357
|
+
|
|
358
|
+
- `rip(code)` — browser REPL for Rip snippets
|
|
359
|
+
- `importRip(url)` — fetch, compile, and import a `.rip` file
|
|
360
|
+
- `compileToJS(code)` — compile Rip source to JS
|
|
361
|
+
- `__rip` — reactive runtime
|
|
362
|
+
- `__ripComponent` — component runtime
|
|
363
|
+
|
|
364
|
+
### Variable Persistence in `rip()`
|
|
365
|
+
|
|
366
|
+
`let` declarations are stripped so values can persist in sloppy-mode eval, while `const` is hoisted to `globalThis`.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Reactivity Implementation
|
|
371
|
+
|
|
372
|
+
The reactive runtime is embedded in `compiler.js` and only emitted when needed.
|
|
373
|
+
|
|
374
|
+
### `offer` / `accept`
|
|
375
|
+
|
|
376
|
+
Context-sensitive keywords that only activate inside component bodies. Elsewhere they remain plain identifiers.
|
|
377
|
+
|
|
378
|
+
```coffee
|
|
379
|
+
export Tabs = component
|
|
380
|
+
offer active := 'overview'
|
|
381
|
+
|
|
382
|
+
export TabContent = component
|
|
383
|
+
accept active
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
`offer` can wrap `:=`, `~=`, `=`, or `=!`. The same reactive object flows through parent and child.
|
|
387
|
+
|
|
388
|
+
Implementation details:
|
|
389
|
+
|
|
390
|
+
- tokenized as `OFFER` / `ACCEPT` only inside component bodies
|
|
391
|
+
- handled via `classifyKeyword` override in `components.js`
|
|
392
|
+
- grammar lives in `ComponentLine`
|
|
393
|
+
- runtime uses existing `setContext`, `getContext`, and `_parent` chain
|
|
394
|
+
|
|
395
|
+
### Component Signal Sharing
|
|
396
|
+
|
|
397
|
+
`<=>` on components is different from `<=>` on HTML elements.
|
|
398
|
+
|
|
399
|
+
The parent passes the signal via `__bind_propName__`. The child checks that prop first:
|
|
400
|
+
|
|
401
|
+
```javascript
|
|
402
|
+
this.checked = __state(props.__bind_checked__ ?? props.checked ?? false)
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Because `__state()` passes through existing signals, parent and child share the same signal object. No event-based synchronization is needed.
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Type System
|
|
410
|
+
|
|
411
|
+
Rip types are erased from JavaScript and exist for `.d.ts` emission, IDE tooling, and documentation.
|
|
412
|
+
|
|
413
|
+
### Syntax
|
|
414
|
+
|
|
415
|
+
```coffee
|
|
416
|
+
def greet(name:: string):: string
|
|
417
|
+
"Hello, #{name}!"
|
|
418
|
+
|
|
419
|
+
type User =
|
|
420
|
+
id: number
|
|
421
|
+
name: string
|
|
422
|
+
email?: string
|
|
423
|
+
|
|
424
|
+
interface Animal
|
|
425
|
+
name: string
|
|
426
|
+
speak: => void
|
|
427
|
+
|
|
428
|
+
enum Status
|
|
429
|
+
Active
|
|
430
|
+
Inactive
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Architecture
|
|
434
|
+
|
|
435
|
+
Type emission logic lives in `types.js`. Type-checking integration and diagnostic filtering live in `typecheck.js`.
|
|
436
|
+
|
|
437
|
+
- `installTypeSupport(Lexer)` adds `rewriteTypes()`
|
|
438
|
+
- `emitTypes(tokens)` emits `.d.ts`
|
|
439
|
+
- `generateEnum()` emits runtime JS for enums
|
|
440
|
+
- `typecheck.js` drives `rip check` and mediates TypeScript diagnostics
|
|
441
|
+
|
|
442
|
+
Types are processed at the token layer before parsing.
|
|
443
|
+
|
|
444
|
+
### Shadow TypeScript
|
|
445
|
+
|
|
446
|
+
`rip --shadow file.rip` dumps the virtual TypeScript file that `rip check` and the VS Code extension feed into the TypeScript language service.
|
|
447
|
+
|
|
448
|
+
Typical debugging sequence:
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
rip --shadow file.rip
|
|
452
|
+
rip -d file.rip
|
|
453
|
+
rip -cd file.rip
|
|
454
|
+
rip -c file.rip
|
|
455
|
+
rip -s file.rip
|
|
456
|
+
```
|
package/src/lexer.js
CHANGED
|
@@ -214,7 +214,7 @@ let UNARY_MATH = new Set(['!', '~']);
|
|
|
214
214
|
// The ! suffix is NOT captured when followed by ? to preserve !? as operator
|
|
215
215
|
let IDENTIFIER_RE = /^(?!\d)((?:(?!\s)[$\w\x7f-\uffff])+(?:!(?!\?)|[?](?![.?![(]))?)([^\n\S]*:(?![=:]))?/;
|
|
216
216
|
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;
|
|
217
|
-
let OPERATOR_RE = /^(
|
|
217
|
+
let OPERATOR_RE = /^(?:<=>|::|[-=]>|~>|~=|:=|=!|===|!==|!\?|\?\!|\?\?|=~|\|>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>*\/%])\2=?|\?\.?|\.{2,3})/;
|
|
218
218
|
let WHITESPACE_RE = /^[^\n\S]+/;
|
|
219
219
|
let NEWLINE_RE = /^(?:\n[^\n\S]*)+/;
|
|
220
220
|
let COMMENT_RE = /^(\s*)###([^#][\s\S]*?)(?:###([^\n\S]*)|###$)|^((?:\s*#(?!##[^#]).*)+)/;
|
|
@@ -1211,7 +1211,6 @@ export class Lexer {
|
|
|
1211
1211
|
// Pipe operator
|
|
1212
1212
|
else if (val === '|>') tag = 'PIPE';
|
|
1213
1213
|
// Type operators
|
|
1214
|
-
else if (val === '::=') tag = 'TYPE_ALIAS';
|
|
1215
1214
|
else if (val === '::' && /^[a-zA-Z_$]/.test(this.chunk[2] || '')) {
|
|
1216
1215
|
// Prototype access: String::trim → String.prototype.trim
|
|
1217
1216
|
this.emit('.', '.');
|