shithub: scrax

Download patch

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))
+}
--