ref: 86a1bffad7063d1f51dfe44c2126bbdf8b7fe5c8
parent: 81d1e4e01d7001e6a0107792e6167136787295d6
author: zamfofex <zamfofex@twdb.moe>
date: Thu Sep 18 01:29:00 EDT 2025
add broken nonsense note: probably none of this works
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,8 @@
*
+!/nonsense
+!/nonsense/*
+
!/.gitignore
!/readme.md
!/index.html
--- /dev/null
+++ b/nonsense/math.js
@@ -1,0 +1,70 @@
+let count = 16
+export let one = toNumber("1")
+let pi180 = toNumber("57.29577951308232087679815481410517033240547246656432154916024386120284714832155263244096899585111094418622338163286489328144826460124831503606826786341194212252638809746726792630798870289311076793826144263826315820961046048702050644425965684112017191205773856628043128496262420337618793729762387079034031598071962408952204518620545992339631484190696622011512660969180151478763736692316410712677403851469016549959419251571198647943521066162438903520230675617779675711331568350620573131336015650134889801878870991777643918115931692001390297976826082932305533970261816604909295932820831549957980319559670071182520584664392317998584567191684399177541316529599530564062790449672487225343407247383306518587900882010719354855206048500684264734907560587438856753293217824660212423313273427212944530891667616714672023231374957844879817729067269084978013518866274395505591858069303343806736897172694639833885465199132675677482576493416923381113824332305320822419851645413959794725671400940417846961515605358215792046021354520190107416210293512926160978984400353223820447327628272090920918017114510890564653020820097912305909402354224041873558370874375538")
+
+export function toNumber(n)
+{
+ let string = toString(n)
+ if (string === "") return 0n
+ let match = string.match(/^(-|\+?)([0-9]*)\.?([0-9]*)(?:e(-|\+?)([0-9]+))?$/)
+ if (!match) return "NaN"
+ let [_full, x, a, b, y = "", e = ""] = match
+ if (a === "" && b === "") return "NaN"
+ let c = count + e * (y + "1")
+ if (c < 0) return 0n
+ return (BigInt(a + b.padEnd(c, "0").slice(0, c)) + BigInt(b[c] >= "5")) * BigInt(x + "1")
+}
+
+export function toString(n)
+{
+ if (typeof n !== "bigint") return String(n)
+ if (n < 0n) return "-" + toString(-n)
+ let x = String(n).padStart(count + 1, "0")
+ return (x.slice(0, -count) + "." + x.slice(-count).replace(/0+$/, "")).replace(/\.$/, "")
+}
+
+export function toFloat(n)
+{
+ return Number(toString(n))
+}
+
+function sign(n)
+{
+ if (n > 0n) return 1n
+ if (n < 0n) return -1n
+ return 0n
+}
+
+export function abs(n)
+{
+ return n * sign(n)
+}
+
+export function divide(n, q)
+{
+ return (n * one + q / 2n * sign(n)) / q
+}
+
+export function multiply(a, b)
+{
+ return divide(a * b, one ** 2n)
+}
+
+export function cos(n)
+{
+ n = abs(n) % (360n * one)
+ if (n > 180n * one) n -= 360n * one
+ let value = 1n
+ let one1 = 1n
+ let n2 = divide(n, pi180) ** 2n
+ for (let i = 32n ; i > 0n ; i -= 2n) {
+ one1 *= one ** 3n
+ value = one1 - divide(n2 * value, i * (i - 1n))
+ }
+ return divide(value, one1)
+}
+
+export function sin(n)
+{
+ return cos(n - 90n * one)
+}
--- /dev/null
+++ b/nonsense/matrix.js
@@ -1,0 +1,223 @@
+import {MakeBlock, flatten, before, remove} from "./model.js"
+import {NaturalSlot, compareName, Define} from "./core.js"
+import {compile} from "./onnx.js"
+import {toNumber} from "./compile.js"
+
+export function extendStage()
+{
+ return {blocks, transform}
+}
+
+async function transform(blocks, sprite)
+{
+ for (let block of blocks) {
+ if (block.type.shape === "reporter") continue
+ if (block.stack?.length) await transform([block.stack[0]], sprite)
+ let stack = []
+ while (block) {
+ if (flatten(block).every(block => isAllowed(blocks, block))) stack.push(block)
+ else await convertReplacing(blocks, stack.splice(0), sprite)
+ let index = block.parent?.stack?.indexOf(block)
+ if (index === undefined) break
+ block = block.parent.stack[index + 1]
+ }
+ await convertReplacing(blocks, stack, sprite)
+ }
+}
+
+function isAllowed(blocks, block, defines = new Set())
+{
+ if (block.type.shape === "slot") return true
+ if (conversions.has(block.type)) return true
+ if (block.type.category !== "custom") return
+ if (block.type.shape === "reporter") return true
+ let define = blocks.find(block1 => block1.type === Define.type && compareName(block1.names[0], block.names[0]))
+ if (defines.has(define)) return
+ defines.add(define)
+ return define.stack.every(block => isAllowed(blocks, block, defines))
+}
+
+async function convertReplacing(blocks0, blocks, sprite)
+{
+ if (blocks.length === 0) return
+ let ctx = new Context()
+ for (let block of blocks) convert(blocks0, block, ctx)
+ let run = await compile(ctx)
+ let Infer = MakeBlock({run: () => Object.assign(run(sprite), {sync: true}), name: () => ["infer"], async: true})
+ let infer = Infer(blocks[0])
+ if (blocks[0].parent) before(blocks[0], infer)
+ for (let block of blocks) remove(block)
+ let index = blocks0.indexOf(blocks[0])
+ if (index >= 0) blocks0[index] = infer
+}
+
+class Context {
+
+ nodes = []
+ initialisers = new Map()
+ inputVariables = new Set()
+ inputLists = new Set()
+ outputVariables = new Set()
+ outputLists = new Set()
+ #variables = new Map()
+ #constants = new Map()
+
+ #fresh()
+ {
+ return this.#bumpName("x")
+ }
+
+ getList(name, update = true)
+ {
+ if (update) this.inputLists.add(name)
+ return this.#getName(`lists/${name}`)
+ }
+
+ getVariable(name, update = true)
+ {
+ if (update) this.inputVariables.add(name)
+ return this.#getName(`vars/${name}`)
+ }
+
+ bumpList(name)
+ {
+ this.outputLists.add(name)
+ return this.#bumpName(`lists/${name}`)
+ }
+
+ bumpVariable(name)
+ {
+ this.outputVariables.add(name)
+ return this.#bumpName(`vars/${name}`)
+ }
+
+ make(opType, attribute, input, output)
+ {
+ if (!output) output = [this.#fresh()]
+ attribute = Object.entries(attribute).map(([name, i]) => ({name, i, type: 2}))
+ this.nodes.push({opType, attribute, input, output})
+ return output[0]
+ }
+
+ constant(...values)
+ {
+ let data = values.join(",")
+ let name0 = this.#constants.get(data)
+ if (name0) return name0
+ let name = this.#fresh()
+ this.initialisers.set(name, values)
+ return name
+ }
+
+ #bumpName(name)
+ {
+ let n = (this.#variables.get(name) ?? 0) + 1
+ this.#variables.set(name, n)
+ return name + "/" + n
+ }
+
+ #getName(name)
+ {
+ let n = this.#variables.get(name) ?? 0
+ return name + "/" + n
+ }
+}
+
+function convert(blocks, block, ctx)
+{
+ if (block.type.shape === "slot") {
+ return ctx.constant(toNumber(block.value))
+ }
+ if (block.type.category === "custom") {
+ for (let block of flattenCustom(blocks, block)) {
+ convert(blocks, block, ctx)
+ }
+ }
+
+ let inputs = block.inputs.map(block1 => convert(blocks, block1, ctx))
+ conversions.get(block.type)(ctx, ...inputs, block)
+}
+
+function flattenCustom(_blocks, block)
+{
+ return [block]
+}
+
+let Add = MakeBlock({references: ["list", "list", "list"], name: (out, a, b) => [getIcon(), a, "+", b, "into", out]})
+let Subtract = MakeBlock({references: ["list", "list", "list"], name: (out, a, b) => [getIcon(), a, "-", b, "into", out]})
+let Multiply = MakeBlock({references: ["list", "list", "list"], name: (out, a, b) => [getIcon(), a, "*", b, "into", out]})
+let Divide = MakeBlock({references: ["list", "list", "list"], name: (out, a, b) => [getIcon(), a, "/", b, "into", out]})
+let MatrixMultiply = MakeBlock({references: ["list", "list", "list"], slots: [NaturalSlot], name: (out, a, b, j) => [getIcon(), a, "\xD7", b, "with", j, "into", out]})
+
+let blocks = [Add, Subtract, Multiply, Divide, MatrixMultiply]
+
+let conversions = new Map([
+ [Add.type, (ctx, block) => binary(ctx, "Add", ctx.getList(block.names[0]), ctx.getList(block.names[1]),ctx.bumpList(block.names[2]))],
+ [Subtract.type, (ctx, block) => binary(ctx, "Sub", ctx.getList(block.names[0]), ctx.getList(block.names[1]),ctx.bumpList(block.names[2]))],
+ [Multiply.type, (ctx, block) => binary(ctx, "Mul", ctx.getList(block.names[0]), ctx.getList(block.names[1]),ctx.bumpList(block.names[2]))],
+ [Divide.type, (ctx, block) => binary(ctx, "Div", ctx.getList(block.names[0]), ctx.getList(block.names[1]),ctx.bumpList(block.names[2]))],
+ [MatrixMultiply.type, (ctx, j, block) =>
+ {
+ let zero = ctx.make("Cast", {to: 7}, [ctx.constant(0)])
+ let one = ctx.make("Cast", {to: 7}, [ctx.constant(1)])
+
+ let j0 = ctx.make("Cast", {to: 7}, [j])
+ let j1 = ctx.make("Max", {}, [j0, one])
+ let j2 = ctx.make("Sub", {}, [j1, one])
+
+ let shape1 = ctx.make("Shape", {}, [ctx.getList(block.names[0])])
+ let shape2 = ctx.make("Shape", {}, [ctx.getList(block.names[1])])
+
+ let a1 = ctx.make("Add", {}, [shape1, j2])
+ let a2 = ctx.make("Add", {}, [shape2, j2])
+
+ let b1 = ctx.make("Div", {}, [a1, j1])
+ let b2 = ctx.make("Div", {}, [a2, j1])
+
+ let c1 = ctx.make("Mul", {}, [b1, j1])
+ let c2 = ctx.make("Mul", {}, [b2, j1])
+
+ let d1 = ctx.make("Sub", {}, [c1, shape1])
+ let d2 = ctx.make("Sub", {}, [c2, shape2])
+
+ let padding1 = ctx.make("Concat", {axis: 0}, [zero, d1])
+ let padding2 = ctx.make("Concat", {axis: 0}, [zero, d2])
+
+ let vector1 = ctx.make("Pad", {}, [ctx.getList(block.names[0]), padding1])
+ let vector2 = ctx.make("Pad", {}, [ctx.getList(block.names[1]), padding2])
+
+ let mShape1 = ctx.make("Concat", {axis: 0}, [b1, j1])
+ let mShape2 = ctx.make("Concat", {axis: 0}, [j1, b2])
+
+ let matrix1 = ctx.make("Reshape", {allowzero: 1}, [vector1, mShape1])
+ let matrix2 = ctx.make("Reshape", {allowzero: 1}, [vector2, mShape2])
+
+ let result = ctx.make("MatMul", {}, [matrix1, matrix2])
+ let shape = ctx.make("Mul", {}, [b1, b2])
+
+ ctx.make("Reshape", {allowzero: 1}, [result, shape], [ctx.bumpList(block.names[2])])
+ }],
+])
+
+function binary(ctx, op, a, b, out)
+{
+ let zero = ctx.make("Cast", {to: 7}, [ctx.constant(0)])
+ let shape1 = ctx.make("Shape", {}, [a])
+ let shape2 = ctx.make("Shape", {}, [b])
+ let shape = ctx.make("Max", {}, [shape1, shape2])
+ let a1 = ctx.make("Pad", {}, [a, ctx.make("Concat", {axis: 0}, [zero, ctx.make("Sub", {}, [shape, shape1])])])
+ let b1 = ctx.make("Pad", {}, [b, ctx.make("Concat", {axis: 0}, [zero, ctx.make("Sub", {}, [shape, shape2])])])
+ ctx.make(op, {}, [a1, b1], [out])
+}
+
+let icon = `<svg viewbox="0 0 24 24" fill="#48E" stroke="#444"><path d="M19.46,8l0.79-1.75L22,5.46c0.39-0.18,0.39-0.73,0-0.91l-1.75-0.79L19.46,2c-0.18-0.39-0.73-0.39-0.91,0l-0.79,1.75 L16,4.54c-0.39,0.18-0.39,0.73,0,0.91l1.75,0.79L18.54,8C18.72,8.39,19.28,8.39,19.46,8z M11.5,9.5L9.91,6 C9.56,5.22,8.44,5.22,8.09,6L6.5,9.5L3,11.09c-0.78,0.36-0.78,1.47,0,1.82l3.5,1.59L8.09,18c0.36,0.78,1.47,0.78,1.82,0l1.59-3.5 l3.5-1.59c0.78-0.36,0.78-1.47,0-1.82L11.5,9.5z M18.54,16l-0.79,1.75L16,18.54c-0.39,0.18-0.39,0.73,0,0.91l1.75,0.79L18.54,22 c0.18,0.39,0.73,0.39,0.91,0l0.79-1.75L22,19.46c0.39-0.18,0.39-0.73,0-0.91l-1.75-0.79L19.46,16 C19.28,15.61,18.72,15.61,18.54,16z"/></svg>`
+
+function getIcon()
+{
+ let span = document.createElement("span")
+ span.insertAdjacentHTML("beforeend", icon)
+ let svg = span.querySelector("svg")
+ svg.style.setProperty("height", "1.5em")
+ svg.style.setProperty("vertical-align", "middle")
+ return span
+}
--- /dev/null
+++ b/nonsense/mini.sh
@@ -1,0 +1,42 @@
+#!/bin/sh -e
+
+# todo: make sure the regex in 'syntax.js' is transpiled correctly
+# todo: figure out how to make the 'dynamic-import-vars' plugin work (for the same directory)
+# todo: make version numbers fixed
+# todo: add support for adding dependencies like in 'hash.js'
+
+rm -rf node_modules package.json package-lock.json
+npm i @rollup/wasm-node \
+ @rollup/plugin-dynamic-import-vars \
+ @rollup/plugin-node-resolve \
+ @rollup/plugin-commonjs \
+ @rollup/plugin-terser \
+ @rollup/plugin-babel \
+ @babel/preset-env \
+ dialog-polyfill \
+ core-js@3
+rm -rf mini
+mkdir mini
+cp -r assets mini
+{
+ sed '/<script/d' index.html
+ echo '<body>'
+ echo '<script>'
+ {
+ echo 'import polyfill from "dialog-polyfill"'
+ echo 'import("./index.js")'
+ echo {
+ echo 'var createElement = document.createElement.bind(document)'
+ echo 'document.createElement = function (name) { var e = createElement(name) ; if (name === "dialog") polyfill.registerDialog(e) ; return e }'
+ echo 'if (typeof AudioContext === "undefined") window.AudioContext = window.webkitAudioContext'
+ echo }
+ } | npx @rollup/wasm-node --format=iife \
+ --inlineDynamicImports \
+ --plugin=babel='{babelHelpers: "bundled", targets: "firefox >= 68, chrome >= 70, safari >= 13", exclude: /\/core-js\//, presets: [["@babel/preset-env", {useBuiltIns: "usage", corejs: "3.42"}]]}' \
+ --plugin=node-resolve \
+ --plugin=commonjs \
+ --plugin=dynamic-import-vars \
+ --plugin=terser
+ echo '</script>'
+} > mini/index.html
+rm -rf node_modules package.json package-lock.json
--- /dev/null
+++ b/nonsense/onnx.js
@@ -1,0 +1,63 @@
+import {parse} from "protobufjs"
+import {InferenceSession, Tensor, env} from "onnxruntime"
+
+env.logLevel = "verbose"
+
+let response = await fetch("https://raw.githubusercontent.com/onnx/onnx/refs/tags/v1.18.0/onnx/onnx.proto3")
+let {root} = parse(await response.text())
+
+let ModelProto = root.lookupType("onnx.ModelProto")
+
+export async function compile(ctx)
+{
+ let message = {
+ irVersion: 10,
+ opsetImport: [{version: 22}],
+ producerName: "Scrax",
+ producer_version: "0",
+ domain: "org.scrax",
+ modelVersion: 0,
+ graph: toGraph(ctx),
+ }
+
+ let error = ModelProto.verify(message)
+ if (error) throw new Error(error)
+ let session = await InferenceSession.create(ModelProto.encode(message).finish(), {executionProviders: ["webnn", "webgpu", "webgl", "wasm"]})
+ return run
+
+ async function run({variables, lists})
+ {
+ let inputs = {}
+ for (let name of ctx.inputVariables) inputs[`vars/${name}/0`] = new Tensor(new Float32Array([variables.get(name).value]))
+ for (let name of ctx.inputLists) inputs[`lists/${name}/0`] = new Tensor(new Float32Array(lists.get(name)))
+
+ let outputs = await session.run(inputs)
+ for (let name of ctx.outputVariables) variables.get(name).value = outputs[ctx.getVariable(name, false).data[0]]
+ for (let name of ctx.outputLists) lists.get(name).splice(0, Infinity, ...outputs[ctx.getList(name, false)].data)
+ }
+}
+
+function toGraph(ctx)
+{
+ let n = 1
+
+ let input = []
+ for (let name of ctx.inputVariables) input.push({name: `vars/${name}/0`, type: {tensorType: {elemType: 1, shape: {dim: []}}}})
+ for (let name of ctx.inputLists) input.push({name: `lists/${name}/0`, type: {tensorType: {elemType: 1, shape: {dim: [{dim_param: `n${n++}`}]}}}})
+
+ let output = []
+ for (let name of ctx.outputVariables) output.push({name: ctx.getVariable(name, false), type: {tensorType: {elemType: 1, shape: {dim: []}}}})
+ for (let name of ctx.outputLists) output.push({name: ctx.getList(name, false), type: {tensorType: {elemType: 1, shape: {dim: [{dim_param: `n${n++}`}]}}}})
+
+ return {
+ name: "graph",
+ node: ctx.nodes,
+ input, output,
+ initializer: [...ctx.initialisers].map(([name, values]) => ({
+ name,
+ dims: [values.length],
+ dataType: 1,
+ floatData: values,
+ })),
+ }
+}
--- /dev/null
+++ b/nonsense/test.js
@@ -1,0 +1,89 @@
+let timeScale = 1
+
+await drag(query(".library .block", /move steps/), query(".program"), {x0: 8, x1: 64, y1: 64})
+await drag(query(".library .block", /forever/), query1(".program .block"), {x0: 8, x1: 0})
+await drag(query(".library .block", /when clicked/), query(".program .block", /forever/), {x0: 8, x1: 0, y1: 32})
+await drag(query(".library .block", /when clicked/), query(".program"), {x0: 8, x1: 64, y1: 64})
+await drag(query(".library .block", /set x to/), query(".program .block .line", /move steps/), {x0: 8, x1: 0, y1: 64})
+await drag(query(".library .block", /if/), query(".program .block", /move steps/), {x0: 8, x1: 0, y1: 64})
+await drag(query(".library .block", /stop/), query(".program .block", /when clicked/), {x0: 8, x1: 0, y1: 64})
+await write(query(".program .block .line", /stop/).querySelector("select"), "all")
+await write(query(".library .block", /wait seconds/).querySelector("input"), "5")
+await drag(query(".library .block", /wait seconds/), query(".program .block", /when clicked/), {x0: 8, x1: 0, y1: 64})
+await write(query(".program .block .line", /set x to/).querySelector("input"), "-400")
+await drag(query(".library .block", />/), query(".program .block .line", /if/).querySelector(".slot"))
+await write(query(".program .block .line", />/).querySelectorAll("input")[1], "400")
+await drag(query(".library .block", /x position/), query(".program .block .line", />/).querySelector("input"))
+document.querySelector(".start-button").click()
+
+function makePointerEvent(type, clientX, clientY, options = {})
+{
+ return new PointerEvent(type, {bubbles: true, clientX, clientY, pointerType: "mouse", buttons: 1, ...options})
+}
+
+function dispatchPointerEvent(type, clientX, clientY)
+{
+ let element = document.elementFromPoint(clientX, clientY) ?? document.body
+ element.dispatchEvent(makePointerEvent(type, clientX, clientY))
+}
+
+async function drag(element0, element1, {x0, y0, x1, y1, t = 1000, d = 500} = {})
+{
+ t /= timeScale
+ d /= timeScale
+
+ element1.scrollIntoView({block: "center", inline: "center", behavior: "instant"})
+ element0.scrollIntoView({block: "center", inline: "center", behavior: "instant"})
+
+ let rect0 = element0.getBoundingClientRect()
+ let rect1 = element1.getBoundingClientRect()
+
+ x0 = rect0.x + (x0 ?? rect0.width / 2)
+ y0 = rect0.y + (y0 ?? rect0.height / 2)
+ x1 = rect1.x + (x1 ?? rect1.width / 2)
+ y1 = rect1.y + (y1 ?? rect1.height / 2)
+
+ dispatchPointerEvent("pointerdown", x0, y0)
+
+ let t0 = performance.now()
+ for (;;) {
+ await new Promise(requestAnimationFrame)
+ let t1 = performance.now() - t0
+ if (t1 >= t) break
+ let s0 = t1 / t
+ let s = (1 - Math.cos(s0 * Math.PI)) / 2
+ let x = x0 + (x1 - x0) * s
+ let y = y0 + (y1 - y0) * s
+ dispatchPointerEvent("pointermove", x, y)
+ }
+
+ dispatchPointerEvent("pointermove", x1, y1)
+ await new Promise(requestAnimationFrame)
+ dispatchPointerEvent("pointerup", x1, y1)
+ await new Promise(resolve => setTimeout(resolve, d))
+}
+
+function query(selector, regex = /.?/, n = 0)
+{
+ let elements = []
+ for (let element of document.querySelectorAll(selector)) {
+ if (regex.test(element.innerText.normalize().replace(/\s+/u, " ").trim())) elements.push(element)
+ }
+ return elements.at(n)
+}
+
+function query1(selector, n = -1)
+{
+ return query(selector, undefined, n)
+}
+
+async function write(input, value, {d = 500} = {})
+{
+ d /= timeScale
+ input.scrollIntoView({block: "center", inline: "center", behavior: "instant"})
+ input.value = value
+ input.dispatchEvent(new InputEvent("input", {bubbles: true}))
+ input.dispatchEvent(new InputEvent("change", {bubbles: true}))
+ input.dispatchEvent(new InputEvent("blur"))
+ await new Promise(resolve => setTimeout(resolve, d))
+}
--
⑨