summaryrefslogtreecommitdiff
path: root/src/toys
diff options
context:
space:
mode:
authorElizabeth Alexander Hunt <me@liz.coffee>2026-07-05 12:42:44 -0700
committerElizabeth Alexander Hunt <me@liz.coffee>2026-07-05 12:42:44 -0700
commit94b912b649150b9863a62096ba4740b2cc8ad21a (patch)
tree1882236fc980fa5946d3089a2f2e4670f3a70580 /src/toys
parentab0ddcaeb1fc37351d14b89100e07d3a343ba9e1 (diff)
downloadlizdotcoffee-94b912b649150b9863a62096ba4740b2cc8ad21a.tar.gz
lizdotcoffee-94b912b649150b9863a62096ba4740b2cc8ad21a.zip
Factor
Diffstat (limited to 'src/toys')
-rw-r--r--src/toys/euler-golf/css/styles.css81
-rw-r--r--src/toys/euler-golf/index.html80
-rw-r--r--src/toys/euler-golf/js/controls.js33
-rw-r--r--src/toys/euler-golf/js/cx.js308
-rw-r--r--src/toys/euler-golf/js/game.js275
-rw-r--r--src/toys/euler-golf/js/json-ds.js19
-rw-r--r--src/toys/euler-golf/js/modal-vanilla.min.js1
-rw-r--r--src/toys/euler-golf/js/sol.js44
-rw-r--r--src/toys/fourier/index.html46
-rw-r--r--src/toys/fourier/js/script.js294
-rw-r--r--src/toys/godel/index.html51
-rw-r--r--src/toys/godel/js/compiler.js226
-rw-r--r--src/toys/godel/js/godel-worker.js42
-rw-r--r--src/toys/godel/js/main.js5
-rw-r--r--src/toys/godel/js/parser.js1059
-rw-r--r--src/toys/godel/js/ui.js224
-rw-r--r--src/toys/julia/index.html51
-rw-r--r--src/toys/julia/julia.js136
-rw-r--r--src/toys/tabloid/index.html53
-rw-r--r--src/toys/tabloid/js/main.js5
-rw-r--r--src/toys/tabloid/js/playground.js120
-rw-r--r--src/toys/tabloid/js/samples.js308
-rw-r--r--src/toys/tabloid/js/tabloid.js707
-rw-r--r--src/toys/turing/css/styles.css66
-rw-r--r--src/toys/turing/index.html42
-rw-r--r--src/toys/turing/js/machine.js75
-rw-r--r--src/toys/turing/js/main.js4
-rw-r--r--src/toys/turing/js/parser.js141
-rw-r--r--src/toys/turing/js/samples.js88
-rw-r--r--src/toys/turing/js/tape.js103
-rw-r--r--src/toys/turing/js/ui.js386
31 files changed, 0 insertions, 5073 deletions
diff --git a/src/toys/euler-golf/css/styles.css b/src/toys/euler-golf/css/styles.css
deleted file mode 100644
index 219012d..0000000
--- a/src/toys/euler-golf/css/styles.css
+++ /dev/null
@@ -1,81 +0,0 @@
-body {
- margin: 0;
- padding: 0;
- overflow: scroll;
- font-family: Lucida Console, Lucida Sans Typewriter, monaco,
- Bitstream Vera Sans Mono, monospace;
- width: 100vw;
- height: 100vh;
- background: rgb(238, 174, 202);
- background: radial-gradient(
- circle,
- rgba(238, 174, 202, 1) 0%,
- rgba(148, 187, 233, 1) 100%
- );
-}
-
-.canvas {
- padding: 0;
- margin: auto;
- display: block;
- border: 1px solid black;
-
- width: 100vw;
- height: 100vw;
-}
-
-button {
- border-radius: 5px;
- padding: 5px;
- cursor: pointer;
- margin-left: 5px;
-}
-
-.controls {
- cursor: pointer;
- padding: 12px;
- position: fixed;
- bottom: 0;
- right: 0;
- background-color: rgba(255, 255, 255, 0.8);
- border: 1px solid white;
- border-radius: 8px;
- margin-right: 6px;
- margin-bottom: 6px;
-}
-
-.buttons {
- display: flex;
- justify-content: space-around;
- align-items: center;
-}
-
-.modal {
- display: flex;
- position: absolute;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%);
-
- width: 80vw;
- max-width: 500px;
- min-height: 200px;
-
- padding: 12px;
-
- background-color: rgba(255, 255, 255, 0.8);
- border: 1px solid black;
- border-radius: 15px;
-}
-
-.modal-body {
- display: flex;
- justify-content: center;
-}
-
-.slider {
- display: flex;
- justify-content: center;
- align-items: center;
- margin-top: 3px;
-}
diff --git a/src/toys/euler-golf/index.html b/src/toys/euler-golf/index.html
deleted file mode 100644
index 8164823..0000000
--- a/src/toys/euler-golf/index.html
+++ /dev/null
@@ -1,80 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <title>Euler Golf 2</title>
- <link rel="stylesheet" type="text/css" href="css/styles.css" />
- <meta charset="utf-8">
- </head>
- <body>
- <canvas id="canvas"></canvas>
-
- <div class="controls" id="controls-container">
- <div id="controls" style="display: none">
- <div class="buttons">
- <button id="reset">Reset</button>
- <button id="solve">Solve</button>
- <button id="directions">Directions</button>
- </div>
- <div class="slider">
- <label>Gap</label>
- <input type="range" min="15" max="80" id="gap" />
- </div>
- </div>
- <span id="expand-show">↑↑</span>
- </div>
-
- <div
- id="directions-modal"
- class="modal"
- style="display: none"
- tabindex="-1"
- role="dialog"
- >
- <button
- type="button"
- class="close"
- data-dismiss="modal"
- aria-label="Close"
- >
- <span aria-hidden="true">X</span>
- </button>
-
- <div class="modal-body">
- <div style="margin: 0; display: inline-block">
- <h1 style="text-align: center">Euler Golf 2</h1>
- <p>
- Use the left and right arrow keys as navigation & hover over the
- bottom right corner for controls.
- </p>
- <p>Rules</p>
- <ul>
- <li>
- Every move consists of a 90 degree rotation around your last
- position.
- </li>
- <li>You begin at the point one unit right from the center.</li>
- <li>
- The inital point that you rotate around is the origin (blue).
- </li>
- <li>You must navigate to the target point (white).</li>
- </ul>
- <p>
- Initial game by
- <a href="https://kylehovey.github.io/EulerGolf/">speleo</a>,
- reimplemented & solved by
- <a href="https://github.com/Simponic">simponic</a>.
- </p>
- </div>
- </div>
- </div>
-
- <script src="js/modal-vanilla.min.js"></script>
-
- <script src="js/cx.js"></script>
- <script src="js/json-ds.js"></script>
- <script src="js/sol.js"></script>
-
- <script src="js/game.js"></script>
- <script src="js/controls.js"></script>
- </body>
-</html>
diff --git a/src/toys/euler-golf/js/controls.js b/src/toys/euler-golf/js/controls.js
deleted file mode 100644
index 7f606cc..0000000
--- a/src/toys/euler-golf/js/controls.js
+++ /dev/null
@@ -1,33 +0,0 @@
-document
- .getElementById("controls-container")
- .addEventListener("mouseover", () => {
- document.getElementById("controls").style.display = "block";
- document.getElementById("expand-show").style.display = "none";
- });
-
-document
- .getElementById("controls-container")
- .addEventListener("mouseout", () => {
- document.getElementById("controls").style.display = "none";
- document.getElementById("expand-show").style.display = "inline";
- });
-
-document.getElementById("reset").addEventListener("click", () => {
- state = reset_state(state);
-
- state.target = rand_target(state.rows, state.cols);
-});
-
-document.getElementById("solve").addEventListener("click", () => {
- if (!cx.eq(state.path.at(-2), new cx(0, 0))) state = reset_state(state);
-
- state.solution = sol(state.target);
-});
-
-document
- .getElementById("directions")
- .addEventListener("click", () => directions_modal.show());
-
-document.getElementById("gap").addEventListener("input", function () {
- state.changes.gap = Number(this.value);
-});
diff --git a/src/toys/euler-golf/js/cx.js b/src/toys/euler-golf/js/cx.js
deleted file mode 100644
index 371415d..0000000
--- a/src/toys/euler-golf/js/cx.js
+++ /dev/null
@@ -1,308 +0,0 @@
-// http://www.russellcottrell.com/fractalsEtc/cx.js
-
-class cx {
- static degrees(d) {
- cx._RD = d ? Math.PI / 180 : 1;
- }
- // Math.PI/180 for degrees, 1 for radians
- // applies to i/o (constructor, get/set arg, and toString etc.)
-
- constructor(x, y, polar) {
- if (!polar) {
- this.re = x;
- this.im = y;
- } else {
- y *= cx._RD; // may be radians or degrees
- this.re = x * Math.cos(y);
- this.im = x * Math.sin(y);
- }
- }
-
- get abs() {
- return Math.sqrt(this.re * this.re + this.im * this.im);
- }
-
- set abs(r) {
- var theta = this._arg;
- this.re = r * Math.cos(theta);
- this.im = r * Math.sin(theta);
- }
-
- get arg() {
- // returns radians or degrees, non-negative
- return (
- ((Math.atan2(this.im, this.re) + 2 * Math.PI) % (2 * Math.PI)) / cx._RD
- );
- }
-
- set arg(theta) {
- // may be radians or degrees
- var r = this.abs;
- this.re = r * Math.cos(theta * cx._RD);
- this.im = r * Math.sin(theta * cx._RD);
- }
-
- get _arg() {
- // internal; returns radians
- return Math.atan2(this.im, this.re);
- }
-
- static get i() {
- return new cx(0, 1);
- }
-
- static set i(x) {
- throw new Error("i is read-only");
- }
-
- toString(polar) {
- if (!polar)
- return (
- this.re.toString() +
- (this.im >= 0 ? " + " : " - ") +
- Math.abs(this.im).toString() +
- "i"
- );
- else return this.abs.toString() + " cis " + this.arg.toString();
- }
-
- toPrecision(n, polar) {
- if (!polar)
- return (
- this.re.toPrecision(n) +
- (this.im >= 0 ? " + " : " - ") +
- Math.abs(this.im).toPrecision(n) +
- "i"
- );
- else return this.abs.toPrecision(n) + " cis " + this.arg.toPrecision(n);
- }
-
- toPrecis(n, polar) {
- // trims trailing zeros
- if (!polar)
- return (
- parseFloat(this.re.toPrecision(n)).toString() +
- (this.im >= 0 ? " + " : " - ") +
- parseFloat(Math.abs(this.im).toPrecision(n)).toString() +
- "i"
- );
- else
- return (
- parseFloat(this.abs.toPrecision(n)).toString() +
- " cis " +
- parseFloat(this.arg.toPrecision(n)).toString()
- );
- }
-
- toFixed(n, polar) {
- if (!polar)
- return (
- this.re.toFixed(n) +
- (this.im >= 0 ? " + " : " - ") +
- Math.abs(this.im).toFixed(n) +
- "i"
- );
- else return this.abs.toFixed(n) + " cis " + this.arg.toFixed(n);
- }
-
- toExponential(n, polar) {
- if (!polar)
- return (
- this.re.toExponential(n) +
- (this.im >= 0 ? " + " : " - ") +
- Math.abs(this.im).toExponential(n) +
- "i"
- );
- else return this.abs.toExponential(n) + " cis " + this.arg.toExponential(n);
- }
-
- static getReals(c, d) {
- // when c or d may be simple or complex
- var x, y, u, v;
- if (c instanceof cx) {
- x = c.re;
- y = c.im;
- } else {
- x = c;
- y = 0;
- }
- if (d instanceof cx) {
- u = d.re;
- v = d.im;
- } else {
- u = d;
- v = 0;
- }
- return [x, y, u, v];
- }
-
- static conj(c) {
- return new cx(c.re, -c.im);
- }
-
- static neg(c) {
- return new cx(-c.re, -c.im);
- }
-
- static add(c, d) {
- var a = cx.getReals(c, d);
- var x = a[0];
- var y = a[1];
- var u = a[2];
- var v = a[3];
- return new cx(x + u, y + v);
- }
-
- static sub(c, d) {
- var a = cx.getReals(c, d);
- var x = a[0];
- var y = a[1];
- var u = a[2];
- var v = a[3];
- return new cx(x - u, y - v);
- }
-
- static mult(c, d) {
- var a = cx.getReals(c, d);
- var x = a[0];
- var y = a[1];
- var u = a[2];
- var v = a[3];
- return new cx(x * u - y * v, x * v + y * u);
- }
-
- static div(c, d) {
- var a = cx.getReals(c, d);
- var x = a[0];
- var y = a[1];
- var u = a[2];
- var v = a[3];
- return new cx(
- (x * u + y * v) / (u * u + v * v),
- (y * u - x * v) / (u * u + v * v)
- );
- }
-
- static pow(c, int) {
- if (Number.isInteger(int) && int >= 0) {
- var r = Math.pow(c.abs, int);
- var theta = int * c._arg;
- return new cx(r * Math.cos(theta), r * Math.sin(theta));
- } else return NaN;
- }
-
- static root(c, int, k) {
- if (!k) k = 0;
- if (
- Number.isInteger(int) &&
- int >= 2 &&
- Number.isInteger(k) &&
- k >= 0 &&
- k < int
- ) {
- var r = Math.pow(c.abs, 1 / int);
- var theta = (c._arg + 2 * k * Math.PI) / int;
- return new cx(r * Math.cos(theta), r * Math.sin(theta));
- } else return NaN;
- }
-
- static log(c) {
- return new cx(Math.log(c.abs), c._arg);
- }
-
- static exp(c) {
- var r = Math.exp(c.re);
- var theta = c.im;
- return new cx(r * Math.cos(theta), r * Math.sin(theta));
- }
-
- static sin(c) {
- var a = c.re;
- var b = c.im;
- return new cx(Math.sin(a) * Math.cosh(b), Math.cos(a) * Math.sinh(b));
- }
-
- static cos(c) {
- var a = c.re;
- var b = c.im;
- return new cx(Math.cos(a) * Math.cosh(b), -Math.sin(a) * Math.sinh(b));
- }
-
- static tan(c) {
- return cx.div(cx.sin(c), cx.cos(c));
- }
-
- static asin(c, k) {
- if (!k) k = 0;
- var ic = cx.mult(cx.i, c);
- var c2 = cx.pow(c, 2);
- return cx.mult(
- cx.neg(cx.i),
- cx.log(cx.add(ic, cx.root(cx.sub(1, c2), 2, k)))
- );
- }
-
- static acos(c, k) {
- if (!k) k = 0;
- var c2 = cx.pow(c, 2);
- return cx.mult(
- cx.neg(cx.i),
- cx.log(cx.add(c, cx.mult(cx.i, cx.root(cx.sub(1, c2), 2, k))))
- );
- }
-
- static atan(c) {
- return cx.mult(
- cx.div(cx.i, 2),
- cx.log(cx.div(cx.add(cx.i, c), cx.sub(cx.i, c)))
- );
- }
-
- static sinh(c) {
- var a = c.re;
- var b = c.im;
- return new cx(Math.sinh(a) * Math.cos(b), Math.cosh(a) * Math.sin(b));
- }
-
- static cosh(c) {
- var a = c.re;
- var b = c.im;
- return new cx(Math.cosh(a) * Math.cos(b), Math.sinh(a) * Math.sin(b));
- }
-
- static tanh(c) {
- return cx.div(cx.sinh(c), cx.cosh(c));
- }
-
- static asinh(c, k) {
- if (!k) k = 0;
- var c2 = cx.pow(c, 2);
- return cx.log(cx.add(c, cx.root(cx.add(c2, 1), 2, k)));
- }
-
- static acosh(c, k) {
- if (!k) k = 0;
- var c2 = cx.pow(c, 2);
- return cx.log(cx.add(c, cx.root(cx.sub(c2, 1), 2, k)));
- }
-
- static atanh(c) {
- return cx.mult(cx.div(1, 2), cx.log(cx.div(cx.add(1, c), cx.sub(1, c))));
- }
-
- static copy(c) {
- return new cx(c.re, c.im);
- }
-
- static eq(c, d, epsilon) {
- if (!epsilon) {
- if (c.re == d.re && c.im == d.im) return true;
- } else {
- if (Math.abs(c.re - d.re) < epsilon && Math.abs(c.im - d.im) < epsilon)
- return true;
- }
- return false;
- }
-}
-
-cx.degrees(true); // need to call this
diff --git a/src/toys/euler-golf/js/game.js b/src/toys/euler-golf/js/game.js
deleted file mode 100644
index 5e123a0..0000000
--- a/src/toys/euler-golf/js/game.js
+++ /dev/null
@@ -1,275 +0,0 @@
-const DEFAULTS = {
- max_rows: 80,
- max_cols: 80,
- min_gap: 30,
- angle_multiplier: 10e-4,
-};
-
-const CANVAS = document.getElementById("canvas");
-
-let state = {
- grid_padding: 30,
- gap: DEFAULTS.min_gap,
- canvas: CANVAS,
- ctx: CANVAS.getContext("2d"),
- last_render: 0,
- keys: {},
- changes: {},
-};
-
-// Rendering
-CanvasRenderingContext2D.prototype.circle = function (x, y, r, color) {
- this.beginPath();
- this.arc(x, y, r, 0, Math.PI * 2);
- this.fillStyle = color;
- this.fill();
- this.closePath();
-};
-
-CanvasRenderingContext2D.prototype.line = function (
- { x_pos: x1, y_pos: y1 },
- { x_pos: x2, y_pos: y2 },
- width,
- color,
- cap = "round"
-) {
- this.lineWidth = width;
- this.strokeStyle = color;
- this.lineCap = cap;
-
- this.beginPath();
- this.moveTo(x1, y1);
- this.lineTo(x2, y2);
- this.stroke();
- this.closePath();
-};
-
-CanvasRenderingContext2D.prototype.draw_cartesian_path = function (
- grid_spec,
- cartesian_path,
- width = 2,
- color = "#fff"
-) {
- const path = cartesian_path.map((coord) => grid_to_canvas(coord, grid_spec));
- path.slice(1).forEach((coord, i) => {
- this.line(path[i], coord, width, color);
- });
-};
-
-CanvasRenderingContext2D.prototype.do_grid = function (
- rows,
- cols,
- draw_at_grid_pos = (ctx, x, y) => ctx.circle(x, y, 10, "#44ff44")
-) {
- for (let y = 0; y < rows; y++) {
- for (let x = 0; x < cols; x++) {
- draw_at_grid_pos(this, x, y);
- }
- }
-};
-
-CanvasRenderingContext2D.prototype.cartesian_grid = function (
- rows,
- cols,
- grid_spec,
- circle_spec_at_coords = (_x, _y) => ({ radius: 5, color: "#000" })
-) {
- this.do_grid(rows, cols, (ctx, x, y) => {
- const { x_pos, y_pos } = grid_to_canvas({ x, y }, grid_spec);
- const { radius, color } = circle_spec_at_coords(x, y);
-
- ctx.circle(x_pos, y_pos, radius, color);
- });
-};
-
-// Utilities
-const move = (prev, curr, c) => cx.add(prev, cx.mult(c, cx.sub(curr, prev)));
-
-const rand_between = (min, max) =>
- Math.floor(Math.random() * (max - min + 1)) + min;
-
-const rand_target = (rows, cols) => {
- const r = Math.floor((rows - 1) / 2);
- const c = Math.floor((cols - 1) / 2);
- const res = new cx(rand_between(-c, c), rand_between(-r, r));
- if (!sol(res)) return rand_target(rows, cols);
-
- return res;
-};
-
-const calculate_grid_spec = ({ rows, cols, width, height, grid_padding }) => {
- const dx = (width - 2 * grid_padding) / cols;
- const dy = (height - 2 * grid_padding) / rows;
-
- return {
- dx,
- dy,
- start_x: grid_padding + dx / 2,
- start_y: grid_padding + dy / 2,
- };
-};
-
-const grid_to_canvas = ({ x, y }, { dx, dy, start_x, start_y }) => ({
- x_pos: x * dx + start_x,
- y_pos: y * dy + start_y,
-});
-
-const complex_to_grid = (c, rows, cols) => {
- const { re, im } = c;
- return {
- x: re + Math.floor(cols / 2),
- y: Math.floor(rows / 2) - im,
- };
-};
-
-// Game loop
-
-const maybe_add_state_angle_move = ({ angle } = state) => {
- if (angle.im <= -1 || angle.im >= 1) {
- angle.im = angle.im <= -1 ? -1 : 1;
- state.path.push(move(state.path.at(-2), state.path.at(-1), angle));
- state.angle = new cx(0, 0);
- }
- return state;
-};
-
-const handle_input = (state, dt) => {
- if (state.keys.ArrowLeft) {
- state.angle.im += DEFAULTS.angle_multiplier * dt;
- } else if (state.keys.ArrowRight) {
- state.angle.im -= DEFAULTS.angle_multiplier * dt;
- }
- state = maybe_add_state_angle_move(state);
-};
-
-const render = ({ width, height, ctx, rows, cols, target, gap } = state) => {
- ctx.clearRect(0, 0, width, height);
- ctx.fillStyle = "rgba(0, 0, 0, 0)";
- ctx.fillRect(0, 0, width, height);
-
- const grid_spec = calculate_grid_spec(state);
-
- const curr = state.path.at(-1);
- const prev = state.path.at(-2);
-
- const v_diff = cx.sub(curr, prev);
- const theta = (state.angle.im * Math.PI) / 2;
-
- const angle_re = Math.cos(theta) * v_diff.re - Math.sin(theta) * v_diff.im;
- const angle_im = Math.sin(theta) * v_diff.re + Math.cos(theta) * v_diff.im;
-
- ctx.draw_cartesian_path(grid_spec, [
- ...state.path.map((c) => complex_to_grid(c, rows, cols)),
- complex_to_grid(cx.add(new cx(angle_re, angle_im), prev), rows, cols),
- ]);
-
- if (!(state.angle.im == state.angle.re && state.angle.re == 0)) {
- // Draw path to next player's target
- const [a, b] = [
- curr,
- move(prev, curr, new cx(0, state.angle.im < 0 ? -1 : 1)),
- ].map((c) => grid_to_canvas(complex_to_grid(c, rows, cols), grid_spec));
-
- ctx.line(a, b, 6, "rgba(127, 127, 127, 0.3)");
- }
-
- const grid_target = complex_to_grid(target, rows, cols);
- ctx.cartesian_grid(rows, cols, grid_spec, (x, y) => {
- if (x == Math.floor(cols / 2) && y == Math.floor(rows / 2)) {
- return {
- radius: 7,
- color: "#2f9c94",
- };
- } else if (x == grid_target.x && y == grid_target.y) {
- return {
- radius: 8,
- color: "#fff",
- };
- } else {
- return {
- radius: 3,
- color: `rgb(${255 * (x / cols)}, 100, 100)`, // todo: animate with last_render
- };
- }
- });
-
- // Render gap value in slider
- document.getElementById("gap").value = gap;
-};
-
-const loop = (now) => {
- const dt = now - state.last_render;
- state.changes.last_render = now;
-
- if (Object.keys(state.changes).length > 0) {
- state = { ...state, ...state.changes };
-
- if (state.changes.width || state.changes.height || state.changes.gap) {
- state.rows = Math.floor(state.height / state.gap);
- state.cols = Math.floor(state.width / state.gap);
- }
-
- state.changes = {};
- }
-
- if (!state.target) state.target = rand_target(state.rows, state.cols);
-
- if (!state.solution) {
- handle_input(state, dt);
- } else {
- if (!state?.solution.length) {
- delete state.solution;
- } else {
- state.angle.im +=
- (state.solution[0] === "-" ? 1 : -1) * DEFAULTS.angle_multiplier * dt;
-
- state = maybe_add_state_angle_move(state);
-
- if (cx.eq(state.angle, new cx(0, 0))) state.solution.shift();
- }
- }
- render(state);
- requestAnimationFrame(loop);
-};
-
-const reset_state = ({ rows, cols } = state) => ({
- ...state,
- solution: null,
- path: [new cx(0, 0), new cx(1, 0)],
- angle: new cx(0, 0),
-});
-
-// DOM
-const directions_modal = new Modal({
- el: document.getElementById("directions-modal"),
-});
-
-const on_resize = () => {
- CANVAS.width = document.body.clientWidth;
- CANVAS.height = document.body.clientHeight;
- state.changes.width = CANVAS.width;
- state.changes.height = CANVAS.height;
-};
-
-const on_keyup = (e) => {
- delete state.keys[e.key];
-};
-
-const on_keydown = (e) => {
- state.keys[e.key] = true;
-};
-
-window.addEventListener("resize", on_resize);
-window.addEventListener("keydown", on_keydown);
-window.addEventListener("keyup", on_keyup);
-
-// main
-on_resize();
-state = reset_state(state);
-
-if (!sessionStorage.getItem("seen-instructions")) {
- directions_modal.show();
- sessionStorage.setItem("seen-instructions", true);
-}
-
-requestAnimationFrame(loop);
diff --git a/src/toys/euler-golf/js/json-ds.js b/src/toys/euler-golf/js/json-ds.js
deleted file mode 100644
index dc7e88e..0000000
--- a/src/toys/euler-golf/js/json-ds.js
+++ /dev/null
@@ -1,19 +0,0 @@
-class JSONSet {
- items = new Set();
-
- constructor(initial) {
- if (Array.isArray(initial)) {
- initial.map((x) => this.apply_set_function("add", x));
- } else {
- this.apply_set_function("add", initial);
- }
-
- ["add", "has", "remove"].forEach(
- (f_name) => (this[f_name] = (x) => this.apply_set_function(f_name, x))
- );
- }
-
- apply_set_function(f_name, x) {
- return this.items[f_name](JSON.stringify(x));
- }
-}
diff --git a/src/toys/euler-golf/js/modal-vanilla.min.js b/src/toys/euler-golf/js/modal-vanilla.min.js
deleted file mode 100644
index 0d314c7..0000000
--- a/src/toys/euler-golf/js/modal-vanilla.min.js
+++ /dev/null
@@ -1 +0,0 @@
-var Modal=function(e){function t(i){if(n[i])return n[i].exports;var o=n[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,i){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:i})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=0)}([function(e,t,n){e.exports=n(1).default},function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function r(e){for(var t in e)Array.isArray(e[t])?e[t].forEach(function(e){r(e)}):null!==e[t]&&"object"===p(e[t])&&Object.freeze(e[t]);return Object.freeze(e)}function a(){return(65536*(1+Math.random())|0).toString(16)+(65536*(1+Math.random())|0).toString(16)}function l(e,t,n){var i=e.data||{};if(void 0===n){if(e.data&&e.data[t])return e.data[t];var o=e.getAttribute("data-"+t);return void 0!==o?o:null}return i[t]=n,e.data=i,e}function d(e,t){return e.nodeName?e:(e=e.replace(/(\t|\n$)/g,""),_||(_=document.createElement("div")),_.innerHTML="",_.innerHTML=e,!0===t?_.childNodes:_.childNodes[0])}function c(){var e=void 0,t=void 0,n=void 0,i=document.createElement("div");return v(i.style,{visibility:"hidden",width:"100px"}),document.body.appendChild(i),n=i.offsetWidth,i.style.overflow="scroll",e=document.createElement("div"),e.style.width="100%",i.appendChild(e),t=n-e.offsetWidth,document.body.removeChild(i),t}function h(e){for(var t=[e];e.parentNode;)e=e.parentNode,t.push(e);return t}Object.defineProperty(t,"__esModule",{value:!0});var u=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}(),v=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},f=n(2),m=function(e){return e&&e.__esModule?e:{default:e}}(f),_=null,y=Object.freeze({el:null,animate:!0,animateClass:"fade",animateInClass:"show",appendTo:"body",backdrop:!0,keyboard:!0,title:!1,header:!0,content:!1,footer:!0,buttons:null,headerClose:!0,construct:!1,transition:300,backdropTransition:150}),b=r({dialog:[{text:"Cancel",value:!1,attr:{class:"btn btn-default","data-dismiss":"modal"}},{text:"OK",value:!0,attr:{class:"btn btn-primary","data-dismiss":"modal"}}],alert:[{text:"OK",attr:{class:"btn btn-primary","data-dismiss":"modal"}}],confirm:[{text:"Cancel",value:!1,attr:{class:"btn btn-default","data-dismiss":"modal"}},{text:"OK",value:!0,attr:{class:"btn btn-primary","data-dismiss":"modal"}}]}),g={container:'<div class="modal"></div>',dialog:'<div class="modal-dialog"></div>',content:'<div class="modal-content"></div>',header:'<div class="modal-header"></div>',headerClose:'<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>',body:'<div class="modal-body"></div>',footer:'<div class="modal-footer"></div>',backdrop:'<div class="modal-backdrop"></div>'},k=function(e){function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};i(this,t);var n=o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this));if(n.id=a(),n.el=null,n._html={},n._events={},n._visible=!1,n._pointerInContent=!1,n._options=v({},t.options,e),n._templates=v({},t.templates,e.templates||{}),n._html.appendTo=document.querySelector(n._options.appendTo),n._scrollbarWidth=c(),null===n._options.buttons&&(n._options.buttons=t.buttons.dialog),n._options.el){var s=n._options.el;if("string"==typeof n._options.el&&!(s=document.querySelector(n._options.el)))throw new Error("Selector: DOM Element "+n._options.el+" not found.");l(s,"modal",n),n.el=s}else n._options.construct=!0;return n._options.construct?n._render():n._mapDom(),n}return s(t,e),u(t,null,[{key:"alert",value:function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return new t(v({},y,{title:e,content:!1,construct:!0,headerClose:!1,buttons:t.buttons.alert},n))}},{key:"confirm",value:function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return new t(v({},y,{title:e,content:!1,construct:!0,headerClose:!1,buttons:t.buttons.confirm},n))}},{key:"templates",set:function(e){this._baseTemplates=e},get:function(){return v({},g,t._baseTemplates||{})}},{key:"buttons",set:function(e){this._baseButtons=e},get:function(){return v({},b,t._baseButtons||{})}},{key:"options",set:function(e){this._baseOptions=e},get:function(){return v({},y,t._baseOptions||{})}},{key:"version",get:function(){return"0.12.0"}}]),u(t,[{key:"_render",value:function(){var e=this._html,t=this._options,n=this._templates,i=!!t.animate&&t.animateClass;return e.container=d(n.container),e.dialog=d(n.dialog),e.content=d(n.content),e.header=d(n.header),e.headerClose=d(n.headerClose),e.body=d(n.body),e.footer=d(n.footer),i&&e.container.classList.add(i),this._setHeader(),this._setContent(),this._setFooter(),this.el=e.container,e.dialog.appendChild(e.content),e.container.appendChild(e.dialog),this}},{key:"_mapDom",value:function(){var e=this._html,t=this._options;return this.el.classList.contains(t.animateClass)&&(t.animate=!0),e.container=this.el,e.dialog=this.el.querySelector(".modal-dialog"),e.content=this.el.querySelector(".modal-content"),e.header=this.el.querySelector(".modal-header"),e.headerClose=this.el.querySelector(".modal-header .close"),e.body=this.el.querySelector(".modal-body"),e.footer=this.el.querySelector(".modal-footer"),this._setHeader(),this._setContent(),this._setFooter(),this}},{key:"_setHeader",value:function(){var e=this._html,t=this._options;t.header&&e.header&&(t.title.nodeName?e.header.innerHTML=t.title.outerHTML:"string"==typeof t.title&&(e.header.innerHTML='<h4 class="modal-title">'+t.title+"</h4>"),null===this.el&&e.headerClose&&t.headerClose&&e.header.appendChild(e.headerClose),t.construct&&e.content.appendChild(e.header))}},{key:"_setContent",value:function(){var e=this._html,t=this._options;t.content&&e.body&&("string"==typeof t.content?e.body.innerHTML=t.content:e.body.innerHTML=t.content.outerHTML,t.construct&&e.content.appendChild(e.body))}},{key:"_setFooter",value:function(){var e=this._html,t=this._options;t.footer&&e.footer&&(t.footer.nodeName?e.footer.ineerHTML=t.footer.outerHTML:"string"==typeof t.footer?e.footer.innerHTML=t.footer:e.footer.children.length||t.buttons.forEach(function(t){var n=document.createElement("button");l(n,"button",t),n.innerHTML=t.text,n.setAttribute("type","button");for(var i in t.attr)n.setAttribute(i,t.attr[i]);e.footer.appendChild(n)}),t.construct&&e.content.appendChild(e.footer))}},{key:"_setEvents",value:function(){var e=(this._options,this._html);this._events.keydownHandler=this._handleKeydownEvent.bind(this),document.body.addEventListener("keydown",this._events.keydownHandler),this._events.mousedownHandler=this._handleMousedownEvent.bind(this),e.container.addEventListener("mousedown",this._events.mousedownHandler),this._events.clickHandler=this._handleClickEvent.bind(this),e.container.addEventListener("click",this._events.clickHandler),this._events.resizeHandler=this._handleResizeEvent.bind(this),window.addEventListener("resize",this._events.resizeHandler)}},{key:"_handleMousedownEvent",value:function(e){var t=this;this._pointerInContent=!1,h(e.target).every(function(e){return!e.classList||!e.classList.contains("modal-content")||(t._pointerInContent=!0,!1)})}},{key:"_handleClickEvent",value:function(e){var t=this;h(e.target).every(function(n){return!("HTML"===n.tagName||!0!==t._options.backdrop&&n.classList.contains("modal")||n.classList.contains("modal-content")||("modal"===n.getAttribute("data-dismiss")?(t.emit("dismiss",t,e,l(e.target,"button")),t.hide(),1):!t._pointerInContent&&n.classList.contains("modal")&&(t.emit("dismiss",t,e,null),t.hide(),1)))}),this._pointerInContent=!1}},{key:"_handleKeydownEvent",value:function(e){27===e.which&&this._options.keyboard&&(this.emit("dismiss",this,e,null),this.hide())}},{key:"_handleResizeEvent",value:function(e){this._resize()}},{key:"show",value:function(){var e=this,t=this._options,n=this._html;return this.emit("show",this),this._checkScrollbar(),this._setScrollbar(),document.body.classList.add("modal-open"),t.construct&&n.appendTo.appendChild(n.container),n.container.style.display="block",n.container.scrollTop=0,!1!==t.backdrop?(this.once("showBackdrop",function(){e._setEvents(),t.animate&&n.container.offsetWidth,n.container.classList.add(t.animateInClass),setTimeout(function(){e._visible=!0,e.emit("shown",e)},t.transition)}),this._backdrop()):(this._setEvents(),t.animate&&n.container.offsetWidth,n.container.classList.add(t.animateInClass),setTimeout(function(){e._visible=!0,e.emit("shown",e)},t.transition)),this._resize(),this}},{key:"toggle",value:function(){this._visible?this.hide():this.show()}},{key:"_resize",value:function(){var e=this._html.container.scrollHeight>document.documentElement.clientHeight;this._html.container.style.paddingLeft=!this.bodyIsOverflowing&&e?this._scrollbarWidth+"px":"",this._html.container.style.paddingRight=this.bodyIsOverflowing&&!e?this._scrollbarWidth+"px":""}},{key:"_backdrop",value:function(){var e=this,t=this._html,n=this._templates,i=this._options,o=!!i.animate&&i.animateClass;t.backdrop=d(n.backdrop),o&&t.backdrop.classList.add(o),t.appendTo.appendChild(t.backdrop),o&&t.backdrop.offsetWidth,t.backdrop.classList.add(i.animateInClass),setTimeout(function(){e.emit("showBackdrop",e)},this._options.backdropTransition)}},{key:"hide",value:function(){var e=this,t=this._html,n=this._options,i=t.container.classList;if(this.emit("hide",this),i.remove(n.animateInClass),n.backdrop){t.backdrop.classList.remove(n.animateInClass)}return this._removeEvents(),setTimeout(function(){document.body.classList.remove("modal-open"),document.body.style.paddingRight=e.originalBodyPad},n.backdropTransition),setTimeout(function(){n.backdrop&&t.backdrop.parentNode.removeChild(t.backdrop),t.container.style.display="none",n.construct&&t.container.parentNode.removeChild(t.container),e._visible=!1,e.emit("hidden",e)},n.transition),this}},{key:"_removeEvents",value:function(){this._events.keydownHandler&&document.body.removeEventListener("keydown",this._events.keydownHandler),this._html.container.removeEventListener("mousedown",this._events.mousedownHandler),this._html.container.removeEventListener("click",this._events.clickHandler),window.removeEventListener("resize",this._events.resizeHandler)}},{key:"_checkScrollbar",value:function(){this.bodyIsOverflowing=document.body.clientWidth<window.innerWidth}},{key:"_setScrollbar",value:function(){if(this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing){var e=parseInt(this.originalBodyPad||0,10);document.body.style.paddingRight=e+this._scrollbarWidth+"px"}}}]),t}(m.default);t.default=k},function(e,t){function n(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function i(e){return"function"==typeof e}function o(e){return"number"==typeof e}function s(e){return"object"==typeof e&&null!==e}function r(e){return void 0===e}e.exports=n,n.EventEmitter=n,n.prototype._events=void 0,n.prototype._maxListeners=void 0,n.defaultMaxListeners=10,n.prototype.setMaxListeners=function(e){if(!o(e)||e<0||isNaN(e))throw TypeError("n must be a positive number");return this._maxListeners=e,this},n.prototype.emit=function(e){var t,n,o,a,l,d;if(this._events||(this._events={}),"error"===e&&(!this._events.error||s(this._events.error)&&!this._events.error.length)){if((t=arguments[1])instanceof Error)throw t;var c=new Error('Uncaught, unspecified "error" event. ('+t+")");throw c.context=t,c}if(n=this._events[e],r(n))return!1;if(i(n))switch(arguments.length){case 1:n.call(this);break;case 2:n.call(this,arguments[1]);break;case 3:n.call(this,arguments[1],arguments[2]);break;default:a=Array.prototype.slice.call(arguments,1),n.apply(this,a)}else if(s(n))for(a=Array.prototype.slice.call(arguments,1),d=n.slice(),o=d.length,l=0;l<o;l++)d[l].apply(this,a);return!0},n.prototype.addListener=function(e,t){var o;if(!i(t))throw TypeError("listener must be a function");return this._events||(this._events={}),this._events.newListener&&this.emit("newListener",e,i(t.listener)?t.listener:t),this._events[e]?s(this._events[e])?this._events[e].push(t):this._events[e]=[this._events[e],t]:this._events[e]=t,s(this._events[e])&&!this._events[e].warned&&(o=r(this._maxListeners)?n.defaultMaxListeners:this._maxListeners)&&o>0&&this._events[e].length>o&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace()),this},n.prototype.on=n.prototype.addListener,n.prototype.once=function(e,t){function n(){this.removeListener(e,n),o||(o=!0,t.apply(this,arguments))}if(!i(t))throw TypeError("listener must be a function");var o=!1;return n.listener=t,this.on(e,n),this},n.prototype.removeListener=function(e,t){var n,o,r,a;if(!i(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(n=this._events[e],r=n.length,o=-1,n===t||i(n.listener)&&n.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(s(n)){for(a=r;a-- >0;)if(n[a]===t||n[a].listener&&n[a].listener===t){o=a;break}if(o<0)return this;1===n.length?(n.length=0,delete this._events[e]):n.splice(o,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},n.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(n=this._events[e],i(n))this.removeListener(e,n);else if(n)for(;n.length;)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},n.prototype.listeners=function(e){return this._events&&this._events[e]?i(this._events[e])?[this._events[e]]:this._events[e].slice():[]},n.prototype.listenerCount=function(e){if(this._events){var t=this._events[e];if(i(t))return 1;if(t)return t.length}return 0},n.listenerCount=function(e,t){return e.listenerCount(t)}}]);
diff --git a/src/toys/euler-golf/js/sol.js b/src/toys/euler-golf/js/sol.js
deleted file mode 100644
index b4e527f..0000000
--- a/src/toys/euler-golf/js/sol.js
+++ /dev/null
@@ -1,44 +0,0 @@
-const DEPTH = 15;
-
-const DIRECTION = {
- 0: new cx(0, 1),
- 1: new cx(0, -1),
-};
-
-const construct_moves = (curr, prev) =>
- Object.keys(DIRECTION).map((x) => move(curr, prev, DIRECTION[x]));
-
-const backtrack = (local_index, depth) =>
- local_index
- .toString(2)
- .padStart(depth, "0")
- .split("")
- .map((direction) => (Number(direction) ? "+" : "-"));
-
-const sol = (target, start_from = new cx(0, 0), start_to = new cx(1, 0)) => {
- const next_moves = construct_moves(start_from, start_to);
- const solved_in_first_move = next_moves.findIndex((move) =>
- cx.eq(move, target)
- );
- if (solved_in_first_move != -1) return backtrack(solved_in_first_move, 1);
-
- let moves = [start_to, ...next_moves];
- let curr_depth = 2;
- while (curr_depth < DEPTH) {
- for (let i = 0; i < Math.pow(2, curr_depth); i++) {
- const direction = DIRECTION[Number(i.toString(2).at(-1))];
- // Current element is at i >> 1 + the offset for the previous group (which is
- // the sum of the geometric series 2**n until curr_depth - 1)
- const current_i = (i >> 1) + (1 - Math.pow(2, curr_depth - 1)) / (1 - 2);
- const previous_i = (i >> 2) + (1 - Math.pow(2, curr_depth - 2)) / (1 - 2);
-
- const new_move = move(moves[previous_i], moves[current_i], direction);
-
- moves.push(new_move);
- if (cx.eq(new_move, target)) return backtrack(i, curr_depth);
- }
- curr_depth++;
- }
-
- return null;
-};
diff --git a/src/toys/fourier/index.html b/src/toys/fourier/index.html
deleted file mode 100644
index ccb8f48..0000000
--- a/src/toys/fourier/index.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <title>Simponic's FT Visualizer</title>
- <style>
- body {
- margin: 0;
- height: 100vh;
- width: 100vw;
- padding: 0;
- }
-
- #calculator {
- height: 50vh;
- width: 100%;
- }
-
- .canvas-holder {
- height: 50vh;
- width: 100%;
- }
-
- .container {
- display: flex;
- flex-direction: column;
- margin-left: auto;
- margin-right: auto;
- max-width: 1300px;
- min-height: 100%;
- padding-left: 2rem;
- padding-right: 2rem;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <div id="calculator"></div>
- <div class="canvas-holder">
- <canvas id="canvas"></canvas>
- </div>
- </div>
-
- <script src="https://www.desmos.com/api/v1.7/calculator.js?apiKey=dcb31709b452b1cf9dc26972add0fda6"></script>
- <script src="js/script.js"></script>
- </body>
-</html>
diff --git a/src/toys/fourier/js/script.js b/src/toys/fourier/js/script.js
deleted file mode 100644
index 8d5af31..0000000
--- a/src/toys/fourier/js/script.js
+++ /dev/null
@@ -1,294 +0,0 @@
-const RENDER_TYPE = {
- LATEX: 1,
- FUNC: 2,
-};
-const THRESHOLD = 1e-12;
-const FONT = "Courier New";
-const FONT_HEIGHT_PX = 24;
-const DX = 4;
-
-const canvas = document.getElementById("canvas");
-const ctx = canvas.getContext("2d");
-let state = {};
-const initializeState = () => {
- const xLabels = [
- "Jan",
- "Feb",
- "Mar",
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sept",
- "Oct",
- "Nov",
- "Dec",
- "Jan",
- ];
- const yLabels = ["Great", "Good", "Meh", "Bad", "Horrible"];
- return {
- width: canvas.parentElement.clientWidth,
- height: canvas.parentElement.clientHeight,
- xLabels,
- yLabels,
- yLabelPadding: 12,
- heights: Array(xLabels.length * DX - 1).fill(0),
- };
-};
-
-const dft = (heights, render = RENDER_TYPE.LATEX, threshold = THRESHOLD) => {
- const n = heights.length;
- return Array(n)
- .fill()
- .map((x, w) => {
- const rate = -2 * Math.PI * w;
- const s = heights.reduce(
- (a, x, i) => ({
- re: a.re + x * Math.cos((rate * i) / n),
- im: a.im + x * Math.sin((rate * i) / n),
- }),
- { re: 0, im: 0 }
- );
-
- Object.entries(s).forEach(
- ([key, value]) =>
- (s[key] = ((Math.abs(value) < threshold ? 0 : 1) * value) / n)
- );
-
- const amp = Math.sqrt(s.re * s.re + s.im * s.im);
- const phase = Math.atan2(s.im, s.re);
-
- switch (render) {
- case RENDER_TYPE.LATEX:
- return `${amp}\\cos\\left(${w}\\frac{${
- 2 * DX
- }\\pi}{${n}}x+${phase}\\right)`;
- case RENDER_TYPE.FUNC:
- return (t) => amp * Math.cos(w * t + phase);
- }
- });
-};
-
-const resizeCanvas = ({ width, height }) => {
- canvas.width = width;
- canvas.style.width = width;
- canvas.height = height;
- canvas.style.height = height;
-};
-
-const loop = () => {
- const stateChanges = Object.keys(state.diff);
- if (stateChanges.length > 0) {
- state = { ...state, ...state.diff };
- if (
- state.diff.width ||
- state.diff.height ||
- state.diff.xLabels ||
- state.diff.yLabels
- ) {
- resizeCanvas(state.diff);
- ctx.font = `${FONT_HEIGHT_PX}px ${FONT}`;
- state.maxYLabelWidth = state.yLabels.reduce(
- (a, label) => Math.max(ctx.measureText(label).width, a),
- -Infinity
- );
-
- state.gridBoxWidth =
- state.width - state.maxYLabelWidth - state.yLabelPadding;
- state.gridBoxHeight = state.height - 2.5 * FONT_HEIGHT_PX; // 2.5 to include bottom part of tall letters ("g", "y", etc.)
-
- state.topLeftGridPos = {
- x: state.maxYLabelWidth + state.yLabelPadding,
- y: FONT_HEIGHT_PX,
- };
-
- state.bottomRightGridPos = {
- x: state.topLeftGridPos.x + state.gridBoxWidth,
- y: state.topLeftGridPos.y + state.gridBoxHeight,
- };
- }
- if (state.diff.heights) drawDesmos();
- draw(state);
- state.diff = {};
- }
-
- requestAnimationFrame(loop);
-};
-
-const drawLine = (pos1, pos2) => {
- ctx.beginPath();
- ctx.moveTo(pos1.x, pos1.y);
- ctx.lineTo(pos2.x, pos2.y);
- ctx.stroke();
-};
-
-const drawDividers = (
- xDividers,
- yDividers,
- topLeftGridPos,
- bottomRightGridPos,
- yLabelPadding
-) => {
- ctx.font = `${FONT_HEIGHT_PX}px ${FONT}`;
- xDividers.forEach(({ label, position }) => {
- ctx.fillText(label, position.x - ctx.measureText(label).width, position.y);
- drawLine(
- { ...position, y: topLeftGridPos.y },
- { ...position, y: bottomRightGridPos.y }
- );
- });
- yDividers.forEach(({ label, position }) => {
- ctx.fillText(
- label,
- topLeftGridPos.x - yLabelPadding - ctx.measureText(label).width,
- position.y
- );
- drawLine(
- { ...position, x: topLeftGridPos.x },
- { ...position, x: bottomRightGridPos.x }
- );
- });
-};
-
-const draw = ({
- heights,
- gridBoxWidth,
- gridBoxHeight,
- topLeftGridPos,
- bottomRightGridPos,
- maxYLabelWidth,
- xLabels,
- yLabels,
- width,
- height,
-}) => {
- ctx.clearRect(0, 0, width, height);
-
- const xDividers = xLabels.map((label, i) => ({
- label,
- position: {
- x: topLeftGridPos.x + (gridBoxWidth / (xLabels.length - 1)) * i,
- y: bottomRightGridPos.y + FONT_HEIGHT_PX,
- },
- }));
-
- const yDividers = yLabels.map((label, i) => ({
- label,
- position: {
- x: 0,
- y: topLeftGridPos.y + (gridBoxHeight / (yLabels.length - 1)) * i,
- },
- }));
-
- drawDividers(xDividers, yDividers, topLeftGridPos, bottomRightGridPos, 12);
-
- const dx = gridBoxWidth / (DX * (xLabels.length - 1));
- const prevStrokeStyle = ctx.strokeStyle;
- ctx.strokeStyle = "red";
- for (let i = 0; i < heights.length; ++i) {
- const x = dx * i + topLeftGridPos.x;
- drawLine(
- { x, y: (gridBoxHeight / 2) * (1 - heights[i]) + topLeftGridPos.y },
- {
- x: x + dx,
- y: (gridBoxHeight / 2) * (1 - heights[i + 1]) + topLeftGridPos.y,
- }
- );
- }
- ctx.strokeStyle = prevStrokeStyle;
-};
-
-const calculator = Desmos.GraphingCalculator(
- document.getElementById("calculator"),
- {
- expressionsCollapsed: true,
- autosize: true,
- }
-);
-calculator.setMathBounds({
- left: -0.8,
- right: 12,
- bottom: -3,
- top: 3,
-});
-
-const drawDesmos = () => {
- const equations = dft(state.heights);
-
- calculator.setExpression({
- id: `graph-total`,
- latex: equations.map((_x, i) => `y_{${i}}`).join(" + "),
- });
- equations.forEach((x, i) =>
- calculator.setExpression({
- id: `graph${i}`,
- latex: `y_{${i}}=${x}`,
- hidden: true,
- })
- );
-};
-
-let isDown = false;
-canvas.addEventListener(
- "mousedown",
- (e) => {
- e.preventDefault();
- isDown = true;
- },
- true
-);
-
-canvas.addEventListener(
- "mouseup",
- (e) => {
- e.preventDefault();
- isDown = false;
- },
- true
-);
-
-canvas.addEventListener(
- "mousemove",
- (e) => {
- e.preventDefault();
- if (isDown) {
- const rect = canvas.getBoundingClientRect();
- const [x, y] = [e.clientX - rect.left, e.clientY - rect.top];
-
- const {
- topLeftGridPos,
- bottomRightGridPos,
- gridBoxWidth,
- gridBoxHeight,
- heights,
- xLabels,
- } = state;
- const delta = gridBoxWidth / (DX * (xLabels.length - 1));
-
- const bin = Math.min(
- Math.round(Math.max(x - topLeftGridPos.x, 0) / delta),
- heights.length - 1
- );
- heights[bin] = Math.min(
- Math.max(1 - (2 * (y - topLeftGridPos.y)) / gridBoxHeight, -1),
- 1
- );
- state.diff.heights = heights;
- }
- },
- true
-);
-
-window.addEventListener("resize", () => {
- state.diff = {
- ...state.diff,
- width: canvas.parentElement.clientWidth,
- height: canvas.parentElement.clientHeight,
- };
-});
-
-(() => {
- state.diff = initializeState();
- window.requestAnimationFrame(loop);
-})();
diff --git a/src/toys/godel/index.html b/src/toys/godel/index.html
deleted file mode 100644
index 50a680a..0000000
--- a/src/toys/godel/index.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel="stylesheet" href="https://adelie.liz.coffee/bundle.css">
- <title>Gödel Numbering</title>
-</head>
-<body>
- <main>
- <article>
- <h3>Gödel Numbering</h3>
- <section>
- <div>
- <h4>L Source</h4>
- <div id="source-editor" class="code-editor-container mb-sm" aria-label="L source code"></div>
- <div>
- <button id="compile-btn" class="primary">Compile</button>
- <button id="copy-btn">Copy State Link</button>
- </div>
- <span id="compile-status" class="status-text muted"></span>
- </div>
- </section>
-
- <section class="mt-sm">
- <div>
- <h4>"Compiled" JS</h4>
- <div id="compiled-editor" class="code-editor-container mb-sm" aria-label="Compiled JavaScript"></div>
- <div class="button-group">
- <button id="eval-btn">Eval</button>
- </div>
- <span id="eval-status" class="status-text muted"></span>
- </div>
- </section>
-
- <section class="mt-sm">
- <h4>Gödel Sequence</h4>
- <pre id="godel-sequence">Compile to view the Gödel sequence.</pre>
- <div>
- <button id="compute-godel-btn">Compute Gödel Number</button>
- </div>
- <pre id="godel-number"></pre>
- </section>
- </article>
- </main>
-
- <script src="https://adelie.liz.coffee/bundle.js"></script>
- <script src="https://adelie.liz.coffee/adelie-editor.js"></script>
- <script type="module" src="./js/main.js"></script>
-</body>
-</html>
diff --git a/src/toys/godel/js/compiler.js b/src/toys/godel/js/compiler.js
deleted file mode 100644
index d15c0f4..0000000
--- a/src/toys/godel/js/compiler.js
+++ /dev/null
@@ -1,226 +0,0 @@
-const INDENT_SIZE = 2;
-
-class CodeBuilder {
- constructor(indentSize = INDENT_SIZE) {
- this.indentSize = indentSize;
- this.indentLevel = 0;
- this.parts = [];
- }
-
- addLine(line = "") {
- const indent = " ".repeat(this.indentLevel * this.indentSize);
- this.parts.push(line ? `${indent}${line}` : "");
- }
-
- open(line) {
- if (line) {
- this.addLine(line);
- }
- this.indentLevel += 1;
- }
-
- close(line) {
- this.indentLevel = Math.max(0, this.indentLevel - 1);
- if (line) {
- this.addLine(line);
- }
- }
-
- toString() {
- return this.parts.join("\n");
- }
-}
-
-const compileGoto = (gotoNode, builder) => {
- builder.addLine(`this.followGoto("${gotoNode.label.symbol}");`);
- builder.addLine("return;");
-};
-
-const compileConditional = (conditionalNode, builder) => {
- const variable = conditionalNode.variable.symbol;
- builder.addLine(`if (this.get("${variable}") !== 0) {`);
- builder.open();
- compileGoto(conditionalNode.goto, builder);
- builder.close("}");
-};
-
-const compileAssignment = (assignmentNode, builder) => {
- const variable = assignmentNode.variable.symbol;
- const expr = assignmentNode.expr || {};
-
- if (expr.opr === "+") {
- builder.addLine(`this.addOne("${variable}");`);
- } else if (expr.opr === "-") {
- builder.addLine(`this.subtractOne("${variable}");`);
- } else {
- builder.addLine("// noop");
- }
-};
-
-const compileInstruction = (instruction, builder) => {
- if (instruction.goto) {
- compileGoto(instruction.goto, builder);
- return;
- }
-
- if (instruction.conditional) {
- compileConditional(instruction.conditional, builder);
- } else if (instruction.assignment) {
- compileAssignment(instruction.assignment, builder);
- }
-
- builder.addLine("this.instructionPointer++;");
-};
-
-const emitMethod = (builder, signature, bodyFn) => {
- builder.addLine(`${signature} {`);
- builder.open();
- bodyFn();
- builder.close("}");
- builder.addLine("");
-};
-
-const emitConstructor = (builder, ast, godelSequence, methodCatalog) => {
- emitMethod(builder, "constructor()", () => {
- builder.addLine("this.variables = new Map();");
- builder.addLine("this.labelInstructions = new Map();");
- builder.addLine("this.instructions = new Map();");
- builder.addLine("this.instructions.set(0, () => this.main());");
- builder.addLine("this.instructionPointer = 0;");
- builder.addLine('this.variables.set("Y", 0);');
- builder.addLine(`this.finalInstruction = ${ast.instructions.length + 1};`);
- builder.addLine('this.labelInstructions.set("E1", this.finalInstruction);');
- builder.addLine("");
- builder.addLine("// instruction bindings");
-
- ast.instructions.forEach((entry, index) => {
- const instructionNode = entry.instruction;
- const instructionIdx = index + 1;
- godelSequence.push(entry.godel);
-
- if (instructionNode.label) {
- const labelName = instructionNode.label.symbol;
- builder.addLine(
- `this.instructions.set(${instructionIdx}, () => this.${labelName}());`
- );
- builder.addLine(
- `this.labelInstructions.set("${labelName}", ${instructionIdx});`
- );
- methodCatalog.push({
- name: labelName,
- index: instructionIdx,
- node: instructionNode.instruction
- });
- } else {
- const methodName = `instruction${instructionIdx}`;
- builder.addLine(
- `this.instructions.set(${instructionIdx}, () => this.${methodName}());`
- );
- methodCatalog.push({
- name: methodName,
- index: instructionIdx,
- node: instructionNode
- });
- }
- });
- });
-};
-
-const emitRuntimeHelpers = (builder, instructionCount) => {
- emitMethod(builder, "get(variable)", () => {
- builder.addLine("if (!this.variables.has(variable)) {");
- builder.open();
- builder.addLine("this.variables.set(variable, 0);");
- builder.close("}");
- builder.addLine("return this.variables.get(variable);");
- });
-
- emitMethod(builder, "addOne(variable)", () => {
- builder.addLine("const val = this.get(variable);");
- builder.addLine("this.variables.set(variable, val + 1);");
- });
-
- emitMethod(builder, "subtractOne(variable)", () => {
- builder.addLine("const val = this.get(variable);");
- builder.addLine("this.variables.set(variable, val - 1);");
- });
-
- emitMethod(builder, "followGoto(label)", () => {
- builder.addLine("this.instructionPointer = this.labelInstructions.get(label);");
- });
-
- emitMethod(builder, "step()", () => {
- builder.addLine("if (!this.isCompleted()) {");
- builder.open();
- builder.addLine("const procedure = this.instructions.get(this.instructionPointer);");
- builder.addLine("procedure();");
- builder.close("}");
- builder.addLine("return this.instructionPointer;");
- });
-
- emitMethod(builder, "isCompleted()", () => {
- builder.addLine("return this.instructionPointer === this.finalInstruction;");
- });
-
- emitMethod(builder, "getResult()", () => {
- builder.addLine('return this.variables.get("Y");');
- });
-
- emitMethod(builder, "run(maxIter = 500_000)", () => {
- builder.addLine("let iter = 0;");
- builder.addLine("while (!this.isCompleted() && ++iter < maxIter) {");
- builder.open();
- builder.addLine("this.step();");
- builder.close("}");
- builder.addLine("if (iter < maxIter) {");
- builder.open();
- builder.addLine("return this.getResult();");
- builder.close("}");
- builder.addLine(
- 'throw new Error("Program exceeded iteration limit. Try optimizing your instructions or increasing the cap.");'
- );
- });
-
- emitMethod(builder, "main()", () => {
- if (instructionCount > 0) {
- builder.addLine("this.instructionPointer = 1;");
- } else {
- builder.addLine("this.instructionPointer = this.finalInstruction;");
- }
- });
-};
-
-const emitInstructionMethods = (builder, catalog) => {
- catalog.forEach((entry) => {
- emitMethod(builder, `${entry.name}()`, () => {
- builder.addLine(`this.instructionPointer = ${entry.index};`);
- compileInstruction(entry.node, builder);
- });
- });
-};
-
-export const compileProgram = (ast) => {
- const builder = new CodeBuilder();
- const godelSequence = [];
- const methodCatalog = [];
-
- builder.addLine("class Program {");
- builder.open();
- emitConstructor(builder, ast, godelSequence, methodCatalog);
- emitRuntimeHelpers(builder, ast.instructions.length);
- emitInstructionMethods(builder, methodCatalog);
- builder.close("}");
- builder.addLine("");
- builder.addLine("// bootstrap");
- builder.addLine("const program = new Program();");
- builder.addLine('// program.variables.set("X1", 2);');
- builder.addLine('// program.variables.set("X2", 3);');
- builder.addLine("program.run();");
- builder.addLine("console.log(program.variables);");
- builder.addLine("program.getResult();");
-
- return {
- js: builder.toString(),
- godelSequence
- };
-};
diff --git a/src/toys/godel/js/godel-worker.js b/src/toys/godel/js/godel-worker.js
deleted file mode 100644
index 1ba9da4..0000000
--- a/src/toys/godel/js/godel-worker.js
+++ /dev/null
@@ -1,42 +0,0 @@
-const isPrime = (n) => {
- if (n < 2) {
- return false;
- }
- if (n === 2) {
- return true;
- }
- if (n % 2 === 0) {
- return false;
- }
- const limit = Math.floor(Math.sqrt(n));
- for (let i = 3; i <= limit; i += 2) {
- if (n % i === 0) {
- return false;
- }
- }
- return true;
-};
-
-const primes = [2];
-const primeAt = (index) => {
- while (primes.length < index) {
- let candidate = primes[primes.length - 1] + 1;
- while (!isPrime(candidate)) {
- candidate += 1;
- }
- primes.push(candidate);
- }
- return primes[index - 1];
-};
-
-const computeGodelNumber = (sequence) => {
- return sequence.reduce((acc, exponent, idx) => {
- const prime = BigInt(primeAt(idx + 1));
- return acc * prime ** BigInt(exponent);
- }, BigInt(1)) - BigInt(1);
-};
-
-self.addEventListener("message", (event) => {
- const result = computeGodelNumber(event.data);
- self.postMessage(result.toString());
-});
diff --git a/src/toys/godel/js/main.js b/src/toys/godel/js/main.js
deleted file mode 100644
index b752206..0000000
--- a/src/toys/godel/js/main.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { GodelPlayground } from "./ui.js";
-
-document.addEventListener("DOMContentLoaded", () => {
- new GodelPlayground();
-});
diff --git a/src/toys/godel/js/parser.js b/src/toys/godel/js/parser.js
deleted file mode 100644
index a9436b6..0000000
--- a/src/toys/godel/js/parser.js
+++ /dev/null
@@ -1,1059 +0,0 @@
-const parser = /*
- * Generated by PEG.js 0.10.0.
- *
- * http://pegjs.org/
- */
-(function() {
- "use strict";
-
- function peg$subclass(child, parent) {
- function ctor() { this.constructor = child; }
- ctor.prototype = parent.prototype;
- child.prototype = new ctor();
- }
-
- function peg$SyntaxError(message, expected, found, location) {
- this.message = message;
- this.expected = expected;
- this.found = found;
- this.location = location;
- this.name = "SyntaxError";
-
- if (typeof Error.captureStackTrace === "function") {
- Error.captureStackTrace(this, peg$SyntaxError);
- }
- }
-
- peg$subclass(peg$SyntaxError, Error);
-
- peg$SyntaxError.buildMessage = function(expected, found) {
- var DESCRIBE_EXPECTATION_FNS = {
- literal: function(expectation) {
- return "\"" + literalEscape(expectation.text) + "\"";
- },
-
- "class": function(expectation) {
- var escapedParts = "",
- i;
-
- for (i = 0; i < expectation.parts.length; i++) {
- escapedParts += expectation.parts[i] instanceof Array
- ? classEscape(expectation.parts[i][0]) + "-" + classEscape(expectation.parts[i][1])
- : classEscape(expectation.parts[i]);
- }
-
- return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]";
- },
-
- any: function(expectation) {
- return "any character";
- },
-
- end: function(expectation) {
- return "end of input";
- },
-
- other: function(expectation) {
- return expectation.description;
- }
- };
-
- function hex(ch) {
- return ch.charCodeAt(0).toString(16).toUpperCase();
- }
-
- function literalEscape(s) {
- return s
- .replace(/\\/g, '\\\\')
- .replace(/"/g, '\\"')
- .replace(/\0/g, '\\0')
- .replace(/\t/g, '\\t')
- .replace(/\n/g, '\\n')
- .replace(/\r/g, '\\r')
- .replace(/[\x00-\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
- .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return '\\x' + hex(ch); });
- }
-
- function classEscape(s) {
- return s
- .replace(/\\/g, '\\\\')
- .replace(/\]/g, '\\]')
- .replace(/\^/g, '\\^')
- .replace(/-/g, '\\-')
- .replace(/\0/g, '\\0')
- .replace(/\t/g, '\\t')
- .replace(/\n/g, '\\n')
- .replace(/\r/g, '\\r')
- .replace(/[\x00-\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
- .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return '\\x' + hex(ch); });
- }
-
- function describeExpectation(expectation) {
- return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);
- }
-
- function describeExpected(expected) {
- var descriptions = new Array(expected.length),
- i, j;
-
- for (i = 0; i < expected.length; i++) {
- descriptions[i] = describeExpectation(expected[i]);
- }
-
- descriptions.sort();
-
- if (descriptions.length > 0) {
- for (i = 1, j = 1; i < descriptions.length; i++) {
- if (descriptions[i - 1] !== descriptions[i]) {
- descriptions[j] = descriptions[i];
- j++;
- }
- }
- descriptions.length = j;
- }
-
- switch (descriptions.length) {
- case 1:
- return descriptions[0];
-
- case 2:
- return descriptions[0] + " or " + descriptions[1];
-
- default:
- return descriptions.slice(0, -1).join(", ")
- + ", or "
- + descriptions[descriptions.length - 1];
- }
- }
-
- function describeFound(found) {
- return found ? "\"" + literalEscape(found) + "\"" : "end of input";
- }
-
- return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
- };
-
- function peg$parse(input, options) {
- options = options !== void 0 ? options : {};
-
- var peg$FAILED = {},
-
- peg$startRuleFunctions = { Program: peg$parseProgram },
- peg$startRuleFunction = peg$parseProgram,
-
- peg$c0 = /^[\n]/,
- peg$c1 = peg$classExpectation(["\n"], false, false),
- peg$c2 = function(lines) {
- return { instructions: lines.filter((line) => typeof line !== "string" || line.trim() != "") };
- },
- peg$c3 = function(instruction) {
- let x = 0;
- let y = 0;
- if (instruction.label) {
- x = instruction.label.godel;
- y = instruction.instruction.godel;
- } else {
- y = instruction.godel;
- }
- return { instruction, godel: ((2 ** x) * ((2 * y) + 1) - 1) };
- },
- peg$c4 = function(label, instruction) {
- return { label, instruction };
- },
- peg$c5 = "[",
- peg$c6 = peg$literalExpectation("[", false),
- peg$c7 = "]",
- peg$c8 = peg$literalExpectation("]", false),
- peg$c9 = function(label) {
- return label;
- },
- peg$c10 = function(conditional) { return { conditional, godel: conditional.godel }; },
- peg$c11 = function(assignment) { return { assignment, godel: assignment.godel }; },
- peg$c12 = function(goto) { return { goto, godel: goto.godel }; },
- peg$c13 = function(label) {
- return { label, godel: label.godel + 2 };
- },
- peg$c14 = "IF",
- peg$c15 = peg$literalExpectation("IF", false),
- peg$c16 = "!=",
- peg$c17 = peg$literalExpectation("!=", false),
- peg$c18 = "0",
- peg$c19 = peg$literalExpectation("0", false),
- peg$c20 = function(variable, goto) {
- const y = variable.godel - 1;
- const x = goto.godel;
- return { variable, goto, godel: ((2 ** x) * ((2 * y) + 1) - 1) };
- },
- peg$c21 = "<-",
- peg$c22 = peg$literalExpectation("<-", false),
- peg$c23 = function(variable, expr) {
- if (expr.left.symbol != variable.symbol) {
- error("left hand variable must match right hand");
- }
- const x = expr.instructionNumber;
- const y = variable.godel - 1;
- return { variable, expr, godel: ((2 ** x) * ((2 * y) + 1) - 1) };
- },
- peg$c24 = "1",
- peg$c25 = peg$literalExpectation("1", false),
- peg$c26 = function(left, opr) {
- const instructionNumber = { "+" : 1, "-" : 2 }[opr];
- return { left, opr, instructionNumber };
- },
- peg$c27 = function(left) {
- return { left, instructionNumber: 0 };
- },
- peg$c28 = "Y",
- peg$c29 = peg$literalExpectation("Y", false),
- peg$c30 = function(symbol) { return { symbol, godel: 1 }; },
- peg$c31 = "X",
- peg$c32 = peg$literalExpectation("X", false),
- peg$c33 = "Z",
- peg$c34 = peg$literalExpectation("Z", false),
- peg$c35 = function(symbol, ind) {
- const index = parseInt(ind);
- const order = ["X", "Z"];
- const godel = index * order.length + order.indexOf(symbol);
- return { symbol: symbol + ind, godel };
- },
- peg$c36 = "GOTO",
- peg$c37 = peg$literalExpectation("GOTO", false),
- peg$c38 = "+",
- peg$c39 = peg$literalExpectation("+", false),
- peg$c40 = "-",
- peg$c41 = peg$literalExpectation("-", false),
- peg$c42 = /^[A-E]/,
- peg$c43 = peg$classExpectation([["A", "E"]], false, false),
- peg$c44 = function(symbol, ind) {
- const index = parseInt(ind);
- const godel = (symbol.charCodeAt(0) - "A".charCodeAt(0) + 1) + 5*(index-1);
- return { symbol: symbol + ind, godel };
- },
- peg$c45 = peg$otherExpectation("integer"),
- peg$c46 = /^[0-9]/,
- peg$c47 = peg$classExpectation([["0", "9"]], false, false),
- peg$c48 = function() { return parseInt(text(), 10); },
- peg$c49 = peg$otherExpectation("whitespace"),
- peg$c50 = /^[ \t]/,
- peg$c51 = peg$classExpectation([" ", "\t"], false, false),
- peg$c52 = function() { },
-
- peg$currPos = 0,
- peg$savedPos = 0,
- peg$posDetailsCache = [{ line: 1, column: 1 }],
- peg$maxFailPos = 0,
- peg$maxFailExpected = [],
- peg$silentFails = 0,
-
- peg$result;
-
- if ("startRule" in options) {
- if (!(options.startRule in peg$startRuleFunctions)) {
- throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
- }
-
- peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
- }
-
- function text() {
- return input.substring(peg$savedPos, peg$currPos);
- }
-
- function location() {
- return peg$computeLocation(peg$savedPos, peg$currPos);
- }
-
- function expected(description, location) {
- location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)
-
- throw peg$buildStructuredError(
- [peg$otherExpectation(description)],
- input.substring(peg$savedPos, peg$currPos),
- location
- );
- }
-
- function error(message, location) {
- location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)
-
- throw peg$buildSimpleError(message, location);
- }
-
- function peg$literalExpectation(text, ignoreCase) {
- return { type: "literal", text: text, ignoreCase: ignoreCase };
- }
-
- function peg$classExpectation(parts, inverted, ignoreCase) {
- return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase };
- }
-
- function peg$anyExpectation() {
- return { type: "any" };
- }
-
- function peg$endExpectation() {
- return { type: "end" };
- }
-
- function peg$otherExpectation(description) {
- return { type: "other", description: description };
- }
-
- function peg$computePosDetails(pos) {
- var details = peg$posDetailsCache[pos], p;
-
- if (details) {
- return details;
- } else {
- p = pos - 1;
- while (!peg$posDetailsCache[p]) {
- p--;
- }
-
- details = peg$posDetailsCache[p];
- details = {
- line: details.line,
- column: details.column
- };
-
- while (p < pos) {
- if (input.charCodeAt(p) === 10) {
- details.line++;
- details.column = 1;
- } else {
- details.column++;
- }
-
- p++;
- }
-
- peg$posDetailsCache[pos] = details;
- return details;
- }
- }
-
- function peg$computeLocation(startPos, endPos) {
- var startPosDetails = peg$computePosDetails(startPos),
- endPosDetails = peg$computePosDetails(endPos);
-
- return {
- start: {
- offset: startPos,
- line: startPosDetails.line,
- column: startPosDetails.column
- },
- end: {
- offset: endPos,
- line: endPosDetails.line,
- column: endPosDetails.column
- }
- };
- }
-
- function peg$fail(expected) {
- if (peg$currPos < peg$maxFailPos) { return; }
-
- if (peg$currPos > peg$maxFailPos) {
- peg$maxFailPos = peg$currPos;
- peg$maxFailExpected = [];
- }
-
- peg$maxFailExpected.push(expected);
- }
-
- function peg$buildSimpleError(message, location) {
- return new peg$SyntaxError(message, null, null, location);
- }
-
- function peg$buildStructuredError(expected, found, location) {
- return new peg$SyntaxError(
- peg$SyntaxError.buildMessage(expected, found),
- expected,
- found,
- location
- );
- }
-
- function peg$parseProgram() {
- var s0, s1, s2;
-
- s0 = peg$currPos;
- s1 = [];
- s2 = peg$parseProgramInstruction();
- if (s2 === peg$FAILED) {
- s2 = peg$parse_();
- if (s2 === peg$FAILED) {
- if (peg$c0.test(input.charAt(peg$currPos))) {
- s2 = input.charAt(peg$currPos);
- peg$currPos++;
- } else {
- s2 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c1); }
- }
- }
- }
- while (s2 !== peg$FAILED) {
- s1.push(s2);
- s2 = peg$parseProgramInstruction();
- if (s2 === peg$FAILED) {
- s2 = peg$parse_();
- if (s2 === peg$FAILED) {
- if (peg$c0.test(input.charAt(peg$currPos))) {
- s2 = input.charAt(peg$currPos);
- peg$currPos++;
- } else {
- s2 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c1); }
- }
- }
- }
- }
- if (s1 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c2(s1);
- }
- s0 = s1;
-
- return s0;
- }
-
- function peg$parseProgramInstruction() {
- var s0, s1, s2, s3, s4;
-
- s0 = peg$currPos;
- s1 = peg$parse_();
- if (s1 === peg$FAILED) {
- s1 = null;
- }
- if (s1 !== peg$FAILED) {
- s2 = peg$parseLabeledInstruction();
- if (s2 === peg$FAILED) {
- s2 = peg$parseInstruction();
- }
- if (s2 !== peg$FAILED) {
- s3 = peg$parse_();
- if (s3 === peg$FAILED) {
- s3 = null;
- }
- if (s3 !== peg$FAILED) {
- if (peg$c0.test(input.charAt(peg$currPos))) {
- s4 = input.charAt(peg$currPos);
- peg$currPos++;
- } else {
- s4 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c1); }
- }
- if (s4 === peg$FAILED) {
- s4 = null;
- }
- if (s4 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c3(s2);
- s0 = s1;
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
-
- return s0;
- }
-
- function peg$parseLabeledInstruction() {
- var s0, s1, s2, s3;
-
- s0 = peg$currPos;
- s1 = peg$parseLabel();
- if (s1 !== peg$FAILED) {
- s2 = peg$parse_();
- if (s2 !== peg$FAILED) {
- s3 = peg$parseInstruction();
- if (s3 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c4(s1, s3);
- s0 = s1;
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
-
- return s0;
- }
-
- function peg$parseLabel() {
- var s0, s1, s2, s3, s4, s5;
-
- s0 = peg$currPos;
- if (input.charCodeAt(peg$currPos) === 91) {
- s1 = peg$c5;
- peg$currPos++;
- } else {
- s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c6); }
- }
- if (s1 !== peg$FAILED) {
- s2 = peg$parse_();
- if (s2 === peg$FAILED) {
- s2 = null;
- }
- if (s2 !== peg$FAILED) {
- s3 = peg$parseLABEL_V();
- if (s3 !== peg$FAILED) {
- s4 = peg$parse_();
- if (s4 === peg$FAILED) {
- s4 = null;
- }
- if (s4 !== peg$FAILED) {
- if (input.charCodeAt(peg$currPos) === 93) {
- s5 = peg$c7;
- peg$currPos++;
- } else {
- s5 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c8); }
- }
- if (s5 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c9(s3);
- s0 = s1;
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
-
- return s0;
- }
-
- function peg$parseInstruction() {
- var s0, s1;
-
- s0 = peg$currPos;
- s1 = peg$parseConditional();
- if (s1 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c10(s1);
- }
- s0 = s1;
- if (s0 === peg$FAILED) {
- s0 = peg$currPos;
- s1 = peg$parseAssignment();
- if (s1 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c11(s1);
- }
- s0 = s1;
- if (s0 === peg$FAILED) {
- s0 = peg$currPos;
- s1 = peg$parseGoto();
- if (s1 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c12(s1);
- }
- s0 = s1;
- }
- }
-
- return s0;
- }
-
- function peg$parseGoto() {
- var s0, s1, s2, s3;
-
- s0 = peg$currPos;
- s1 = peg$parseGOTO();
- if (s1 !== peg$FAILED) {
- s2 = peg$parse_();
- if (s2 !== peg$FAILED) {
- s3 = peg$parseLABEL_V();
- if (s3 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c13(s3);
- s0 = s1;
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
-
- return s0;
- }
-
- function peg$parseConditional() {
- var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
-
- s0 = peg$currPos;
- if (input.substr(peg$currPos, 2) === peg$c14) {
- s1 = peg$c14;
- peg$currPos += 2;
- } else {
- s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c15); }
- }
- if (s1 !== peg$FAILED) {
- s2 = peg$parse_();
- if (s2 !== peg$FAILED) {
- s3 = peg$parseVAR();
- if (s3 !== peg$FAILED) {
- s4 = peg$parse_();
- if (s4 === peg$FAILED) {
- s4 = null;
- }
- if (s4 !== peg$FAILED) {
- if (input.substr(peg$currPos, 2) === peg$c16) {
- s5 = peg$c16;
- peg$currPos += 2;
- } else {
- s5 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c17); }
- }
- if (s5 !== peg$FAILED) {
- s6 = peg$parse_();
- if (s6 === peg$FAILED) {
- s6 = null;
- }
- if (s6 !== peg$FAILED) {
- if (input.charCodeAt(peg$currPos) === 48) {
- s7 = peg$c18;
- peg$currPos++;
- } else {
- s7 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c19); }
- }
- if (s7 !== peg$FAILED) {
- s8 = peg$parse_();
- if (s8 !== peg$FAILED) {
- s9 = peg$parseGoto();
- if (s9 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c20(s3, s9);
- s0 = s1;
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
-
- return s0;
- }
-
- function peg$parseAssignment() {
- var s0, s1, s2, s3, s4, s5;
-
- s0 = peg$currPos;
- s1 = peg$parseVAR();
- if (s1 !== peg$FAILED) {
- s2 = peg$parse_();
- if (s2 !== peg$FAILED) {
- if (input.substr(peg$currPos, 2) === peg$c21) {
- s3 = peg$c21;
- peg$currPos += 2;
- } else {
- s3 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c22); }
- }
- if (s3 !== peg$FAILED) {
- s4 = peg$parse_();
- if (s4 !== peg$FAILED) {
- s5 = peg$parseExpression();
- if (s5 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c23(s1, s5);
- s0 = s1;
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
-
- return s0;
- }
-
- function peg$parseExpression() {
- var s0, s1, s2, s3, s4, s5;
-
- s0 = peg$currPos;
- s1 = peg$parseVAR();
- if (s1 !== peg$FAILED) {
- s2 = peg$parse_();
- if (s2 !== peg$FAILED) {
- s3 = peg$parseOPERATION();
- if (s3 !== peg$FAILED) {
- s4 = peg$parse_();
- if (s4 !== peg$FAILED) {
- if (input.charCodeAt(peg$currPos) === 49) {
- s5 = peg$c24;
- peg$currPos++;
- } else {
- s5 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c25); }
- }
- if (s5 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c26(s1, s3);
- s0 = s1;
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- if (s0 === peg$FAILED) {
- s0 = peg$currPos;
- s1 = peg$parseVAR();
- if (s1 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c27(s1);
- }
- s0 = s1;
- }
-
- return s0;
- }
-
- function peg$parseVAR() {
- var s0, s1, s2, s3;
-
- s0 = peg$currPos;
- if (input.charCodeAt(peg$currPos) === 89) {
- s1 = peg$c28;
- peg$currPos++;
- } else {
- s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c29); }
- }
- if (s1 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c30(s1);
- }
- s0 = s1;
- if (s0 === peg$FAILED) {
- s0 = peg$currPos;
- if (input.charCodeAt(peg$currPos) === 88) {
- s1 = peg$c31;
- peg$currPos++;
- } else {
- s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c32); }
- }
- if (s1 === peg$FAILED) {
- if (input.charCodeAt(peg$currPos) === 90) {
- s1 = peg$c33;
- peg$currPos++;
- } else {
- s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c34); }
- }
- }
- if (s1 !== peg$FAILED) {
- s2 = [];
- s3 = peg$parseInteger();
- if (s3 !== peg$FAILED) {
- while (s3 !== peg$FAILED) {
- s2.push(s3);
- s3 = peg$parseInteger();
- }
- } else {
- s2 = peg$FAILED;
- }
- if (s2 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c35(s1, s2);
- s0 = s1;
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- }
-
- return s0;
- }
-
- function peg$parseGOTO() {
- var s0;
-
- if (input.substr(peg$currPos, 4) === peg$c36) {
- s0 = peg$c36;
- peg$currPos += 4;
- } else {
- s0 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c37); }
- }
-
- return s0;
- }
-
- function peg$parseOPERATION() {
- var s0;
-
- if (input.charCodeAt(peg$currPos) === 43) {
- s0 = peg$c38;
- peg$currPos++;
- } else {
- s0 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c39); }
- }
- if (s0 === peg$FAILED) {
- if (input.charCodeAt(peg$currPos) === 45) {
- s0 = peg$c40;
- peg$currPos++;
- } else {
- s0 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c41); }
- }
- }
-
- return s0;
- }
-
- function peg$parseLABEL_V() {
- var s0, s1, s2, s3;
-
- s0 = peg$currPos;
- if (peg$c42.test(input.charAt(peg$currPos))) {
- s1 = input.charAt(peg$currPos);
- peg$currPos++;
- } else {
- s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c43); }
- }
- if (s1 !== peg$FAILED) {
- s2 = [];
- s3 = peg$parseInteger();
- if (s3 !== peg$FAILED) {
- while (s3 !== peg$FAILED) {
- s2.push(s3);
- s3 = peg$parseInteger();
- }
- } else {
- s2 = peg$FAILED;
- }
- if (s2 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c44(s1, s2);
- s0 = s1;
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
- } else {
- peg$currPos = s0;
- s0 = peg$FAILED;
- }
-
- return s0;
- }
-
- function peg$parseInteger() {
- var s0, s1, s2;
-
- peg$silentFails++;
- s0 = peg$currPos;
- s1 = [];
- if (peg$c46.test(input.charAt(peg$currPos))) {
- s2 = input.charAt(peg$currPos);
- peg$currPos++;
- } else {
- s2 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c47); }
- }
- if (s2 !== peg$FAILED) {
- while (s2 !== peg$FAILED) {
- s1.push(s2);
- if (peg$c46.test(input.charAt(peg$currPos))) {
- s2 = input.charAt(peg$currPos);
- peg$currPos++;
- } else {
- s2 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c47); }
- }
- }
- } else {
- s1 = peg$FAILED;
- }
- if (s1 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c48();
- }
- s0 = s1;
- peg$silentFails--;
- if (s0 === peg$FAILED) {
- s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c45); }
- }
-
- return s0;
- }
-
- function peg$parse_() {
- var s0, s1, s2;
-
- peg$silentFails++;
- s0 = peg$currPos;
- s1 = [];
- if (peg$c50.test(input.charAt(peg$currPos))) {
- s2 = input.charAt(peg$currPos);
- peg$currPos++;
- } else {
- s2 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c51); }
- }
- if (s2 !== peg$FAILED) {
- while (s2 !== peg$FAILED) {
- s1.push(s2);
- if (peg$c50.test(input.charAt(peg$currPos))) {
- s2 = input.charAt(peg$currPos);
- peg$currPos++;
- } else {
- s2 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c51); }
- }
- }
- } else {
- s1 = peg$FAILED;
- }
- if (s1 !== peg$FAILED) {
- peg$savedPos = s0;
- s1 = peg$c52();
- }
- s0 = s1;
- peg$silentFails--;
- if (s0 === peg$FAILED) {
- s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$c49); }
- }
-
- return s0;
- }
-
- peg$result = peg$startRuleFunction();
-
- if (peg$result !== peg$FAILED && peg$currPos === input.length) {
- return peg$result;
- } else {
- if (peg$result !== peg$FAILED && peg$currPos < input.length) {
- peg$fail(peg$endExpectation());
- }
-
- throw peg$buildStructuredError(
- peg$maxFailExpected,
- peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,
- peg$maxFailPos < input.length
- ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)
- : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
- );
- }
- }
-
- return {
- SyntaxError: peg$SyntaxError,
- parse: peg$parse
- };
-})();
-
-export default parser;
-export const SyntaxError = parser.SyntaxError;
diff --git a/src/toys/godel/js/ui.js b/src/toys/godel/js/ui.js
deleted file mode 100644
index 97e5209..0000000
--- a/src/toys/godel/js/ui.js
+++ /dev/null
@@ -1,224 +0,0 @@
-import parser from "./parser.js";
-import { compileProgram } from "./compiler.js";
-
-const SAMPLE_PROGRAM = `// THIS PROGRAM COMPUTES X1 + X2
-
-// Y <- X1
-[ A1 ] IF X1 != 0 GOTO A2
- GOTO B1
-[ A2 ] X1 <- X1 - 1
- Y <- Y + 1
- GOTO A1
-
-// Z1 <- X2
-[ B1 ] IF X2 != 0 GOTO B2
- GOTO C1
-[ B2 ] X2 <- X2 - 1
- Z1 <- Z1 + 1
- GOTO B1
-
-// Y <- Y + Z1
-[ C1 ] IF Z1 != 0 GOTO C2
- GOTO E1
-[ C2 ] Z1 <- Z1 - 1
- Y <- Y + 1
- GOTO C1`;
-
-const STATUS_VARIANTS = {
- success: "text-success",
- error: "text-error",
- info: "muted"
-};
-
-export class GodelPlayground {
- constructor() {
- this.elements = {
- compileBtn: document.getElementById("compile-btn"),
- copyBtn: document.getElementById("copy-btn"),
- evalBtn: document.getElementById("eval-btn"),
- computeNumberBtn: document.getElementById("compute-godel-btn"),
- compileStatus: document.getElementById("compile-status"),
- evalStatus: document.getElementById("eval-status"),
- godelSequence: document.getElementById("godel-sequence"),
- godelNumber: document.getElementById("godel-number")
- };
-
- this.sourceEditor = null;
- this.compiledEditor = null;
- this.worker = null;
- this.latestSequence = [];
-
- this.init();
- }
-
- init() {
- this.setupEditors();
- this.bindEvents();
- this.hydrateFromParams();
- if (!this.getSource().trim()) {
- this.setSource(SAMPLE_PROGRAM);
- }
- this.compileSource();
- }
-
- setupEditors() {
- this.sourceEditor = adelieEditor.init("#source-editor", {
- language: "javascript"
- });
- this.compiledEditor = adelieEditor.init("#compiled-editor", {
- language: "javascript"
- });
- this.setSource(SAMPLE_PROGRAM);
- }
-
- bindEvents() {
- this.elements.compileBtn.addEventListener("click", () => this.compileSource());
- this.elements.evalBtn.addEventListener("click", () => this.evaluateCompiled());
- this.elements.copyBtn.addEventListener("click", () => this.copyShareLink());
- this.elements.computeNumberBtn.addEventListener("click", () => this.computeGodelNumber());
-
- document.addEventListener("keydown", (event) => {
- if (event.ctrlKey && event.key === "Enter") {
- event.preventDefault();
- this.compileSource();
- }
- });
- }
-
- hydrateFromParams() {
- const params = new URLSearchParams(window.location.search);
- const encoded = params.get("instructions");
- if (encoded) {
- try {
- const decoded = atob(encoded);
- this.setSource(decoded);
- } catch (error) {
- console.warn("Failed to decode instructions from URL", error);
- }
- }
- }
-
- getSource() {
- return this.sourceEditor.state.doc.toString();
- }
-
- setSource(content) {
- const docLength = this.sourceEditor.state.doc.toString().length;
- this.sourceEditor.dispatch({
- changes: { from: 0, to: docLength, insert: content }
- });
- }
-
- getCompiled() {
- return this.compiledEditor.state.doc.toString();
- }
-
- setCompiled(content) {
- const docLength = this.compiledEditor.state.doc.toString().length;
- this.compiledEditor.dispatch({
- changes: { from: 0, to: docLength, insert: content }
- });
- }
-
- prepareSource(source) {
- return source.replace(/\/\/.*$/gm, "").trim();
- }
-
- compileSource() {
- const raw = this.getSource();
- const prepared = this.prepareSource(raw);
-
- if (!prepared) {
- this.setStatus("compile", "Provide some source to compile", "error");
- return;
- }
-
- try {
- const ast = parser.parse(prepared);
- const { js, godelSequence } = compileProgram(ast);
- this.latestSequence = godelSequence;
- this.setCompiled(js);
- this.renderSequence(godelSequence);
- this.setStatus("compile", "Successful compilation", "success");
- this.elements.computeNumberBtn.disabled = godelSequence.length === 0;
- } catch (error) {
- this.latestSequence = [];
- this.renderSequence([]);
- this.elements.computeNumberBtn.disabled = true;
- this.setStatus("compile", error.message || "Error compiling", "error");
- }
- }
-
- evaluateCompiled() {
- const js = this.getCompiled();
- if (!js.trim()) {
- this.setStatus("eval", "Compile a program first", "error");
- return;
- }
-
- try {
- const result = (0, eval)(js);
- this.setStatus(
- "eval",
- `Result: ${typeof result === "undefined" ? "(no return)" : result}`,
- "success"
- );
- } catch (error) {
- this.setStatus("eval", error.message || "Failed to evaluate program", "error");
- }
- }
-
- renderSequence(sequence) {
- if (!sequence.length) {
- this.elements.godelSequence.textContent = "Compile to view the Gödel sequence.";
- this.elements.godelNumber.textContent = "";
- return;
- }
- this.elements.godelSequence.textContent = `[${sequence.join(", ")}]`;
- this.elements.godelNumber.textContent = "";
- }
-
- copyShareLink() {
- const data = btoa(this.getSource());
- const url = `${window.location.href.split("?")[0]}?instructions=${data}`;
-
- navigator.clipboard.writeText(url)
- .then(() => alert("Shareable link copied to clipboard"))
- .catch(() => alert("Failed to copy link"));
- }
-
- computeGodelNumber() {
- if (!this.latestSequence.length) {
- this.setStatus("compile", "Compile a program to produce its Gödel sequence", "error");
- return;
- }
-
- this.elements.godelNumber.textContent = "Working...";
- this.elements.computeNumberBtn.disabled = true;
-
- const worker = this.getWorker();
- worker.onmessage = (event) => {
- this.elements.godelNumber.textContent = event.data;
- this.elements.computeNumberBtn.disabled = false;
- };
- worker.onerror = () => {
- this.elements.godelNumber.textContent = "Failed to compute Gödel number";
- this.elements.computeNumberBtn.disabled = false;
- };
- worker.postMessage(this.latestSequence);
- }
-
- getWorker() {
- if (!this.worker) {
- const workerUrl = new URL("./godel-worker.js", import.meta.url);
- this.worker = new Worker(workerUrl, { type: "module" });
- }
- return this.worker;
- }
-
- setStatus(kind, message, variant = "info") {
- const element = kind === "compile" ? this.elements.compileStatus : this.elements.evalStatus;
- element.textContent = message;
- element.className = `status-text ${STATUS_VARIANTS[variant] || ""}`;
- }
-}
diff --git a/src/toys/julia/index.html b/src/toys/julia/index.html
deleted file mode 100644
index 9d9de51..0000000
--- a/src/toys/julia/index.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!DOCTYPE html>
-
-<html>
- <head>
- <style>
- body {
- height: 100vh;
- width: 100vw;
- margin: 0;
- }
- .bottomRight {
- padding: 12px;
- position: fixed;
- bottom: 0;
- right: 0;
- background-color: rgba(255,255,255,0.5);
- border: 1px solid white;
- border-radius: 8px;
- margin-right: 6px;
- margin-bottom: 6px;
- }
- .verticalSlider
- {
- writing-mode: bt-lr; /* IE */
- -webkit-appearance: slider-vertical; /* Chromium */
- width: 8px;
- padding: 0 5px;
- }
- </style>
- </head>
- <body>
- <div id="canvasHolder">
-
- </div>
- <div class="bottomRight">
- <input orient="vertical" type="range" min="-1000" max="1000" value="0" id="imaginarySlider" class="verticalSlider">
- <input type="range" min="-1000" max="1000" value="0" id="realSlider">
- <br>
- imaginary: <input id="imag-val" size="10" type="number" step="0.001"></input>
- <button id="animate-imag" onclick="startAnim('animate-imag', 'ci')">Animate</button>
- <br>
- real: <input id="real-val" size="10" type="number" step="0.001"></input>
- <button id="animate-real" onclick="startAnim('animate-real', 'cr')">Animate</button>
- <br>
- powered by <a href="https://github.com/gpujs/gpu.js">gpu.js</a>
- </div>
-
- <script src="https://cdn.jsdelivr.net/npm/gpu.js@latest/dist/gpu-browser.min.js"></script>
- <script src="julia.js"></script>
- </body>
-</html>
diff --git a/src/toys/julia/julia.js b/src/toys/julia/julia.js
deleted file mode 100644
index 38bcd4d..0000000
--- a/src/toys/julia/julia.js
+++ /dev/null
@@ -1,136 +0,0 @@
-const MIN_ZOOM = 0.0001;
-const MAX_ZOOM = 4;
-const C_THRESHOLD = Math.sqrt(2)/2;
-const SLIDER_DIV = 2000*C_THRESHOLD;
-
-const gpu = new GPU.GPU();
-const buildRender = (width, height) => gpu.createKernel(function (maxIterations, cr, ci, centerX, centerY, zoom, colorMultipliers) {
- let zx = (this.output.x / this.output.y) * (centerX + (4 * this.thread.x / this.output.x - 2) * (zoom / 4));
- let zy = centerY + (4 * this.thread.y / this.output.y - 2) * (zoom / 4);
- let iterations = 0;
- for (let i = 0; i < maxIterations; i++) {
- const xtemp = zx * zx - zy * zy + cr;
- zy = 2 * zx * zy + ci;
- zx = xtemp;
- if (zx * zx + zy * zy > 4) {
- iterations = i;
- break;
- }
- }
- if (iterations == 0 || iterations == maxIterations) {
- this.color(0, 0, 0);
- } else {
- this.color(colorMultipliers[0] * iterations, colorMultipliers[1] * iterations, colorMultipliers[2] * iterations);
- }
-}, { output: [width, height], graphical: true });
-
-const canvasHolder = document.getElementById('canvasHolder');
-let render; // The GPU kernel built from buildRender
-let state = {
- colorMultipliers: [0.01 * Math.random() + 0.015, 0.03 * Math.random() + 0.007, 0.02 * Math.random() + 0.010],
- changes: {
- centerX: 0,
- centerY: 0,
- zoom: 3,
- cr: parseFloat(document.getElementById('realSlider').value) / SLIDER_DIV,
- ci: parseFloat(document.getElementById('imaginarySlider').value) / SLIDER_DIV,
- maxIterations: 1000,
- width: document.body.clientWidth,
- height: document.body.clientHeight,
- },
-};
-
-const doRender = (renderF, state) => {
- // gpu.js doesn't support JS objects as kernel parameters - https://github.com/gpujs/gpu.js/issues/245
- renderF(state.maxIterations, state.cr, state.ci, state.centerX, state.centerY, state.zoom, state.colorMultipliers);
-};
-
-const loop = () => {
- const stateChanges = Object.keys(state.changes);
- if (stateChanges.length > 0) {
- state = {...state, ...state.changes};
- if (state.changes.width || state.changes.height) {
- render = buildRender(state.width, state.height);
- canvasHolder.appendChild(render.canvas);
- }
- if (typeof state.changes.ci !== 'undefined') {
- document.getElementById('imag-val').value = state.ci.toFixed(4);
- document.getElementById('imaginarySlider').value = state.changes.ci * SLIDER_DIV;
- }
- if (typeof state.changes.cr !== 'undefined') {
- document.getElementById('real-val').value = state.cr.toFixed(4);
- document.getElementById('realSlider').value = state.changes.cr * SLIDER_DIV;
- }
- state.changes = {};
- doRender(render, state);
- }
-
- requestAnimationFrame(loop);
-};
-
-loop();
-
-// UI Code
-
-const startAnim = (sliderId, complexComponentName='ci') => {
- const restart = (interval) => {
- clearInterval(interval);
- document.getElementById(sliderId).innerHTML = 'Animate';
- document.getElementById(sliderId).onclick = ()=>startAnim(sliderId, complexComponentName);
- return;
- };
- const start = setInterval(() => {
- if (state[complexComponentName] >= C_THRESHOLD) {
- restart(start);
- }
-
- state.changes[complexComponentName] = state[complexComponentName] + 0.001;
- }, 1000/60);
- document.getElementById(sliderId).innerHTML = 'Stop';
- document.getElementById(sliderId).onclick = ()=>restart(start);
-};
-
-document.getElementById('imaginarySlider').oninput = function() {
- state.changes.ci = parseFloat(this.value) / SLIDER_DIV;
-};
-document.getElementById('realSlider').oninput = function() {
- state.changes.cr = parseFloat(this.value) / SLIDER_DIV;
-};
-
-document.getElementById('imag-val').addEventListener('change', function (e) {
- state.changes.ci = parseFloat(this.value);
-});
-document.getElementById('real-val').addEventListener('change', function (e) {
- state.changes.cr = parseFloat(this.value);
-});
-
-canvasHolder.addEventListener('wheel', (e) => {
- e.preventDefault();
- state.changes.zoom = Math.min(Math.max(state.zoom + e.deltaY * 0.001 * state.zoom, MIN_ZOOM), MAX_ZOOM);
-});
-
-let isDown = false;
-canvasHolder.addEventListener('mousedown', (e) => {
- e.preventDefault();
- isDown = true;
-}, true);
-
-canvasHolder.addEventListener('mouseup', (e) => {
- e.preventDefault();
- isDown = false;
-}, true);
-
-canvasHolder.addEventListener('mousemove', (e) => {
- e.preventDefault();
- if (isDown) {
- let deltaX = -e.movementX * state.zoom / document.body.clientWidth;
- let deltaY = e.movementY * state.zoom / document.body.clientHeight;
- state.changes.centerX = state.centerX + deltaX;
- state.changes.centerY = state.centerY + deltaY;
- }
-}, true);
-
-window.addEventListener('resize', () => {
- state.changes.width = document.body.clientWidth;
- state.changes.height = document.body.clientHeight;
-});
diff --git a/src/toys/tabloid/index.html b/src/toys/tabloid/index.html
deleted file mode 100644
index 61ec937..0000000
--- a/src/toys/tabloid/index.html
+++ /dev/null
@@ -1,53 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel="icon" href="https://adelie.liz.coffee/img/favicon.ico" />
- <link rel="stylesheet" href="https://adelie.liz.coffee/bundle.css">
- <title>Tabloid</title>
-</head>
-<body>
- <main>
- <article>
- <h3>Tabloid</h3>
- <p>Forgotten how to write Tabloid since your schooldays? Here's <a target="_blank" href="https://github.com/thesephist/tabloid/blob/master/README.md#language-overview">a refresher</a>.</p>
-
- <div>
- <label for="program-select">Sample:</label>
- <select id="program-select" aria-label="Sample Tabloid programs"></select>
- </div>
-
- <section class="mt-md">
- <div
- id="code-editor"
- class="code-editor-container"
- aria-label="Tabloid source code"
- spellcheck="false"
- autocomplete="off"
- autocorrect="off"
- ></div>
- </section>
-
- <div class="mt-sm">
- <button id="run-button" class="primary">Run (Ctrl + Enter)</button>
- <button id="clear-button">Clear Output</button>
- </div>
-
- <section class="mt-md">
- <h4>stdout</h4>
- <pre id="stdout-content" aria-live="polite"></pre>
- </section>
-
- <section class="mt-md">
- <h4>stderr</h4>
- <pre class="text-error" id="error-content" aria-live="polite"></pre>
- </section>
- </article>
- </main>
-
- <script src="https://adelie.liz.coffee/bundle.js"></script>
- <script src="https://adelie.liz.coffee/adelie-editor.js"></script>
- <script type="module" src="./js/main.js"></script>
-</body>
-</html>
diff --git a/src/toys/tabloid/js/main.js b/src/toys/tabloid/js/main.js
deleted file mode 100644
index 1f0b5dd..0000000
--- a/src/toys/tabloid/js/main.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { TabloidPlayground } from "./playground.js";
-
-document.addEventListener("DOMContentLoaded", () => {
- new TabloidPlayground();
-});
diff --git a/src/toys/tabloid/js/playground.js b/src/toys/tabloid/js/playground.js
deleted file mode 100644
index d6ff71a..0000000
--- a/src/toys/tabloid/js/playground.js
+++ /dev/null
@@ -1,120 +0,0 @@
-import { tokenize, Parser, Environment } from "./tabloid.js";
-import { SAMPLE_PROGRAMS } from "./samples.js";
-
-class TabloidRunner {
- run(code) {
- const stdout = [];
- const stderr = [];
-
- if (!code) {
- stderr.push("No code to execute.");
- return { stdout, stderr };
- }
-
- try {
- const tokens = tokenize(code);
- const nodes = new Parser(tokens).parse();
- const env = new Environment({
- print: (msg) => stdout.push(String(msg)),
- input: (promptText) => window.prompt(promptText) ?? ""
- });
- env.run(nodes);
- } catch (error) {
- stderr.push(error.message || error.toString());
- }
-
- return { stdout, stderr };
- }
-}
-
-export class TabloidPlayground {
- constructor() {
- this.runner = new TabloidRunner();
- this.programLookup = new Map(SAMPLE_PROGRAMS.map((program) => [program.id, program]));
- this.activeProgramId = this.defaultProgramId = SAMPLE_PROGRAMS[0].id;
-
- this.stdoutElement = document.getElementById("stdout-content");
- this.errorElement = document.getElementById("error-content");
- this.programSelect = document.getElementById("program-select");
- this.runButton = document.getElementById("run-button");
- this.clearButton = document.getElementById("clear-button");
-
- this.editor = adelieEditor.init("#code-editor", {
- language: "tabloid"
- });
-
- this.init();
- }
-
- init() {
- this.populateProgramSelect();
- this.registerEvents();
- this.loadProgram(this.activeProgramId);
- }
-
- populateProgramSelect() {
- if (!this.programSelect) {
- return;
- }
-
- this.programSelect.innerHTML = "";
- SAMPLE_PROGRAMS.forEach((program) => {
- const option = document.createElement("option");
- option.value = program.id;
- option.textContent = program.label;
- this.programSelect.appendChild(option);
- });
-
- this.programSelect.value = this.activeProgramId;
- }
-
- registerEvents() {
- this.runButton.addEventListener("click", () => this.runCode());
- this.clearButton.addEventListener("click", () => this.resetOutput());
-
- this.programSelect.addEventListener("change", () => {
- this.loadProgram(this.programSelect.value);
- });
-
- document.addEventListener("keydown", (event) => {
- if (event.ctrlKey && event.key === "Enter") {
- event.preventDefault();
- this.runCode();
- }
- });
- }
-
- setEditorContent(content) {
- const length = this.editor.state.doc.toString().length;
- this.editor.dispatch({
- changes: { from: 0, to: length, insert: content }
- });
- }
-
- loadProgram(programId = this.defaultProgramId) {
- const selectedProgram = this.programLookup.get(programId);
- if (!selectedProgram) {
- return;
- }
-
- this.activeProgramId = selectedProgram.id;
- this.setEditorContent(selectedProgram.code);
- this.resetOutput();
-
- if (this.programSelect) {
- this.programSelect.value = this.activeProgramId;
- }
- }
-
- resetOutput() {
- this.stdoutElement.textContent = "";
- this.errorElement.textContent = "";
- }
-
- runCode() {
- const code = this.editor.state.doc.toString();
- const result = this.runner.run(code);
- this.stdoutElement.textContent = result.stdout.join("\n");
- this.errorElement.textContent = result.stderr.join("\n");
- }
-}
diff --git a/src/toys/tabloid/js/samples.js b/src/toys/tabloid/js/samples.js
deleted file mode 100644
index dc7bd58..0000000
--- a/src/toys/tabloid/js/samples.js
+++ /dev/null
@@ -1,308 +0,0 @@
-const CONS_SNIPPET = [
- "DISCOVER HOW TO cons WITH a, b",
- "RUMOR HAS IT",
- " DISCOVER HOW TO retrieve WITH is_first",
- " RUMOR HAS IT",
- " WHAT IF is_first IS ACTUALLY TOTALLY RIGHT",
- " SHOCKING DEVELOPMENT a",
- " LIES!",
- " SHOCKING DEVELOPMENT b",
- " END OF STORY",
- " SHOCKING DEVELOPMENT retrieve",
- "END OF STORY",
-].join('\n');
-
-const BINARY_INORDER_TRAVERSAL_PROGRAM = [
- CONS_SNIPPET,
- '',
- "DISCOVER HOW TO in_order_traverse WITH node, is_dual_ptr",
- "RUMOR HAS IT",
- " EXPERTS CLAIM left TO BE node OF TOTALLY RIGHT",
- " EXPERTS CLAIM right TO BE node OF COMPLETELY WRONG",
- '',
- " WHAT IF is_dual_ptr IS ACTUALLY COMPLETELY WRONG",
- " RUMOR HAS IT",
- " YOU WON'T WANT TO MISS left",
- " WHAT IF right IS ACTUALLY COMPLETELY WRONG",
- " 1",
- " LIES!",
- " in_order_traverse OF right, TOTALLY RIGHT",
- " END OF STORY",
- " LIES!",
- " RUMOR HAS IT",
- " WHAT IF left IS ACTUALLY COMPLETELY WRONG",
- " 1",
- " LIES!",
- " in_order_traverse OF left, COMPLETELY WRONG",
- '',
- " WHAT IF right IS ACTUALLY COMPLETELY WRONG",
- " 1",
- " LIES!",
- " in_order_traverse OF right, COMPLETELY WRONG",
- " END OF STORY",
- "END OF STORY",
- '',
- "EXPERTS CLAIM l TO BE cons OF 1, COMPLETELY WRONG",
- "EXPERTS CLAIM r TO BE cons OF 3, COMPLETELY WRONG",
- "EXPERTS CLAIM root TO BE cons OF l, r",
- "EXPERTS CLAIM head TO BE cons OF 2, root",
- '',
- "in_order_traverse OF head, COMPLETELY WRONG",
- '',
- "PLEASE LIKE AND SUBSCRIBE",
-].join('\n');
-
-const MERGE_SORT_PROGRAM = [
- CONS_SNIPPET,
- '',
- "DISCOVER HOW TO print WITH x",
- "RUMOR HAS IT",
- " YOU WON'T WANT TO MISS x",
- "END OF STORY",
- '',
- "DISCOVER HOW TO map WITH fn, list",
- "RUMOR HAS IT",
- " WHAT IF list IS ACTUALLY COMPLETELY WRONG",
- " SHOCKING DEVELOPMENT COMPLETELY WRONG",
- " LIES!",
- " RUMOR HAS IT",
- " EXPERTS CLAIM car TO BE list OF TOTALLY RIGHT",
- " EXPERTS CLAIM cdr TO BE list OF COMPLETELY WRONG",
- " EXPERTS CLAIM new_car TO BE fn OF car",
- " EXPERTS CLAIM rest_mapped TO BE map OF fn, cdr",
- '',
- " SHOCKING DEVELOPMENT cons OF new_car, rest_mapped",
- " END OF STORY",
- "END OF STORY",
- '',
- "DISCOVER HOW TO reduce WITH fn, list, accumulator",
- "RUMOR HAS IT",
- " WHAT IF list IS ACTUALLY COMPLETELY WRONG",
- " SHOCKING DEVELOPMENT accumulator",
- " LIES!",
- " RUMOR HAS IT",
- " EXPERTS CLAIM car TO BE list OF TOTALLY RIGHT",
- " EXPERTS CLAIM cdr TO BE list OF COMPLETELY WRONG",
- " EXPERTS CLAIM added_accumulator TO BE fn OF car, accumulator",
- '',
- " SHOCKING DEVELOPMENT reduce OF fn, cdr, added_accumulator",
- " END OF STORY",
- "END OF STORY",
- '',
- "DISCOVER HOW TO str_join_reducer WITH element, accumulator",
- "RUMOR HAS IT",
- " EXPERTS CLAIM added_comma TO BE element PLUS ', '",
- " SHOCKING DEVELOPMENT added_comma PLUS accumulator",
- "END OF STORY",
- '',
- "DISCOVER HOW TO join WITH list",
- "RUMOR HAS IT",
- " SHOCKING DEVELOPMENT reduce OF str_join_reducer, list, ''",
- "END OF STORY",
- '',
- "DISCOVER HOW TO append WITH n, m",
- "RUMOR HAS IT",
- " WHAT IF n IS ACTUALLY COMPLETELY WRONG",
- " SHOCKING DEVELOPMENT m",
- " LIES!",
- " RUMOR HAS IT",
- " EXPERTS CLAIM car_n TO BE n OF TOTALLY RIGHT",
- " EXPERTS CLAIM cdr_n TO BE n OF COMPLETELY WRONG",
- " EXPERTS CLAIM appended TO BE append OF cdr_n, m",
- '',
- " SHOCKING DEVELOPMENT cons OF car_n, appended",
- " END OF STORY",
- "END OF STORY",
- '',
- "DISCOVER HOW TO reverse WITH l",
- "RUMOR HAS IT",
- " WHAT IF l IS ACTUALLY COMPLETELY WRONG",
- " SHOCKING DEVELOPMENT COMPLETELY WRONG",
- " LIES!",
- " 1",
- '',
- " EXPERTS CLAIM car TO BE l OF TOTALLY RIGHT",
- " EXPERTS CLAIM cdr TO BE l OF COMPLETELY WRONG",
- " EXPERTS CLAIM reversed_cdr TO BE reverse OF cdr",
- " EXPERTS CLAIM car_cons TO BE cons OF car, COMPLETELY WRONG",
- '',
- " SHOCKING DEVELOPMENT append OF reversed_cdr, car_cons",
- "END OF STORY",
- '',
- "DISCOVER HOW TO merge WITH x, y",
- "RUMOR HAS IT",
- " WHAT IF x IS ACTUALLY COMPLETELY WRONG",
- " SHOCKING DEVELOPMENT y",
- " LIES!",
- " 1",
- '',
- " WHAT IF y IS ACTUALLY COMPLETELY WRONG",
- " RUMOR HAS IT",
- " WHAT IF x IS ACTUALLY COMPLETELY WRONG",
- " SHOCKING DEVELOPMENT COMPLETELY WRONG",
- " LIES!",
- " SHOCKING DEVELOPMENT x",
- " END OF STORY",
- " LIES!",
- " 1",
- '',
- " EXPERTS CLAIM car_x TO BE x OF TOTALLY RIGHT",
- " EXPERTS CLAIM car_y TO BE y OF TOTALLY RIGHT",
- " EXPERTS CLAIM cdr_x TO BE x OF COMPLETELY WRONG",
- " EXPERTS CLAIM cdr_y TO BE y OF COMPLETELY WRONG",
- '',
- " EXPERTS CLAIM x_gt_y TO BE car_x BEATS car_y",
- '',
- " WHAT IF x_gt_y IS ACTUALLY TOTALLY RIGHT",
- " RUMOR HAS IT",
- " EXPERTS CLAIM rest_x_merge_y TO BE merge OF cdr_x, y",
- " SHOCKING DEVELOPMENT cons OF car_x, rest_x_merge_y",
- " END OF STORY",
- " LIES!",
- " RUMOR HAS IT",
- " EXPERTS CLAIM x_merge_rest_y TO BE merge OF x, cdr_y",
- " SHOCKING DEVELOPMENT cons OF car_y, x_merge_rest_y",
- " END OF STORY",
- "END OF STORY",
- '',
- "DISCOVER HOW TO split_middle_helper WITH slow, fast, mid_to_head",
- "RUMOR HAS IT",
- " WHAT IF fast IS ACTUALLY COMPLETELY WRONG",
- " RUMOR HAS IT",
- " EXPERTS CLAIM head_to_mid TO BE reverse OF mid_to_head",
- " SHOCKING DEVELOPMENT cons OF head_to_mid, slow",
- " END OF STORY",
- " LIES!",
- " 1",
- '',
- " EXPERTS CLAIM fast_cdr TO BE fast OF COMPLETELY WRONG",
- " EXPERTS CLAIM slow_car TO BE slow OF TOTALLY RIGHT",
- " EXPERTS CLAIM slow_cdr TO BE slow OF COMPLETELY WRONG",
- '',
- " WHAT IF fast_cdr IS ACTUALLY COMPLETELY WRONG",
- " RUMOR HAS IT",
- " EXPERTS CLAIM mid_to_head_plus_slow TO BE cons OF slow_car, mid_to_head",
- " EXPERTS CLAIM head_to_mid_plus_slow TO BE reverse OF mid_to_head_plus_slow",
- '',
- " SHOCKING DEVELOPMENT cons OF head_to_mid_plus_slow, slow_cdr",
- " END OF STORY",
- " LIES!",
- " 1",
- '',
- " EXPERTS CLAIM fast_cddr TO BE fast_cdr OF COMPLETELY WRONG",
- " EXPERTS CLAIM slow_car_mid_to_head TO BE cons OF slow_car, mid_to_head",
- '',
- " SHOCKING DEVELOPMENT split_middle_helper OF slow_cdr, fast_cddr, slow_car_mid_to_head",
- "END OF STORY",
- '',
- "DISCOVER HOW TO split_middle WITH start",
- "RUMOR HAS IT",
- " EXPERTS CLAIM cdr TO BE start OF COMPLETELY WRONG",
- '',
- " SHOCKING DEVELOPMENT split_middle_helper OF start, cdr, COMPLETELY WRONG",
- "END OF STORY",
- '',
- "DISCOVER HOW TO sort WITH root",
- "RUMOR HAS IT",
- " WHAT IF root IS ACTUALLY COMPLETELY WRONG",
- " SHOCKING DEVELOPMENT root",
- " LIES!",
- " 1",
- '',
- " EXPERTS CLAIM root_cdr TO BE root OF COMPLETELY WRONG",
- " WHAT IF root_cdr IS ACTUALLY COMPLETELY WRONG",
- " SHOCKING DEVELOPMENT root",
- " LIES!",
- " 1",
- '',
- " EXPERTS CLAIM left_right_cons_cell TO BE split_middle OF root",
- " EXPERTS CLAIM left TO BE left_right_cons_cell OF TOTALLY RIGHT",
- " EXPERTS CLAIM right TO BE left_right_cons_cell OF COMPLETELY WRONG",
- " EXPERTS CLAIM left_s TO BE sort OF left",
- " EXPERTS CLAIM right_s TO BE sort OF right",
- '',
- " SHOCKING DEVELOPMENT merge OF left_s, right_s",
- "END OF STORY",
- '',
- "EXPERTS CLAIM a_3 TO BE cons OF 3, COMPLETELY WRONG",
- "EXPERTS CLAIM a_2 TO BE cons OF 1, a_3",
- "EXPERTS CLAIM a_1 TO BE cons OF -2, a_2",
- "EXPERTS CLAIM a_0 TO BE cons OF 5, a_1",
- "EXPERTS CLAIM b_3 TO BE cons OF 2, a_0",
- "EXPERTS CLAIM b_2 TO BE cons OF 7, b_3",
- "EXPERTS CLAIM b_1 TO BE cons OF 3, b_2",
- "EXPERTS CLAIM b_0 TO BE cons OF -1, b_1",
- '',
- "EXPERTS CLAIM b_sorted TO BE sort OF b_0",
- '',
- "YOU WON'T WANT TO MISS join OF b_sorted",
- '',
- "PLEASE LIKE AND SUBSCRIBE",
-].join('\n');
-
-export const SAMPLE_PROGRAMS = [
- {
- id: 'fibonacci',
- label: 'Fibonacci',
- code: [
- "DISCOVER HOW TO fibonacci WITH a, b, n",
- "RUMOR HAS IT",
- " WHAT IF n SMALLER THAN 1",
- " SHOCKING DEVELOPMENT b",
- " LIES! RUMOR HAS IT",
- " YOU WON'T WANT TO MISS b",
- " SHOCKING DEVELOPMENT",
- " fibonacci OF b, a PLUS b, n MINUS 1",
- " END OF STORY",
- "END OF STORY",
- "",
- "EXPERTS CLAIM limit TO BE 10",
- "",
- "fibonacci OF 0, 1, limit",
- "",
- "PLEASE LIKE AND SUBSCRIBE",
- ].join('\n'),
- },
- {
- id: 'countdown',
- label: 'Countdown',
- code: [
- "EXPERTS CLAIM start TO BE 4",
- "YOU WON'T WANT TO MISS 't minus 5...'",
- "",
- "DISCOVER HOW TO countdown WITH current",
- "RUMOR HAS IT",
- " WHAT IF current SMALLER THAN 1 RUMOR HAS IT",
- " SHOCKING DEVELOPMENT 'Blastoff!'",
- " END OF STORY",
- " LIES! RUMOR HAS IT",
- " YOU WON'T WANT TO MISS current",
- " SHOCKING DEVELOPMENT countdown OF current MINUS 1",
- " END OF STORY",
- "END OF STORY",
- "",
- "YOU WON'T WANT TO MISS countdown OF start",
- "",
- "PLEASE LIKE AND SUBSCRIBE",
- ].join('\n'),
- },
- {
- id: 'hello',
- label: 'Hello!',
- code: [
- "YOU WON'T WANT TO MISS ('Hello, ' PLUS (LATEST NEWS ON 'What is your name?')) PLUS '!'",
- "",
- "PLEASE LIKE AND SUBSCRIBE",
- ].join('\n'),
- },
- {
- id: 'binary-inorder-traversal',
- label: 'Binary tree in-order traversal',
- code: BINARY_INORDER_TRAVERSAL_PROGRAM,
- },
- {
- id: 'merge-sort',
- label: 'Merge sort linked list',
- code: MERGE_SORT_PROGRAM,
- },
-];
diff --git a/src/toys/tabloid/js/tabloid.js b/src/toys/tabloid/js/tabloid.js
deleted file mode 100644
index 468ee9a..0000000
--- a/src/toys/tabloid/js/tabloid.js
+++ /dev/null
@@ -1,707 +0,0 @@
-/* Tabloid: the clickbait headline programming language */
-
-/* tokenizer */
-
-/**
- * Reads in char or word chunks
- */
-class Reader {
- constructor(str, base = '') {
- this.base = base;
- this.i = 0;
- this.str = str;
- }
- peek() {
- return this.str[this.i];
- }
- next() {
- return this.str[this.i++];
- }
- hasNext() {
- return this.str[this.i] !== undefined;
- }
- backstep() {
- this.i--;
- }
- readUntil(pred) {
- let result = this.base.slice();
- while (this.hasNext() && !pred(this.peek())) {
- result += this.next();
- }
- return result;
- }
- dropWhitespace() {
- this.readUntil(c => !!c.trim());
- }
- expect(tok) {
- const next = this.next();
- if (next !== tok) {
- throw new Error(`Parsing error: expected ${tok}, got ${next}`);
- }
- }
-}
-
-/**
- * Split into words for easier tokenization
- * with keywords.
- */
-class Wordifier {
- constructor(str) {
- this.reader = new Reader(str.trim());
- this.tokens = [];
- }
- wordify() {
- if (this.tokens.length) return this.tokens;
-
- while (this.reader.hasNext()) {
- const next = this.reader.next();
- switch (next) {
- case '(': {
- this.tokens.push('(');
- break;
- }
- case ')': {
- this.tokens.push(')');
- break;
- }
- case ',': {
- this.tokens.push(',');
- break;
- }
- case '"':
- case "'": {
- this.wordifyString(next);
- break;
- }
- default: {
- // read until WS
- this.reader.backstep();
- this.tokens.push(this.reader.readUntil(c => {
- return !c.trim() || ['(', ')', ','].includes(c)
- }));
- }
- }
- this.reader.dropWhitespace();
- }
- return this.tokens;
- }
- wordifyString(endChar) {
- let acc = '';
- acc += this.reader.readUntil(c => c == endChar);
- while (acc.endsWith('\\') || !this.reader.hasNext()) {
- acc = acc.substr(0, acc.length - 1);
- this.reader.next(); // endChar
- acc += endChar + this.reader.readUntil(c => c == endChar);
- }
- this.reader.next(); // throw away closing char
- this.tokens.push('"' + acc);
- }
-}
-
-const T = {
- LParen: Symbol('LParen'),
- RParen: Symbol('RParen'),
- Comma: Symbol('Comma'),
- DiscoverHowTo: Symbol('DiscoverHowTo'),
- With: Symbol('With'),
- Of: Symbol('Of'),
- RumorHasIt: Symbol('RumorHasIt'),
- WhatIf: Symbol('WhatIf'),
- LiesBang: Symbol('LiesBang'),
- EndOfStory: Symbol('EndOfStory'),
- ExpertsClaim: Symbol('ExpertsClaim'),
- ToBe: Symbol('ToBe'),
- YouWontWantToMiss: Symbol('YouWontWantToMiss'),
- LatestNewsOn: Symbol('LatestNewsOn'),
- TotallyRight: Symbol('TotallyRight'),
- CompletelyWrong: Symbol('CompletelyWrong'),
- IsActually: Symbol('IsActually'),
- And: Symbol('And'),
- Or: Symbol('Or'),
- Plus: Symbol('Plus'),
- Minus: Symbol('Minus'),
- Times: Symbol('Times'),
- DividedBy: Symbol('DividedBy'),
- Modulo: Symbol('Modulo'),
- Beats: Symbol('Beats'), // >
- SmallerThan: Symbol('SmallerThan'), // <
- ShockingDevelopment: Symbol('ShockingDevelopment'),
- PleaseLikeAndSubscribe: Symbol('PleaseLikeAndSubscribe'),
-}
-
-const BINARY_OPS = [
- T.IsActually,
- T.And,
- T.Or,
- T.Plus,
- T.Minus,
- T.Times,
- T.DividedBy,
- T.Modulo,
- T.Beats,
- T.SmallerThan,
-];
-
-export function tokenize(prog) {
- const reader = new Reader(new Wordifier(prog).wordify(), []);
- const tokens = [];
-
- while (reader.hasNext()) {
- const next = reader.next();
- switch (next) {
- case 'DISCOVER': {
- reader.expect('HOW');
- reader.expect('TO');
- tokens.push(T.DiscoverHowTo);
- break;
- }
- case 'WITH': {
- tokens.push(T.With);
- break;
- }
- case 'OF': {
- tokens.push(T.Of);
- break;
- }
- case 'RUMOR': {
- reader.expect('HAS');
- reader.expect('IT');
- tokens.push(T.RumorHasIt);
- break;
- }
- case 'WHAT': {
- reader.expect('IF');
- tokens.push(T.WhatIf);
- break;
- }
- case 'LIES!': {
- tokens.push(T.LiesBang);
- break;
- }
- case 'END': {
- reader.expect('OF');
- reader.expect('STORY');
- tokens.push(T.EndOfStory);
- break;
- }
- case 'EXPERTS': {
- reader.expect('CLAIM');
- tokens.push(T.ExpertsClaim);
- break;
- }
- case 'TO': {
- reader.expect('BE');
- tokens.push(T.ToBe);
- break;
- }
- case 'YOU': {
- reader.expect('WON\'T');
- reader.expect('WANT');
- reader.expect('TO');
- reader.expect('MISS');
- tokens.push(T.YouWontWantToMiss);
- break;
- }
- case 'LATEST': {
- reader.expect('NEWS');
- reader.expect('ON');
- tokens.push(T.LatestNewsOn);
- break;
- }
- case 'IS': {
- reader.expect('ACTUALLY');
- tokens.push(T.IsActually);
- break;
- }
- case 'AND': {
- tokens.push(T.And);
- break;
- }
- case 'OR': {
- tokens.push(T.Or);
- break;
- }
- case 'PLUS': {
- tokens.push(T.Plus);
- break;
- }
- case 'MINUS': {
- tokens.push(T.Minus);
- break;
- }
- case 'TIMES': {
- tokens.push(T.Times);
- break;
- }
- case 'DIVIDED': {
- reader.expect('BY');
- tokens.push(T.DividedBy);
- break;
- }
- case 'MODULO': {
- tokens.push(T.Modulo);
- break;
- }
- case 'BEATS': {
- tokens.push(T.Beats);
- break;
- }
- case 'SMALLER': {
- reader.expect('THAN');
- tokens.push(T.SmallerThan);
- break;
- }
- case 'SHOCKING': {
- reader.expect('DEVELOPMENT');
- tokens.push(T.ShockingDevelopment);
- break;
- }
- case 'PLEASE': {
- reader.expect('LIKE');
- reader.expect('AND');
- reader.expect('SUBSCRIBE');
- tokens.push(T.PleaseLikeAndSubscribe);
- break;
- }
- case 'TOTALLY': {
- reader.expect('RIGHT');
- tokens.push(T.TotallyRight);
- break;
- }
- case 'COMPLETELY': {
- reader.expect('WRONG');
- tokens.push(T.CompletelyWrong);
- break;
- }
- case '(': {
- tokens.push(T.LParen);
- break;
- }
- case ')': {
- tokens.push(T.RParen);
- break;
- }
- case ',': {
- tokens.push(T.Comma);
- break;
- }
- default: {
- if (!isNaN(parseFloat(next))) {
- // number literal
- tokens.push(parseFloat(next));
- } else {
- // string or varname
- tokens.push(next);
- }
- }
- }
- }
- return tokens;
-}
-
-/* parser */
-
-const N = {
- NumberLiteral: Symbol('NumberLiteral'),
- StringLiteral: Symbol('StringLiteral'),
- BoolLiteral: Symbol('BoolLiteral'),
- FnDecl: Symbol('FnDecl'),
- FnCall: Symbol('FnCall'),
- Ident: Symbol('Ident'),
- Assignment: Symbol('Assignment'),
- BinaryOp: Symbol('BinaryOp'),
- IfExpr: Symbol('IfExpr'),
- ExprGroup: Symbol('ExprGroup'),
- ReturnExpr: Symbol('ReturnExpr'),
- ProgEndExpr: Symbol('ProgEndExpr'),
- PrintExpr: Symbol('PrintExpr'),
- InputExpr: Symbol('InputExpr'),
-}
-
-export class Parser {
- constructor(tokens) {
- this.tokens = new Reader(tokens, []);
- }
- /**
- * Atom
- * Ident
- * NumberLiteral
- * StringLiteral
- * BoolLiteral
- * FnCall
- * FnDecl
- * ExprGroup
- *
- * Expression:
- * (begins with atom)
- * BinaryOp
- * Atom
- * (begins with keyword)
- * IfExpr
- * Assignment
- * ReturnExpr
- * ProgEndExpr
- * PrintExpr
- * InputExpr
- *
- */
- parse() {
- const nodes = [];
- while (this.tokens.hasNext()) {
- nodes.push(this.expr());
- }
-
- if (nodes[nodes.length - 1].type !== N.ProgEndExpr) {
- throw new Error('Parsing error: A Tabloid program MUST end with PLEASE LIKE AND SUBSCRIBE');
- }
-
- return nodes;
- }
- expectIdentString() {
- const ident = this.tokens.next();
- if (typeof ident === 'string' && !ident.startsWith('"')) {
- return ident;
- }
- throw new Error(`Parsing error: expected identifier, got ${ident.toString()}`);
- }
- atom() {
- const next = this.tokens.next();
- if (typeof next === 'number') {
- return {
- type: N.NumberLiteral,
- val: next,
- }
- } else if (typeof next === 'string') {
- if (next.startsWith('"')) {
- return {
- type: N.StringLiteral,
- val: next.substr(1),
- }
- }
- const ident = {
- type: N.Ident,
- val: next,
- }
- if (this.tokens.peek() === T.Of) {
- return this.fnCall(ident);
- }
- return ident;
- } else if (next === T.TotallyRight) {
- return {
- type: N.BoolLiteral,
- val: true,
- }
- } else if (next === T.CompletelyWrong) {
- return {
- type: N.BoolLiteral,
- val: false,
- }
- } else if (next === T.DiscoverHowTo) {
- // fn literal
- const fnName = this.tokens.next();
- if (this.tokens.peek(T.With)) {
- this.tokens.next(); // with
- // with args
- const args = [this.expectIdentString()];
- while (this.tokens.peek() === T.Comma) {
- this.tokens.next(); // comma
- args.push(this.expectIdentString());
- }
- return {
- type: N.FnDecl,
- name: fnName,
- args: args,
- body: this.expr(),
- }
- } else {
- return {
- type: N.FnDecl,
- name: fnName,
- args: [],
- body: this.expr(),
- }
- }
- } else if (next === T.RumorHasIt) {
- // block
- const exprs = [];
- while (this.tokens.hasNext() && this.tokens.peek() !== T.EndOfStory) {
- exprs.push(this.expr());
- }
- this.tokens.expect(T.EndOfStory);
- return {
- type: N.ExprGroup,
- exprs: exprs,
- };
- } else if (next === T.LParen) {
- // block, but guarded by parens, for binary exprs
- const exprs = [];
- while (this.tokens.hasNext() && this.tokens.peek() !== T.RParen) {
- exprs.push(this.expr());
- }
- this.tokens.expect(T.RParen);
- return {
- type: N.ExprGroup,
- exprs: exprs,
- };
- }
-
- throw new Error(`Parsing error: expected ident, literal, or block, got ${
- next.toString()
- } before ${this.tokens.peek().toString()}`);
- }
- expr() {
- const next = this.tokens.next();
- if (next === T.WhatIf) {
- // if expr
- const cond = this.expr();
- const ifBody = this.expr();
-
- let elseBody = null;
- if (this.tokens.peek() == T.LiesBang) {
- this.tokens.next(); // LiesBang
- elseBody = this.expr();
- }
- return {
- type: N.IfExpr,
- cond: cond,
- ifBody: ifBody,
- elseBody: elseBody,
- }
- } else if (next === T.ExpertsClaim) {
- // assignment
- const name = this.expectIdentString();
- this.tokens.expect(T.ToBe);
- const val = this.expr();
- return {
- type: N.Assignment,
- name,
- val,
- }
- } else if (next === T.ShockingDevelopment) {
- // return
- return {
- type: N.ReturnExpr,
- val: this.expr(),
- }
- } else if (next === T.PleaseLikeAndSubscribe) {
- // prog end
- return {
- type: N.ProgEndExpr,
- }
- } else if (next === T.YouWontWantToMiss) {
- // print expr
- return {
- type: N.PrintExpr,
- val: this.expr(),
- }
- } else if (next === T.LatestNewsOn) {
- // input expr
- return {
- type: N.InputExpr,
- val: this.expr(),
- }
- }
-
- this.tokens.backstep();
- const atom = this.atom();
- if (BINARY_OPS.includes(this.tokens.peek())) {
- // infix binary ops
- const left = atom;
- const op = this.tokens.next();
- const right = this.atom();
- return {
- type: N.BinaryOp,
- op,
- left,
- right,
- }
- }
-
- return atom;
- }
- fnCall(fnNode) {
- this.tokens.expect(T.Of);
- const args = [this.expr()];
- while (this.tokens.peek() === T.Comma) {
- this.tokens.next(); // comma
- args.push(this.expr());
- }
- return {
- type: N.FnCall,
- fn: fnNode,
- args: args,
- }
- }
-}
-
-/* executor (tree walk) */
-
-/**
- * Abused (slightly) to easily return values upstack
- */
-class ReturnError {
- constructor(value) {
- this.value = value;
- }
- unwrap() {
- return this.value;
- }
-}
-
-export class Environment {
- constructor(runtime) {
- /**
- * Runtime contains the following functions:
- * - print(s)
- * - input(s)
- */
- this.runtime = runtime;
- this.scopes = [{}]; // begin with global scope
- }
- run(nodes) {
- let rv;
- for (const node of nodes) {
- rv = this.eval(node);
- }
- return rv;
- }
- eval(node) {
- const scope = this.scopes[this.scopes.length - 1];
-
- switch (node.type) {
- case N.NumberLiteral:
- case N.StringLiteral:
- case N.BoolLiteral:
- return node.val;
- case N.FnDecl: {
- const fnValue = {
- node: node,
- closure: { ...scope }
- };
- scope[node.name] = fnValue;
- return fnValue;
- }
- case N.FnCall: {
- const fn = this.eval(node.fn);
- const args = node.args.map(arg => this.eval(arg));
-
- const calleeScope = {};
- fn.node.args.forEach((argName, i) => {
- calleeScope[argName] = args[i];
- });
-
- this.scopes.push(fn.closure);
- this.scopes.push(calleeScope);
- let rv;
- try {
- this.eval(fn.node.body);
- } catch (maybeReturnErr) {
- if (maybeReturnErr instanceof ReturnError) {
- rv = maybeReturnErr.unwrap();
- } else {
- throw maybeReturnErr;
- }
- }
- this.scopes.pop();
- this.scopes.pop();
-
- return rv;
- }
- case N.Ident: {
- let i = this.scopes.length - 1;
- while (i >= 0) {
- if (node.val in this.scopes[i]) {
- return this.scopes[i][node.val];
- }
- i --;
- }
- throw new Error(`Runtime error: Undefined variable "${node.val}"`);
- }
- case N.Assignment: {
- scope[node.name] = this.eval(node.val);
- return scope[node.name];
- }
- case N.BinaryOp: {
- const left = this.eval(node.left);
- const right = this.eval(node.right);
- switch (node.op) {
- case T.IsActually:
- return left === right;
- case T.And:
- return left && right;
- case T.Or:
- return left || right;
- case T.Plus:
- return left + right;
- case T.Minus:
- return left - right;
- case T.Times:
- return left * right;
- case T.DividedBy:
- return left / right;
- case T.Modulo:
- return left % right;
- case T.Beats:
- return left > right;
- case T.SmallerThan:
- return left < right;
- default:
- throw new Error(`Runtime error: Unknown binary op ${node.op.toString()}`);
- }
- }
- case N.IfExpr: {
- if (this.eval(node.cond)) {
- return this.eval(node.ifBody);
- }
- if (node.elseBody != null) {
- return this.eval(node.elseBody);
- }
- }
- case N.ExprGroup: {
- if (!node.exprs.length) {
- throw new Error('Runtime error: Empty expression group with no expressions');
- }
-
- let rv;
- for (const expr of node.exprs) {
- rv = this.eval(expr);
- }
- return rv;
- }
- case N.ReturnExpr: {
- const rv = this.eval(node.val);
- throw new ReturnError(rv);
- }
- case N.ProgEndExpr: {
- // do nothing
- break;
- }
- case N.PrintExpr: {
- let val = this.eval(node.val);
- // shim for boolean to-string's
- if (val === true) {
- val = 'TOTALLY RIGHT';
- } else if (val === false) {
- val = 'COMPLETELY WRONG';
- }
- this.runtime.print(val);
- return val;
- }
- case N.InputExpr: {
- let val = this.eval(node.val);
- // shim for boolean to-string's
- if (val === true) {
- val = 'TOTALLY RIGHT';
- } else if (val === false) {
- val = 'COMPLETELY WRONG';
- }
- return this.runtime.input(val);
- }
- default:
- console.log(JSON.stringify(node, null, 2));
- throw new Error(`Runtime error: Unknown AST Node of type ${
- node.type.toString()
- }:\n${JSON.stringify(node, null, 2)}`);
- }
- }
-}
diff --git a/src/toys/turing/css/styles.css b/src/toys/turing/css/styles.css
deleted file mode 100644
index 2407cb4..0000000
--- a/src/toys/turing/css/styles.css
+++ /dev/null
@@ -1,66 +0,0 @@
-.tape {
- display: flex;
- gap: 0.25rem;
- padding: var(--space-md);
- overflow-x: auto;
- background: var(--surface-alt);
- border: var(--border-width) solid var(--border);
- box-shadow: inset 1px 1px 0 var(--border-light),
- inset -1px -1px 0 var(--border-dark);
- min-height: 5rem;
-}
-
-.cell {
- position: relative;
- width: 3.25rem;
- padding: var(--space-xs);
- background: var(--surface);
- border: 2px solid var(--border-dark);
- box-shadow: inset 1px 1px 0 var(--border-light),
- inset -1px -1px 0 var(--border-dark);
- flex-shrink: 0;
-}
-
-.cell input {
- width: 100%;
- text-align: center;
- border: none;
- background: transparent;
- font-family: var(--font-mono);
- color: var(--fg);
- font-size: 0.875rem;
- padding: 0.25rem;
-}
-
-.cell input:focus {
- outline: 1px dotted var(--fg);
- outline-offset: 2px;
-}
-
-.cell.active {
- border-color: var(--primary);
- background: color-mix(in srgb, var(--primary) 15%, var(--surface));
-}
-
-.cell.active::after {
- content: '▲';
- position: absolute;
- bottom: -1.5rem;
- left: 50%;
- transform: translateX(-50%);
- color: var(--primary);
- font-size: 1rem;
- line-height: 1;
-}
-
-#state-text {
- font-family: var(--font-mono);
- font-weight: 700;
- margin-top: var(--space-md);
-}
-
-.controls {
- display: flex;
- gap: var(--space-sm);
- flex-wrap: wrap;
-}
diff --git a/src/toys/turing/index.html b/src/toys/turing/index.html
deleted file mode 100644
index 838c5bf..0000000
--- a/src/toys/turing/index.html
+++ /dev/null
@@ -1,42 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel="stylesheet" href="https://adelie.liz.coffee/bundle.css">
- <link rel="stylesheet" href="./css/styles.css">
- <title>Turing Machine</title>
-</head>
-<body>
- <main>
- <article>
- <h3>Turing Machine</h3>
-
- <p id="state-text">State: _, Step: 0</p>
-
- <div id="tape" class="tape mt-md"></div>
-
- <div class="controls mt-sm">
- <button id="run-btn" class="primary">Run (Ctrl + Enter)</button>
- <button id="step-btn">Step</button>
- <button id="reset-btn">Reset</button>
- <button id="copy-btn">Copy State</button>
- </div>
-
- <section class="mt-lg">
- <div class="mt-sm">
- <label for="program-select">Example:</label>
- <select id="program-select"></select>
- </div>
-
- <div id="code-editor" class="code-editor-container mt-sm"></div>
- </section>
- </article>
- </main>
-
- <script src="https://adelie.liz.coffee/bundle.js"></script>
- <script src="https://adelie.liz.coffee/adelie-editor.js"></script>
- <script type="module" src="./js/main.js">
- </script>
-</body>
-</html>
diff --git a/src/toys/turing/js/machine.js b/src/toys/turing/js/machine.js
deleted file mode 100644
index 6af4be6..0000000
--- a/src/toys/turing/js/machine.js
+++ /dev/null
@@ -1,75 +0,0 @@
-export class TuringMachine {
- constructor({
- tape,
- rules,
- startState,
- acceptStates = [],
- rejectStates = []
- }) {
- this.tape = tape;
- this.rules = rules;
- this.state = startState;
- this.acceptStates = new Set(acceptStates);
- this.rejectStates = new Set(rejectStates);
- this.iteration = 0;
- }
-
- step() {
- if (this.isHalted()) {
- return false;
- }
-
- const currentSymbol = this.tape.readHead();
- const ruleKey = this.getRuleKey(this.state, currentSymbol);
- if (!this.rules.has(ruleKey)) {
- return false;
- }
-
- const { nextState, writeSymbol, direction } = this.rules.get(ruleKey);
- this.tape.writeHead(writeSymbol);
-
- if (direction === "R") {
- this.tape.moveRight();
- } else if (direction === "L") {
- this.tape.moveLeft();
- }
-
- this.state = nextState;
- this.iteration += 1;
- return !this.isHalted();
- }
-
- canStep() {
- if (this.isHalted()) {
- return false;
- }
-
- const currentSymbol = this.tape.readHead();
- const ruleKey = this.getRuleKey(this.state, currentSymbol);
- return this.rules.has(ruleKey);
- }
-
- getRuleKey(state, symbol) {
- return `${state}:${symbol}`;
- }
-
- isAccepting() {
- return this.acceptStates.has(this.state);
- }
-
- isRejecting() {
- return this.rejectStates.has(this.state);
- }
-
- isHalted() {
- return this.isAccepting() || this.isRejecting();
- }
-
- getStateStatus() {
- return `State: ${this.state}, Step: ${this.iteration}`;
- }
-
- getState() {
- return this.state;
- }
-}
diff --git a/src/toys/turing/js/main.js b/src/toys/turing/js/main.js
deleted file mode 100644
index c9f37fc..0000000
--- a/src/toys/turing/js/main.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import { TuringMachineUI } from "./ui.js";
-document.addEventListener("DOMContentLoaded", (event) => {
- new TuringMachineUI();
-});
diff --git a/src/toys/turing/js/parser.js b/src/toys/turing/js/parser.js
deleted file mode 100644
index dd65613..0000000
--- a/src/toys/turing/js/parser.js
+++ /dev/null
@@ -1,141 +0,0 @@
-export function parseInstructionSet(code) {
- const lines = code.split("\n");
- const instructions = [];
- const config = {
- startState: null,
- acceptStates: new Set(),
- rejectStates: new Set()
- };
-
- lines.forEach((line, lineIndex) => {
- const withoutComments = line.replace(/\/\/.*$/, "").trim();
- if (!withoutComments) {
- return;
- }
-
- if (withoutComments.startsWith("#")) {
- applyDirective(withoutComments.slice(1).trim(), config, lineIndex + 1);
- return;
- }
-
- const parts = withoutComments.split(/\s+/).filter(Boolean);
- if (parts.length !== 5) {
- throw new Error(`Invalid instruction on line ${lineIndex + 1}: expected 5 parts, received ${parts.length}`);
- }
-
- const [fromState, readSymbol, writeSymbol, direction, toState] = parts;
- if (!config.startState) {
- config.startState = fromState;
- }
-
- instructions.push({ fromState, readSymbol, writeSymbol, direction, toState, line: lineIndex + 1 });
- });
-
- if (!instructions.length) {
- throw new Error("No instructions provided");
- }
-
- const { acceptStates, rejectStates } = deriveHaltingStates(instructions, config);
- const rules = buildRuleMap(instructions);
-
- return {
- rules,
- startState: config.startState ?? instructions[0].fromState,
- acceptStates,
- rejectStates
- };
-}
-
-function applyDirective(directiveLine, config, lineNumber) {
- if (!directiveLine) {
- return;
- }
-
- const [keyword, ...values] = directiveLine.split(/\s+/).filter(Boolean);
- if (!keyword) {
- return;
- }
-
- switch (keyword.toLowerCase()) {
- case "start": {
- if (values.length !== 1) {
- throw new Error(`#start on line ${lineNumber} must provide exactly one state`);
- }
- config.startState = values[0];
- break;
- }
- case "accept":
- case "accepts":
- case "accepting": {
- if (!values.length) {
- throw new Error(`#${keyword} on line ${lineNumber} must include at least one state`);
- }
- values.forEach((value) => config.acceptStates.add(value));
- break;
- }
- case "reject":
- case "rejects":
- case "rejecting": {
- if (!values.length) {
- throw new Error(`#${keyword} on line ${lineNumber} must include at least one state`);
- }
- values.forEach((value) => config.rejectStates.add(value));
- break;
- }
- default:
- throw new Error(`Unknown directive '#${keyword}' on line ${lineNumber}`);
- }
-}
-
-function deriveHaltingStates(instructions, config) {
- const fromStates = new Set();
- const toStates = new Set();
- const allStates = new Set();
-
- instructions.forEach(({ fromState, toState }) => {
- fromStates.add(fromState);
- toStates.add(toState);
- allStates.add(fromState);
- allStates.add(toState);
- });
-
- // Remove any overlap so rejects always win
- config.rejectStates.forEach((state) => config.acceptStates.delete(state));
-
- if (!config.acceptStates.size) {
- for (const state of allStates) {
- if (!fromStates.has(state) && toStates.has(state) && !config.rejectStates.has(state)) {
- config.acceptStates.add(state);
- }
- }
- }
-
- return {
- acceptStates: Array.from(config.acceptStates),
- rejectStates: Array.from(config.rejectStates)
- };
-}
-
-function buildRuleMap(instructions) {
- const rules = new Map();
-
- instructions.forEach(({ fromState, readSymbol, writeSymbol, direction, toState, line }) => {
- const dir = direction.toUpperCase();
- if (!["L", "R", "S"].includes(dir)) {
- throw new Error(`Invalid direction '${direction}' on line ${line}. Use L, R, or S.`);
- }
-
- const key = `${fromState}:${readSymbol}`;
- if (rules.has(key)) {
- throw new Error(`Duplicate rule for state '${fromState}' reading '${readSymbol}' (line ${line})`);
- }
-
- rules.set(key, {
- nextState: toState,
- writeSymbol,
- direction: dir
- });
- });
-
- return rules;
-}
diff --git a/src/toys/turing/js/samples.js b/src/toys/turing/js/samples.js
deleted file mode 100644
index 0998ee4..0000000
--- a/src/toys/turing/js/samples.js
+++ /dev/null
@@ -1,88 +0,0 @@
-// Example programs with initial tape states
-export const EXAMPLE_PROGRAMS = [
- {
- name: "Replace two B's",
- code: `#start q0
-#accept acc
-#reject rej
-
-q0 B 1 R q1
-q1 1 1 R q1
-q1 B 1 R acc`,
- initialTape: ""
- },
- {
- name: "Binary equality checker",
- code: `// https://stackoverflow.com/questions/59045832
-
-#start q0
-#accept acc
-#reject rej
-
-q0 0 X R q1
-q0 1 X R q2
-q0 = = R q7
-q1 0 0 R q1
-q1 1 1 R q1
-q1 = = R q3
-q2 0 0 R q2
-q2 1 1 R q2
-q2 = = R q4
-q3 X X R q3
-q3 0 X L q5
-q3 1 1 L rej
-q3 B B L rej
-q4 X X R q4
-q4 0 0 L rej
-q4 B B L rej
-q4 1 X L q5
-q5 X X L q5
-q5 = = L q6
-q6 0 0 L q6
-q6 1 1 L q6
-q6 X X R q0
-q7 X X R q7
-q7 B B L q8
-q7 0 0 L rej
-q7 1 1 L rej
-q8 X X L q8
-q8 0 0 L q8
-q8 1 1 L q8
-q8 = = L acc`,
- initialTape: "1011=1011"
- },
- {
- name: "Binary addition",
- code: `// https://stackoverflow.com/questions/59045832
-
-#start q0
-#accept acc
-#reject rej
-
-q0 B B R q0
-q0 0 0 R q0
-q0 1 1 R q0
-q0 + + R q1
-q1 0 0 R q1
-q1 1 1 R q1
-q1 B B L q2
-q2 0 1 L q2
-q2 1 0 L q3
-q2 + + R q5
-q3 0 0 L q3
-q3 1 1 L q3
-q3 + + L q4
-q4 0 1 R q0
-q4 1 0 L q4
-q4 B 1 R q0
-q5 1 B R q5
-q5 B B R q6
-q6 B B L q6
-q6 + B L q7
-q7 0 0 L q7
-q7 1 1 L q7
-q7 B B R acc
-`,
- initialTape: "101+110"
- }
-];
diff --git a/src/toys/turing/js/tape.js b/src/toys/turing/js/tape.js
deleted file mode 100644
index fd05366..0000000
--- a/src/toys/turing/js/tape.js
+++ /dev/null
@@ -1,103 +0,0 @@
-const ESCAPE_REGEX = /[.*+?^${}()|[\]\\]/g;
-
-function escapeForRegex(value) {
- return value.replace(ESCAPE_REGEX, "\\$&");
-}
-
-export class Tape {
- constructor({
- initialContent = "",
- blankSymbol = "B",
- minLength = 50,
- padding = 40
- } = {}) {
- this.blankSymbol = blankSymbol;
- this.minLength = minLength;
- this.padding = padding;
- this.reset(initialContent);
- }
-
- reset(initialContent = "") {
- const targetLength = Math.max(this.minLength, initialContent.length + this.padding);
- this.cells = Array(targetLength).fill(this.blankSymbol);
- const startOffset = Math.floor((targetLength - initialContent.length) / 2);
- for (let i = 0; i < initialContent.length; i++) {
- this.cells[startOffset + i] = initialContent[i];
- }
- this.headIndex = startOffset;
- }
-
- get length() {
- return this.cells.length;
- }
-
- getHeadIndex() {
- return this.headIndex;
- }
-
- readHead() {
- return this.getCell(this.headIndex);
- }
-
- writeHead(symbol) {
- this.cells[this.headIndex] = symbol || this.blankSymbol;
- }
-
- readAt(index) {
- return this.getCell(index);
- }
-
- writeAt(index, symbol) {
- if (index < 0) {
- throw new Error("Cannot write to a negative tape index");
- }
- this.ensureRightCapacity(index);
- this.cells[index] = symbol || this.blankSymbol;
- }
-
- setHead(index) {
- if (index < 0) {
- throw new Error("Head index cannot be negative");
- }
- this.ensureRightCapacity(index);
- this.headIndex = index;
- }
-
- moveLeft() {
- if (this.headIndex === 0) {
- this.cells.unshift(this.blankSymbol);
- } else {
- this.headIndex -= 1;
- return;
- }
- }
-
- moveRight() {
- this.headIndex += 1;
- if (this.headIndex >= this.cells.length) {
- this.cells.push(this.blankSymbol);
- }
- }
-
- getCell(index) {
- if (index < 0 || index >= this.cells.length) {
- return this.blankSymbol;
- }
- return this.cells[index];
- }
-
- ensureRightCapacity(index) {
- while (index >= this.cells.length) {
- this.cells.push(this.blankSymbol);
- }
- }
-
- getContents({ trimTrailing = true } = {}) {
- let snapshot = this.cells.join("");
- if (trimTrailing) {
- const regex = new RegExp(`${escapeForRegex(this.blankSymbol)}+$`, "g");
- snapshot = snapshot.replace(regex, "");
- }
- return snapshot;
- }
-}
diff --git a/src/toys/turing/js/ui.js b/src/toys/turing/js/ui.js
deleted file mode 100644
index ae01a4b..0000000
--- a/src/toys/turing/js/ui.js
+++ /dev/null
@@ -1,386 +0,0 @@
-import { Tape } from "./tape.js";
-import { TuringMachine } from "./machine.js";
-import { parseInstructionSet } from "./parser.js";
-import { EXAMPLE_PROGRAMS } from "./samples.js";
-
-const SCROLL_THRESHOLD = 3;
-
-export class TuringMachineUI {
- constructor() {
- this.machine = null;
- this.editor = null;
- this.intervalId = null;
- this.isRunning = false;
- this.initialTapeSize = 50;
- this.blankSymbol = "B";
- this.currentProgramIndex = 0;
- this.loadedFromURL = false;
- this.urlTapeState = "";
- this.lastScrollPosition = 0;
- this.renderedTapeLength = 0;
- this.simulationInterval = 200;
-
- this.elements = {
- tape: document.getElementById("tape"),
- stateText: document.getElementById("state-text"),
- runBtn: document.getElementById("run-btn"),
- stepBtn: document.getElementById("step-btn"),
- resetBtn: document.getElementById("reset-btn"),
- copyBtn: document.getElementById("copy-btn"),
- programSelect: document.getElementById("program-select")
- };
-
- this.init();
- }
-
- init() {
- this.setupEditor();
- this.populateProgramSelect();
- this.setupEventListeners();
- this.loadFromURL();
- }
-
- setupEditor() {
- this.editor = adelieEditor.init("#code-editor", {
- language: "javascript"
- });
-
- this.editor.dom.addEventListener("input", () => {
- this.machine = null;
- });
- }
-
- populateProgramSelect() {
- if (!this.elements.programSelect) {
- return;
- }
-
- this.elements.programSelect.innerHTML = "";
- EXAMPLE_PROGRAMS.forEach((program, index) => {
- const option = document.createElement("option");
- option.value = index.toString();
- option.textContent = program.name;
- this.elements.programSelect.appendChild(option);
- });
- }
-
- setupEventListeners() {
- this.elements.runBtn.addEventListener("click", () => this.toggleRun());
- this.elements.stepBtn.addEventListener("click", () => this.step());
- this.elements.resetBtn.addEventListener("click", () => this.reset());
- this.elements.copyBtn.addEventListener("click", () => this.copyState());
-
- this.elements.programSelect.addEventListener("change", (event) => {
- this.loadProgram(parseInt(event.target.value, 10));
- });
-
- document.addEventListener("keydown", (event) => {
- if (event.ctrlKey && event.key === "Enter") {
- event.preventDefault();
- this.reset();
- setTimeout(() => this.run(), 0);
- }
- });
-
- this.elements.tape.addEventListener("input", (event) => this.handleTapeInput(event));
- this.elements.tape.addEventListener("focusin", () => {
- if (this.isRunning) {
- this.pause();
- }
- });
- }
-
- handleTapeInput(event) {
- if (!this.machine) {
- return;
- }
- const target = event.target;
- if (!(target instanceof HTMLInputElement)) {
- return;
- }
-
- const parentCell = target.closest(".cell");
- if (!parentCell) {
- return;
- }
-
- const index = Number(parentCell.dataset.index);
- if (Number.isNaN(index)) {
- return;
- }
-
- const sanitized = (target.value || this.blankSymbol).slice(0, 1);
- target.value = sanitized;
- this.machine.tape.writeAt(index, sanitized || this.blankSymbol);
- }
-
- loadFromURL() {
- const urlParams = new URLSearchParams(window.location.search);
- const startState = urlParams.get("start") ?? "";
- const instructions = urlParams.get("instructions");
-
- if (!instructions) {
- this.loadProgram(0);
- return;
- }
-
- try {
- const code = atob(instructions);
- this.setEditorContent(code);
- this.loadedFromURL = true;
- this.urlTapeState = startState;
- this.compile(this.urlTapeState);
- } catch (error) {
- console.error("Failed to load from URL", error);
- this.loadedFromURL = false;
- this.loadProgram(0);
- }
- }
-
- setEditorContent(content) {
- const length = this.editor.state.doc.toString().length;
- this.editor.dispatch({
- changes: { from: 0, to: length, insert: content }
- });
- }
-
- getEditorContent() {
- return this.editor.state.doc.toString();
- }
-
- loadProgram(index = 0) {
- const program = EXAMPLE_PROGRAMS[index];
- if (!program) {
- return;
- }
-
- this.loadedFromURL = false;
- this.currentProgramIndex = index;
- this.elements.programSelect.value = index.toString();
- this.setEditorContent(program.code);
-
- try {
- this.compile(program.initialTape);
- } catch (error) {
- this.elements.stateText.innerHTML = `<span class="text-error">Error: ${error.message}</span>`;
- }
- }
-
- compile(initialTape = "") {
- this.pause();
-
- const code = this.getEditorContent();
- const instructionSet = parseInstructionSet(code);
- const tapeSeed = initialTape || EXAMPLE_PROGRAMS[this.currentProgramIndex]?.initialTape || "";
-
- const tape = new Tape({
- initialContent: tapeSeed,
- blankSymbol: this.blankSymbol,
- minLength: Math.max(this.initialTapeSize, tapeSeed.length + 40)
- });
-
- this.machine = new TuringMachine({
- tape,
- rules: instructionSet.rules,
- startState: instructionSet.startState,
- acceptStates: instructionSet.acceptStates,
- rejectStates: instructionSet.rejectStates
- });
-
- this.renderedTapeLength = 0;
- this.lastScrollPosition = tape.getHeadIndex();
- this.updateStateDisplay();
- this.updateTape(true);
- }
-
- reset() {
- this.pause();
-
- if (this.loadedFromURL) {
- try {
- this.compile(this.urlTapeState);
- return;
- } catch (error) {
- this.elements.stateText.innerHTML = `<span class="text-error">Error: ${error.message}</span>`;
- }
- }
-
- this.loadProgram(this.currentProgramIndex);
- }
-
- step() {
- if (!this.machine) {
- try {
- this.compile();
- } catch (error) {
- this.elements.stateText.innerHTML = `<span class="text-error">Error: ${error.message}</span>`;
- return;
- }
- }
-
- const canContinue = this.machine.step();
- this.updateStateDisplay();
- this.updateTape();
-
- if (!canContinue) {
- this.pause();
- }
- }
-
- toggleRun() {
- if (this.isRunning) {
- this.pause();
- } else {
- this.run();
- }
- }
-
- run() {
- if (this.isRunning) {
- return;
- }
-
- if (!this.machine) {
- try {
- this.compile();
- } catch (error) {
- this.elements.stateText.innerHTML = `<span class="text-error">Error: ${error.message}</span>`;
- return;
- }
- }
-
- this.isRunning = true;
- this.elements.runBtn.textContent = "⏸ Pause";
- this.elements.runBtn.classList.remove("primary");
-
- this.intervalId = setInterval(() => {
- const canContinue = this.machine.step();
- this.updateStateDisplay();
- this.updateTape();
- if (!canContinue) {
- this.pause();
- }
- }, this.simulationInterval);
- }
-
- pause() {
- if (!this.isRunning) {
- return;
- }
-
- this.isRunning = false;
- clearInterval(this.intervalId);
- this.intervalId = null;
-
- this.elements.runBtn.textContent = "▶ Run (Ctrl + Enter)";
- this.elements.runBtn.classList.add("primary");
- }
-
- updateStateDisplay() {
- if (!this.machine) {
- this.elements.stateText.textContent = "State: _, Step: 0";
- return;
- }
-
- const status = this.machine.getStateStatus();
-
- if (!this.machine.canStep()) {
- if (this.machine.isAccepting()) {
- this.elements.stateText.innerHTML = `<span class="text-success">Accept(${status})</span>`;
- } else if (this.machine.isRejecting()) {
- this.elements.stateText.innerHTML = `<span class="text-error">Reject(${status})</span>`;
- } else {
- this.elements.stateText.innerHTML = `<span class="text-error">Halt(${status})</span>`;
- }
- return;
- }
-
- this.elements.stateText.textContent = status;
- }
-
- updateTape(forceRender = false) {
- if (!this.machine) {
- return;
- }
-
- const tape = this.machine.tape;
- if (forceRender || this.renderedTapeLength !== tape.length) {
- this.renderTape();
- }
-
- const cells = this.elements.tape.querySelectorAll(".cell");
- const headIndex = tape.getHeadIndex();
-
- cells.forEach((cell, index) => {
- cell.dataset.index = index.toString();
- const input = cell.querySelector("input");
- const value = tape.getCell(index);
- if (input.value !== value) {
- input.value = value;
- }
-
- if (index === headIndex) {
- cell.classList.add("active");
- this.maybeScrollIntoView(cell, headIndex, forceRender);
- } else {
- cell.classList.remove("active");
- }
- });
- }
-
- renderTape() {
- const fragment = document.createDocumentFragment();
- const tape = this.machine.tape;
-
- for (let i = 0; i < tape.length; i++) {
- fragment.appendChild(this.createCell(i, tape.getCell(i)));
- }
-
- this.elements.tape.innerHTML = "";
- this.elements.tape.appendChild(fragment);
- this.renderedTapeLength = tape.length;
- }
-
- createCell(index, value) {
- const cell = document.createElement("div");
- cell.classList.add("cell");
- cell.dataset.index = index.toString();
-
- const input = document.createElement("input");
- input.type = "text";
- input.maxLength = 1;
- input.value = value;
-
- cell.appendChild(input);
- return cell;
- }
-
- maybeScrollIntoView(cell, headIndex, forceImmediate = false) {
- if (forceImmediate) {
- cell.scrollIntoView({ behavior: "auto", block: "nearest", inline: "center" });
- this.lastScrollPosition = headIndex;
- return;
- }
-
- if (Math.abs(headIndex - this.lastScrollPosition) < SCROLL_THRESHOLD) {
- return;
- }
-
- cell.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" });
- this.lastScrollPosition = headIndex;
- }
-
- copyState() {
- if (!this.machine) {
- return;
- }
-
- const tapeState = this.machine.tape.getContents();
- const instructions = btoa(this.getEditorContent());
- const url = `${window.location.href.split("?")[0]}?start=${tapeState}&instructions=${instructions}`;
-
- navigator.clipboard.writeText(url)
- .then(() => alert("State copied to clipboard!"))
- .catch(() => alert("Failed to copy to clipboard"));
- }
-}