├── .gitignore
├── .prettierrc
├── README.md
├── assets
├── disco.jpg
└── liquid.jpg
├── demo
├── popcorn.fs
├── webgl.fs
└── xt16.fs
├── deploy.sh
├── index.html
├── lib
├── audio.fs
├── bench.fs
├── canvas.fs
├── glsl.fs
├── list.fs
├── math.fs
├── swizzle.fs
└── synth.fs
├── package.json
├── src
├── index.ts
├── kernel.ts
├── repl.ts
└── vm.ts
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | .cache
2 | dist
3 | out
4 | node_modules
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "tabWidth": 4,
4 | "semi": true,
5 | "singleQuote": false,
6 | "arrowParens": "always",
7 | "endOfLine": "lf"
8 | }
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @thi.ng/charlie
2 |
3 | ## About
4 |
5 | This is a slightly updated version of my first
6 | [Forth](http://thinking-forth.sourceforge.net/) VM implementation from
7 | 2015, originally written in JavaScript, now updated to TypeScript. Charlie is named in honour of Charles Moore, inventor of Forth.
8 |
9 | Several parts of the VM and core vocabulary are based on @phaendal's
10 | [mkforth4-js](https://github.com/phaendal/mkforth4-js), which evolved in
11 | parallel at roughly the same time span and was highly educational. Other
12 | parts are inspired by [Factor](http://factorcode.org),
13 | [Popr](https://github.com/HackerFoo/poprc) and other [concatenative
14 | languages](http://concatenative.org/).
15 |
16 | The VM & REPL (10KB total) are available online via
17 | [forth.thi.ng](http://forth.thi.ng). The project has been online since
18 | 2015, but was semi-broken due to CSS layout issues (now fixed).
19 |
20 | Related projects resulting from this experiment:
21 |
22 | - [@thi.ng/pointfree-lang](https://github.com/thi-ng/umbrella/tree/develop/packages/pointfree-lang)
23 | - [thi.ng/synstack](https://github.com/thi-ng/synstack/)
24 |
25 | ## Videos / screencasts
26 |
27 | - [WebAudio multi-track synth & fx livecoding (from scratch)](https://youtu.be/NU4PSkA3pAE?t=130)
28 | - [Forth-to-GLSL shader transpiler](https://youtu.be/30s3mgrkzQ0?t=123)
29 | - [Mini text adventure (directed graph)](https://twitter.com/forthcharlie/status/618463324473303040)
30 | - [Closures & destructuring](https://twitter.com/forthcharlie/status/618090661137522688)
31 | - [Vector algebra](https://twitter.com/forthcharlie/status/616465114871504896)
32 | - [Lisp style cons](https://twitter.com/forthcharlie/status/616429574331744256)
33 | - [Prime numbers](https://twitter.com/forthcharlie/status/616296680225435649)
34 | - [Unit conversion](https://twitter.com/forthcharlie/status/616292997261598720)
35 | - [ASCII art](https://twitter.com/forthcharlie/status/616290572706430977)
36 | - [FizzBuzz](https://twitter.com/forthcharlie/status/616281804866211840)
37 |
38 | ## Libraries & demos
39 |
40 | The following libraries and demos are included (also available in the
41 | online REPL):
42 |
43 | - [lib/audio.fs](https://github.com/thi-ng/charlie/tree/master/lib/audio.fs)
44 | - [lib/bench.fs](https://github.com/thi-ng/charlie/tree/master/lib/bench.fs)
45 | - [lib/canvas.fs](https://github.com/thi-ng/charlie/tree/master/lib/canvas.fs)
46 | - [lib/glsl.fs](https://github.com/thi-ng/charlie/tree/master/lib/glsl.fs)
47 | - [lib/list.fs](https://github.com/thi-ng/charlie/tree/master/lib/list.fs)
48 | - [lib/math.fs](https://github.com/thi-ng/charlie/tree/master/lib/math.fs)
49 | - [lib/swizzle.fs](https://github.com/thi-ng/charlie/tree/master/lib/swizzle.fs)
50 | - [lib/synth.fs](https://github.com/thi-ng/charlie/tree/master/lib/synth.fs)
51 |
52 | ### GLSL live coding & cross-compiler
53 |
54 | The above
55 | [lib/glsl.fs](https://github.com/thi-ng/charlie/tree/master/lib/glsl.fs)
56 | library contains a Forth -> GLSL cross-compiler, based on word inlining
57 | and emulating a stack machine via multiple variables.
58 |
59 | The concept was inspired by [Brad Nelson](https://flagxor.com/)'s [Forth
60 | Haiku](https://forthsalon.appspot.com/), however here (as an exercise)
61 | the cross-compiler is entirely written in Forth itself...
62 |
63 | Demo source: [demo/webgl.fs](https://github.com/thi-ng/charlie/tree/master/demo/webgl.fs)
64 |
65 | Usage in the REPL:
66 |
67 | ```
68 | ( this includes the cross-compiler automatically )
69 | "demo/webgl.fs" include*
70 | ```
71 |
72 | Some small examples (more are included in the demo source, also see
73 | lib/glsl for available functions):
74 |
75 | **IMPORTANT:** All shader code must be wrapped by `glsl> ... ;;`
76 |
77 | #### Liquid paint (ported from [GLSL](http://glslsandbox.com/e#8067.3))
78 |
79 | 
80 |
81 | ```
82 | glsl>
83 | : ti 0.3 * t + ;
84 | : amp 0.6 swap / * ;
85 | : col 3 * sin 0.5 * 0.5 + ;
86 | : i ( x y i -- x' y' i' )
87 | >r over over r@ * r@ ti + sin r@ amp + 1 + -rot
88 | swap r@ * r@ 10 + ti + sin r@ amp + 1.4 - r> 1 + ;
89 | : i5 i i i i i ;
90 | : i10 i5 i5 ;
91 |
92 | x 2 * y 2 *
93 | 1 i10 i10 i10
94 | drop over over
95 | + sin -rot col swap col
96 |
97 | ;; reset
98 | ```
99 |
100 | #### Disco floor (based on [Forth Haiku](https://forthsalon.appspot.com/haiku-view/ahBzfmZvcnRoc2Fsb24taHJkcg0LEgVIYWlrdRim4xMM)):
101 |
102 | 
103 |
104 | ```
105 | glsl>
106 | : stripes 9.5 * sin ;
107 | : fade t * sin * ;
108 |
109 | x stripes y stripes *
110 | 2 fade
111 | dup 2 fade
112 | dup 3 fade
113 |
114 | ;; reset
115 | ```
116 |
117 | ### Web audio demo
118 |
119 | Source: [demo/popcorn.fs](https://github.com/thi-ng/charlie/tree/master/demo/popcorn.fs)
120 |
121 | Usage in the REPL:
122 |
123 | ```text
124 | "demo/popcorn.fs" include*
125 | ```
126 |
127 | Once all lib files are loaded (give it a few seconds to be sure)...
128 |
129 | ```
130 | popcorn
131 | ```
132 |
133 | ## Building
134 |
135 | ```bash
136 | git clone https://github.com/thi-ng/charlie.git
137 | cd charlie
138 |
139 | yarn start # start dev server
140 |
141 | yarn build # production build (written to /out)
142 | ```
143 |
--------------------------------------------------------------------------------
/assets/disco.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thi-ng/charlie/2ca68e0364835d6af85d0665db4bd003d01d4e50/assets/disco.jpg
--------------------------------------------------------------------------------
/assets/liquid.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thi-ng/charlie/2ca68e0364835d6af85d0665db4bd003d01d4e50/assets/liquid.jpg
--------------------------------------------------------------------------------
/demo/popcorn.fs:
--------------------------------------------------------------------------------
1 | "lib/synth.fs" include*
2 |
3 | "waiting for libs..." .
4 | 2000 sleep
5 |
6 | 1 softy instr-osc1 !
7 | 10 softy instr-detune2 !
8 | 80 softy instr-env-release !
9 |
10 | 180 val> 1/8
11 | 1/8 2 * val> 1/4
12 |
13 | : play swap softy play sleep ;
14 |
15 | : popcorn-a ( offset -- )
16 | C6 over + 1/8 play
17 | A#5 over + 1/8 play
18 | C6 over + 1/8 play
19 | G5 over + 1/8 play
20 | D#5 over + 1/8 play
21 | G5 over + 1/8 play
22 | C5 swap + 1/4 play ;
23 |
24 | : popcorn-b ( offset -- )
25 | C6 over + 1/8 play
26 | D6 over + 1/8 play
27 | D#6 over + 1/8 play
28 | D6 over + 1/8 play
29 | D#6 over + 1/8 play
30 | C6 over + 1/8 play
31 | D6 over + 1/8 play
32 | C6 over + 1/8 play
33 | D6 over + 1/8 play
34 | A#5 over + 1/8 play
35 | C6 over + 1/8 play
36 | A#5 over + 1/8 play
37 | C6 over + 1/8 play
38 | G#5 over + 1/8 play
39 | C6 swap + 1/4 play ;
40 |
41 | : popcorn ( -- )
42 | 0 popcorn-a
43 | 0 popcorn-a
44 | 0 popcorn-b
45 | 12 popcorn-a
46 | 12 popcorn-a
47 | 12 popcorn-b ;
48 |
49 | "ok" .
--------------------------------------------------------------------------------
/demo/webgl.fs:
--------------------------------------------------------------------------------
1 | (
2 | webgl demo including forth -> glsl transpiler (all written in charlie forth)
3 | inspired by, though not based on:
4 | forthsalon.appspot.com
5 | 06bacb42fe3bdfbd.paste.se
6 | )
7 |
8 | "lib/glsl.fs"
9 | "lib/canvas.fs"
10 | "lib/swizzle.fs"
11 | "lib/bench.fs"
12 | include*
13 |
14 | "#version 300 es
15 |
16 | in vec2 position;
17 | uniform vec2 resolution;
18 | uniform float time;
19 |
20 | void main(void) {
21 | gl_Position = vec4(position, 0.0, 1.0);
22 | }" val> vs-src
23 |
24 | nil val> gl
25 | nil val> canvas
26 | nil var> *shader*
27 | nil var> *vbo*
28 | nil var> *time*
29 | nil var> *resolution*
30 |
31 | : init { fs-src gl }
32 | vs-src "VERTEX_SHADER" gl gl-compile-shader "vs-status: " swap + . { vs }
33 | fs-src "FRAGMENT_SHADER" gl gl-compile-shader "fs-status: " swap + . { fs }
34 | vs fs gl gl-program "prog status: " swap + . { shader }
35 | shader "position" gl gl-attribute-loc dup gl gl-enable-vertex-attrib { vattr }
36 | gl gl-buffer dup gl gl-bind-buffer { vbo }
37 | -1 -1 1 -1 1 1 -1 -1 1 1 -1 1 12 dup float32 ds->array
38 | gl gl-buffer-data
39 | vattr 2 gl gl-vertex-attrib-pointer
40 | shader "time" gl gl-uniform-loc *time* !
41 | shader "resolution" gl gl-uniform-loc *resolution* !
42 | shader *shader* !
43 | vbo *vbo* !
44 | canvas add-to-repl ;
45 |
46 | : reset gl init clear-repl-out canvas add-to-repl ;
47 |
48 | : update
49 | *shader* @ gl gl-use-program
50 | *time* @ now 0.001 * 86400 mod gl gl-uniform1f!
51 | *resolution* @ 600 600 gl gl-uniform2f!
52 | gl "TRIANGLES" js@ 6 gl gl-draw-arrays ;
53 |
54 | : repeatedly { word }
55 | [ drop word apply rdrop ] quot->js-callback 16
56 | *js-window* "setInterval" js@ js-call-2 drop ;
57 |
58 |
59 | 600 600 "webgl2" make-canvas set> gl set> canvas
60 |
61 | glsl> x y t fract ;; dup .
62 |
63 | gl init [ update ] repeatedly
64 |
65 | (
66 |
67 | glsl> x 2 pi * * t + y t 2 * + cos 10 * + sin ;; gl init
68 |
69 | ( ------------- )
70 | glsl>
71 | x 2 * y 3 * t + sin + 3 * 1 mod .5 <
72 | y 2 * x 2 * y 3 * t + sin + t + cos + 3 * 1 mod .5 < and ;;
73 | gl init
74 |
75 | glsl>
76 | x 2 * y 3 * t + sin + 3 * 1 mod
77 | y 2 * x 2 * y 3 * t + sin + t + cos + 3 * 1 mod max
78 | ;;
79 | gl init
80 |
81 | glsl>
82 | x 2 * y 3 * t + sin + 3 * 1 mod
83 | y 2 * x 2 * y 3 * t + sin + t + cos + 3 * 1 mod 2dup max
84 | ;;
85 | gl init
86 |
87 | ( ---------------- )
88 |
89 | glsl>
90 | y 0.5 - 5 * @r ** ** x 0.5 - 5 * @r ** ** - r> r> * -
91 | t 1.5 * sin 14 * - abs t 3 / cos pow
92 | 3 * dup 1 + sin swap dup sin swap 5 + sin
93 | ;;
94 | gl init
95 |
96 | glsl>
97 | : ^2 dup * ;
98 | : x' x .5 - 5 * ;
99 | : y' y .5 - 5 * ;
100 | : rollettcurve y' ^2 ^2 x' ^2 ^2 - x' y' * - ;
101 | : rainbow
102 | dup 3 * 1 + sin swap
103 | dup 3 * 0 + sin swap
104 | dup 3 * 5 + sin swap drop ;
105 |
106 | rollettcurve t 1.5 * sin 14 * - abs t 3 / cos pow rainbow
107 | ;; .
108 |
109 |
110 | ( based on shader by Manwe )
111 | glsl>
112 | : j t 0.12 * sin 0.02 * 0.6666 + ;
113 | : i 2dup z* 0.04 t 0.5 * tau mod cos * j z+ ;
114 | : f i i i i i i i i i ;
115 | : rainbow 3 * dup 1 + sin swap dup sin swap 5 + sin ;
116 | t 0.1 * fract tau * sin 0.5 * 1 + dup y 0.5 - * swap 0.5 x - *
117 | f f f f f f f swap drop rainbow ;; gl init
118 |
119 |
120 | ( pacman )
121 | glsl>
122 | : d dup ;
123 | : m 1 min ;
124 | : f d floor - ;
125 | : c cos abs ;
126 | : j t 4 + 2 * x 8 * floor 8 / + 4 * c 2 / t 4 + 2 / c 4 pow * - ;
127 | : a 1 x x 8 * floor 0.5 + 8 / - d * y ;
128 | : b - d * + sqrt 50 * 8 pow ;
129 | : p x t 4 + pi / f 1.6 * - 0.2 + ;
130 | : v t 4 + pi 2 * / f ;
131 | a j 0.5 b -
132 | v d 0.5 < * 4 * m *
133 | 1 p d * y 0.5 - d * + 36 * 30 pow m -
134 | y 0.5 - p atan2 abs t 10 * c 0.8 * - 16 * m * 0 max
135 | a 0.5 b - 0 max d p 16 * < * +
136 | p d * y 0.58 b m *
137 | v 0.5 >= *
138 | + d 0.2 ;; gl init
139 |
140 | ( tunnel )
141 | glsl>
142 | : x' x 0.5 - t sin 0.2 * + ;
143 | : y' y 0.5 - t 1.5 * cos 0.2 * + ;
144 | : dist x' x' + y' y' + * sqrt ;
145 | : xor + abs 2 mod ;
146 | : b / floor 2 mod ;
147 | : m 256 * floor ;
148 | : a dup rot swap b -rot b xor ;
149 | : w dup
150 |
151 | x' y' atan2 pi / 512 * t 100 * + 256 mod
152 | 128 dist / t 500 * + 256 mod
153 |
154 | rot a * ;
155 | 1 w 2 w 4 w 8 w 16 w 32 w 64 w 128 w
156 | + + + + + + + 256 / dist * dup dup ;; gl init
157 |
158 | ( fractal2 )
159 | glsl>
160 | : x' x .5 - 2.4 * ;
161 | : y' y .7 - 2.4 * ;
162 | : dot dup * swap dup * + ;
163 | : l dup -0.04 * r> r>
164 | 2dup * 2 * x' + >r 2dup z* drop y' + r>
165 | 2dup >r >r dot + abs
166 | rot min swap rot over 1.32457 * t +
167 | r> r> 2dup >r >r
168 | rot dup cos -2 * swap sin -2 *
169 | z+ dot min -rot 1 + ;
170 |
171 | y' x' >r >r 4 4 0 l l l l l l l l l drop
172 | log 8 / negate
173 | swap log 8 / negate
174 | swap dup >r 2 pow
175 | over 3 pow + r> 3 pow
176 | r> r> drop drop ;;
177 | gl init
178 |
179 | ( checker board )
180 | glsl> : xor + 2 mod ; : tile 8 * floor ; x tile y tile xor ;; gl init
181 |
182 | ( x-checkers )
183 | x 25 * cos y 25 * cos <
184 |
185 | ( binary )
186 | : d ( x n - d ) 2 swap ** * floor 2 mod ;
187 | x y 8 * floor d
188 |
189 | ( bit clock )
190 | : h 0.66 t 3600 / floor ;
191 | : m 0.50 t 60 / floor 60 mod ;
192 | : s 0.34 t floor 60 mod ;
193 | : x5 0.10 32 ;
194 | : x4 0.26 16 ;
195 | : x3 0.42 8 ;
196 | : x2 0.58 4 ;
197 | : x1 0.74 2 ;
198 | : x0 0.90 1 ;
199 | : circle y - 2.5 pow swap x - 2.5 pow + sqrt 0.04 < ;
200 | : bit ( x0 b y0 n -- bool ) rot / floor 2 mod -rot circle and ;
201 | : hb h bit + ;
202 | : mb m bit + ;
203 | : sb s bit + ;
204 | x5 h bit
205 | x4 hb
206 | x3 hb
207 | x2 hb
208 | x1 hb
209 | x0 hb
210 | x5 m bit
211 | x4 mb
212 | x3 mb
213 | x2 mb
214 | x1 mb
215 | x0 mb
216 | x5 s bit
217 | x4 sb
218 | x3 sb
219 | x2 sb
220 | x1 sb
221 | x0 sb
222 |
223 | ( 10 PRINT ... 20 GOTO 10 )
224 | ( https://forthsalon.appspot.com/haiku-view/ahBzfmZvcnRoc2Fsb24taHJkchILEgVIYWlrdRiAgICApaaHCgw )
225 | : ' 20 * 1 mod ;
226 | : l dup -0.2 >= swap 0.2 < * ;
227 | : x x 2 * ;
228 | : y t 2 * floor 10 mod y - 2 * ;
229 | : _ 20 * floor 5 + ;
230 | x _ y _ ( t 8 * floor - ) 7 + cos x
231 | _ sin / * 1 mod 0.5 >= dup x '
232 | y ' - l * swap 1 swap - 1 x '
233 | - y ' - l * + dup dup 0.22 *
234 | 0.2 + swap 0.22 * 0.15 + rot
235 | 0.24 * 0.47 +
236 |
237 | ( beauty basic sphere )
238 | x 2 * 1 - dup * y 2 * 1 - dup * + x y
239 |
240 | ( amiga ball )
241 | : q dup * ;
242 | : d2 q swap q + ;
243 | : acos dup q 1 - negate sqrt swap 1 + atan2 2 * ;
244 |
245 | : r 0.5 ;
246 | : r2 r q ;
247 | : tl 1.58 t sin 5 / + ;
248 |
249 | : ' 0.5 - ;
250 | : 's ' tl cos * ;
251 | : 'c ' tl sin * ;
252 | : x' x 'c y 's - ;
253 | : y' y 'c x 's + ;
254 | : l2 x ' y ' d2 ;
255 |
256 | : in? l2 r2 < ;
257 | : z r2 l2 - sqrt ;
258 |
259 | : th y' acos 2 * pi / ;
260 | : ph z x' atan2 pi / t 9 / + ;
261 |
262 | : txtr 25 25 z* cos >r cos r> < ;
263 |
264 | z in? * 1.5 *
265 | ph th txtr
266 | over * dup rot
267 |
268 | ( basic operations on a complex numbers )
269 |
270 | : z x .5 - y .5 - ; ( a complex number stored as a pair of numbers )
271 |
272 | : z- swap -rot - push - pop ; ( difference between two complex numbers )
273 | : z1/ over dup * over dup * + rot over / -rot / ; ( 1 divided by a complex number )
274 | : zmodule ( module of a complex number ) dup * swap dup * + sqrt ;
275 | : zarg ( arg of a complex number ) swap atan2 ;
276 | : e^ ( e raised to a complex power ) over exp over cos * -rot sin swap exp * ;
277 | : zln ( logarithm of a complex number ) 2dup zmodule log -rot zarg ;
278 | : z^ ( complex number raised to a complex power ) push push zln pop pop z* e^ ;
279 |
280 | : a 2 1.4 ;
281 | : b 1 -4 ;
282 | : c -2 t 2 / sin 4 * 1 + ;
283 | : d 0 1 ;
284 |
285 | a z z* b z+
286 | c z z* d z+ c z^
287 | z1/ e^
288 | z* zln
289 |
290 | 2dup zmodule 4 /
291 | swap
292 |
293 |
294 | ( cellular texture )
295 | : s - 2 pow ;
296 | : d y s swap x s + sqrt ;
297 | : h 158 * tan tan tan 1 mod ;
298 | : r r> 1 + dup >r h ;
299 | : i r r d min ;
300 | : 8i i i i i i i i i ;
301 | 1 0 >r 8i 8i r> drop
302 | 0.5 swap - 2 *
303 | dup dup
304 |
305 | ( cellular anim toxi )
306 | glsl>
307 | : s - 2 pow ;
308 | : d y s swap x s + sqrt ;
309 | : h t 0.01 * * sin 1 mod ;
310 | : r r> 1 + dup >r h ;
311 | : i r r d min ;
312 | : 8i i i i i i i i i i i ;
313 | 1 0 >r 8i 8i r> drop
314 | 0.5 swap - 2 * 0.2 over 3 * -1 + sin 0.2 * 0.8 + ;; gl init
315 |
316 | ( mode7 rotation )
317 | ( https://forthsalon.appspot.com/haiku-view/ahBzfmZvcnRoc2Fsb24taHJkcg0LEgVIYWlrdRiWlRMM )
318 | : h .5 - * ; : z 1 1 y h / -1 * ;
319 | : s h t sin ; : d x z * z 2 / - ;
320 | t cos y z * s d h - t - 2 *
321 | floor t cos d s y z * h +
322 | 2 * + 2 mod floor z .2 * /
323 |
324 | ( xor tunnel )
325 | ( https://forthsalon.appspot.com/haiku-view/ahBzfmZvcnRoc2Fsb24taHJkcg0LEgVIYWlrdRiDxxIM )
326 | glsl>
327 | : p 2 * pi * cos 0.5 * 0.5 + ;
328 | : col dup dup p swap 1 3 / + p rot 2 3 / + p ;
329 | : x' x 0.5 - t sin 0.2 * + ;
330 | : y' y 0.5 - t 1.5 * cos 0.2 * + ;
331 | : dist x' x' * y' y' * + sqrt ;
332 | : b / floor 2 mod ;
333 | : w dup x' y' atan2 pi / 512 * t 100 * + 256 mod 128 dist / t 500 * + 256 mod rot dup rot swap b -rot b + abs 2 mod * + ;
334 | 0 4 w 8 w 16 w 32 w 64 w 128 w 256 / t + col
335 | dist 2 * * swap dist 2 * * rot dist 2 * *
336 | ;; gl init
337 |
338 | ( http://www.thesands.ru/forth-demotool/ )
339 | glsl>
340 | : q dup * ;
341 | : sincos ( t - s c ) dup sin swap cos ;
342 | : l
343 | 1 x .5 - r@ 1 + *
344 | r@ 3 - t cos t sin z*
345 | y .5 - r@ 1 + *
346 | swap
347 | t 2.7 / cos t 2.7 / sin z* -rot
348 | q swap q +
349 | dup 8 * swap rot
350 | q + 1.8 + q
351 | - abs 1 min .03 - - 15 pow
352 | r@ / r> .2 - >r
353 | max ;
354 | : j l l l l ;
355 | 0 0 4.2 >r j j j j r> drop 1.5 * dup q swap
356 | ;; gl init
357 |
358 |
359 | ( 2d rotation )
360 | glsl>
361 | : sincos ( x theta -- sin cos ) @r sin over * swap r> cos * ;
362 | : abs2d ( x y -- x y ) abs swap abs swap ;
363 | : add2d ( x y n -- x y ) @r + r> rot + swap ;
364 | : rot2x ( x y theta -- x y ) @r sincos rot r> sincos -rot + -rot swap - swap ;
365 |
366 | x y -0.5 add2d t 2 * rot2x abs2d
367 | ;; gl init
368 |
369 | ( light tunnel // http://glslsandbox.com/e#35712.0 )
370 |
371 | glsl>
372 | : add2d ( x y n -- x y ) @r + r> rot + swap ;
373 | : mul2d ( x y n -- x y ) @r * r> rot * swap ;
374 | : len2d ( x y -- l ) dup * swap dup * + sqrt ;
375 |
376 | t 0.14 * sin 0.2 * t 4 * 3 mod + ( f )
377 |
378 | x y -0.5 add2d 2 mul2d over over ( f x y x y )
379 | atan2 30 * t 16 * - sin 0.02 * 0.5 + ( f x y a ) 1 swap /
380 | mul2d ( f x y )
381 | len2d ( f l )
382 | - abs 10 * ( d )
383 |
384 | 0.8 over / swap ( r d )
385 | 0.2 over / swap ( r g d )
386 | 2.2 swap /
387 |
388 | ;; gl init
389 |
390 | ( particles // http://glslsandbox.com/e#35609.0 )
391 |
392 | : mix ( a b t -- x ) >r over - r> * + ;
393 |
394 | glsl>
395 | : add2n ( x y n -- x y ) @r + r> rot + swap ;
396 | : add2d ( x y x' y' -- x y ) rot + ( x x' y ) -rot + swap ;
397 | : mul2n ( x y n -- x y ) @r * r> rot * swap ;
398 | : len2d ( x y -- l ) dup * swap dup * + sqrt ;
399 | : dup2d over over ;
400 |
401 | : speed t 0.075 * ;
402 |
403 | : bfx ( ax fx -- x' ) speed 0.9 / * sin * 10 * ;
404 | : bfy ( ay fy -- y' fy ) @r speed 2.0 / * cos * 10 * r> ;
405 |
406 | : ball ( x y ax fx ay fy -- c x y )
407 | bfy t 0.01 * * sin 1 swap / >r -rot
408 | bfx swap add2d r> mul2n len2d 0.05 swap / -rot ;
409 |
410 | x 2 * 1 - y 2 * 1 - dup2d ( x y x y )
411 |
412 | 0.03 31 0.09 22 ball dup2d ( c x y x y )
413 | 0.04 22.5 0.04 22.5 ball dup2d ( c c2 x y x y )
414 | 0.05 12 0.03 23 ball dup2d
415 | 0.06 32.5 0.04 33.5 ball dup2d
416 | 0.07 23 0.03 24 ball dup2d
417 | 0.08 21.5 0.02 22.5 ball dup2d
418 | 0.09 33.1 0.07 21.5 ball
419 | drop drop
420 | + + + + + +
421 | 1.6 *
422 | dup 0.22 * swap
423 | dup 0.34 * swap
424 | 0.9 t sin * *
425 | ;; gl init
426 |
427 |
428 | ( http://glslsandbox.com/e#8067.3 // liquid paint )
429 | glsl>
430 | : ti 0.3 * t + ;
431 | : amp 0.6 swap / * ;
432 | : col 3 * sin 0.5 * 0.5 + ;
433 | : i ( x y i -- x' y' i' )
434 | >r over over r@ * r@ ti + sin r@ amp + 1 + -rot
435 | swap r@ * r@ 10 + ti + sin r@ amp + 1.4 - r> 1 + ;
436 | : i5 i i i i i ;
437 | : i10 i5 i5 ;
438 |
439 | x 2 * y 2 *
440 | 1 i10 i10 i10
441 | drop over over
442 | + sin -rot col swap col
443 | ;; gl init
444 |
445 | ( laser lines )
446 |
447 | glsl>
448 | : trunc 1 * fract ;
449 | : x' x trunc ;
450 | : y' y trunc ;
451 | : w t sin 0.49 * 0.5 + 20 * ;
452 | : line
453 | x' - rot x' swap - rot
454 | y' - * -rot y' rot - *
455 | - abs 2 * 1 min 1 swap - ;
456 |
457 | 0.5 0 t sin 1 line
458 | 1 0.5 0.2 t 1.5 * sin line
459 | 0.25 1 t cos 0.25 line
460 | ;; gl init
461 |
462 | ( disco floor )
463 | glsl>
464 | x 9.4 * sin
465 | y 9.4 * sin
466 | t 4 * sin
467 | * *
468 | dup t 2 * sin *
469 | dup t 3 * sin *
470 | ;; gl init
471 |
472 | ( pattern )
473 | glsl>
474 | x 1280 * 640 - dup * y 1280 * 640 - dup * + t 1 / -
475 | 2 * dup
476 | 2 * dup
477 | 2 *
478 | sin rot sin rot sin
479 | ;;
480 | gl init
481 |
482 |
483 | ( sun in the sky )
484 |
485 | glsl>
486 | ( sun )
487 | 0.6 0.85 1
488 | 0.8 x y 10 * 2 t * + sin 0.005 * + - abs dup *
489 | 0.5 y x 10 * 3 t * + cos 0.005 * + - abs dup * + sqrt
490 | - smoothstep
491 | dup y 2 * * swap
492 | dup y 1 * * rot ( r g m )
493 | ( sky )
494 | 1 swap -
495 | dup 0.8 y 0.5 * - * swap ( r g r m )
496 | dup 1 y 0.5 * - * swap ( r g r g b )
497 | >r ( r g r g )
498 | rot max ( r r g )
499 | -rot max swap ( r g )
500 | r>
501 | ;; gl init
502 |
503 | )
504 |
--------------------------------------------------------------------------------
/demo/xt16.fs:
--------------------------------------------------------------------------------
1 | ( beauty sphere )
2 | glsl>
3 | x 2 * 1 - dup * y 2 * 1 - dup * + x y
4 | ;; gl init
5 |
6 | ( liquid paint )
7 | glsl>
8 | : ti 0.3 * t + ;
9 | : amp 0.6 swap / * ;
10 | : col 3 * sin 0.5 * 0.5 + ;
11 | : i ( x y i -- x' y' i' )
12 | >r over over r@ * r@ ti + sin r@ amp + 1 + -rot
13 | swap r@ * r@ 10 + ti + sin r@ amp + 1.4 - r> 1 + ;
14 | : i5 i i i i i ;
15 | : i10 i5 i5 ;
16 |
17 | x 2 * y 2 *
18 | 1 i10 i10 i10
19 | drop over over
20 | + sin -rot col swap col
21 | ;; gl init
22 |
23 | ( laser lines )
24 |
25 | glsl>
26 | : trunc 1 * fract ;
27 | : x' x trunc ;
28 | : y' y trunc ;
29 | : w t sin 0.49 * 0.5 + 20 * ;
30 | : line
31 | x' - rot x' swap - rot
32 | y' - * -rot y' rot - *
33 | - abs 2 * 1 min 1 swap - ;
34 |
35 | 0.5 0 t sin 1 line
36 | 1 0.5 0.2 t 1.5 * sin line
37 | 0.25 1 t cos 0.25 line
38 | ;; gl init
39 |
40 | ( disco floor )
41 | glsl>
42 | x 9.4 * sin
43 | y 9.4 * sin
44 | t 4 * sin
45 | * *
46 | dup t 2 * sin *
47 | dup t 3 * sin *
48 | ;; gl init
49 |
50 | ( particles )
51 |
52 | glsl>
53 | : add2n ( x y n -- x y ) @r + r> rot + swap ;
54 | : add2d ( x y x' y' -- x y ) rot + ( x x' y ) -rot + swap ;
55 | : mul2n ( x y n -- x y ) @r * r> rot * swap ;
56 | : len2d ( x y -- l ) dup * swap dup * + sqrt ;
57 | : dup2d over over ;
58 |
59 | : speed t 0.075 * ;
60 |
61 | : bfx ( ax fx -- x' ) speed 0.9 / * sin * 10 * ;
62 | : bfy ( ay fy -- y' fy ) @r speed 2.0 / * cos * 10 * r> ;
63 |
64 | : ball ( x y ax fx ay fy -- c x y )
65 | bfy t 0.01 * * sin 1 swap / >r -rot
66 | bfx swap add2d r> mul2n len2d 0.05 swap / -rot ;
67 |
68 | x 2 * 1 - y 2 * 1 - dup2d ( x y x y )
69 |
70 | 0.03 31 0.09 22 ball dup2d ( c x y x y )
71 | 0.04 22.5 0.04 22.5 ball dup2d ( c c2 x y x y )
72 | 0.05 12 0.03 23 ball dup2d
73 | 0.06 32.5 0.04 33.5 ball dup2d
74 | 0.07 23 0.03 24 ball dup2d
75 | 0.08 21.5 0.02 22.5 ball dup2d
76 | 0.09 33.1 0.07 21.5 ball
77 | drop drop
78 | + + + + + +
79 | 1.6 *
80 | dup 0.22 * swap
81 | dup 0.34 * swap
82 | 0.9 t sin * *
83 | ;; gl init
84 |
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | readonly BUCKET="s3://forth-thi-ng"
4 | readonly OPTS="--profile thing-2025 --acl public-read"
5 |
6 | gzip -r -k -9 dist
7 |
8 | for f in $(find dist -name "*.gz"); do
9 | src="${f/dist\//}"
10 | dest="$BUCKET/${src%.gz}"
11 | name=$(basename -- "${f%.gz}")
12 | ext="${name##*.}"
13 | case $ext in
14 | js) mime="application/javascript;charset=utf-8" ;;
15 | json) mime="application/json;charset=utf-8" ;;
16 | html) mime="text/html;charset=utf-8" ;;
17 | svg) mime="text/svg+xml;charset=utf-8" ;;
18 | css) mime="text/css;charset=utf-8" ;;
19 | png) mime="image/png" ;;
20 | jpg) mime="image/jpeg" ;;
21 | fs) mime="text/plain;charset=utf-8" ;;
22 | *) mime="application/octet-stream";;
23 | esac
24 | echo "$src -> $dest ($mime)"
25 | aws s3 cp $f $dest $OPTS --content-type $mime --content-encoding gzip
26 | done
27 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 | CharlieREPL
9 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
Data stack
222 |
223 |
224 |
225 |
226 |
227 |
Return stack
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
239 |
240 |
245 |
246 |
247 |
248 |
249 |
250 |
--------------------------------------------------------------------------------
/lib/audio.fs:
--------------------------------------------------------------------------------
1 | vocab/ Audio
2 |
3 | : new-context
4 | *js-window* "AudioContext" js@ js-new ;
5 |
6 | : new-buffer ^( chan len rate ctx )
7 | dup "createBuffer" js@ 3 js-call-n-with ;
8 |
9 | : buffer-channel-data ^( buf ch -- array )
10 | swap dup "getChannelData" js@ js-call-1-with ;
11 |
12 | : fill-buffer!
13 | { buf from n [osc] }
14 | n [ dup [osc] apply buf rot from + js! ] ;
15 |
16 | : connect! ^( ctx node -- )
17 | swap "destination" js@ swap dup "connect" js@ js-call-1-with drop ;
18 |
19 | : disconnect! ^( ctx node -- )
20 | swap "destination" js@ swap dup "disconnect" js@ js-call-1-with drop ;
21 |
22 | : new-source ^( buf loop? ctx )
23 | { buf loop? ctx }
24 | ctx dup "createBufferSource" js@ js-call-with { src }
25 | loop? src "loop" js!
26 | buf src "buffer" js!
27 | ctx src connect!
28 | src ;
29 |
30 | : new-script ^( quot frames ch ctx )
31 | { quote frames ch ctx }
32 | frames 0 ch ctx dup "createScriptProcessor" js@ 3 js-call-n-with { proc }
33 | ch 1 = if
34 | [ "outputBuffer" js@ dup "getChannelData" js@ 0 -rot js-call-1-with quote apply ]
35 | else
36 | [ dup "outputBuffer" js@ dup "getChannelData" js@ 0 -rot js-call-1-with
37 | swap "outputBuffer" js@ dup "getChannelData" js@ 1 -rot js-call-1-with
38 | quote apply ]
39 | then
40 | quot->js-callback proc "onaudioprocess" js!
41 | ctx proc connect!
42 | proc ;
43 |
44 | : start-source ^( src -- )
45 | dup "start" js@ js-call-with drop ;
46 |
47 | : stop-source ^( src -- )
48 | dup "stop" js@ js-call-with drop ;
49 |
50 | : new-track ^( ctx frames rate -- buf bdata src )
51 | { ctx frames rate }
52 | 1 frames rate ctx new-buffer { buf }
53 | buf 0 buffer-channel-data { bdata }
54 | buf true ctx new-source { src }
55 | buf bdata src ;
56 |
57 | : regular-beat { note off n len }
58 | 0 begin dup n < while dup len * off + note apply 1+ repeat drop ;
59 |
60 | : apply-regular { off len n }
61 | 0 begin dup n < while dup len * off + rot apply 1+ repeat drop ;
62 |
63 | : replicate { x n } 0 begin dup n < while x swap 1+ repeat drop ;
64 |
65 | : wrap { t frames -- t' } t dup neg? if frames + then ;
66 |
67 | : echo { bdata frames delay dry wet }
68 | 0 begin
69 | dup frames < while
70 | bdata over ( t bd t )
71 | js@ dry * ( t dv )
72 | over bdata swap ( t dv bd t )
73 | delay - frames wrap ( t dv bd t' )
74 | js@ wet * + ( t dw )
75 | over bdata swap js!
76 | 1+ repeat drop ;
77 |
78 | : reverse { buf len }
79 | len 1 >> { max }
80 | len 1- set> len
81 | 0 begin
82 | dup max <= while
83 | { i } buf i js@
84 | buf len i - dup { j } js@
85 | buf i js!
86 | buf j js!
87 | i 1+ repeat drop ;
88 |
89 | /vocab
--------------------------------------------------------------------------------
/lib/bench.fs:
--------------------------------------------------------------------------------
1 | *js-window* "Date" js@ val> Date
2 |
3 | : now Date js-new dup "getTime" js@ js-call-with ;
4 | : timed { [timed-part] } now { t0 } [timed-part] apply "" now t0 - + "ms" + . ;
5 |
6 | : timed-n ^( n block -- )
7 | { n block }
8 | n dup pos? if block timed 1- block tail-recur then drop ;
9 |
--------------------------------------------------------------------------------
/lib/canvas.fs:
--------------------------------------------------------------------------------
1 | : get-context ^( ctx-type canvas -- gl )
2 | dup "getContext" js@ js-call-1-with ;
3 |
4 | : make-canvas
5 | ^( w h ctx-type -- canvas ctx )
6 | { w h ctx }
7 | ctx "canvas" js-create-element
8 | dup w swap "width" js-attr!
9 | dup h swap "height" js-attr!
10 | tuck get-context ;
11 |
12 | : fill-style! ^( col ctx -- ) "fillStyle" js! ;
13 |
14 | : rect ^( x y w h ctx -- )
15 | { x y w h ctx }
16 | x y w h ctx dup "fillRect" js@ 4 js-call-n-with drop ;
17 |
18 | : gl-context ^( canvas -- gl )
19 | "webgl2" swap dup "getContext" js@ js-call-1-with ;
20 |
21 | *js-window* "WebGLRenderingContext" js@ val> *gl*
22 |
23 | : gl-compile-shader { src type gl -- shader status }
24 | gl dup type js@ swap dup "createShader" js@ js-call-1-with { shader }
25 | shader src gl dup "shaderSource" js@ js-call-2-with drop
26 | shader gl dup "compileShader" js@ js-call-1-with drop
27 | ( shader gl dup "getShaderInfoLog" js@ js-call-1-with . )
28 | shader dup gl dup "COMPILE_STATUS" js@ swap dup "getShaderParameter" js@ js-call-2-with ;
29 |
30 | : gl-attach-shader ^( program shader gl -- )
31 | dup "attachShader" js@ js-call-2-with drop ;
32 |
33 | : gl-program { vs fs gl -- program status }
34 | gl dup "createProgram" js@ js-call-with dump-ds { program }
35 | program vs gl gl-attach-shader
36 | program fs gl gl-attach-shader
37 | program gl dup "linkProgram" js@ js-call-1-with drop
38 | program dup gl dup "LINK_STATUS" js@ swap dup "getProgramParameter" js@ js-call-2-with ;
39 |
40 | : gl-use-program ^( program gl )
41 | dup "useProgram" js@ js-call-1-with drop ;
42 |
43 | : gl-attribute-loc ^( program att gl -- loc )
44 | dup "getAttribLocation" js@ js-call-2-with ;
45 |
46 | : gl-uniform-loc ^( program uni gl -- loc )
47 | dup "getUniformLocation" js@ js-call-2-with ;
48 |
49 | : gl-uniform1f! ^( uni x gl -- )
50 | dup "uniform1f" js@ js-call-2-with drop ;
51 |
52 | : gl-uniform2f! ^( uni x y gl -- )
53 | dup "uniform2f" js@ 3 js-call-n-with drop ;
54 |
55 | : gl-buffer ^( gl -- vbo ) dup "createBuffer" js@ js-call-with ;
56 |
57 | : gl-bind-buffer ^( vbo gl -- )
58 | dup "ARRAY_BUFFER" js@ -rot dup "bindBuffer" js@ js-call-2-with drop ;
59 |
60 | : gl-buffer-data ^( data gl -- )
61 | dup "ARRAY_BUFFER" js@ -rot
62 | dup "STATIC_DRAW" js@ swap
63 | dup "bufferData" js@ 3 js-call-n-with drop ;
64 |
65 | : gl-vertex-attrib-pointer { loc stride gl }
66 | loc stride gl "FLOAT" js@ false 0 0 gl dup "vertexAttribPointer" js@ 6 js-call-n-with drop ;
67 |
68 | : gl-enable-vertex-attrib ^( vattr gl -- )
69 | dup "enableVertexAttribArray" js@ js-call-1-with drop ;
70 |
71 | : gl-draw-arrays ^( type n gl -- )
72 | 0 -rot dup "drawArrays" js@ 3 js-call-n-with drop ;
73 |
--------------------------------------------------------------------------------
/lib/glsl.fs:
--------------------------------------------------------------------------------
1 | 1 var> *glsl-comments*
2 |
3 | : make-var-table { prefix n }
4 | n [ n swap - prefix swap + ] n dup heap-allot ds->heap ;
5 |
6 | "s" 64 make-var-table val> *ds* -1 dup var> *dsp* var> *max-dsp*
7 | "r" 64 make-var-table val> *rs* -1 dup var> *rsp* var> *max-rsp*
8 |
9 | : stack! ^( sp n ) over @ + swap ! ;
10 | : ds@ ^( n ) *dsp* @ + *ds* + @ ;
11 | : rs@ ^( n ) *rsp* @ + *rs* + @ ;
12 | : ds! ^( n ) *dsp* swap stack! ;
13 | : rs! ^( n ) *rsp* swap stack! ;
14 |
15 | : reset-stack
16 | -1 dup *dsp* ! *max-dsp* !
17 | -1 dup *rsp* ! *max-rsp* ! ;
18 |
19 | : update-max-stack
20 | *dsp* @ *max-dsp* @ max *max-dsp* !
21 | *rsp* @ *max-rsp* @ max *max-rsp* ! ;
22 |
23 | js-obj var> *glsl-dict*
24 |
25 | ( GLSL preamble )
26 |
27 | "#version 300 es
28 |
29 | #ifdef GL_FRAGMENT_PRECISION_HIGH
30 | precision highp int;
31 | precision highp float;
32 | #else
33 | precision mediump int;
34 | precision mediump float;
35 | #endif
36 | uniform vec2 resolution;
37 | uniform float time;
38 | layout(location=0) out vec4 fragColor;
39 |
40 | float random(in vec2 p, inout float seed) {
41 | seed = fract(sin(mod(seed + dot(p.xy, vec2(12.9898, 78.233)), 3.14)) * 43758.5453);
42 | return seed;
43 | }
44 | " val> *glsl-preamble*
45 |
46 | ( main function header )
47 |
48 | "void main() {
49 | float seed = time;
50 | float _x = gl_FragCoord.x / resolution.x;
51 | float _y = gl_FragCoord.y / resolution.y;
52 | float tmp;
53 | " val> *glsl-header*
54 |
55 | : cast "(" + swap + ")" + ;
56 | : vec4 { r g b a } r ", " g ", " b ", " a + + + + + + "vec4" cast ;
57 | : tos= 0 ds@ " = " + swap + ;
58 | : rtos= 0 rs@ " = " + swap + ;
59 | : math-op -1 ds! 0 ds@ swap 1 ds@ + + tos= ;
60 | : math-fn1 "(" 0 ds@ ")" + + + tos= ;
61 | : math-fn2 -1 ds! "(" 0 ds@ ", " 1 ds@ ")" + + + + + tos= ;
62 | : math-fn3 -2 ds! "(" 0 ds@ ", " 1 ds@ ", " 2 ds@ ")" + + + + + + + tos= ;
63 | : logic-op -1 ds! 0 ds@ swap 1 ds@ " ? 1.0 : 0.0" + + + tos= ;
64 | : logic-op* -1 ds! 0 ds@ "bool" cast swap 1 ds@ "bool" cast " ? 1.0 : 0.0" + + + tos= ;
65 | : int-op2 -1 ds! 0 ds@ "int" cast swap 1 ds@ "int" cast + + "float" cast tos= ;
66 |
67 | : ->x 1 ds! "_x" tos= ;
68 | : ->y 1 ds! "_y" tos= ;
69 | : ->t 1 ds! "time" tos= ;
70 | : ->pi 1 ds! 3.14159265358979 tos= ;
71 | : ->half-pi 1 ds! 1.5707963267949 tos= ;
72 | : ->tau 1 ds! 6.28318530717959 tos= ;
73 | : ->random 1 ds! "random(gl_FragCoord.xy / resolution.xy, seed)" tos= ;
74 | : ->dup 1 ds! -1 ds@ tos= ;
75 | : ->2dup 2 ds! -2 ds@ "; " -1 ds@ " = " -3 ds@ + + + + tos= ;
76 | : ->over 1 ds! -2 ds@ tos= ;
77 | : ->drop -1 ds! "" ;
78 | : ->>r -1 ds! 1 rs! 1 ds@ rtos= ;
79 | : ->r> 1 ds! -1 rs! 1 rs@ tos= ;
80 | : ->@r 1 rs! 0 ds@ rtos= ;
81 | : ->r@ 1 ds! 0 rs@ tos= ;
82 | : ->swap "tmp = " -1 ds@ "; " over " = " 0 ds@ "; " over " = tmp" + + + + + + + + ;
83 | : ->rswap "tmp = " -1 rs@ "; " over " = " 0 rs@ "; " over " = tmp" + + + + + + + + ;
84 | : ->rot "tmp = " -2 ds@ "; " over " = " -1 ds@ "; "
85 | over " = " 0 ds@ "; " over " = tmp" + + + + + + + + + + + + ;
86 | : ->-rot "tmp = " -2 ds@ "; " over " = " 0 ds@ "; "
87 | over " = " -1 ds@ "; " over " = tmp" + + + + + + + + + + + + ;
88 | : ->madd -2 ds! 0 ds@ " * " 1 ds@ " + " 2 ds@ + + + + tos= ;
89 | : ->z+ -2 ds! -1 ds@ " = " over " + " 1 ds@ "; " 0 ds@ " = " over " + " 2 ds@ + + + + + + + + + + ;
90 | : ->z* -2 ds! "tmp = " -1 ds@ " * " 1 ds@ " - " 0 ds@ " * " 2 ds@ "; " + + + + + + + +
91 | -1 ds@ " * " 2 ds@ " + " 0 ds@ " * " 1 ds@ + + + + + + tos=
92 | "; " -1 ds@ " = tmp" + + + + ;
93 | : ->** 0 ds@ " * " over + + tos= ;
94 |
95 | : ->+ " + " math-op ;
96 | : ->- " - " math-op ;
97 | : ->* " * " math-op ;
98 | : ->/ " / " math-op ;
99 | : ->= " == " logic-op ;
100 | : ->> " > " logic-op ;
101 | : ->< " < " logic-op ;
102 | : ->>= " >= " logic-op ;
103 | : -><= " <= " logic-op ;
104 | : ->not= " != " logic-op ;
105 | : ->and " && " logic-op* ;
106 | : ->or " || " logic-op* ;
107 | : ->bitand " & " int-op2 ;
108 | : ->bitor " | " int-op2 ;
109 | : ->bitxor " ^ " int-op2 ;
110 | : ->mod "mod" math-fn2 ;
111 | : ->pow "pow" math-fn2 ;
112 | : ->min "min" math-fn2 ;
113 | : ->max "max" math-fn2 ;
114 | : ->atan2 "atan" math-fn2 ;
115 | : ->step "step" math-fn2 ;
116 | : ->sin "sin" math-fn1 ;
117 | : ->cos "cos" math-fn1 ;
118 | : ->tan "tan" math-fn1 ;
119 | : ->log "log" math-fn1 ;
120 | : ->exp "exp" math-fn1 ;
121 | : ->sqrt "sqrt" math-fn1 ;
122 | : ->floor "floor" math-fn1 ;
123 | : ->ceil "ceil" math-fn1 ;
124 | : ->abs "abs" math-fn1 ;
125 | : ->fract "fract" math-fn1 ;
126 | : ->negate "-" math-fn1 ;
127 | : ->mix "mix" math-fn3 ;
128 | : ->clamp "clamp" math-fn3 ;
129 | : ->smoothstep "smoothstep" math-fn3 ;
130 |
131 | : ?float "." over dup "indexOf" js@ js-call-1-with -1 = if ".0" + then ;
132 | : push-literal 1 ds! ?float tos= ;
133 |
134 | : glsl-stack-vars { addr n }
135 | n 0 >= if
136 | "float " 0 begin
137 | dup n <= while
138 | @r addr + @ + r@ n = if ";\n" else ", " then + r> 1+
139 | repeat drop
140 | else "" then ;
141 |
142 | : glsl-frag-color
143 | "fragColor = "
144 | *dsp* @ 1+
145 | case/
146 | 0 of "0.0" dup dup "1.0" endof
147 | 1 of 0 ds@ dup dup "1.0" endof
148 | 2 of -1 ds@ 0 ds@ "0.0" "1.0" endof
149 | 3 of -2 ds@ -1 ds@ 0 ds@ "1.0" endof
150 | default -3 ds@ -2 ds@ -1 ds@ 0 ds@
151 | /case
152 | vec4 + ";\n" + ;
153 |
154 | : glsl-word-comment ^( acc word -- acc' word )
155 | *glsl-comments* @
156 | if dup ":" not= if dup "// " swap + "\n" + rot swap + swap then then ;
157 |
158 | : glsl-slurp-comment drop 1 begin -proc-comment dup zero? until drop ;
159 |
160 | : glsl-end-line dup "length" js@ pos? if ";\n" + then ;
161 |
162 | : glsl-continue? dup dup "" not= swap ";;" not= and ;
163 |
164 | : glsl-lookup-word ^( word -- def ) *glsl-dict* @ swap js@ ;
165 |
166 | : glsl-define-word
167 | read-token> { word }
168 | ( "// define word : " word + ) ""
169 | js-array
170 | begin read-token> dup dup "" not= swap ";" not= and while
171 | dup "(" = if glsl-slurp-comment else js-apush then
172 | repeat drop
173 | *glsl-dict* @ word js! ;
174 |
175 | : glsl-custom-word?
176 | dup glsl-lookup-word js-array? ;
177 |
178 | 0 var> *inline-recur*
179 |
180 | : glsl-inline-word { word }
181 | *glsl-comments* @ if "// inline word: " word "\n" + + else "" then
182 | word glsl-lookup-word dup "length" js@ { def n }
183 | 0 dup { i } begin i n < while
184 | def over js@
185 | ( acc i word )
186 | rot swap glsl-word-comment rot swap
187 | glsl-custom-word? not
188 | ( acc i word flag )
189 | if dup "->" swap + find ( acc i word addr )
190 | dup undefined =
191 | if drop push-literal else nip call then
192 | rot swap glsl-end-line + swap
193 | update-max-stack
194 | else ( recursive expansion )
195 | swap >r n >r def >r word >r
196 | *inline-recur* @ call +
197 | r> set> word r> set> def r> set> n r> dup set> i
198 | then
199 | 1+ dup set> i
200 | repeat drop
201 | *glsl-comments* @ if "// end inline" "\n" + + then ;
202 |
203 | find> glsl-inline-word *inline-recur* !
204 |
205 | : glsl-compile-word ^( token -- glsl )
206 | dup ":" =
207 | if drop glsl-define-word
208 | else glsl-custom-word?
209 | if glsl-inline-word
210 | else dup "->" swap + find dup undefined =
211 | if drop push-literal else nip call then
212 | then
213 | then ;
214 |
215 | : glsl>
216 | reset-stack
217 | "" begin
218 | read-token> glsl-continue? while
219 | dup "(" =
220 | if glsl-slurp-comment
221 | else
222 | glsl-word-comment glsl-compile-word glsl-end-line +
223 | update-max-stack
224 | then
225 | repeat drop
226 | *glsl-preamble* *glsl-header* +
227 | *ds* *max-dsp* @ glsl-stack-vars +
228 | *rs* *max-rsp* @ glsl-stack-vars +
229 | swap + glsl-frag-color + "}" + ;
--------------------------------------------------------------------------------
/lib/list.fs:
--------------------------------------------------------------------------------
1 | vocab/ HList
2 | : node ^( n -- addr ) 2 heap-allot @r ! -1 r@ 1+ ! r> ;
3 | : next ^( addr -- addr ) 1+ @ ;
4 | : proceed? ^( addr -- addr' flag ) dup pos? if next dup 0 >= else false then ;
5 | : cons ^( n addr -- addr ) swap node @r 1+ ! r> ;
6 | : rcons ^( n addr -- addr ) swap node @r swap 1+ ! r> ;
7 | : find-node ^( n addr -- addr ) begin dup pos? if 2dup @ not= else false then while 1+ @ repeat nip ;
8 | : contains? ^( n addr -- flag ) find-node pos? ;
9 | : list ^( a b .. n -- addr ) dup pos? if >r node r> 1- [ drop cons ] else drop then ;
10 | : list/ 0 begin read-token> dup "/list" not= while swap 1+ repeat drop list ;
11 | : map ^( quote addr -- ) 2dup swap apply begin proceed? while 2dup swap apply repeat 2drop ;
12 | : first ^( addr -- n ) dup pos? if @ else drop nil then ;
13 | : second ^( addr -- n ) proceed? if @ else drop nil then ;
14 | : third ^( addr -- n ) proceed? if second else drop nil then ;
15 | : length ^( addr -- len ) 1 >r begin proceed? while r> 1+ >r repeat drop r> ;
16 | : nth ^( addr n -- n' ) [ drop next ] @ ;
17 | /vocab
18 |
19 | \ HList. list/ 10 20 30 /list val> a
--------------------------------------------------------------------------------
/lib/math.fs:
--------------------------------------------------------------------------------
1 | vocab/ Math
2 | *js-window* "Math" js@ val> *math*
3 | *math* "sin" js@ val> *sin*
4 | *math* "cos" js@ val> *cos*
5 | *math* "floor" js@ val> *floor*
6 | *math* "ceil" js@ val> *ceil*
7 | *math* "pow" js@ val> *pow*
8 | *math* "random" js@ val> *random*
9 | *math* "sqrt" js@ val> *sqrt*
10 | *math* "min" js@ val> *min*
11 | *math* "max" js@ val> *max*
12 | *math* "PI" js@ val> pi
13 | pi 2 / val> half-pi
14 | pi 2 * val> two-pi
15 | : sin *sin* js-call-1 ;
16 | : cos *cos* js-call-1 ;
17 | : floor *floor* js-call-1 ;
18 | : ceil *ceil* js-call-1 ;
19 | : sqrt *sqrt* js-call-1 ;
20 | : pow *pow* js-call-2 ;
21 | : min *min* js-call-2 ;
22 | : max *max* js-call-2 ;
23 | : random *random* js-call 2 * 1- ;
24 | /vocab
--------------------------------------------------------------------------------
/lib/swizzle.fs:
--------------------------------------------------------------------------------
1 | : float32 *js-window* "Float32Array" js@ js-new-1 ;
2 |
3 | : sw2@ ^( i1 i2 v -- v1 v2 ) @r swap js@ r> rot js@ swap ;
4 | : sw4@ ^( i1 i2 i3 i4 v -- v1 v2 v3 v4 ) @r sw2@ r> -rot sw2@ 2r> ;
5 |
6 | : swxx@ ^( v -- x x ) 0 js@ dup ;
7 | : swxy@ ^( v -- x y ) dup 0 js@ swap 1 js@ ;
8 | : swxz@ ^( v -- x z ) dup 0 js@ swap 2 js@ ;
9 |
10 | : sw2xy! ^( x y v -- ) dup -rot 1 js! 0 js! ;
11 | : sw2xz! ^( x z v -- ) dup -rot 2 js! 0 js! ;
12 | : sw2yz! ^( y z v -- ) dup -rot 2 js! 1 js! ;
13 | : sw1xy! ^( n v -- ) 2dup 0 js! 1 js! ;
14 | : sw1xz! ^( n v -- ) 2dup 0 js! 2 js! ;
15 | : sw1xw! ^( n v -- ) 2dup 0 js! 3 js! ;
16 | : sw1yz! ^( n v -- ) 2dup 1 js! 2 js! ;
17 | : sw1yw! ^( n v -- ) 2dup 1 js! 3 js! ;
18 | : sw1zw! ^( n v -- ) 2dup 2 js! 3 js! ;
19 |
20 | : sw-add-xy ^( a b c -- )
21 | -rot
22 | 2dup 0 js@ swap 0 js@ + >r
23 | 1 js@ swap 1 js@ +
24 | over 1 js!
25 | r> swap 0 js! ;
26 |
27 | : sw-add-xyz ^( a b c -- )
28 | -rot
29 | 2dup 0 js@ swap 0 js@ + >r
30 | 2dup 1 js@ swap 1 js@ + >r
31 | 2 js@ swap 2 js@ + over 2 js!
32 | r> over 1 js!
33 | r> swap 0 js! ;
34 |
35 | : mat4-identity ^( addr ) 1 over 0 js! 1 over 5 js! 1 over 10 js! 1 swap 15 js! ;
36 |
37 | : madd2 ^( a1 a2 b1 b2 -- prodsum ) * >r * r> + ;
38 |
39 | : madd4 ^( a1 a2 b1 b2 c1 c2 d1 d2 -- prodsum ) madd2 >r madd2 r> + ;
40 |
41 | : mat4@ { addr } 0 begin dup 4 < while addr over js@ swap 1+ repeat drop ;
42 | : mat16@ { addr } 0 begin dup 16 < while addr over js@ swap 1+ repeat drop ;
43 |
44 | : mul4x4 ^( m1addr m2addr )
45 | >r mat16@ { m00 m01 m02 m03 m10 m11 m12 m13 m20 m21 m22 m23 m30 m31 m32 m33 }
46 | r> mat16@ { n00 n01 n02 n03 n10 n11 n12 n13 n20 n21 n22 n23 n30 n31 n32 n33 }
47 | m00 n00 m10 n01 m20 n02 m30 n03 madd4
48 | m01 n00 m11 n01 m21 n02 m31 n03 madd4
49 | m02 n00 m12 n01 m22 n02 m32 n03 madd4
50 | m03 n00 m13 n01 m23 n02 m33 n03 madd4
51 |
52 | m00 n10 m10 n11 m20 n12 m30 n13 madd4
53 | m01 n10 m11 n11 m21 n12 m31 n13 madd4
54 | m02 n10 m12 n11 m22 n12 m32 n13 madd4
55 | m03 n10 m13 n11 m23 n12 m33 n13 madd4
56 |
57 | m00 n20 m10 n21 m20 n22 m30 n23 madd4
58 | m01 n20 m11 n21 m21 n22 m31 n23 madd4
59 | m02 n20 m12 n21 m22 n22 m32 n23 madd4
60 | m03 n20 m13 n21 m23 n22 m33 n23 madd4
61 |
62 | m00 n30 m10 n31 m20 n32 m30 n33 madd4
63 | m01 n30 m11 n31 m21 n32 m31 n33 madd4
64 | m02 n30 m12 n31 m22 n32 m32 n33 madd4
65 | m03 n30 m13 n31 m23 n32 m33 n33 madd4 ;
66 |
67 | : trace4 { a b c d } a " " + b + " " + c + " " + d + . ;
68 |
69 | : trace16 { m00 m01 m02 m03 m10 m11 m12 m13 m20 m21 m22 m23 m30 m31 m32 m33 }
70 | m00 m01 m02 m03 trace4
71 | m10 m11 m12 m13 trace4
72 | m20 m21 m22 m23 trace4
73 | m30 m31 m32 m33 trace4 ;
--------------------------------------------------------------------------------
/lib/synth.fs:
--------------------------------------------------------------------------------
1 | "lib/audio.fs"
2 | "lib/math.fs"
3 | "lib/bench.fs"
4 | "lib/swizzle.fs"
5 | include*
6 |
7 | 44100 val> *rate*
8 | *rate* 44100 / val> *rate-scale*
9 | *rate-scale* 4 * val> *ctrl-rate-scale*
10 | 174.614115728 *rate* / val> *freq-scale*
11 | 0x20000 val> *max-delay*
12 | *max-delay* float32 val> *delay-buffer-left*
13 | *max-delay* float32 val> *delay-buffer-right*
14 | 27 val> *instr-size*
15 | 19 val> *note-size*
16 | 8 val> *max-poly*
17 | *note-size* *max-poly*
18 | * heap-allot val> *note-buf*
19 | *max-poly* heap-allot
20 | dup val> *active-notes* *max-poly* clear-mem!
21 |
22 | Math. two-pi val> tau
23 | Math. *sin* val> sin
24 |
25 | "C" "C#" "D" "D#" "E" "F" "F#" "G" "G#" "A" "A#" "B"
26 | 12 dup heap-allot ds->heap val> *raw-note-names*
27 |
28 | \ C3 = 99, C4 = 111, C5 = 123
29 | : note->freq ( n ) 128 - 12 / 2 swap Math. pow *freq-scale* * ;
30 |
31 | : -make-note-word ^( name note )
32 | swap create-word compile> lit >dict compile> exit ;
33 |
34 | : make-notes
35 | 0 begin dup 60 < while
36 | dup 12 mod *raw-note-names* + @ ( i name )
37 | over 12 / 0 bit-or 3 + + ( i name )
38 | over 99 + ( i name note )
39 | -make-note-word
40 | 1+ repeat drop ;
41 |
42 | make-notes
43 |
44 | : note> read-token> read-token> -make-note-word ;
45 |
46 | : osc-sin ^( f ) tau * sin js-call-1 ;
47 | : osc-saw ^( f ) 1 mod 2 * 1- ;
48 | : osc-sq ^( f ) 1 mod 0.5 < if 1 else -1 then ;
49 | : osc-tri ^( f ) 1 mod 4 * dup 2 < if 1- else 3 swap - then ;
50 |
51 | [ osc-sin osc-saw osc-sq osc-tri ] val> *osc-table*
52 |
53 | : get-osc ( id ) *osc-table* + @ ;
54 |
55 | \ \\\\\\\\\\\\\\\\\\\\ instrument parameters
56 |
57 | : instr-osc1 ;
58 | : instr-osc1@ @ get-osc ;
59 | : instr-vol1 1+ ;
60 | : instr-semi1 2 + ;
61 | : instr-xenv1 3 + ;
62 | : instr-osc2 4 + ;
63 | : instr-osc2@ 4 + @ get-osc ;
64 | : instr-vol2 5 + ;
65 | : instr-semi2 6 + ;
66 | : instr-detune2 7 + ;
67 | : instr-xenv2 8 + ;
68 | : instr-noise-vol 9 + ;
69 | : instr-env-attack 10 + ;
70 | : instr-env-sustain 11 + ;
71 | : instr-env-release 12 + ;
72 | : instr-lfo-osc 13 + ;
73 | : instr-lfo-osc@ 13 + @ get-osc ;
74 | : instr-lfo-amp 14 + ;
75 | : instr-lfo-freq 15 + ;
76 | : instr-lfo-osc-freq 16 + ;
77 | : instr-lfo-fx-freq 17 + ;
78 | : instr-fx-filter 18 + ;
79 | : instr-fx-freq 19 + ;
80 | : instr-fx-res 20 + ;
81 | : instr-fx-dist 21 + ;
82 | : instr-fx-drive 22 + ;
83 | : instr-fx-pan 23 + ;
84 | : instr-fx-pan-freq 24 + ;
85 | : instr-fx-delay-amp 25 + ;
86 | : instr-fx-delay-time 26 + ;
87 |
88 | \ \\\\\\\\\\\\\\\\\\\\ instrument definition
89 |
90 | : instrument> *instr-size* dup heap-allot ds->heap val> ;
91 |
92 | : clone-instrument ^( addr -- addr' ) *instr-size* heap-allot @r *instr-size* mem-copy r> ;
93 |
94 | 2 100 128 0 3 201 133 10 0 0 5 6 160 0 195 6 0.1 1 2 135 0 0 32 147 6 121 6 instrument> softy
95 |
96 | \ \\\\\\\\\\\\\\\\\\\\ note structure
97 |
98 | : note-start@ @ ;
99 | : note-progress 1+ ;
100 | : note-osc1-time 2 + ;
101 | : note-osc2-time 3 + ;
102 | : note-osc1-freq 4 + ;
103 | : note-osc2-freq 5 + ;
104 | : note-instr 6 + ; \ 13 entries
105 | : note-age ^( t idx -- age ) *note-size* * *note-buf* + @ - ;
106 |
107 | : init-note { addr note inst }
108 | addr now addr !
109 | note inst instr-semi1 @ + 128 - note->freq
110 | over note-osc1-freq !
111 | note inst instr-semi2 @ + 128 - note->freq
112 | inst instr-detune2 @ 0.0008 * 1+ *
113 | over note-osc2-freq !
114 | 0 over 2dup 2dup note-progress ! note-osc1-time ! note-osc2-time !
115 | inst over note-instr 13 mem-copy ;
116 |
117 | ( check if note slot is active )
118 | : free-note? ^( idx -- addr flag ) *active-notes* + @ zero? ;
119 |
120 | ( returns 1st free note slot or else oldest )
121 | : find-free-note ( -- idx )
122 | now { t } -1 { idx } 0 { oldest-idx } -100 { max-age }
123 | *max-poly* 1-
124 | begin
125 | dup 0 >= idx neg? and while
126 | dup free-note?
127 | if dup set> idx
128 | else t over note-age dup max-age >
129 | if set> max-age dup set> oldest-idx else drop then
130 | then 1-
131 | repeat
132 | drop idx neg?
133 | if oldest-idx else idx then ;
134 |
135 | : note-address ^( id ) *note-size* * *note-buf* + ;
136 | : note-active? *active-notes* + @ 1 = ;
137 |
138 | : clear-buffer! { buf start end } 0 buf end 1- begin dup start >= while js!! 1- repeat 2drop drop ;
139 |
140 | : free-note-slot ^( id -- ) 0 swap *active-notes* + ! ;
141 |
142 | : scaled-env-param ^( param -- param' ) dup *ctrl-rate-scale* * * ;
143 |
144 | : note-env-params ^( noteaddr -- a s r invr )
145 | note-instr
146 | dup instr-env-attack @ scaled-env-param swap
147 | dup instr-env-sustain @ scaled-env-param swap
148 | instr-env-release @ scaled-env-param
149 | dup -1 swap / ;
150 |
151 | : generate-note { lbuf rbuf size id instr }
152 | id note-active?
153 | if
154 | 1 { e }
155 | id note-address dup { note }
156 | note-env-params 4dup { attack sustain release inv-release } drop
157 | note note-progress @ dup { progress } - + + { remaining }
158 | attack sustain + { sustain-end }
159 | remaining size <=
160 | if id free-note-slot else size set> remaining then
161 | note dup 2dup 2dup
162 | note-osc1-freq @ { osc1f } note-osc1-time @ { osc1t }
163 | note-osc2-freq @ { osc2f } note-osc2-time @ { osc2t }
164 | note-instr dup 2dup 2dup
165 | instr-osc1@ { osc1 } instr-vol1 @ { osc1vol } instr-xenv1 @ { xenv1 }
166 | instr-osc2@ { osc2 } instr-vol2 @ { osc2vol } instr-xenv2 @ { xenv2 }
167 | rbuf 0 begin dup remaining < while
168 | progress dup attack < if
169 | attack / set> e
170 | else
171 | sustain-end >=
172 | if progress sustain-end - inv-release * 1+ set> e then
173 | then
174 | osc1f xenv1 if e dup * * then osc1t + dup set> osc1t osc1 call osc1vol *
175 | osc2f xenv2 if e dup * * then osc2t + dup set> osc2t osc2 call osc2vol * +
176 | e 0.002441481 * *
177 | >r 2dup 2dup js@ r> + -rot js! ( update rbuf )
178 | progress 1+ set> progress
179 | 1+
180 | repeat
181 | 2drop
182 | ( note )
183 | osc1t over note-osc1-time !
184 | osc2t over note-osc2-time !
185 | progress swap note-progress !
186 | then ;
187 |
188 | \ *note-buf* 135 softy init-note 1 *active-notes* ! drop
189 |
190 | \ 2048 float32 val> buf
191 |
192 | \ *note-buf* 7 *note-size* * + 99 softy init-note 1 *active-notes* 7 + !
193 |
194 | \ *note-buf* *note-size* 8 * dump
195 |
196 | : render-slice { frames }
197 | [ dup 0 frames clear-buffer!
198 | nil swap frames 0 softy generate-note
199 | rdrop rdrop ] ;
200 |
201 | : play { note instr } *note-buf* note instr init-note drop 1 *active-notes* ! ;
202 |
203 | Audio. new-context val> ctx
204 | 2048 render-slice 2048 1 ctx Audio. new-script val> script
205 |
206 | (
207 |
208 | *note-buf* C6 softy init-note drop
209 | 250 sleep
210 | *note-buf* D#6 softy init-note drop
211 | 375 sleep
212 | *note-buf* A6 softy init-note drop
213 |
214 | )
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@thi.ng/charlie",
3 | "version": "1.0.2",
4 | "main": "index.js",
5 | "repository": "https://github.com/thi-ng/charlie",
6 | "author": "Karsten Schmidt ",
7 | "license": "MIT",
8 | "scripts": {
9 | "clean": "rm -rf .cache build out",
10 | "start": "vite --open",
11 | "build": "yarn clean && vite build --base='./' && cp -R demo lib dist/",
12 | "preview": "vite preview --host --open"
13 | },
14 | "dependencies": {
15 | "@thi.ng/api": "^8.11.21",
16 | "@thi.ng/expose": "^1.2.48"
17 | },
18 | "devDependencies": {
19 | "typescript": "^5.8.2",
20 | "vite": "^6.2.0"
21 | },
22 | "browserslist": [
23 | "last 3 Chrome versions"
24 | ],
25 | "browser": {
26 | "process": false
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | // _______ ___ ___ _______ _______ ___ ___ _______ ___ ___ ___ ___
2 | // | _ | Y | _ | _ | | | | _ | | Y | Y |
3 | // |. 1___|. 1 |. 1 |. l |. | |. |. 1___| |. | |. |
4 | // |. |___|. _ |. _ |. _ |. |___|. |. __)_ ______|. | |. \_/ |
5 | // |: 1 |: | |: | |: | |: 1 |: |: 1 |______|: 1 |: | |
6 | // |::.. . |::.|:. |::.|:. |::.|:. |::.. . |::.|::.. . | \:.. ./|::.|:. |
7 | // `-------`--- ---`--- ---`--- ---`-------`---`-------' `---' `--- ---'
8 | //
9 | // (c) 2015 - 2025 Karsten Schmidt // MIT licensed
10 |
11 | import { Charlie } from "./vm.js";
12 | import { KERNEL } from "./kernel.js";
13 | import { REPL } from "./repl.js";
14 |
15 | Charlie.interpreter(KERNEL);
16 |
17 | new REPL(Charlie).start();
18 |
--------------------------------------------------------------------------------
/src/kernel.ts:
--------------------------------------------------------------------------------
1 | // _______ ___ ___ _______ _______ ___ ___ _______ ___ ___ ___ ___
2 | // | _ | Y | _ | _ | | | | _ | | Y | Y |
3 | // |. 1___|. 1 |. 1 |. l |. | |. |. 1___| |. | |. |
4 | // |. |___|. _ |. _ |. _ |. |___|. |. __)_ ______|. | |. \_/ |
5 | // |: 1 |: | |: | |: | |: 1 |: |: 1 |______|: 1 |: | |
6 | // |::.. . |::.|:. |::.|:. |::.|:. |::.. . |::.|::.. . | \:.. ./|::.|:. |
7 | // `-------`--- ---`--- ---`--- ---`-------`---`-------' `---' `--- ---'
8 | //
9 | // (c) 2015 - 2025 Karsten Schmidt // MIT licensed
10 |
11 | /**
12 | * Partially based on:
13 | * https://github.com/phaendal/mkforth4-js/blob/master/builtin.fs
14 | */
15 | export const KERNEL = [
16 | ": find> read-token> find ;",
17 | ": xt->dfa xt->cfa cfa->dfa ;",
18 | ": vm-mode? vm-mode@ = ;",
19 | ": compile-mode? *vm-mode-compile* vm-mode? ;",
20 | ": immediate-mode? *vm-mode-immediate* vm-mode? ;",
21 | ": ^immediate immediate! ; immediate!",
22 | ": postpone ^immediate find> >dict ;",
23 | ": 'lit lit lit >dict ;",
24 | ": '>dict lit >dict >dict ;",
25 | ": compile> ^immediate 'lit find> >dict '>dict ;",
26 | ": recur ^immediate latest@ >dict ;",
27 | ": latest-start latest@ xt->dfa ;",
28 | ": tail-recur ^immediate compile> branch latest-start >dict ;",
29 |
30 | // control flow
31 | // if
32 |
33 | ": dict-here@ 0 >dict ;",
34 | ": if ^immediate compile> alt-branch ;",
35 | ": dict-here@ swap ! ;",
36 | ": then ^immediate ;",
37 | ": else ^immediate compile> branch swap ;",
38 |
39 | // loop
40 |
41 | ": begin ^immediate dict-here@ ;",
42 | ": until ^immediate compile> alt-branch >dict ;",
43 | ": again ^immediate compile> branch >dict ;",
44 | ": while ^immediate compile> alt-branch dict-here@ 0 >dict ;",
45 | ": repeat ^immediate compile> branch swap >dict dict-here@ swap ! ;",
46 |
47 | // comments
48 |
49 | ': l-paren? dup dup "(" = swap "^(" = or ;',
50 | ': r-paren? dup ")" = ;',
51 | ": -proc-r-paren r-paren? if drop 1- else drop then ;",
52 | ": -proc-l-paren l-paren? if drop 1+ else -proc-r-paren then ;",
53 | ": -proc-comment read-token> -proc-l-paren ;",
54 | ": ( ^immediate 1 begin -proc-comment dup zero? until drop ;",
55 |
56 | // define word
57 |
58 | ": create-word ( name -- ) new-word-header latest@ set-word-name! ;",
59 |
60 | // values
61 |
62 | ": create-value create-word compile> lit >dict compile> exit ;",
63 | ": val> ( x -- ) read-token> create-value ;",
64 | ": value-addr ( xt -- addr ) xt->dfa 1+ ;",
65 | ": set-when-compile ( addr -- ) compile> lit >dict compile> ! ;",
66 | ": set> ( ?n -- ) ^immediate find> value-addr compile-mode? if set-when-compile else ! then ;",
67 |
68 | // doc comments
69 |
70 | "0 val> *doc-string*",
71 | ": -doc-proc-r-paren r-paren? if drop 1-",
72 | ' else *doc-string* swap + " " + set> *doc-string* then ;',
73 | ": -doc-proc-l-paren l-paren? if drop 1+ else -doc-proc-r-paren then ;",
74 | ": -doc-proc-comment read-token> -doc-proc-l-paren ;",
75 |
76 | ': ^( ^immediate "" set> *doc-string* 1 begin',
77 | " -doc-proc-comment dup zero? until drop",
78 | " compile-mode?",
79 | " if *doc-string* latest@ set-word-doc! then ;",
80 |
81 | ": get-word-name ^( xt -- name ) @ 0 js@ ;",
82 | ": get-word-doc ^( xt -- doc ) @ 4 js@ ;",
83 | ": hidden? ^( xt -- bool ) @ 2 js@ ;",
84 | ": immediate? ^( xt -- bool ) @ 1 js@ ;",
85 |
86 | ": doc> find>",
87 | " dup undefined = if",
88 | ' "Word not found" .',
89 | " else",
90 | ' dup get-word-name " ( " + swap get-word-doc',
91 | ' dup undefined = if drop "no doc" then',
92 | ' + " )" + .',
93 | " then ;",
94 |
95 | // post-add doc strings
96 |
97 | ": set-word-doc> ^( doc -- ) find> set-word-doc! ;",
98 | ": set-doc-latest! ^( doc -- ) latest@ set-word-doc! ;",
99 | '"name --" set-word-doc> create-word',
100 | '"x --" set-word-doc> val>',
101 | '"x --" set-word-doc> set>',
102 |
103 | // vars
104 |
105 | ": cells-after ( n -- addr after-addr ) dict-here@ dup rot + ;",
106 | ": allot ( n -- addr ) cells-after dict-here! ;",
107 | ": create-variable ( name -- addr ) 1 allot swap create-word compile> lit >dict compile> exit ;",
108 | ": var> ( n -- ) read-token> create-variable latest@ call ! ;",
109 | ": ready-value ( n addr - addr value n ) dup @ rot ;",
110 | ": +! ( n addr -- ) ready-value + swap ! ;", // FIXME: really need those?
111 | ": -! ( n addr -- ) ready-value - swap ! ;", // FIXME:
112 | ": inc! ( addr -- ) dup @ 1+ swap ! ;",
113 | ": dec! ( addr -- ) dup @ 1- swap ! ;",
114 |
115 | // local variables
116 |
117 | "0 val> *locals-target-word*",
118 | ": -clear-target-word-addr 0 set> *locals-target-word* ;",
119 | ": -save-target-word latest@ set> *locals-target-word* ;",
120 |
121 | "0 val> *locals-true-prev*",
122 | ": -save-true-prev latest@ prev-word@ set> *locals-true-prev* ;",
123 | ": -restore-true-prev *locals-true-prev* latest@ prev-word! ;",
124 |
125 | ": -2prev-chain ^( xt -- xt prev1 prev2 ) dup prev-word@ ( xt prev1 ) dup prev-word@ ;",
126 |
127 | ": -swap-prev-chain",
128 | " ^( var -> word -> prev => word -> var -> prev )",
129 | " latest@ -2prev-chain ( -- &var &word &prev )",
130 | " rot tuck ( -- &word &var &prev &var )",
131 | " prev-word! swap tuck ( -- &word &var &word )",
132 | " prev-word! ( -- &word )",
133 | " latest! ;",
134 |
135 | "0 val> *local-var-name*",
136 | "0 val> *local-var-addr*",
137 |
138 | ": -allot-var-space ^( -- addr ) compile> branch dict-here@ 0 >dict ;",
139 | ": -jump-to-here ^( addr -- ) dict-here@ swap ! ;",
140 | ": create-local-var ^( name -- ) 0 swap create-value ;",
141 | ": -save-local-var-addr latest@ set> *local-var-addr* ;",
142 | ": -save-target-word *locals-target-word* zero? if -save-target-word -save-true-prev then ;",
143 | ": -restore-target-word *locals-target-word* zero? not if -restore-true-prev then ;",
144 | ": -compile-set-local compile> lit *local-var-addr* value-addr >dict compile> ! ;",
145 |
146 | ": -compile-local-variable-definition ^( name -- addr )",
147 | " set> *local-var-name*",
148 | " -save-target-word",
149 | " -allot-var-space",
150 | " *local-var-name* create-local-var",
151 | " -save-local-var-addr",
152 | " -swap-prev-chain",
153 | " -jump-to-here",
154 | " -compile-set-local ;",
155 |
156 | ': -close-local-variable? ^( token -- bool ) dup "}" = ;',
157 | ': -close-stack-input? ^( token -- bool ) dup "--" = ;',
158 | ": -close-local-definition read-token> -close-local-variable? not if drop tail-recur then ;",
159 |
160 | ": -read-local-definitions",
161 | " read-token>",
162 | " -close-local-variable? not if",
163 | " -close-stack-input? not if",
164 | " tail-recur",
165 | " then drop -close-local-definition",
166 | " then drop ;",
167 |
168 | ": { ^immediate",
169 | " 0 -read-local-definitions",
170 | " begin dup zero? not while",
171 | " -compile-local-variable-definition",
172 | " repeat drop ;",
173 |
174 | ": : -clear-target-word-addr : ;",
175 |
176 | ": ; ^immediate -restore-target-word postpone ; ;",
177 |
178 | // quotation
179 |
180 | ": -open-quote-compile ( -- addr2 )",
181 | " compile> lit dict-here@ 0 >dict ( -- addr1 )",
182 | " compile> branch dict-here@ 0 >dict ( -- addr1 addr2 )",
183 | " swap ( -- addr2 addr1 )",
184 | " dict-here@ ( -- addr2 addr1 start-quotation )",
185 | " swap ! ; ( -- addr2 )",
186 |
187 | ": -open-quote-immediate ( -- addr ) dict-here@ postpone compile-mode! ;",
188 | ": -close-quote-compile ( addr2 -- ) dict-here@ swap ! ;",
189 |
190 | ": [ ^immediate ( -- vm-mode addr2|addr1 )",
191 | " vm-mode@ compile-mode? if -open-quote-compile else -open-quote-immediate then ;",
192 |
193 | ": ] ^immediate ( vm-mode addr2|addr1 -- )",
194 | " compile> exit swap vm-mode! compile-mode? if -close-quote-compile then ;",
195 |
196 | ": apply ^immediate",
197 | " compile-mode? if",
198 | " compile> lit",
199 | " dict-here@ 0 >dict",
200 | " compile> >r",
201 | " compile> jump",
202 | " dict-here@ swap !",
203 | " else",
204 | " 0 >r jump",
205 | " then ;",
206 |
207 | ": quote> ^immediate ( -- dfa )",
208 | " compile-mode? if",
209 | " compile> lit find> >dict compile> xt->dfa",
210 | " else",
211 | " find> xt->dfa",
212 | " then ;",
213 |
214 | // stack ops
215 |
216 | ": 2over 3 pick 3 pick ;",
217 | ": 3dup dup 2over rot ;",
218 | ": 4dup 2over 2over ;",
219 |
220 | // combinators
221 |
222 | "\\ https://gist.github.com/crcx/8060687",
223 | "\\ >r ... r>",
224 | ": dip swap >r apply r> ;",
225 | "\\ dup >r ... r>",
226 | ": sip over >r apply r> ;",
227 | "\\ 12 [ 3 * ] [ 4 * ] bi => 12 3 * 12 4 *",
228 | ": bi >r sip r> apply ;",
229 | "\\ 2 4 [ 3 * ] [ 5 * ] bi* => 2 3 * 4 5 *",
230 | ": bi* >r dip r> apply ;",
231 | "\\ 2 4 [ 3 * ] bi@ => 2 3 * 4 3 *",
232 | ": bi@ dup bi* ;",
233 |
234 | // control flow w/ quotation
235 |
236 | ": { cond [true-part] [false-part] -- ... }",
237 | " cond if [true-part] else [false-part] then apply ;",
238 |
239 | ": ^( cond quot ) [ ] ;",
240 |
241 | ": ^( cond quot ) [ ] swap ;",
242 |
243 | ": ^( [cond-part] [loop-part] -- )",
244 | " { [cond-part] [loop-part] }",
245 | " begin [cond-part] apply while [loop-part] apply repeat ;",
246 |
247 | ": ",
248 | " ^( counter [loop-part] -- ? )",
249 | " { counter [loop-part] }",
250 | " [ counter 0 > ]",
251 | " [ counter [loop-part] apply counter 1- set> counter ]",
252 | " ;",
253 |
254 | ": { cin cout [loop-part] }",
255 | " cin { cin' }",
256 | " [ cout 0 >= ]",
257 | " [ cin cout [loop-part] apply",
258 | " cin 1- dup neg? if drop cin' then dup set> cin",
259 | " cin' = if cout 1- set> cout then ]",
260 | " ;",
261 |
262 | // Case / switch
263 |
264 | ": case/ ^immediate 0 ;",
265 | ": of ^immediate",
266 | " compile> swap",
267 | " compile> tuck",
268 | " compile> =",
269 | " postpone if",
270 | " compile> drop ;",
271 | ": endof ^immediate postpone else ;",
272 | ": default ^immediate compile> drop ;",
273 | ": /case ^immediate",
274 | " begin dup zero? not while postpone then repeat drop ;",
275 |
276 | // debug
277 |
278 | '*js-window* "String" js@ dup val> String "fromCharCode" js@ val> *charcode->str*',
279 | ": [char] *charcode->str* js-call-1 ;",
280 | ": hexdigit dup 10 < if 48 else 87 then + [char] ;",
281 | ": hex8 dup 4 >> 15 bit-and hexdigit swap 15 bit-and hexdigit + ;",
282 | ': hex16 "0x" swap dup 8 >> hex8 swap 255 bit-and hex8 + + ;',
283 | ': hex24 "0x" swap dup 16 >> hex8 swap dup 8 >> hex8 swap 255 bit-and hex8 + + + ;',
284 |
285 | ": hide! ( xt ) true set-hidden! ;",
286 | ": forget { xt -- } xt prev-word@ latest! xt dict-here! ;",
287 | ": forget> find> forget ;",
288 | ': dump-memory dup hex24 " " + swap @ + . ;',
289 | ": dump { addr len i -- } addr i + dump-memory len 1- pos? [ addr len 1- i 1+ tail-recur ] ;",
290 | ": dump ( addr len -- ) 0 dump ;",
291 |
292 | ': dump-ds dsp@ 0 begin 2dup >= while dup "" swap + ": " + over 3 + pick + . 1+ repeat 2drop ;',
293 | ': spy> "entering: " find> @r get-word-name + . dump-ds r> call ;',
294 |
295 | ': show-rsp { rsp -- } rsp ": " + rsp rpick @ get-word-name + . ;',
296 | ": dump-rs { rsp -- } rsp 0 >= [ rsp show-rsp rsp 1- tail-recur ] ;",
297 | ": dump-rs rsp@ 1- dump-rs ;",
298 |
299 | ": show-words",
300 | " latest@ { adr }",
301 | ' ""',
302 | " [ adr pos? ]",
303 | ' [ adr get-word-name + " " + adr prev-word@ set> adr ]',
304 | " . ;",
305 |
306 | ': starts-with? ^( str pre -- flag ) swap dup "startsWith" js@ js-call-1-with ;',
307 | ': public-word? "-" starts-with? not ;',
308 |
309 | ": show-public-words",
310 | " latest@ { adr }",
311 | ' ""',
312 | " [ adr pos? ]",
313 | ' [ adr get-word-name dup public-word? if + " " + else drop then adr prev-word@ set> adr ]',
314 | " . ;",
315 |
316 | ": count-words",
317 | " latest@ { adr }",
318 | " 0",
319 | " [ adr pos? ]",
320 | " [ 1+ adr prev-word@ set> adr ]",
321 | " ;",
322 |
323 | // see
324 |
325 | ": word-length { now-addr word-addr -- }",
326 | " now-addr prev-word@ word-addr =",
327 | " [ now-addr word-addr - ]",
328 | " [ now-addr prev-word@ word-addr tail-recur ] ;",
329 |
330 | ": word-length { word-addr } latest@ word-addr word-length ;",
331 |
332 | ": latest-word-length ( -- len ) dict-here@ latest@ - ;",
333 |
334 | ": word-length { word-addr -- }",
335 | " latest@ word-addr = [ latest-word-length ] [ word-addr word-length ] ;",
336 |
337 | ": ' ^immediate compile> lit find> >dict ;",
338 |
339 | ": -see-1-addr { addr name -- str addr-inc }",
340 | ' "\n" addr hex24 +',
341 | ' " " + name + " " +',
342 | " addr 1+ @ hex24 +",
343 | " 2 ;",
344 |
345 | ': -see-mem-else { obj -- str } " " obj get-word-name + ;',
346 |
347 | ": -see-mem-else { obj -- str }",
348 | ' obj js-fn? [ " [js-fn]" ] [ obj -see-mem-else ] ;',
349 |
350 | ": -see-mem-else { obj -- str }",
351 | ' obj js-array? [ " [js-array]" ] [ obj -see-mem-else ] ;',
352 |
353 | ": -see-mem-else { obj -- str }",
354 | ' obj js-obj? [ " [js-obj]" ] [ obj -see-mem-else ] ;',
355 |
356 | ": see-mem { addr -- str addr-inc }",
357 | " addr @ case/",
358 | ' \' lit of addr "lit" -see-1-addr endof',
359 | ' \' branch of addr "branch" -see-1-addr endof',
360 | ' \' alt-branch of addr "alt-branch" -see-1-addr endof',
361 | ' \' exit of "" 1 endof',
362 | ' default "\n" addr hex24 + addr @ -see-mem-else + 1',
363 | " /case ;",
364 |
365 | ": -see-memory { str addr i -- str i }",
366 | " addr i + see-mem ( str addr-inc )",
367 | " i + set> i",
368 | " str swap + i ;",
369 |
370 | ": see { str addr len i -- code(str) }",
371 | " i len >=",
372 | ' [ str " ;" + ]',
373 | " [ str addr i -see-memory ( str i )",
374 | " set> i set> str",
375 | " str addr len i tail-recur ] ;",
376 |
377 | ": see { name addr len -- code(str) }",
378 | ' addr hex24 " : " + name +',
379 | " addr 2 + len 2 -",
380 | " 0 see ;",
381 |
382 | ": see { addr -- code(str) }",
383 | " addr get-word-name",
384 | " addr",
385 | " addr word-length",
386 | " see ;",
387 |
388 | ": see> find>",
389 | " dup undefined =",
390 | ' [ drop "word not found" . ]',
391 | " [ see . ] ;",
392 |
393 | // heap
394 | ": heap-allot ^( n -- addr ) heap-here@ dup rot + heap-here! ;",
395 |
396 | ": mem-copy ^( addr addr2 n -- )",
397 | " { n } 0 begin dup n < while >r over @ over ! 1+ swap 1+ swap r> 1+ repeat",
398 | " 2drop drop ;",
399 |
400 | ": clear-mem! { addr len }",
401 | " 0 len 1- begin dup 0 >= while 2dup addr + ! 1- repeat 2drop ;",
402 |
403 | ": ds->heap ^( ... n addr )",
404 | " over + 1-",
405 | " swap 1- begin dup 0 >= while >r tuck ! 1- r> 1- repeat",
406 | " drop 1+ ;",
407 |
408 | // arrays
409 |
410 | ": array/ js-array begin",
411 | ' read-token> dup "/array" not= while',
412 | " js-apush",
413 | " repeat drop ;",
414 |
415 | ": ds->array ^( a b c .. n array -- array )",
416 | " { n array }",
417 | " n [ dup pick array rot n swap - js! ] ",
418 | " n begin dup 0 > while swap drop 1- repeat drop array ;",
419 |
420 | // private
421 |
422 | ': -create-priv-anchor "*private-anchor*" create-word ;',
423 | ": compile-prev-to-latest-code ( this-addr -- )",
424 | " compile> lit",
425 | " prev-word@ >dict",
426 | " compile> latest!",
427 | " compile> exit ;",
428 |
429 | ": -compile-first-anchor",
430 | " dict-here@ { this-addr }",
431 | " -create-priv-anchor",
432 | " this-addr compile-prev-to-latest-code ;",
433 |
434 | ': -get-priv-anchor-prev ( -- addr ) "*private-anchor*" find prev-word@ ;',
435 |
436 | ': -hide-priv-anchor ( -- ) "*private-anchor*" find hide! ;',
437 |
438 | ": -compile-prev-to-prev-code ( this-addr prev -- )",
439 | " compile> lit >dict",
440 | " compile> lit >dict",
441 | " compile> prev-word! ;",
442 |
443 | ": -compile-hide-self-code ( this-addr ) compile> lit >dict compile> hide! ;",
444 |
445 | ": -compile-reveal-anchor-code { prev this-addr -- }",
446 | " prev this-addr -compile-prev-to-prev-code",
447 | " compile> exit ;",
448 |
449 | ": -compile-reveal-anchor",
450 | " -get-priv-anchor-prev { prev }",
451 | " -hide-priv-anchor",
452 | " dict-here@ { this-addr }",
453 | " -create-priv-anchor",
454 | " this-addr prev -compile-reveal-anchor-code ;",
455 |
456 | ": private/ ^immediate -compile-first-anchor ;",
457 | ': /private ^immediate "*private-anchor*" find call ;',
458 | ": reveal>> ^immediate -compile-reveal-anchor ;",
459 |
460 | // exceptions
461 |
462 | "0 val> *no-exception-sign*",
463 | ": exception-marker ( -- no-exception-sign ) rdrop *no-exception-sign* ;",
464 | "quote> exception-marker val> *exception-marker-dfa*",
465 |
466 | ": catch ( dfa -- exception-code )",
467 | " dsp@ 1- >r",
468 | " *exception-marker-dfa* >r",
469 | " apply ;",
470 |
471 | ": exception-marker? ( rsp -- ) rpick *exception-marker-dfa* = ;",
472 |
473 | ": continue-search-emarker? { rsp -- bool }",
474 | " rsp -1 > rsp exception-marker? not and ;",
475 |
476 | ": throw-exception { code rsp -- }",
477 | " rsp 1- set> rsp",
478 | " rsp rsp!",
479 | " r> dsp!",
480 | " code ;",
481 |
482 | ": aborted-or-uncaught-throw ( code -- )",
483 | " case/",
484 | ' -1 of "[ABORTED!]" . endof',
485 | ' "[UNCAUGHT THROW] " swap + .',
486 | " /case",
487 | " -1 rsp!",
488 | " 0 >r ;",
489 |
490 | ": throw { exception-code -- }",
491 | " rsp@ { rsp }",
492 | " [ rsp continue-search-emarker? ]",
493 | " [ rsp 1- set> rsp ] ",
494 | " rsp -1 >",
495 | " [ exception-code rsp throw-exception ]",
496 | " [ exception-code aborted-or-uncaught-throw ] ;",
497 |
498 | ": throw { exception-code -- }",
499 | " exception-code 0 not= [ exception-code throw ] ;",
500 |
501 | ": abort -1 throw ;",
502 |
503 | // vocabs
504 | "0 val> *vocab*",
505 | ": vocab->anchor-addr ( addr -- addr ) 5 + ;",
506 | ": vocab->latest-addr ( addr -- addr ) 6 + ;",
507 | ": vocab->prevoc-addr ( addr -- addr ) 7 + ;",
508 | ": vocab->prev ( addr -- addr )",
509 | " vocab->anchor-addr @ prev-word@ ;",
510 |
511 | ": prev-vocab@ ( addr -- addr ) vocab->prevoc-addr @ ;",
512 | ": prev-vocab! ( addr voc-addr -- ) vocab->prevoc-addr ! ;",
513 |
514 | ": -set-anchor-prev { latest vocab-addr -- }",
515 | " latest vocab-addr vocab->anchor-addr @ ( latest anchor-addr )",
516 | " prev-word! ;",
517 |
518 | ": -set-vocab-anchor ( anchor-addr vocab-addr -- ) vocab->anchor-addr ! ;",
519 | ": -set-vocab-latest ( latest-addr vocab-addr -- ) vocab->latest-addr ! ;",
520 |
521 | ": -create-vocab-header { name -- }",
522 | " dict-here@",
523 | " name create-word",
524 | " compile> lit",
525 | " >dict",
526 | " compile> exit ;",
527 |
528 | ": -allot-vocab-addrs",
529 | " 0 >dict ( anchor-addr )",
530 | " 0 >dict ( latest-addr )",
531 | " 0 >dict ( prevoc-addr )",
532 | " ;",
533 |
534 | ": -create-anchor-word ( -- addr ) new-word-header latest@ ;",
535 |
536 | ": search-in { vocab name -- addr }",
537 | " latest@ { latest }",
538 | " vocab vocab->latest-addr @ latest!",
539 | " name find",
540 | " latest latest! ;",
541 |
542 | ": access-word-in ( vocab name -- )",
543 | " search-in { word }",
544 | " word immediate? not compile-mode? and",
545 | " [ word >dict ]",
546 | " [ word call ] ;",
547 |
548 | ": -create-accessor ( name -- addr )",
549 | ' "." + create-word',
550 | " postpone ^immediate",
551 | " compile> lit",
552 | " dict-here@ 0 >dict",
553 | " compile> read-token>",
554 | " compile> access-word-in",
555 | " compile> exit ;",
556 |
557 | ": create-vocab { name -- }",
558 | " name -create-accessor { accessor }",
559 | " name -create-vocab-header",
560 | " latest@ accessor !",
561 | " -allot-vocab-addrs",
562 | " latest@ { vaddr }",
563 | " -create-anchor-word { aaddr }",
564 | " aaddr vaddr -set-vocab-anchor",
565 | " aaddr vaddr -set-vocab-latest",
566 | " vaddr latest! ;",
567 |
568 | ": open-vocab { addr -- }",
569 | " latest@ addr -set-anchor-prev",
570 | " addr vocab->latest-addr @ latest!",
571 | " *vocab* addr prev-vocab!",
572 | " addr set> *vocab* ;",
573 |
574 | ": does-not-exist? ( name ) find undefined = ;",
575 |
576 | ": create-vocab { name -- }",
577 | " name does-not-exist?",
578 | " [ name create-vocab ] ;",
579 |
580 | ": vocab/",
581 | " read-token> { name }",
582 | " name create-vocab",
583 | " name find open-vocab ;",
584 |
585 | ": /vocab",
586 | " latest@ *vocab* -set-vocab-latest",
587 | " *vocab* vocab->prev latest!",
588 | " *vocab* prev-vocab@ set> *vocab* ;",
589 |
590 | // Core vocab
591 |
592 | "0 val> *kernel-latest*",
593 | '"Core" create-vocab',
594 | ": -set-prev-to-now-prev { addr -- }",
595 | " *vocab* vocab->prev addr -set-anchor-prev ;",
596 |
597 | ": -set-now-prev-to-latest ( addr -- )",
598 | " vocab->latest-addr @ *vocab* -set-anchor-prev ;",
599 |
600 | ": with { addr -- }",
601 | " addr -set-prev-to-now-prev ",
602 | " addr -set-now-prev-to-latest ;",
603 |
604 | ": with> find> with ;",
605 |
606 | ": <>",
607 | " /vocab",
608 | " *kernel-latest* latest!",
609 | " Core open-vocab ;",
610 |
611 | "latest@ set> *kernel-latest*",
612 | "Core open-vocab",
613 |
614 | // List
615 |
616 | ': -mark-as-cons-list { list -- } true list "cons?" js! ;',
617 | ": list? { obj -- bool }",
618 | ' obj js-obj? [ obj "cons?" js@ true = ] [ obj nil? ] ;',
619 |
620 | ': set-first ( value list -- ) "first" js! ;',
621 | ': set-rest ( value list -- ) "rest" js! ;',
622 |
623 | ": cons { rest first -- list }",
624 | " js-obj { list }",
625 | " list -mark-as-cons-list",
626 | " first list set-first",
627 | " rest list set-rest",
628 | " list ;",
629 |
630 | ': first ^( list -- value ) dup nil? [ "first" js@ ] ;',
631 | ': rest ^( list -- xs ) dup nil? [ "rest" js@ ] ;',
632 | ": second ^( list -- value ) rest first ;",
633 | ": third ^( list -- value ) rest rest first ;",
634 |
635 | ": list> nil read-token> create-value ;",
636 |
637 | ": reverse { xs acc -- xs }",
638 | " xs nil? [ acc ] [ xs rest acc xs first cons tail-recur ] ;",
639 | ": reverse ( xs ) nil reverse ;",
640 |
641 | ": length { xs acc -- len }",
642 | " xs nil? [ acc ] [ xs rest acc 1+ tail-recur ] ;",
643 | ": length ( xs -- len ) 0 length ;",
644 |
645 | "private/",
646 | " : push-when-compile",
647 | " find> { vaddr }",
648 | " vaddr >dict",
649 | " compile> swap",
650 | " compile> cons",
651 | " compile> lit",
652 | " vaddr value-addr >dict",
653 | " compile> ! ;",
654 |
655 | " : push-when-immediate",
656 | " find> { vaddr }",
657 | " vaddr call swap cons",
658 | " vaddr value-addr ! ;",
659 |
660 | " : pop-when-compile",
661 | " find> { vaddr }",
662 | " vaddr >dict",
663 | " compile> first",
664 | " vaddr >dict",
665 | " compile> rest",
666 | " compile> lit",
667 | " vaddr value-addr >dict",
668 | " compile> ! ;",
669 |
670 | " : pop-when-immediate",
671 | " find> { vaddr }",
672 | " vaddr call first",
673 | " vaddr call rest",
674 | " vaddr value-addr ! ;",
675 |
676 | " reveal>>",
677 | " : push> ^immediate compile-mode? [ push-when-compile ] [ push-when-immediate ] ;",
678 | " : pop> ^immediate compile-mode? [ pop-when-compile ] [ pop-when-immediate ] ;",
679 | "/private",
680 |
681 | "list> *list-expr-dsp*",
682 |
683 | "private/",
684 | " : accum-list-fin ( -- ) pop> *list-expr-dsp* drop ;",
685 | " : accum-list-end? ( -- bool ) dsp@ *list-expr-dsp* first = ;",
686 | " : accum-list { acc -- xs }",
687 | " accum-list-end? [ accum-list-fin acc ] [ acc swap cons tail-recur ] ;",
688 | " reveal>>",
689 | " : list/ ( -- ) dsp@ push> *list-expr-dsp* ;",
690 | " : /list ( -- list ) nil accum-list ;",
691 | "/private",
692 |
693 | // DOM
694 |
695 | " vocab/ DOM private/",
696 | " reveal>>",
697 | ' : create-div ^( -- elm ) "div" js-create-element ;',
698 | ' : create-span ^( -- elm ) "span" js-create-element ;',
699 |
700 | " : set-style { name value elm -- }",
701 | ' elm "style" js@ { style }',
702 | " value style name js! ;",
703 |
704 | " : first-second-rest { xs -- f s r }",
705 | " xs first",
706 | " xs second",
707 | " xs rest rest ;",
708 |
709 | " : set-styles { css-list elm -- }",
710 | " css-list nil?",
711 | " [ css-list first-second-rest { name value rest-list }",
712 | " name value elm set-style",
713 | " rest-list elm tail-recur ] ;",
714 |
715 | ' : hide { elm -- } "display" "none" elm set-style ;',
716 | ' : show { elm -- } "display" "" elm set-style ;',
717 |
718 | " : delete { elm -- }",
719 | " elm js-parent-node { parent }",
720 | " elm parent js-remove-child ;",
721 |
722 | " : add-event-listener { quot elm event -- }",
723 | ' elm "addEventListener" js@ { adder }',
724 | " quot quot->js-callback { callback }",
725 | " callback event elm adder js-call-2-with drop ;",
726 | "/private /vocab",
727 | ].join("\n");
728 |
--------------------------------------------------------------------------------
/src/repl.ts:
--------------------------------------------------------------------------------
1 | // _______ ___ ___ _______ _______ ___ ___ _______ ___ ___ ___ ___
2 | // | _ | Y | _ | _ | | | | _ | | Y | Y |
3 | // |. 1___|. 1 |. 1 |. l |. | |. |. 1___| |. | |. |
4 | // |. |___|. _ |. _ |. _ |. |___|. |. __)_ ______|. | |. \_/ |
5 | // |: 1 |: | |: | |: | |: 1 |: |: 1 |______|: 1 |: | |
6 | // |::.. . |::.|:. |::.|:. |::.|:. |::.. . |::.|::.. . | \:.. ./|::.|:. |
7 | // `-------`--- ---`--- ---`--- ---`-------`---`-------' `---' `--- ---'
8 | //
9 | // (c) 2015 - 2025 Karsten Schmidt // MIT licensed
10 |
11 | import type { Fn } from "@thi.ng/api";
12 | import { exposeGlobal } from "@thi.ng/expose";
13 | import type { IVM, Stack } from "./vm.js";
14 |
15 | const MAX_HISTORY = 50;
16 | const MAX_SHOW = 200;
17 | const MAX_SHOW_STACK = 20;
18 |
19 | const replInput = document.getElementById("repl-input")!;
20 | const btEval = document.getElementById("bt-eval");
21 | const replOutput = document.getElementById("repl-out");
22 | const dsItems = document.getElementById("ds-items");
23 | const rsItems = document.getElementById("rs-items");
24 |
25 | const removeAllChildren = (elm: HTMLElement) => {
26 | while (elm.firstChild) {
27 | elm.removeChild(elm.firstChild);
28 | }
29 | };
30 |
31 | export class REPL {
32 | history: string[] = [];
33 | historySel: number;
34 | wordStart: number;
35 | wordCache: string[];
36 | wordCandidates: string[];
37 | nextCandidate: number;
38 | edited: boolean;
39 | codePrinter: Fn;
40 | helpPrinter: Fn;
41 | textPrinter: Fn;
42 | errorPrinter: Fn;
43 |
44 | constructor(public vm: IVM) {}
45 |
46 | replPush(elm) {
47 | replOutput.appendChild(elm);
48 | if (replOutput.children.length > MAX_SHOW) {
49 | replOutput.removeChild(replOutput.firstChild);
50 | }
51 | elm.scrollIntoView();
52 | replInput.focus();
53 | }
54 |
55 | defPrinter(class_name, prefix = "") {
56 | return (str: string) => {
57 | const elm = document.createElement("pre");
58 | elm.textContent = prefix + str;
59 | elm.className = class_name;
60 | if (class_name == "repl-out-code") {
61 | elm.addEventListener("click", () => {
62 | (replInput).value = str;
63 | replInput.focus();
64 | });
65 | }
66 | this.replPush(elm);
67 | };
68 | }
69 |
70 | repl(code: string) {
71 | this.codePrinter(code);
72 | this.vm.interpreter(code);
73 | this.showDS();
74 | this.showRS();
75 | this.updateWordCache();
76 | }
77 |
78 | pushHistory(code) {
79 | const history = this.history;
80 | if (history[history.length - 1] === code) {
81 | return;
82 | }
83 | history.push(code);
84 | if (history.length > MAX_HISTORY) {
85 | history.shift();
86 | }
87 | }
88 |
89 | clearInput() {
90 | replInput.value = "";
91 | replInput.focus();
92 | this.historySel = undefined;
93 | }
94 |
95 | restoreHistory() {
96 | replInput.value = this.history[this.historySel];
97 | }
98 |
99 | historyBack() {
100 | if (!history.length) {
101 | return;
102 | }
103 | if (this.historySel === undefined || this.historySel === 0) {
104 | this.historySel = this.history.length - 1;
105 | } else {
106 | this.historySel--;
107 | }
108 | this.restoreHistory();
109 | }
110 |
111 | historyForward() {
112 | if (!history.length) {
113 | return;
114 | }
115 | if (
116 | this.historySel === undefined ||
117 | this.historySel === this.history.length - 1
118 | ) {
119 | this.historySel = 0;
120 | } else {
121 | this.historySel++;
122 | }
123 | this.restoreHistory();
124 | }
125 |
126 | clearReplOut() {
127 | removeAllChildren(replOutput);
128 | }
129 |
130 | clearStackView(stack: HTMLElement) {
131 | removeAllChildren(stack);
132 | }
133 |
134 | showStackItem(stack: HTMLElement, val: any) {
135 | val = JSON.stringify(val).substr(0, 256);
136 | const el = document.createElement("pre");
137 | el.textContent = val;
138 | el.className = "stack-item";
139 | stack.appendChild(el);
140 | }
141 |
142 | showStack(sp: number, stack: Stack, el: HTMLElement) {
143 | this.clearStackView(el);
144 | for (var i = 0; sp >= 0 && i < MAX_SHOW_STACK; sp--, i++) {
145 | this.showStackItem(el, stack[sp]);
146 | }
147 | }
148 |
149 | showDS() {
150 | this.showStack(this.vm.DSP(), this.vm.DS, dsItems);
151 | }
152 |
153 | showRS() {
154 | this.showStack(this.vm.RSP(), this.vm.RS, rsItems);
155 | }
156 |
157 | evalInput() {
158 | this.pushHistory(replInput.value);
159 | this.repl(replInput.value);
160 | this.clearInput();
161 | }
162 |
163 | updateWordCache() {
164 | this.wordCache = this.vm.allWords();
165 | }
166 |
167 | injectNextCandidate() {
168 | const candidate = this.wordCandidates[this.nextCandidate];
169 | const src = replInput.value;
170 | replInput.value =
171 | src.substr(0, this.wordStart) +
172 | candidate +
173 | src.substr(replInput.selectionStart);
174 | replInput.selectionStart = replInput.selectionEnd =
175 | this.wordStart + candidate.length;
176 | this.nextCandidate =
177 | (this.nextCandidate + 1) % this.wordCandidates.length;
178 | }
179 |
180 | updateCandidates() {
181 | this.wordStart = replInput.selectionStart - 1;
182 | while (
183 | this.wordStart >= 0 &&
184 | replInput.value.charAt(this.wordStart) != " "
185 | ) {
186 | this.wordStart--;
187 | }
188 | this.wordStart++;
189 | const word = replInput.value.substr(
190 | this.wordStart,
191 | replInput.selectionStart
192 | );
193 | this.wordCandidates = [];
194 | if (word.length) {
195 | for (let i = 0, n = this.wordCache.length; i < n; i++) {
196 | const w = this.wordCache[i];
197 | if (w.indexOf(word) === 0) {
198 | this.wordCandidates.push(w);
199 | }
200 | }
201 | }
202 | this.nextCandidate = 0;
203 | }
204 |
205 | autoComplete() {
206 | if (this.edited) {
207 | this.updateCandidates();
208 | }
209 | if (this.wordCandidates.length > 0) {
210 | this.injectNextCandidate();
211 | }
212 | }
213 |
214 | includeURI(url) {
215 | const req = new XMLHttpRequest();
216 | req.open("GET", url, true);
217 | req.responseType = "text";
218 | req.onload = () => this.repl(req.response);
219 | req.onerror = () => this.errorPrinter(`failed to load URL: ${url}`);
220 | req.send();
221 | }
222 |
223 | start() {
224 | replInput.addEventListener("keydown", (e) => {
225 | //console.log(e.which);
226 | if (e.metaKey) {
227 | switch (e.which) {
228 | case 13: // ENTER
229 | this.evalInput();
230 | break;
231 | case 38: // UP
232 | this.historyBack();
233 | break;
234 | case 40: // DOWN
235 | this.historyForward();
236 | break;
237 | case 75:
238 | this.clearReplOut();
239 | break;
240 | case 82:
241 | e.preventDefault();
242 | this.updateWordCache();
243 | this.helpPrinter(
244 | `word cache refreshed (${this.wordCache.length} words)`
245 | );
246 | break;
247 | }
248 | }
249 | if (e.which == 9) {
250 | e.preventDefault();
251 | this.autoComplete();
252 | this.edited = false;
253 | } else {
254 | this.edited = true;
255 | }
256 | });
257 |
258 | this.codePrinter = this.defPrinter("repl-out-code");
259 | this.helpPrinter = this.defPrinter("repl-out-help");
260 | this.vm.stdout = this.textPrinter = this.defPrinter("repl-out-text");
261 | this.vm.stderr = this.errorPrinter = this.defPrinter(
262 | "repl-out-error",
263 | "[ERROR] "
264 | );
265 |
266 | btEval.addEventListener("click", this.evalInput.bind(this));
267 |
268 | exposeGlobal("REPL", this, true);
269 |
270 | this.vm.interpreter(
271 | [
272 | '*js-window* "REPL" js@ val> *repl*',
273 | "500 val> *include-delay*",
274 | '"REPL JS class" set-doc-latest!',
275 | '"repl-out" js-element-by-id val> *repl-out*',
276 | '"REPL\'s output DOM element" set-doc-latest!',
277 | ": add-to-repl ^( elm -- ) *repl-out* js-append-child ;",
278 | ': include ^( url -- ) *repl* dup "includeURI" js@ js-call-1-with drop ;',
279 | ": include* dsp@ 1+ >r begin dsp@ 0 >= while include repeat r> *include-delay* * sleep ;",
280 | ": clear-repl-out ^( -- )",
281 | ' *repl* dup "clearReplOut" js@ js-call-with drop ;',
282 | ].join("\n")
283 | );
284 |
285 | this.updateWordCache();
286 |
287 | this.helpPrinter(
288 | [
289 | " _______ ___ ___ _______ _______ ___ ___ _______ ",
290 | " | _ | | Y | | _ | | _ \\ | | | | | _ |",
291 | " |. 1___| |. 1 | |. 1 | |. l / |. | |. | |. 1___|",
292 | " |. |___ |. _ | |. _ | |. _ 1 |. |___ |. | |. __)_ ",
293 | " |: 1 | |: | | |: | | |: | | |: 1 | |: | |: 1 |",
294 | " |::.. . | |::.|:. | |::.|:. | |::.|:. | |::.. . | |::.| |::.. . |",
295 | " `-------' `--- ---' `--- ---' `--- ---' `-------' `---' `-------'",
296 | " ---=== thi.ng/charlie ===--- ",
297 | "",
298 | " Command + Enter .................... evaluate",
299 | " Command + up/down .................. cycle history",
300 | " Command + R ........................ refresh autocomplete cache",
301 | " TAB ................................ autocomplete (repeat for alts)",
302 | " `show-words` / `show-public-words` . display known word list",
303 | " `doc> word` ........................ display word documentation",
304 | " `see> word` ........................ disassemble word definition",
305 | ].join("\n")
306 | );
307 |
308 | replInput.focus();
309 | }
310 | }
311 |
--------------------------------------------------------------------------------
/src/vm.ts:
--------------------------------------------------------------------------------
1 | // _______ ___ ___ _______ _______ ___ ___ _______ ___ ___ ___ ___
2 | // | _ | Y | _ | _ | | | | _ | | Y | Y |
3 | // |. 1___|. 1 |. 1 |. l |. | |. |. 1___| |. | |. |
4 | // |. |___|. _ |. _ |. _ |. |___|. |. __)_ ______|. | |. \_/ |
5 | // |: 1 |: | |: | |: | |: 1 |: |: 1 |______|: 1 |: | |
6 | // |::.. . |::.|:. |::.|:. |::.|:. |::.. . |::.|::.. . | \:.. ./|::.|:. |
7 | // `-------`--- ---`--- ---`--- ---`-------`---`-------' `---' `--- ---'
8 | //
9 | // (c) 2015 - 2025 Karsten Schmidt // MIT licensed
10 |
11 | import type { Fn0, Fn } from "@thi.ng/api";
12 |
13 | const VERSION = "1.0.2";
14 |
15 | const BASE_DICT_ADDR = 0;
16 | const BASE_HEAP_ADDR = 0x10000;
17 | const MODE_IMMEDIATE = 0;
18 | const MODE_COMPILE = 1;
19 |
20 | const REGEXP_LIT_NUMBER = /^-?[0-9]*(\.[0-9]+)?$/;
21 | const REGEXP_LIT_NUMBER_HEX = /^0x[0-9a-fA-F]{1,8}?$/;
22 | const REGEXP_LIT_STRING = /^"(.*)$/;
23 |
24 | // VM state
25 |
26 | const PRIMS = {};
27 | const MEM: any[] = [0];
28 | const DS = [];
29 | const RS = [];
30 | let DSP = -1;
31 | let RSP = -1;
32 | let DP = BASE_DICT_ADDR + 1;
33 | let IP = 0;
34 | let NP = 0;
35 | let HP = BASE_HEAP_ADDR;
36 | let LATEST = 0;
37 | let ERROR = false;
38 | let SUSPEND = false;
39 | let MODE = MODE_IMMEDIATE;
40 |
41 | let TIB;
42 | let READER: Reader;
43 | let TOKEN_READER;
44 |
45 | export type Stack = any[];
46 | export type Memory = any[];
47 | export type GetPtr = Fn0;
48 | export type Reader = (prior?: boolean) => string;
49 | export type TokenReader = Fn0;
50 |
51 | export interface VMState {
52 | IP: number;
53 | NP: number;
54 | TIB: string;
55 | READER: Reader;
56 | TOKEN_READER: TokenReader;
57 | }
58 |
59 | export interface IVM {
60 | DS: Stack;
61 | DSP: GetPtr;
62 | RS: Stack;
63 | RSP: GetPtr;
64 | MEM: Memory;
65 | PRIMS: Record;
66 | LATEST: GetPtr;
67 | IP: GetPtr;
68 | NP: GetPtr;
69 | VERSION: string;
70 | stdout: Fn;
71 | stderr: Fn;
72 | throwError: Fn;
73 | isError: Fn0;
74 | isSuspended: Fn0;
75 | popD: typeof popD;
76 | pushD: typeof pushD;
77 | popR: typeof popR;
78 | pushR: typeof pushR;
79 | pushDict: typeof pushDict;
80 | next: typeof next;
81 | doColon: typeof doColon;
82 | defWord: typeof defWord;
83 | defColon: typeof defColon;
84 | defConst: typeof defConst;
85 | doWord: typeof doWord;
86 | doContinue: typeof doContinue;
87 | allWords: typeof allWords;
88 | interpreter: typeof interpreter;
89 | }
90 |
91 | export const Charlie = {
92 | DS: DS,
93 | DSP: () => DSP,
94 | RS: RS,
95 | RSP: () => RSP,
96 | MEM: MEM,
97 | PRIMS: PRIMS,
98 | LATEST: () => LATEST,
99 | IP: () => IP,
100 | NP: () => NP,
101 | VERSION: VERSION,
102 | stdout(out) {
103 | console.log(out);
104 | },
105 | stderr(out) {
106 | console.error(out);
107 | },
108 | throwError(err) {
109 | this.stderr(err);
110 | ERROR = true;
111 | },
112 | isError: () => ERROR,
113 | isSuspended: () => SUSPEND,
114 | };
115 |
116 | const getVMState = (): VMState => ({
117 | IP: IP,
118 | NP: NP,
119 | TIB: TIB,
120 | READER: READER,
121 | TOKEN_READER: TOKEN_READER,
122 | });
123 |
124 | const setVMState = (state: VMState) => {
125 | IP = state.IP;
126 | NP = state.NP;
127 | TIB = state.TIB;
128 | READER = state.READER;
129 | TOKEN_READER = state.TOKEN_READER;
130 | };
131 |
132 | const popD = () => {
133 | if (DSP !== -1) {
134 | return DS[DSP--];
135 | } else {
136 | Charlie.throwError("DS underflow");
137 | return undefined;
138 | }
139 | };
140 |
141 | const pushD = (x) => {
142 | DS[++DSP] = x;
143 | };
144 |
145 | const popR = () => {
146 | if (RSP !== -1) {
147 | return RS[RSP--];
148 | } else {
149 | Charlie.throwError("RS underflow");
150 | return undefined;
151 | }
152 | };
153 |
154 | const pushR = (x: any) => {
155 | RS[++RSP] = x;
156 | };
157 |
158 | const pushDict = (x: any) => {
159 | MEM[DP++] = x;
160 | return (DP - 1) | 0;
161 | };
162 |
163 | const next = () => {
164 | IP = MEM[NP];
165 | NP++;
166 | };
167 |
168 | const doColon = () => {
169 | pushR(NP);
170 | IP++;
171 | NP = (IP + 1) | 0;
172 | IP = MEM[IP];
173 | };
174 |
175 | function defWord(name: string, fn: Fn0): number;
176 | function defWord(name: string, immediate: boolean, fn: Fn0): number;
177 | function defWord(name: string, immediate, fn?) {
178 | if (!fn) {
179 | fn = immediate;
180 | immediate = false;
181 | }
182 | const hd = [name || "", !!immediate, false, LATEST];
183 | LATEST = DP;
184 | pushDict(hd);
185 | pushDict(fn);
186 | PRIMS[name] = LATEST;
187 | return LATEST;
188 | }
189 |
190 | function defColon(name: string, words: string): number;
191 | function defColon(name: string, immediate: boolean, words?: string): number;
192 | function defColon(name: string, immediate: any, words?) {
193 | if (!words) {
194 | words = immediate;
195 | immediate = false;
196 | }
197 | if (typeof words === "string") {
198 | words = words.split(/[ \n]+/);
199 | }
200 | defWord(name, immediate, doColon);
201 | words.forEach(function (w) {
202 | pushDict(PRIMS[w]);
203 | });
204 | pushDict(PRIMS["exit"]);
205 | return LATEST;
206 | }
207 |
208 | const defConst = (name: string, val: any) =>
209 | defWord(name, false, () => {
210 | pushD(val);
211 | next();
212 | });
213 |
214 | const findWord = (name: string) => {
215 | let curr = LATEST;
216 | while (true) {
217 | const currMem = MEM[curr];
218 | if (currMem[2] || currMem[0] !== name) {
219 | curr = currMem[3]; // prev
220 | if (curr === 0) {
221 | curr = undefined;
222 | break;
223 | }
224 | } else {
225 | break;
226 | }
227 | }
228 | return curr;
229 | };
230 |
231 | const allWords = (): string[] => {
232 | const words = [];
233 | let curr = LATEST;
234 | while (curr > 0) {
235 | const currMem = MEM[curr];
236 | if (!currMem[2]) {
237 | if (words.indexOf(currMem[0]) == -1) {
238 | words.push(currMem[0]);
239 | }
240 | }
241 | curr = currMem[3];
242 | }
243 | return words;
244 | };
245 |
246 | const setWordFlag = (flag) => () => {
247 | const f = popD();
248 | MEM[popD()][flag] = f;
249 | next();
250 | };
251 |
252 | const doWord = (addr: number, np: number) => {
253 | IP = addr;
254 | NP = np;
255 | try {
256 | do {
257 | MEM[++IP]();
258 | } while (!ERROR && !SUSPEND && IP !== 0);
259 | } catch (e) {
260 | Charlie.throwError(e);
261 | }
262 | };
263 |
264 | const doContinue = (state: VMState) => {
265 | console.log("state:", state);
266 | SUSPEND = false;
267 | setVMState(state);
268 | if (IP > 0) {
269 | doWord(IP, NP);
270 | }
271 | return doInterpreter();
272 | };
273 |
274 | const makeReader = (str: string) => {
275 | let i = 0;
276 | let ch: string;
277 | TIB = str;
278 | return (isPrior = false) => {
279 | if (isPrior) {
280 | return ch;
281 | }
282 | if (TIB === undefined) {
283 | return undefined;
284 | }
285 | ch = TIB[i++];
286 | return ch;
287 | };
288 | };
289 |
290 | const isTokenSeparator = (ch: string) => ch === " " || ch === "\n";
291 |
292 | const isEOS = (ch: string) => ch === undefined;
293 |
294 | const makeTokenReader = (read: Reader) => () => {
295 | let ch: string;
296 | let token = "";
297 | while (true) {
298 | ch = read();
299 | if (isTokenSeparator(ch)) {
300 | if (token === "") {
301 | continue;
302 | } else {
303 | break;
304 | }
305 | }
306 | if (isEOS(ch)) {
307 | break;
308 | }
309 | token += ch;
310 | }
311 | return token;
312 | };
313 |
314 | const readString = (str: string) => {
315 | const first = str.match(/(.*)"$/);
316 | if (first) {
317 | return first[1];
318 | }
319 | str += READER(true);
320 | let ch = "";
321 | while (ch !== undefined) {
322 | ch = READER();
323 | if (ch === "\\") {
324 | str += READER();
325 | continue;
326 | }
327 | if (ch === '"') {
328 | break;
329 | }
330 | str += ch;
331 | }
332 | return str;
333 | };
334 |
335 | const tokenValue = (token: string) => {
336 | if (token.match(REGEXP_LIT_NUMBER)) {
337 | return parseFloat(token);
338 | }
339 | if (token.match(REGEXP_LIT_NUMBER_HEX)) {
340 | return parseInt(token.substr(2), 16);
341 | }
342 | const str = token.match(REGEXP_LIT_STRING);
343 | if (str) {
344 | return readString(str[1]).replace(/\\n/g, "\n");
345 | }
346 | Charlie.throwError("Unknown word: " + token);
347 | };
348 |
349 | const compileToken = (token: string) => {
350 | const word = findWord(token);
351 | if (word === undefined) {
352 | const literal = tokenValue(token);
353 | if (literal !== undefined) {
354 | pushDict(PRIMS["lit"]);
355 | pushDict(literal);
356 | }
357 | return;
358 | }
359 | if (MEM[word] && MEM[word][1]) {
360 | // immediate mode
361 | doWord(word, 0);
362 | return;
363 | }
364 | pushDict(word);
365 | };
366 |
367 | const executeToken = (token: string) => {
368 | const word = findWord(token);
369 | if (word === undefined) {
370 | const literal = tokenValue(token);
371 | if (literal !== undefined) {
372 | pushD(literal);
373 | }
374 | return;
375 | }
376 | //console.log("exec: "+word);
377 | doWord(word, 0);
378 | };
379 |
380 | const INTERPRETER = [];
381 | INTERPRETER[MODE_COMPILE] = compileToken;
382 | INTERPRETER[MODE_IMMEDIATE] = executeToken;
383 |
384 | const doInterpreter = () => {
385 | ERROR = false;
386 | SUSPEND = false;
387 |
388 | let token;
389 |
390 | while (!ERROR && !SUSPEND) {
391 | token = TOKEN_READER();
392 | if (token === "") {
393 | break;
394 | }
395 | INTERPRETER[MODE](token);
396 | }
397 | return DS;
398 | };
399 |
400 | const interpreter = (code: string) => {
401 | READER = makeReader(code);
402 | TOKEN_READER = makeTokenReader(READER);
403 | return doInterpreter();
404 | };
405 |
406 | // primitives
407 |
408 | defWord("exit", () => {
409 | NP = popR();
410 | next();
411 | });
412 |
413 | defWord("suspend!", () => {
414 | SUSPEND = true;
415 | next();
416 | });
417 |
418 | defWord("sleep", () => {
419 | const pause = popD();
420 | next();
421 | const state = getVMState();
422 | setTimeout(() => {
423 | doContinue(state);
424 | }, pause);
425 | SUSPEND = true;
426 | });
427 |
428 | defConst("true", true);
429 | defConst("false", false);
430 | defConst("nil", null);
431 | defConst("undefined", undefined);
432 | defConst("*version*", VERSION);
433 | defConst("*vm-mode-immediate*", MODE_IMMEDIATE);
434 | defConst("*vm-mode-compile*", MODE_COMPILE);
435 | defWord("compile-mode!", true, () => {
436 | MODE = MODE_COMPILE;
437 | next();
438 | });
439 | defWord("immediate-mode!", true, () => {
440 | MODE = MODE_IMMEDIATE;
441 | next();
442 | });
443 |
444 | defWord("\\", () => {
445 | let ch = "";
446 | while (ch !== undefined && ch !== "\n") {
447 | ch = READER();
448 | }
449 | next();
450 | });
451 |
452 | defWord("latest@", () => {
453 | pushD(LATEST);
454 | next();
455 | });
456 | defWord("latest!", () => {
457 | LATEST = popD();
458 | next();
459 | });
460 | defWord("heap-here@", () => {
461 | pushD(HP);
462 | next();
463 | });
464 | defWord("heap-here!", () => {
465 | HP = popD();
466 | next();
467 | });
468 | defWord("dict-here@", () => {
469 | pushD(DP);
470 | next();
471 | });
472 | defWord("dict-here!", () => {
473 | DP = popD();
474 | next();
475 | });
476 | defWord(">dict", () => {
477 | pushDict(popD());
478 | next();
479 | });
480 | defWord("dsp@", () => {
481 | pushD(DSP);
482 | next();
483 | });
484 | defWord("dsp!", () => {
485 | DSP = popD();
486 | next();
487 | });
488 | defWord("pick", () => {
489 | pushD(DS[DSP - popD() - 1]);
490 | next();
491 | });
492 | defWord("rsp@", () => {
493 | pushD(RSP);
494 | next();
495 | });
496 | defWord("rsp!", () => {
497 | RSP = popD();
498 | next();
499 | });
500 | defWord("rpick", () => {
501 | pushD(RS[RSP - popD() - 1]);
502 | next();
503 | });
504 | defWord("vm-mode@", () => {
505 | pushD(MODE);
506 | next();
507 | });
508 | defWord("vm-mode!", () => {
509 | MODE = popD();
510 | next();
511 | });
512 | defWord("vm-state@", () => {
513 | pushD(getVMState());
514 | next();
515 | });
516 | defWord("vm-state!", () => {
517 | doContinue(popD());
518 | });
519 | defWord("tib@", () => {
520 | pushD(TIB);
521 | next();
522 | });
523 | defWord("tib!", () => {
524 | next();
525 | const state = getVMState();
526 | interpreter(popD());
527 | doContinue(state);
528 | });
529 |
530 | defWord("find", () => {
531 | pushD(findWord(popD()));
532 | next();
533 | });
534 | defWord("call", () => {
535 | IP = popD();
536 | });
537 | defWord("jump", () => {
538 | NP = popD();
539 | next();
540 | });
541 | defWord("lit", () => {
542 | pushD(MEM[NP++]);
543 | next();
544 | });
545 | defWord("prev-word@", () => {
546 | pushD(MEM[popD()][3]);
547 | next();
548 | });
549 | defWord("prev-word!", () => {
550 | const w = popD();
551 | MEM[w][3] = popD();
552 | next();
553 | });
554 | defWord("xt->cfa", () => {
555 | pushD(popD() + 1);
556 | next();
557 | });
558 | defWord("cfa->dfa", () => {
559 | pushD(popD() + 1);
560 | next();
561 | });
562 |
563 | defWord("read-token>", () => {
564 | pushD(TOKEN_READER());
565 | next();
566 | });
567 | defWord("new-word-header", () => {
568 | defWord("", false, doColon);
569 | next();
570 | });
571 | defWord("set-word-name!", () => {
572 | const w = popD();
573 | MEM[w][0] = popD();
574 | next();
575 | });
576 | defWord("set-word-doc!", () => {
577 | const w = popD();
578 | MEM[w][4] = popD();
579 | next();
580 | });
581 | defWord("set-immediate!", setWordFlag(1));
582 | defWord("set-hidden!", setWordFlag(2));
583 |
584 | defColon("hide-latest!", "latest@ true set-hidden!");
585 | defColon("show-latest!", "latest@ false set-hidden!");
586 | defColon("immediate!", "latest@ true set-immediate!");
587 | defColon(
588 | "new-word-header>",
589 | "read-token> new-word-header latest@ set-word-name!"
590 | );
591 | defColon(":", "new-word-header> hide-latest! compile-mode!");
592 | defColon(";", true, "lit exit >dict immediate-mode! show-latest!");
593 |
594 | defWord(".", () => {
595 | Charlie.stdout(popD());
596 | next();
597 | });
598 |
599 | // logic
600 | defWord("and", () => {
601 | const a = !!popD();
602 | const b = !!popD();
603 | pushD(a && b);
604 | next();
605 | });
606 | defWord("or", () => {
607 | const a = !!popD();
608 | const b = !!popD();
609 | pushD(a || b);
610 | next();
611 | });
612 | defWord("not", () => {
613 | pushD(!popD());
614 | next();
615 | });
616 | defWord("=", () => {
617 | pushD(popD() === popD());
618 | next();
619 | });
620 | defWord("not=", () => {
621 | pushD(popD() !== popD());
622 | next();
623 | });
624 | defWord("<", () => {
625 | pushD(popD() > popD());
626 | next();
627 | });
628 | defWord(">", () => {
629 | pushD(popD() < popD());
630 | next();
631 | });
632 | defWord("<=", () => {
633 | pushD(popD() >= popD());
634 | next();
635 | });
636 | defWord(">=", () => {
637 | pushD(popD() <= popD());
638 | next();
639 | });
640 | defWord("zero?", () => {
641 | pushD(popD() === 0);
642 | next();
643 | });
644 | defWord("pos?", () => {
645 | pushD(popD() > 0);
646 | next();
647 | });
648 | defWord("neg?", () => {
649 | pushD(popD() < 0);
650 | next();
651 | });
652 | defWord("nil?", () => {
653 | pushD(popD() === null);
654 | next();
655 | });
656 |
657 | // maths
658 |
659 | defWord("+", () => {
660 | const x = popD();
661 | pushD(popD() + x);
662 | next();
663 | });
664 | defWord("*", () => {
665 | pushD(popD() * popD());
666 | next();
667 | });
668 | defWord("-", () => {
669 | const x = popD();
670 | pushD(popD() - x);
671 | next();
672 | });
673 | defWord("/", () => {
674 | const x = popD();
675 | pushD(popD() / x);
676 | next();
677 | });
678 | defWord("mod", () => {
679 | const x = popD();
680 | pushD(popD() % x);
681 | next();
682 | });
683 | defWord("1+", () => {
684 | pushD(popD() + 1);
685 | next();
686 | });
687 | defWord("1-", () => {
688 | pushD(popD() - 1);
689 | next();
690 | });
691 | defWord("min", () => {
692 | const x = popD();
693 | pushD(Math.min(popD(), x));
694 | next();
695 | });
696 | defWord("max", () => {
697 | const x = popD();
698 | pushD(Math.max(popD(), x));
699 | next();
700 | });
701 | defWord("bit-and", () => {
702 | const b = popD();
703 | pushD(popD() & b);
704 | next();
705 | });
706 | defWord("bit-or", () => {
707 | const b = popD();
708 | pushD(popD() | b);
709 | next();
710 | });
711 | defWord("bit-xor", () => {
712 | const b = popD();
713 | pushD(popD() ^ b);
714 | next();
715 | });
716 | defWord("<<", () => {
717 | const b = popD();
718 | pushD(popD() << b);
719 | next();
720 | });
721 | defWord(">>", () => {
722 | const b = popD();
723 | pushD(popD() >> b);
724 | next();
725 | });
726 | defWord(">>>", () => {
727 | const b = popD();
728 | pushD(popD() >>> b);
729 | next();
730 | });
731 |
732 | // binary
733 | defWord("bit-and", () => {
734 | pushD(popD() & popD());
735 | next();
736 | });
737 | defWord("bit-or", () => {
738 | pushD(popD() | popD());
739 | next();
740 | });
741 | defWord("bit-xor", () => {
742 | pushD(popD() ^ popD());
743 | next();
744 | });
745 |
746 | // data stack ops
747 |
748 | defWord("drop", () => {
749 | popD();
750 | next();
751 | });
752 | defWord("dup", () => {
753 | pushD(DS[DSP]);
754 | next();
755 | });
756 | defWord("?dup", () => {
757 | const x = DS[DSP];
758 | if (x !== 0) {
759 | pushD(x);
760 | }
761 | next();
762 | });
763 | defWord("swap", () => {
764 | const b = DS[DSP];
765 | const a = DS[DSP - 1];
766 | DS[DSP - 1] = b;
767 | DS[DSP] = a;
768 | next();
769 | });
770 | defWord("nip", () => {
771 | const x = popD();
772 | popD();
773 | pushD(x);
774 | next();
775 | });
776 | defWord("tuck", () => {
777 | const b = popD();
778 | const a = popD();
779 | pushD(b);
780 | pushD(a);
781 | pushD(b);
782 | next();
783 | });
784 | defWord("over", () => {
785 | pushD(DS[DSP - 1]);
786 | next();
787 | });
788 | defWord("rot", () => {
789 | const c = DS[DSP];
790 | const b = DS[DSP - 1];
791 | const a = DS[DSP - 2];
792 | DS[DSP - 2] = b;
793 | DS[DSP - 1] = c;
794 | DS[DSP] = a;
795 | next();
796 | });
797 | defWord("-rot", () => {
798 | const c = DS[DSP];
799 | const b = DS[DSP - 1];
800 | const a = DS[DSP - 2];
801 | DS[DSP - 2] = c;
802 | DS[DSP - 1] = a;
803 | DS[DSP] = b;
804 | next();
805 | });
806 | defWord("2dup", () => {
807 | const b = DS[DSP];
808 | const a = DS[DSP - 1];
809 | pushD(a);
810 | pushD(b);
811 | next();
812 | });
813 | defWord("2drop", () => {
814 | popD();
815 | popD();
816 | next();
817 | });
818 | defWord("2swap", () => {
819 | const d = popD();
820 | const c = popD();
821 | const b = popD();
822 | const a = popD();
823 | pushD(c);
824 | pushD(d);
825 | pushD(a);
826 | pushD(b);
827 | next();
828 | });
829 |
830 | // return stack ops
831 |
832 | defWord(">r", () => {
833 | pushR(popD());
834 | next();
835 | });
836 | defWord("r>", () => {
837 | pushD(popR());
838 | next();
839 | });
840 | defWord("@r", () => {
841 | pushR(DS[DSP]);
842 | next();
843 | });
844 | defWord("r@", () => {
845 | pushD(RS[RSP]);
846 | next();
847 | });
848 | defWord("rdrop", () => {
849 | popR();
850 | next();
851 | });
852 | defWord("rdup", () => {
853 | pushR(RS[RSP]);
854 | next();
855 | });
856 | defWord("rswap", () => {
857 | const b = popR(),
858 | a = popR();
859 | pushR(b);
860 | pushR(a);
861 | next();
862 | });
863 | defWord("2>r", () => {
864 | const b = popD(),
865 | a = popD();
866 | pushR(a);
867 | pushR(b);
868 | next();
869 | });
870 | defWord("2r>", () => {
871 | const b = popR(),
872 | a = popR();
873 | pushD(a);
874 | pushD(b);
875 | next();
876 | });
877 |
878 | // memory
879 |
880 | defWord("@", () => {
881 | pushD(MEM[popD()]);
882 | next();
883 | });
884 | defWord("!", () => {
885 | MEM[popD()] = popD();
886 | next();
887 | });
888 | defWord("+!", () => {
889 | MEM[popD()] += popD();
890 | next();
891 | });
892 | defWord("-!", () => {
893 | MEM[popD()] -= popD();
894 | next();
895 | });
896 |
897 | // branching
898 |
899 | defWord("branch", () => {
900 | NP = MEM[NP];
901 | next();
902 | });
903 | defWord("alt-branch", () => {
904 | popD() ? NP++ : (NP = MEM[NP]);
905 | next();
906 | });
907 |
908 | // JS
909 |
910 | defWord("js-call", () => {
911 | pushD(popD()());
912 | next();
913 | });
914 | defWord("js-call-1", () => {
915 | pushD(popD()(popD()));
916 | next();
917 | });
918 | defWord("js-call-2", () => {
919 | const fn = popD();
920 | const b = popD();
921 | pushD(fn(popD(), b));
922 | next();
923 | });
924 | defWord("js-call-n", () => {
925 | const n = popD() - 1;
926 | const fn = popD();
927 | const args = [];
928 | let m = n;
929 | // TODO refactor
930 | for (; m >= 0; m--) {
931 | args.push(DS[DSP - m]);
932 | }
933 | for (m = n; m >= 0; m--) {
934 | popD();
935 | }
936 | pushD(fn.apply(window, args));
937 | next();
938 | });
939 | defWord("js-call-with", () => {
940 | const fn = popD();
941 | pushD(fn.call(popD()));
942 | next();
943 | });
944 | defWord("js-call-1-with", () => {
945 | const fn = popD();
946 | const obj = popD();
947 | pushD(fn.call(obj, popD()));
948 | next();
949 | });
950 | defWord("js-call-2-with", () => {
951 | const fn = popD();
952 | const obj = popD();
953 | const b = popD();
954 | pushD(fn.call(obj, popD(), b));
955 | next();
956 | });
957 | defWord("js-call-n-with", () => {
958 | const n = popD() - 1;
959 | const fn = popD();
960 | const obj = popD();
961 | const args = [];
962 | let m = n;
963 | // TODO refactor
964 | for (; m >= 0; m--) {
965 | args.push(DS[DSP - m]);
966 | }
967 | for (m = n; m >= 0; m--) {
968 | popD();
969 | }
970 | pushD(fn.apply(obj, args));
971 | next();
972 | });
973 | defWord("js-new", () => {
974 | const obj = popD();
975 | pushD(new obj());
976 | next();
977 | });
978 | defWord("js-new-1", () => {
979 | const obj = popD();
980 | pushD(new obj(popD()));
981 | next();
982 | });
983 | defWord("js-new-2", () => {
984 | const obj = popD();
985 | const b = popD();
986 | pushD(new obj(popD(), b));
987 | next();
988 | });
989 | defWord("js-new-n", () => {
990 | const n = popD();
991 | const obj = popD();
992 | const args = [];
993 | const inst = Object.create(obj.prototype);
994 | let m = n;
995 | // TODO refactor
996 | for (; m >= 0; m--) {
997 | args.push(DS[DSP - m]);
998 | }
999 | for (m = n; m >= 0; m--) {
1000 | popD();
1001 | }
1002 | pushD(obj.apply(inst, args) || inst);
1003 | next();
1004 | });
1005 | defWord("js-apply", () => {
1006 | pushD(popD().apply(window, popD()));
1007 | next();
1008 | });
1009 | defWord("js-apply-with", () => {
1010 | pushD(popD().apply(popD(), popD()));
1011 | next();
1012 | });
1013 | defWord("js-obj", () => {
1014 | pushD({});
1015 | next();
1016 | });
1017 | defWord("js-array", () => {
1018 | pushD([]);
1019 | next();
1020 | });
1021 | defWord("js@", () => {
1022 | const key = popD();
1023 | const obj = popD();
1024 | pushD(obj === undefined || obj === null ? obj : obj[key]);
1025 | next();
1026 | });
1027 | defWord("js!", () => {
1028 | const key = popD();
1029 | const obj = popD();
1030 | obj[key] = popD();
1031 | next();
1032 | });
1033 | defWord("js!!", () => {
1034 | const key = DS[DSP];
1035 | const obj = DS[DSP - 1];
1036 | obj[key] = DS[DSP - 2];
1037 | next();
1038 | });
1039 | defWord("js-apush", () => {
1040 | DS[DSP - 1].push(popD());
1041 | next();
1042 | });
1043 | defWord("typeof", () => {
1044 | pushD(typeof popD());
1045 | next();
1046 | });
1047 | defWord("js-fn?", () => {
1048 | pushD(typeof popD() === "function");
1049 | next();
1050 | });
1051 | defWord("js-obj?", () => {
1052 | const obj = popD();
1053 | pushD(obj !== null && typeof obj === "object" && !Array.isArray(obj));
1054 | next();
1055 | });
1056 | defWord("js-array?", () => {
1057 | pushD(Array.isArray(popD()));
1058 | next();
1059 | });
1060 | defWord("quot->js-callback", () => {
1061 | const qaddr = popD();
1062 | pushD((e) => {
1063 | pushD(e);
1064 | interpreter(qaddr + " apply");
1065 | });
1066 | next();
1067 | });
1068 | defWord("quot->js-fn", () => {
1069 | const qaddr = popD();
1070 | pushD(() => {
1071 | pushR(NP);
1072 | NP = qaddr;
1073 | });
1074 | next();
1075 | });
1076 | defWord("*js-window*", () => {
1077 | pushD(window);
1078 | next();
1079 | });
1080 | defWord("*js-document*", () => {
1081 | pushD(document);
1082 | next();
1083 | });
1084 | defWord("js-create-element", () => {
1085 | pushD(document.createElement(popD()));
1086 | next();
1087 | });
1088 | defWord("js-element-by-id", () => {
1089 | pushD(document.getElementById(popD()));
1090 | next();
1091 | });
1092 | defWord("js-text!", () => {
1093 | const elm = popD();
1094 | elm.textContent = popD();
1095 | next();
1096 | });
1097 | defWord("js-append-child", () => {
1098 | const parent = popD();
1099 | parent.appendChild(popD());
1100 | next();
1101 | });
1102 | defWord("js-remove-child", () => {
1103 | const parent = popD();
1104 | parent.removeChild(popD());
1105 | next();
1106 | });
1107 | defWord("js-parent-node", () => {
1108 | pushD(popD().parentNode);
1109 | next();
1110 | });
1111 | defWord("js-attr!", () => {
1112 | const attr = popD();
1113 | const elm = popD();
1114 | elm.setAttribute(attr, popD());
1115 | next();
1116 | });
1117 | defWord("js-console-log", () => {
1118 | console.log(popD());
1119 | next();
1120 | });
1121 | defWord("js-add-event-listener", () => {
1122 | const elm = popD();
1123 | const event = popD();
1124 | const fn = popD();
1125 | elm.addEventListener(event, fn);
1126 | next();
1127 | });
1128 |
1129 | Charlie.popD = popD;
1130 | Charlie.pushD = pushD;
1131 | Charlie.popR = popR;
1132 | Charlie.pushR = pushR;
1133 | Charlie.pushDict = pushDict;
1134 | Charlie.next = next;
1135 | Charlie.doColon = doColon;
1136 | Charlie.defWord = defWord;
1137 | Charlie.defColon = defColon;
1138 | Charlie.defConst = defConst;
1139 | Charlie.doWord = doWord;
1140 | Charlie.doContinue = doContinue;
1141 | Charlie.allWords = allWords;
1142 | Charlie.interpreter = interpreter;
1143 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2022",
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "noUnusedLocals": true,
7 | "noUnusedParameters": true,
8 | "experimentalDecorators": true,
9 | "verbatimModuleSyntax": true
10 | },
11 | "include": ["src/**/*.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@esbuild/aix-ppc64@0.25.0":
6 | version "0.25.0"
7 | resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz#499600c5e1757a524990d5d92601f0ac3ce87f64"
8 | integrity sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==
9 |
10 | "@esbuild/android-arm64@0.25.0":
11 | version "0.25.0"
12 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz#b9b8231561a1dfb94eb31f4ee056b92a985c324f"
13 | integrity sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==
14 |
15 | "@esbuild/android-arm@0.25.0":
16 | version "0.25.0"
17 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.0.tgz#ca6e7888942505f13e88ac9f5f7d2a72f9facd2b"
18 | integrity sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==
19 |
20 | "@esbuild/android-x64@0.25.0":
21 | version "0.25.0"
22 | resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.0.tgz#e765ea753bac442dfc9cb53652ce8bd39d33e163"
23 | integrity sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==
24 |
25 | "@esbuild/darwin-arm64@0.25.0":
26 | version "0.25.0"
27 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz#fa394164b0d89d4fdc3a8a21989af70ef579fa2c"
28 | integrity sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==
29 |
30 | "@esbuild/darwin-x64@0.25.0":
31 | version "0.25.0"
32 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz#91979d98d30ba6e7d69b22c617cc82bdad60e47a"
33 | integrity sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==
34 |
35 | "@esbuild/freebsd-arm64@0.25.0":
36 | version "0.25.0"
37 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz#b97e97073310736b430a07b099d837084b85e9ce"
38 | integrity sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==
39 |
40 | "@esbuild/freebsd-x64@0.25.0":
41 | version "0.25.0"
42 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz#f3b694d0da61d9910ec7deff794d444cfbf3b6e7"
43 | integrity sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==
44 |
45 | "@esbuild/linux-arm64@0.25.0":
46 | version "0.25.0"
47 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz#f921f699f162f332036d5657cad9036f7a993f73"
48 | integrity sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==
49 |
50 | "@esbuild/linux-arm@0.25.0":
51 | version "0.25.0"
52 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz#cc49305b3c6da317c900688995a4050e6cc91ca3"
53 | integrity sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==
54 |
55 | "@esbuild/linux-ia32@0.25.0":
56 | version "0.25.0"
57 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz#3e0736fcfab16cff042dec806247e2c76e109e19"
58 | integrity sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==
59 |
60 | "@esbuild/linux-loong64@0.25.0":
61 | version "0.25.0"
62 | resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz#ea2bf730883cddb9dfb85124232b5a875b8020c7"
63 | integrity sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==
64 |
65 | "@esbuild/linux-mips64el@0.25.0":
66 | version "0.25.0"
67 | resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz#4cababb14eede09248980a2d2d8b966464294ff1"
68 | integrity sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==
69 |
70 | "@esbuild/linux-ppc64@0.25.0":
71 | version "0.25.0"
72 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz#8860a4609914c065373a77242e985179658e1951"
73 | integrity sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==
74 |
75 | "@esbuild/linux-riscv64@0.25.0":
76 | version "0.25.0"
77 | resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz#baf26e20bb2d38cfb86ee282dff840c04f4ed987"
78 | integrity sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==
79 |
80 | "@esbuild/linux-s390x@0.25.0":
81 | version "0.25.0"
82 | resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz#8323afc0d6cb1b6dc6e9fd21efd9e1542c3640a4"
83 | integrity sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==
84 |
85 | "@esbuild/linux-x64@0.25.0":
86 | version "0.25.0"
87 | resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz#08fcf60cb400ed2382e9f8e0f5590bac8810469a"
88 | integrity sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==
89 |
90 | "@esbuild/netbsd-arm64@0.25.0":
91 | version "0.25.0"
92 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz#935c6c74e20f7224918fbe2e6c6fe865b6c6ea5b"
93 | integrity sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==
94 |
95 | "@esbuild/netbsd-x64@0.25.0":
96 | version "0.25.0"
97 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz#414677cef66d16c5a4d210751eb2881bb9c1b62b"
98 | integrity sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==
99 |
100 | "@esbuild/openbsd-arm64@0.25.0":
101 | version "0.25.0"
102 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz#8fd55a4d08d25cdc572844f13c88d678c84d13f7"
103 | integrity sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==
104 |
105 | "@esbuild/openbsd-x64@0.25.0":
106 | version "0.25.0"
107 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz#0c48ddb1494bbc2d6bcbaa1429a7f465fa1dedde"
108 | integrity sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==
109 |
110 | "@esbuild/sunos-x64@0.25.0":
111 | version "0.25.0"
112 | resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz#86ff9075d77962b60dd26203d7352f92684c8c92"
113 | integrity sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==
114 |
115 | "@esbuild/win32-arm64@0.25.0":
116 | version "0.25.0"
117 | resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz#849c62327c3229467f5b5cd681bf50588442e96c"
118 | integrity sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==
119 |
120 | "@esbuild/win32-ia32@0.25.0":
121 | version "0.25.0"
122 | resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz#f62eb480cd7cca088cb65bb46a6db25b725dc079"
123 | integrity sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==
124 |
125 | "@esbuild/win32-x64@0.25.0":
126 | version "0.25.0"
127 | resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz#c8e119a30a7c8d60b9d2e22d2073722dde3b710b"
128 | integrity sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==
129 |
130 | "@rollup/rollup-android-arm-eabi@4.34.9":
131 | version "4.34.9"
132 | resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz#661a45a4709c70e59e596ec78daa9cb8b8d27604"
133 | integrity sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==
134 |
135 | "@rollup/rollup-android-arm64@4.34.9":
136 | version "4.34.9"
137 | resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.9.tgz#128fe8dd510d880cf98b4cb6c7add326815a0c4b"
138 | integrity sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==
139 |
140 | "@rollup/rollup-darwin-arm64@4.34.9":
141 | version "4.34.9"
142 | resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz#363467bc49fd0b1e17075798ac8e9ad1e1e29535"
143 | integrity sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==
144 |
145 | "@rollup/rollup-darwin-x64@4.34.9":
146 | version "4.34.9"
147 | resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz#c2fe3d85fffe47f0ed0f076b3563ada22c8af19c"
148 | integrity sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==
149 |
150 | "@rollup/rollup-freebsd-arm64@4.34.9":
151 | version "4.34.9"
152 | resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.9.tgz#d95bd8f6eaaf829781144fc8bd2d5d71d9f6a9f5"
153 | integrity sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==
154 |
155 | "@rollup/rollup-freebsd-x64@4.34.9":
156 | version "4.34.9"
157 | resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.9.tgz#c3576c6011656e4966ded29f051edec636b44564"
158 | integrity sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==
159 |
160 | "@rollup/rollup-linux-arm-gnueabihf@4.34.9":
161 | version "4.34.9"
162 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.9.tgz#48c87d0dee4f8dc9591a416717f91b4a89d77e3d"
163 | integrity sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==
164 |
165 | "@rollup/rollup-linux-arm-musleabihf@4.34.9":
166 | version "4.34.9"
167 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.9.tgz#f4c4e7c03a7767f2e5aa9d0c5cfbf5c0f59f2d41"
168 | integrity sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==
169 |
170 | "@rollup/rollup-linux-arm64-gnu@4.34.9":
171 | version "4.34.9"
172 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz#1015c9d07a99005025d13b8622b7600029d0b52f"
173 | integrity sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==
174 |
175 | "@rollup/rollup-linux-arm64-musl@4.34.9":
176 | version "4.34.9"
177 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz#8f895eb5577748fc75af21beae32439626e0a14c"
178 | integrity sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==
179 |
180 | "@rollup/rollup-linux-loongarch64-gnu@4.34.9":
181 | version "4.34.9"
182 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.9.tgz#c9cd5dbbdc6b3ca4dbeeb0337498cf31949004a0"
183 | integrity sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==
184 |
185 | "@rollup/rollup-linux-powerpc64le-gnu@4.34.9":
186 | version "4.34.9"
187 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.9.tgz#7ebb5b4441faa17843a210f7d0583a20c93b40e4"
188 | integrity sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==
189 |
190 | "@rollup/rollup-linux-riscv64-gnu@4.34.9":
191 | version "4.34.9"
192 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.9.tgz#10f5d7349fbd2fe78f9e36ecc90aab3154435c8d"
193 | integrity sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==
194 |
195 | "@rollup/rollup-linux-s390x-gnu@4.34.9":
196 | version "4.34.9"
197 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.9.tgz#196347d2fa20593ab09d0b7e2589fb69bdf742c6"
198 | integrity sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==
199 |
200 | "@rollup/rollup-linux-x64-gnu@4.34.9":
201 | version "4.34.9"
202 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz#7193cbd8d128212b8acda37e01b39d9e96259ef8"
203 | integrity sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==
204 |
205 | "@rollup/rollup-linux-x64-musl@4.34.9":
206 | version "4.34.9"
207 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz#29a6867278ca0420b891574cfab98ecad70c59d1"
208 | integrity sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==
209 |
210 | "@rollup/rollup-win32-arm64-msvc@4.34.9":
211 | version "4.34.9"
212 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz#89427dcac0c8e3a6d32b13a03a296a275d0de9a9"
213 | integrity sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==
214 |
215 | "@rollup/rollup-win32-ia32-msvc@4.34.9":
216 | version "4.34.9"
217 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.9.tgz#ecb9711ba2b6d2bf6ee51265abe057ab90913deb"
218 | integrity sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==
219 |
220 | "@rollup/rollup-win32-x64-msvc@4.34.9":
221 | version "4.34.9"
222 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz#1973871850856ae72bc678aeb066ab952330e923"
223 | integrity sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==
224 |
225 | "@thi.ng/api@^8.11.21":
226 | version "8.11.21"
227 | resolved "https://registry.yarnpkg.com/@thi.ng/api/-/api-8.11.21.tgz#afdfbc7c935879820c6e77123348e7c2f0c39b7f"
228 | integrity sha512-J6BUdUtFtwZirL3M9tkCiqBXj228z7zkxWOaDWTymwBeqY9s02vJP3mQV8l5p+YPDIRmYx/q7XVuLW1UTJRN/A==
229 |
230 | "@thi.ng/expose@^1.2.48":
231 | version "1.2.48"
232 | resolved "https://registry.yarnpkg.com/@thi.ng/expose/-/expose-1.2.48.tgz#6ea2027ef5f6752e8ba8ec60fce4d39fc762c875"
233 | integrity sha512-1PaGUKqYVoz4Z2U/BeNg245kdY4s/CvlPiGkmdO+qh5oNBbBA3XK8Xwt+5SACQpBw6jdxlDoecSI5Hgvsqsktg==
234 |
235 | "@types/estree@1.0.6":
236 | version "1.0.6"
237 | resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
238 | integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
239 |
240 | esbuild@^0.25.0:
241 | version "0.25.0"
242 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.0.tgz#0de1787a77206c5a79eeb634a623d39b5006ce92"
243 | integrity sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==
244 | optionalDependencies:
245 | "@esbuild/aix-ppc64" "0.25.0"
246 | "@esbuild/android-arm" "0.25.0"
247 | "@esbuild/android-arm64" "0.25.0"
248 | "@esbuild/android-x64" "0.25.0"
249 | "@esbuild/darwin-arm64" "0.25.0"
250 | "@esbuild/darwin-x64" "0.25.0"
251 | "@esbuild/freebsd-arm64" "0.25.0"
252 | "@esbuild/freebsd-x64" "0.25.0"
253 | "@esbuild/linux-arm" "0.25.0"
254 | "@esbuild/linux-arm64" "0.25.0"
255 | "@esbuild/linux-ia32" "0.25.0"
256 | "@esbuild/linux-loong64" "0.25.0"
257 | "@esbuild/linux-mips64el" "0.25.0"
258 | "@esbuild/linux-ppc64" "0.25.0"
259 | "@esbuild/linux-riscv64" "0.25.0"
260 | "@esbuild/linux-s390x" "0.25.0"
261 | "@esbuild/linux-x64" "0.25.0"
262 | "@esbuild/netbsd-arm64" "0.25.0"
263 | "@esbuild/netbsd-x64" "0.25.0"
264 | "@esbuild/openbsd-arm64" "0.25.0"
265 | "@esbuild/openbsd-x64" "0.25.0"
266 | "@esbuild/sunos-x64" "0.25.0"
267 | "@esbuild/win32-arm64" "0.25.0"
268 | "@esbuild/win32-ia32" "0.25.0"
269 | "@esbuild/win32-x64" "0.25.0"
270 |
271 | fsevents@~2.3.2:
272 | version "2.3.2"
273 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
274 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
275 |
276 | fsevents@~2.3.3:
277 | version "2.3.3"
278 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
279 | integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
280 |
281 | nanoid@^3.3.8:
282 | version "3.3.8"
283 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf"
284 | integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==
285 |
286 | picocolors@^1.1.1:
287 | version "1.1.1"
288 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
289 | integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
290 |
291 | postcss@^8.5.3:
292 | version "8.5.3"
293 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb"
294 | integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==
295 | dependencies:
296 | nanoid "^3.3.8"
297 | picocolors "^1.1.1"
298 | source-map-js "^1.2.1"
299 |
300 | rollup@^4.30.1:
301 | version "4.34.9"
302 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.34.9.tgz#e1eb397856476778aeb6ac2ac3d09b2ce177a558"
303 | integrity sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==
304 | dependencies:
305 | "@types/estree" "1.0.6"
306 | optionalDependencies:
307 | "@rollup/rollup-android-arm-eabi" "4.34.9"
308 | "@rollup/rollup-android-arm64" "4.34.9"
309 | "@rollup/rollup-darwin-arm64" "4.34.9"
310 | "@rollup/rollup-darwin-x64" "4.34.9"
311 | "@rollup/rollup-freebsd-arm64" "4.34.9"
312 | "@rollup/rollup-freebsd-x64" "4.34.9"
313 | "@rollup/rollup-linux-arm-gnueabihf" "4.34.9"
314 | "@rollup/rollup-linux-arm-musleabihf" "4.34.9"
315 | "@rollup/rollup-linux-arm64-gnu" "4.34.9"
316 | "@rollup/rollup-linux-arm64-musl" "4.34.9"
317 | "@rollup/rollup-linux-loongarch64-gnu" "4.34.9"
318 | "@rollup/rollup-linux-powerpc64le-gnu" "4.34.9"
319 | "@rollup/rollup-linux-riscv64-gnu" "4.34.9"
320 | "@rollup/rollup-linux-s390x-gnu" "4.34.9"
321 | "@rollup/rollup-linux-x64-gnu" "4.34.9"
322 | "@rollup/rollup-linux-x64-musl" "4.34.9"
323 | "@rollup/rollup-win32-arm64-msvc" "4.34.9"
324 | "@rollup/rollup-win32-ia32-msvc" "4.34.9"
325 | "@rollup/rollup-win32-x64-msvc" "4.34.9"
326 | fsevents "~2.3.2"
327 |
328 | source-map-js@^1.2.1:
329 | version "1.2.1"
330 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
331 | integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
332 |
333 | typescript@^5.8.2:
334 | version "5.8.2"
335 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.2.tgz#8170b3702f74b79db2e5a96207c15e65807999e4"
336 | integrity sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==
337 |
338 | vite@^6.2.0:
339 | version "6.2.0"
340 | resolved "https://registry.yarnpkg.com/vite/-/vite-6.2.0.tgz#9dcb543380dab18d8384eb840a76bf30d78633f0"
341 | integrity sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==
342 | dependencies:
343 | esbuild "^0.25.0"
344 | postcss "^8.5.3"
345 | rollup "^4.30.1"
346 | optionalDependencies:
347 | fsevents "~2.3.3"
348 |
--------------------------------------------------------------------------------