integration work continues
This commit is contained in:
parent
173fdb913c
commit
4eceb62ce5
|
@ -11,7 +11,6 @@ crate-type = ["cdylib", "rlib"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chumsky = "0.10.1"
|
chumsky = "0.10.1"
|
||||||
imbl = "3.0.0"
|
imbl = "3.0.0"
|
||||||
ran = "2.0.1"
|
|
||||||
num-derive = "0.4.2"
|
num-derive = "0.4.2"
|
||||||
num-traits = "0.2.19"
|
num-traits = "0.2.19"
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
|
@ -19,5 +18,4 @@ wasm-bindgen = "0.2"
|
||||||
wasm-bindgen-futures = "0.4.50"
|
wasm-bindgen-futures = "0.4.50"
|
||||||
serde = {version = "1.0", features = ["derive"]}
|
serde = {version = "1.0", features = ["derive"]}
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tokio = {version = "1.45.1", features = ["macros", "rt-multi-thread"]}
|
console_error_panic_hook = "0.1.7"
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
&&& buffers: shared memory with Rust
|
||||||
|
box console = []
|
||||||
|
box input = ""
|
||||||
|
|
||||||
& the very base: know something's type
|
& the very base: know something's type
|
||||||
fn type {
|
fn type {
|
||||||
"Returns a keyword representing the type of the value passed in."
|
"Returns a keyword representing the type of the value passed in."
|
||||||
|
@ -408,8 +412,6 @@ fn to_number {
|
||||||
(num as :string) -> base :number (num)
|
(num as :string) -> base :number (num)
|
||||||
}
|
}
|
||||||
|
|
||||||
box console = []
|
|
||||||
|
|
||||||
fn print! {
|
fn print! {
|
||||||
"Sends a text representation of Ludus values to the console."
|
"Sends a text representation of Ludus values to the console."
|
||||||
(...args) -> {
|
(...args) -> {
|
||||||
|
@ -1269,6 +1271,9 @@ fn sleep! {
|
||||||
|
|
||||||
link!
|
link!
|
||||||
|
|
||||||
|
console
|
||||||
|
input
|
||||||
|
|
||||||
abs
|
abs
|
||||||
abs
|
abs
|
||||||
add
|
add
|
||||||
|
|
24
justfile
24
justfile
|
@ -1,11 +1,23 @@
|
||||||
wasm:
|
wasm: && clean-wasm-pack
|
||||||
|
# build with wasm-pack
|
||||||
wasm-pack build --target web
|
wasm-pack build --target web
|
||||||
rm pkg/.gitignore
|
|
||||||
cp pkg/rudus.js pkg/rudus.js.backup
|
wasm-dev: && clean-wasm-pack
|
||||||
echo 'import {io} from "../worker.js"' > rudus.js
|
wasm-pack build --dev --target web
|
||||||
cat rudus.js.backup | tail -n+2>> rudus.js
|
|
||||||
rm rudus.js.backup
|
clean-wasm-pack:
|
||||||
|
# delete cruft from wasm-pack
|
||||||
|
rm pkg/.gitignore pkg/package.json pkg/README.md
|
||||||
rm -rf pkg/snippets
|
rm -rf pkg/snippets
|
||||||
|
# fix imports of rudus.js
|
||||||
|
cp pkg/rudus.js pkg/rudus.js.backup
|
||||||
|
echo 'import { io } from "./worker.js"' > pkg/rudus.js
|
||||||
|
cat pkg/rudus.js.backup | tail -n+2>> pkg/rudus.js
|
||||||
|
rm pkg/rudus.js.backup
|
||||||
|
|
||||||
|
|
||||||
|
serve:
|
||||||
|
miniserve pkg && open http://localhost:8080/index.html
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@just --list
|
@just --list
|
||||||
|
|
0
pkg/.gitignore
vendored
0
pkg/.gitignore
vendored
|
@ -6,13 +6,7 @@
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="module">
|
<script src="./ludus.js" type="module"></script>
|
||||||
import {run} from "./ludus.js";
|
|
||||||
|
|
||||||
window.ludus = run;
|
|
||||||
|
|
||||||
console.log(run(":foobar"));
|
|
||||||
</script>
|
|
||||||
<p>
|
<p>
|
||||||
Open the console. All the action's in there.
|
Open the console. All the action's in there.
|
||||||
</p>
|
</p>
|
||||||
|
|
95
pkg/ludus.js
95
pkg/ludus.js
|
@ -1,18 +1,72 @@
|
||||||
import init, {ludus} from "./rudus.js";
|
if (window) window.ludus = {run, kill, flush_console, p5, svg, turtle_commands, result, input}
|
||||||
|
|
||||||
|
const worker = new Worker("worker.js", {type: "module"})
|
||||||
|
|
||||||
let res = null
|
let outbox = []
|
||||||
|
|
||||||
let code = null
|
worker.onmessage = async (e) => {
|
||||||
|
let msgs
|
||||||
export function run (source) {
|
try {
|
||||||
code = source
|
msgs = JSON.parse(e.data)
|
||||||
const output = ludus(source)
|
} catch {
|
||||||
res = JSON.parse(output)
|
console.log(e.data)
|
||||||
return res
|
throw Error("bad json from Ludus")
|
||||||
|
}
|
||||||
|
for (const msg of msgs) {
|
||||||
|
console.log("Main: message received from worker:", msg);
|
||||||
|
switch (msg.verb) {
|
||||||
|
case "complete": {
|
||||||
|
console.log("completed ludus run!")
|
||||||
|
console.log("with", msg.data)
|
||||||
|
res = msg.data
|
||||||
|
running = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "console": {
|
||||||
|
console.log("console msg from msg.data")
|
||||||
|
console.log(msg.data)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stdout () {
|
let res = null
|
||||||
|
let code = null
|
||||||
|
let running = false
|
||||||
|
let io_interval_id = null
|
||||||
|
|
||||||
|
const io_poller = () => {
|
||||||
|
if (io_interval_id && !running) {
|
||||||
|
clearInterval(io_interval_id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
worker.postMessage(outbox)
|
||||||
|
outbox = []
|
||||||
|
}
|
||||||
|
|
||||||
|
function poll_io () {
|
||||||
|
io_interval_id = setInterval(io_poller, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function run (source) {
|
||||||
|
if (running) "TODO: handle this? should not be running"
|
||||||
|
running = true
|
||||||
|
code = source
|
||||||
|
outbox.push({verb: "run", data: source})
|
||||||
|
poll_io()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function kill () {
|
||||||
|
running = false
|
||||||
|
outbox.push({verb: "kill"})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function input (text) {
|
||||||
|
outbox.push({verb: "input", data: text})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function flush_console () {
|
||||||
if (!res) return ""
|
if (!res) return ""
|
||||||
return res.io.stdout.data
|
return res.io.stdout.data
|
||||||
}
|
}
|
||||||
|
@ -375,25 +429,4 @@ export function p5 (commands) {
|
||||||
return p5_calls
|
return p5_calls
|
||||||
}
|
}
|
||||||
|
|
||||||
window.ludus = {run, console, p5, svg, stdout, turtle_commands, result}
|
|
||||||
|
|
||||||
await init()
|
|
||||||
|
|
||||||
const worker = new Worker("worker.js", {type: "module"})
|
|
||||||
|
|
||||||
let outbox = {}
|
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
worker.postMessage(outbox)
|
|
||||||
outbox = {}
|
|
||||||
})
|
|
||||||
|
|
||||||
worker.onmessage = async (msgs) => {
|
|
||||||
for (const msg of msgs) {
|
|
||||||
switch (msg[0]) {
|
|
||||||
case "stdout": {
|
|
||||||
stdout = msg[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
13
pkg/rudus.d.ts
vendored
13
pkg/rudus.d.ts
vendored
|
@ -1,16 +1,21 @@
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export function ludus(src: string): string;
|
export function ludus(src: string): Promise<string>;
|
||||||
|
|
||||||
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
||||||
|
|
||||||
export interface InitOutput {
|
export interface InitOutput {
|
||||||
readonly memory: WebAssembly.Memory;
|
readonly memory: WebAssembly.Memory;
|
||||||
readonly ludus: (a: number, b: number) => [number, number];
|
readonly ludus: (a: number, b: number) => any;
|
||||||
readonly __wbindgen_export_0: WebAssembly.Table;
|
readonly __wbindgen_exn_store: (a: number) => void;
|
||||||
|
readonly __externref_table_alloc: () => number;
|
||||||
|
readonly __wbindgen_export_2: WebAssembly.Table;
|
||||||
|
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
||||||
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
||||||
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
||||||
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
readonly __wbindgen_export_6: WebAssembly.Table;
|
||||||
|
readonly closure347_externref_shim: (a: number, b: number, c: any) => void;
|
||||||
|
readonly closure371_externref_shim: (a: number, b: number, c: any, d: any) => void;
|
||||||
readonly __wbindgen_start: () => void;
|
readonly __wbindgen_start: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
359
pkg/rudus.js
359
pkg/rudus.js
|
@ -1,6 +1,41 @@
|
||||||
|
import { io } from "./worker.js"
|
||||||
|
|
||||||
let wasm;
|
let wasm;
|
||||||
|
|
||||||
let WASM_VECTOR_LEN = 0;
|
function addToExternrefTable0(obj) {
|
||||||
|
const idx = wasm.__externref_table_alloc();
|
||||||
|
wasm.__wbindgen_export_2.set(idx, obj);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleError(f, args) {
|
||||||
|
try {
|
||||||
|
return f.apply(this, args);
|
||||||
|
} catch (e) {
|
||||||
|
const idx = addToExternrefTable0(e);
|
||||||
|
wasm.__wbindgen_exn_store(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function logError(f, args) {
|
||||||
|
try {
|
||||||
|
return f.apply(this, args);
|
||||||
|
} catch (e) {
|
||||||
|
let error = (function () {
|
||||||
|
try {
|
||||||
|
return e instanceof Error ? `${e.message}\n\nStack:\n${e.stack}` : e.toString();
|
||||||
|
} catch(_) {
|
||||||
|
return "<failed to stringify thrown value>";
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
console.error("wasm-bindgen: imported JS function that was not marked as `catch` threw an error:", error);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
|
||||||
|
|
||||||
|
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
|
||||||
|
|
||||||
let cachedUint8ArrayMemory0 = null;
|
let cachedUint8ArrayMemory0 = null;
|
||||||
|
|
||||||
|
@ -11,6 +46,13 @@ function getUint8ArrayMemory0() {
|
||||||
return cachedUint8ArrayMemory0;
|
return cachedUint8ArrayMemory0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStringFromWasm0(ptr, len) {
|
||||||
|
ptr = ptr >>> 0;
|
||||||
|
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
||||||
|
}
|
||||||
|
|
||||||
|
let WASM_VECTOR_LEN = 0;
|
||||||
|
|
||||||
const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );
|
const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );
|
||||||
|
|
||||||
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
||||||
|
@ -28,6 +70,8 @@ const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
||||||
|
|
||||||
function passStringToWasm0(arg, malloc, realloc) {
|
function passStringToWasm0(arg, malloc, realloc) {
|
||||||
|
|
||||||
|
if (typeof(arg) !== 'string') throw new Error(`expected a string argument, found ${typeof(arg)}`);
|
||||||
|
|
||||||
if (realloc === undefined) {
|
if (realloc === undefined) {
|
||||||
const buf = cachedTextEncoder.encode(arg);
|
const buf = cachedTextEncoder.encode(arg);
|
||||||
const ptr = malloc(buf.length, 1) >>> 0;
|
const ptr = malloc(buf.length, 1) >>> 0;
|
||||||
|
@ -56,7 +100,7 @@ function passStringToWasm0(arg, malloc, realloc) {
|
||||||
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
||||||
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
||||||
const ret = encodeString(arg, view);
|
const ret = encodeString(arg, view);
|
||||||
|
if (ret.read !== arg.length) throw new Error('failed to pass whole string');
|
||||||
offset += ret.written;
|
offset += ret.written;
|
||||||
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
||||||
}
|
}
|
||||||
|
@ -65,31 +109,144 @@ function passStringToWasm0(arg, malloc, realloc) {
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
|
let cachedDataViewMemory0 = null;
|
||||||
|
|
||||||
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
|
function getDataViewMemory0() {
|
||||||
|
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
|
||||||
|
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
|
||||||
|
}
|
||||||
|
return cachedDataViewMemory0;
|
||||||
|
}
|
||||||
|
|
||||||
function getStringFromWasm0(ptr, len) {
|
function isLikeNone(x) {
|
||||||
ptr = ptr >>> 0;
|
return x === undefined || x === null;
|
||||||
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
}
|
||||||
|
|
||||||
|
function _assertBoolean(n) {
|
||||||
|
if (typeof(n) !== 'boolean') {
|
||||||
|
throw new Error(`expected a boolean argument, found ${typeof(n)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined')
|
||||||
|
? { register: () => {}, unregister: () => {} }
|
||||||
|
: new FinalizationRegistry(state => {
|
||||||
|
wasm.__wbindgen_export_6.get(state.dtor)(state.a, state.b)
|
||||||
|
});
|
||||||
|
|
||||||
|
function makeMutClosure(arg0, arg1, dtor, f) {
|
||||||
|
const state = { a: arg0, b: arg1, cnt: 1, dtor };
|
||||||
|
const real = (...args) => {
|
||||||
|
// First up with a closure we increment the internal reference
|
||||||
|
// count. This ensures that the Rust closure environment won't
|
||||||
|
// be deallocated while we're invoking it.
|
||||||
|
state.cnt++;
|
||||||
|
const a = state.a;
|
||||||
|
state.a = 0;
|
||||||
|
try {
|
||||||
|
return f(a, state.b, ...args);
|
||||||
|
} finally {
|
||||||
|
if (--state.cnt === 0) {
|
||||||
|
wasm.__wbindgen_export_6.get(state.dtor)(a, state.b);
|
||||||
|
CLOSURE_DTORS.unregister(state);
|
||||||
|
} else {
|
||||||
|
state.a = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
real.original = state;
|
||||||
|
CLOSURE_DTORS.register(real, state, state);
|
||||||
|
return real;
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugString(val) {
|
||||||
|
// primitive types
|
||||||
|
const type = typeof val;
|
||||||
|
if (type == 'number' || type == 'boolean' || val == null) {
|
||||||
|
return `${val}`;
|
||||||
|
}
|
||||||
|
if (type == 'string') {
|
||||||
|
return `"${val}"`;
|
||||||
|
}
|
||||||
|
if (type == 'symbol') {
|
||||||
|
const description = val.description;
|
||||||
|
if (description == null) {
|
||||||
|
return 'Symbol';
|
||||||
|
} else {
|
||||||
|
return `Symbol(${description})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type == 'function') {
|
||||||
|
const name = val.name;
|
||||||
|
if (typeof name == 'string' && name.length > 0) {
|
||||||
|
return `Function(${name})`;
|
||||||
|
} else {
|
||||||
|
return 'Function';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// objects
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
const length = val.length;
|
||||||
|
let debug = '[';
|
||||||
|
if (length > 0) {
|
||||||
|
debug += debugString(val[0]);
|
||||||
|
}
|
||||||
|
for(let i = 1; i < length; i++) {
|
||||||
|
debug += ', ' + debugString(val[i]);
|
||||||
|
}
|
||||||
|
debug += ']';
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
// Test for built-in
|
||||||
|
const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
|
||||||
|
let className;
|
||||||
|
if (builtInMatches && builtInMatches.length > 1) {
|
||||||
|
className = builtInMatches[1];
|
||||||
|
} else {
|
||||||
|
// Failed to match the standard '[object ClassName]'
|
||||||
|
return toString.call(val);
|
||||||
|
}
|
||||||
|
if (className == 'Object') {
|
||||||
|
// we're a user defined class or Object
|
||||||
|
// JSON.stringify avoids problems with cycles, and is generally much
|
||||||
|
// easier than looping through ownProperties of `val`.
|
||||||
|
try {
|
||||||
|
return 'Object(' + JSON.stringify(val) + ')';
|
||||||
|
} catch (_) {
|
||||||
|
return 'Object';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// errors
|
||||||
|
if (val instanceof Error) {
|
||||||
|
return `${val.name}: ${val.message}\n${val.stack}`;
|
||||||
|
}
|
||||||
|
// TODO we could test for more things here, like `Set`s and `Map`s.
|
||||||
|
return className;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param {string} src
|
* @param {string} src
|
||||||
* @returns {string}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
export function ludus(src) {
|
export function ludus(src) {
|
||||||
let deferred2_0;
|
|
||||||
let deferred2_1;
|
|
||||||
try {
|
|
||||||
const ptr0 = passStringToWasm0(src, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
const ptr0 = passStringToWasm0(src, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
const len0 = WASM_VECTOR_LEN;
|
const len0 = WASM_VECTOR_LEN;
|
||||||
const ret = wasm.ludus(ptr0, len0);
|
const ret = wasm.ludus(ptr0, len0);
|
||||||
deferred2_0 = ret[0];
|
return ret;
|
||||||
deferred2_1 = ret[1];
|
|
||||||
return getStringFromWasm0(ret[0], ret[1]);
|
|
||||||
} finally {
|
|
||||||
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _assertNum(n) {
|
||||||
|
if (typeof(n) !== 'number') throw new Error(`expected a number argument, found ${typeof(n)}`);
|
||||||
|
}
|
||||||
|
function __wbg_adapter_22(arg0, arg1, arg2) {
|
||||||
|
_assertNum(arg0);
|
||||||
|
_assertNum(arg1);
|
||||||
|
wasm.closure347_externref_shim(arg0, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function __wbg_adapter_50(arg0, arg1, arg2, arg3) {
|
||||||
|
_assertNum(arg0);
|
||||||
|
_assertNum(arg1);
|
||||||
|
wasm.closure371_externref_shim(arg0, arg1, arg2, arg3);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function __wbg_load(module, imports) {
|
async function __wbg_load(module, imports) {
|
||||||
|
@ -126,8 +283,150 @@ async function __wbg_load(module, imports) {
|
||||||
function __wbg_get_imports() {
|
function __wbg_get_imports() {
|
||||||
const imports = {};
|
const imports = {};
|
||||||
imports.wbg = {};
|
imports.wbg = {};
|
||||||
|
imports.wbg.__wbg_call_672a4d21634d4a24 = function() { return handleError(function (arg0, arg1) {
|
||||||
|
const ret = arg0.call(arg1);
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_call_7cccdd69e0791ae2 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||||
|
const ret = arg0.call(arg1, arg2);
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function() { return logError(function (arg0, arg1) {
|
||||||
|
let deferred0_0;
|
||||||
|
let deferred0_1;
|
||||||
|
try {
|
||||||
|
deferred0_0 = arg0;
|
||||||
|
deferred0_1 = arg1;
|
||||||
|
console.error(getStringFromWasm0(arg0, arg1));
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
|
||||||
|
}
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_io_4b41f8089de924df = function() { return logError(function (arg0, arg1) {
|
||||||
|
let deferred0_0;
|
||||||
|
let deferred0_1;
|
||||||
|
try {
|
||||||
|
deferred0_0 = arg0;
|
||||||
|
deferred0_1 = arg1;
|
||||||
|
const ret = io(getStringFromWasm0(arg0, arg1));
|
||||||
|
return ret;
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
|
||||||
|
}
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_log_86d603e98cc11395 = function() { return logError(function (arg0, arg1) {
|
||||||
|
let deferred0_0;
|
||||||
|
let deferred0_1;
|
||||||
|
try {
|
||||||
|
deferred0_0 = arg0;
|
||||||
|
deferred0_1 = arg1;
|
||||||
|
console.log(getStringFromWasm0(arg0, arg1));
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
|
||||||
|
}
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_log_9e426f8e841e42d3 = function() { return logError(function (arg0, arg1) {
|
||||||
|
console.log(getStringFromWasm0(arg0, arg1));
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_new_23a2665fac83c611 = function() { return logError(function (arg0, arg1) {
|
||||||
|
try {
|
||||||
|
var state0 = {a: arg0, b: arg1};
|
||||||
|
var cb0 = (arg0, arg1) => {
|
||||||
|
const a = state0.a;
|
||||||
|
state0.a = 0;
|
||||||
|
try {
|
||||||
|
return __wbg_adapter_50(a, state0.b, arg0, arg1);
|
||||||
|
} finally {
|
||||||
|
state0.a = a;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const ret = new Promise(cb0);
|
||||||
|
return ret;
|
||||||
|
} finally {
|
||||||
|
state0.a = state0.b = 0;
|
||||||
|
}
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_new_8a6f238a6ece86ea = function() { return logError(function () {
|
||||||
|
const ret = new Error();
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_newnoargs_105ed471475aaf50 = function() { return logError(function (arg0, arg1) {
|
||||||
|
const ret = new Function(getStringFromWasm0(arg0, arg1));
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_now_8dddb61fa4928554 = function() { return logError(function () {
|
||||||
|
const ret = Date.now();
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_queueMicrotask_97d92b4fcc8a61c5 = function() { return logError(function (arg0) {
|
||||||
|
queueMicrotask(arg0);
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_queueMicrotask_d3219def82552485 = function() { return logError(function (arg0) {
|
||||||
|
const ret = arg0.queueMicrotask;
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_random_57c118f142535bb6 = function() { return logError(function () {
|
||||||
|
const ret = Math.random();
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_resolve_4851785c9c5f573d = function() { return logError(function (arg0) {
|
||||||
|
const ret = Promise.resolve(arg0);
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_stack_0ed75d68575b0f3c = function() { return logError(function (arg0, arg1) {
|
||||||
|
const ret = arg1.stack;
|
||||||
|
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
||||||
|
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_static_accessor_GLOBAL_88a902d13a557d07 = function() { return logError(function () {
|
||||||
|
const ret = typeof global === 'undefined' ? null : global;
|
||||||
|
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_static_accessor_GLOBAL_THIS_56578be7e9f832b0 = function() { return logError(function () {
|
||||||
|
const ret = typeof globalThis === 'undefined' ? null : globalThis;
|
||||||
|
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_static_accessor_SELF_37c5d418e4bf5819 = function() { return logError(function () {
|
||||||
|
const ret = typeof self === 'undefined' ? null : self;
|
||||||
|
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_static_accessor_WINDOW_5de37043a91a9c40 = function() { return logError(function () {
|
||||||
|
const ret = typeof window === 'undefined' ? null : window;
|
||||||
|
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_then_44b73946d2fb3e7d = function() { return logError(function (arg0, arg1) {
|
||||||
|
const ret = arg0.then(arg1);
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_then_48b406749878a531 = function() { return logError(function (arg0, arg1, arg2) {
|
||||||
|
const ret = arg0.then(arg1, arg2);
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
||||||
|
const obj = arg0.original;
|
||||||
|
if (obj.cnt-- == 1) {
|
||||||
|
obj.a = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const ret = false;
|
||||||
|
_assertBoolean(ret);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_closure_wrapper7663 = function() { return logError(function (arg0, arg1, arg2) {
|
||||||
|
const ret = makeMutClosure(arg0, arg1, 348, __wbg_adapter_22);
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
|
||||||
|
const ret = debugString(arg1);
|
||||||
|
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
||||||
|
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
||||||
|
};
|
||||||
imports.wbg.__wbindgen_init_externref_table = function() {
|
imports.wbg.__wbindgen_init_externref_table = function() {
|
||||||
const table = wasm.__wbindgen_export_0;
|
const table = wasm.__wbindgen_export_2;
|
||||||
const offset = table.grow(4);
|
const offset = table.grow(4);
|
||||||
table.set(0, undefined);
|
table.set(0, undefined);
|
||||||
table.set(offset + 0, undefined);
|
table.set(offset + 0, undefined);
|
||||||
|
@ -136,6 +435,31 @@ function __wbg_get_imports() {
|
||||||
table.set(offset + 3, false);
|
table.set(offset + 3, false);
|
||||||
;
|
;
|
||||||
};
|
};
|
||||||
|
imports.wbg.__wbindgen_is_function = function(arg0) {
|
||||||
|
const ret = typeof(arg0) === 'function';
|
||||||
|
_assertBoolean(ret);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_is_undefined = function(arg0) {
|
||||||
|
const ret = arg0 === undefined;
|
||||||
|
_assertBoolean(ret);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
|
||||||
|
const obj = arg1;
|
||||||
|
const ret = typeof(obj) === 'string' ? obj : undefined;
|
||||||
|
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
var len1 = WASM_VECTOR_LEN;
|
||||||
|
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
||||||
|
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
|
||||||
|
const ret = getStringFromWasm0(arg0, arg1);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
|
||||||
|
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||||
|
};
|
||||||
|
|
||||||
return imports;
|
return imports;
|
||||||
}
|
}
|
||||||
|
@ -147,6 +471,7 @@ function __wbg_init_memory(imports, memory) {
|
||||||
function __wbg_finalize_init(instance, module) {
|
function __wbg_finalize_init(instance, module) {
|
||||||
wasm = instance.exports;
|
wasm = instance.exports;
|
||||||
__wbg_init.__wbindgen_wasm_module = module;
|
__wbg_init.__wbindgen_wasm_module = module;
|
||||||
|
cachedDataViewMemory0 = null;
|
||||||
cachedUint8ArrayMemory0 = null;
|
cachedUint8ArrayMemory0 = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
11
pkg/rudus_bg.wasm.d.ts
vendored
11
pkg/rudus_bg.wasm.d.ts
vendored
|
@ -1,9 +1,14 @@
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export const memory: WebAssembly.Memory;
|
export const memory: WebAssembly.Memory;
|
||||||
export const ludus: (a: number, b: number) => [number, number];
|
export const ludus: (a: number, b: number) => any;
|
||||||
export const __wbindgen_export_0: WebAssembly.Table;
|
export const __wbindgen_exn_store: (a: number) => void;
|
||||||
|
export const __externref_table_alloc: () => number;
|
||||||
|
export const __wbindgen_export_2: WebAssembly.Table;
|
||||||
|
export const __wbindgen_free: (a: number, b: number, c: number) => void;
|
||||||
export const __wbindgen_malloc: (a: number, b: number) => number;
|
export const __wbindgen_malloc: (a: number, b: number) => number;
|
||||||
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
||||||
export const __wbindgen_free: (a: number, b: number, c: number) => void;
|
export const __wbindgen_export_6: WebAssembly.Table;
|
||||||
|
export const closure347_externref_shim: (a: number, b: number, c: any) => void;
|
||||||
|
export const closure371_externref_shim: (a: number, b: number, c: any, d: any) => void;
|
||||||
export const __wbindgen_start: () => void;
|
export const __wbindgen_start: () => void;
|
||||||
|
|
|
@ -1,15 +1,40 @@
|
||||||
import init from "./rudus.js";
|
import init, {ludus} from "./rudus.js";
|
||||||
|
|
||||||
console.log("Worker: starting Ludus VM.")
|
console.log("Worker: starting Ludus VM.")
|
||||||
|
|
||||||
export function io (out) {
|
export function io (out) {
|
||||||
if (Object.keys(out).length > 0) postMessage(out)
|
if (out.length > 0) postMessage(out)
|
||||||
return new Promise((resolve, _) => {
|
return new Promise((resolve, _) => {
|
||||||
onmessage = (e) => resolve(e.data)
|
onmessage = (e) => {
|
||||||
|
console.log(e.data)
|
||||||
|
resolve(JSON.stringify(e.data))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let loaded_wasm = false
|
||||||
|
|
||||||
|
async function run(e) {
|
||||||
|
if (!loaded_wasm) {
|
||||||
await init()
|
await init()
|
||||||
|
loaded_wasm = true
|
||||||
|
}
|
||||||
|
let msgs = e.data
|
||||||
|
for (const msg of msgs) {
|
||||||
|
if (msg.verb === "run" && typeof msg.data === 'string') {
|
||||||
|
console.log("running ludus!")
|
||||||
|
onmessage = () => {}
|
||||||
|
let result = await ludus(msg.data)
|
||||||
|
console.log(result)
|
||||||
|
onmessage = run
|
||||||
|
} else {
|
||||||
|
console.log("Did not get valid startup message. Instead got:")
|
||||||
|
console.log(e.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onmessage = run
|
||||||
|
|
||||||
console.log("Worker: Ludus VM is running.")
|
console.log("Worker: Ludus VM is running.")
|
||||||
|
|
||||||
|
|
32
sandbox.ld
32
sandbox.ld
|
@ -1,31 +1 @@
|
||||||
fn agent (val) -> receive {
|
:foobar
|
||||||
(:set, new) -> agent (new)
|
|
||||||
(:get, pid) -> {
|
|
||||||
send (pid, (:response, val))
|
|
||||||
agent (val)
|
|
||||||
}
|
|
||||||
(:update, f) -> agent (f (val))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn agent/set (pid, val) -> {
|
|
||||||
send (pid, (:set, val))
|
|
||||||
val
|
|
||||||
}
|
|
||||||
|
|
||||||
fn agent/get (pid) -> {
|
|
||||||
send (pid, (:get, self ()))
|
|
||||||
receive {
|
|
||||||
(:response, val) -> val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn agent/update (pid, f) -> {
|
|
||||||
send (pid, (:update, f))
|
|
||||||
agent/get (pid)
|
|
||||||
}
|
|
||||||
|
|
||||||
let myagent = spawn! (fn () -> agent (42))
|
|
||||||
|
|
||||||
print! ("incrementing agent value to", agent/update (myagent, inc))
|
|
||||||
|
|
||||||
:done!
|
|
||||||
|
|
17
src/base.rs
17
src/base.rs
|
@ -1,7 +1,7 @@
|
||||||
use crate::value::*;
|
use crate::value::*;
|
||||||
use imbl::*;
|
use imbl::*;
|
||||||
use ran::ran_f64;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum BaseFn {
|
pub enum BaseFn {
|
||||||
|
@ -481,8 +481,14 @@ pub fn floor(x: &Value) -> Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn random() -> Value {
|
#[wasm_bindgen]
|
||||||
Value::Number(ran_f64())
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_namespace = Math)]
|
||||||
|
fn random() -> f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_random() -> Value {
|
||||||
|
Value::Number(random())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn round(x: &Value) -> Value {
|
pub fn round(x: &Value) -> Value {
|
||||||
|
@ -610,7 +616,10 @@ pub fn make_base() -> Value {
|
||||||
("pi", Value::Number(std::f64::consts::PI)),
|
("pi", Value::Number(std::f64::consts::PI)),
|
||||||
("print!", Value::BaseFn(BaseFn::Unary("print!", print))),
|
("print!", Value::BaseFn(BaseFn::Unary("print!", print))),
|
||||||
("process", Value::Process),
|
("process", Value::Process),
|
||||||
("random", Value::BaseFn(BaseFn::Nullary("random", random))),
|
(
|
||||||
|
"random",
|
||||||
|
Value::BaseFn(BaseFn::Nullary("random", base_random)),
|
||||||
|
),
|
||||||
("range", Value::BaseFn(BaseFn::Binary("range", range))),
|
("range", Value::BaseFn(BaseFn::Binary("range", range))),
|
||||||
("rest", Value::BaseFn(BaseFn::Unary("rest", rest))),
|
("rest", Value::BaseFn(BaseFn::Unary("rest", rest))),
|
||||||
("round", Value::BaseFn(BaseFn::Unary("round", round))),
|
("round", Value::BaseFn(BaseFn::Unary("round", round))),
|
||||||
|
|
33
src/io.rs
33
src/io.rs
|
@ -11,6 +11,12 @@ extern "C" {
|
||||||
async fn io (output: String) -> JsValue;
|
async fn io (output: String) -> JsValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
|
fn log(s: String);
|
||||||
|
}
|
||||||
|
|
||||||
type Lines = Value; // expect a list of values
|
type Lines = Value; // expect a list of values
|
||||||
type Commands = Value; // expect a list of values
|
type Commands = Value; // expect a list of values
|
||||||
type Url = Value; // expect a string representing a URL
|
type Url = Value; // expect a string representing a URL
|
||||||
|
@ -25,7 +31,7 @@ pub enum MsgOut {
|
||||||
Console(Lines),
|
Console(Lines),
|
||||||
Commands(Commands),
|
Commands(Commands),
|
||||||
SlurpRequest(Url),
|
SlurpRequest(Url),
|
||||||
Complete(Result<Value, Panic>),
|
Complete(FinalValue),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for MsgOut {
|
impl std::fmt::Display for MsgOut {
|
||||||
|
@ -38,7 +44,10 @@ impl MsgOut {
|
||||||
pub fn to_json(&self) -> String {
|
pub fn to_json(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
MsgOut::Complete(value) => match value {
|
MsgOut::Complete(value) => match value {
|
||||||
Ok(value) => make_json_payload("complete", value.to_json().unwrap()),
|
Ok(value) => {
|
||||||
|
log(format!("value is: {}", value.show()));
|
||||||
|
make_json_payload("complete", serde_json::to_string(&value.show()).unwrap())
|
||||||
|
},
|
||||||
Err(_) => make_json_payload("complete", "\"null\"".to_string())
|
Err(_) => make_json_payload("complete", "\"null\"".to_string())
|
||||||
},
|
},
|
||||||
MsgOut::Commands(commands) => {
|
MsgOut::Commands(commands) => {
|
||||||
|
@ -55,7 +64,7 @@ impl MsgOut {
|
||||||
}
|
}
|
||||||
MsgOut::Console(lines) => {
|
MsgOut::Console(lines) => {
|
||||||
let lines = lines.as_list();
|
let lines = lines.as_list();
|
||||||
let json_lines = lines.iter().map(|line| line.stringify()).collect::<Vec<_>>().join("\n");
|
let json_lines = lines.iter().map(|line| line.stringify()).collect::<Vec<_>>().join("\\n");
|
||||||
let json_lines = format!("\"{json_lines}\"");
|
let json_lines = format!("\"{json_lines}\"");
|
||||||
make_json_payload("console", json_lines)
|
make_json_payload("console", json_lines)
|
||||||
}
|
}
|
||||||
|
@ -72,6 +81,16 @@ pub enum MsgIn {
|
||||||
Keyboard(Vec<String>),
|
Keyboard(Vec<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for MsgIn {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
MsgIn::Input(str) => write!(f, "input: {str}"),
|
||||||
|
MsgIn::Kill => write!(f, "kill"),
|
||||||
|
_ => todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MsgIn {
|
impl MsgIn {
|
||||||
pub fn to_value(self) -> Value {
|
pub fn to_value(self) -> Value {
|
||||||
match self {
|
match self {
|
||||||
|
@ -99,8 +118,14 @@ pub async fn do_io (msgs: Vec<MsgOut>) -> Vec<MsgIn> {
|
||||||
let outbox = format!("[{}]", msgs.iter().map(|msg| msg.to_json()).collect::<Vec<_>>().join(","));
|
let outbox = format!("[{}]", msgs.iter().map(|msg| msg.to_json()).collect::<Vec<_>>().join(","));
|
||||||
let inbox = io (outbox).await;
|
let inbox = io (outbox).await;
|
||||||
let inbox = inbox.as_string().expect("response should be a string");
|
let inbox = inbox.as_string().expect("response should be a string");
|
||||||
|
log(format!("response is: {inbox}"));
|
||||||
let inbox: Vec<MsgIn> = serde_json::from_str(inbox.as_str()).expect("response from js should be valid");
|
let inbox: Vec<MsgIn> = serde_json::from_str(inbox.as_str()).expect("response from js should be valid");
|
||||||
|
if !inbox.is_empty() {
|
||||||
|
log("got messages in ludus!".to_string());
|
||||||
|
for msg in inbox.iter() {
|
||||||
|
log(format!("{}", msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
inbox
|
inbox
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
41
src/lib.rs
41
src/lib.rs
|
@ -1,6 +1,8 @@
|
||||||
use chumsky::{input::Stream, prelude::*};
|
use chumsky::{input::Stream, prelude::*};
|
||||||
use imbl::HashMap;
|
use imbl::HashMap;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
const DEBUG_SCRIPT_COMPILE: bool = false;
|
const DEBUG_SCRIPT_COMPILE: bool = false;
|
||||||
const DEBUG_SCRIPT_RUN: bool = false;
|
const DEBUG_SCRIPT_RUN: bool = false;
|
||||||
|
@ -15,7 +17,7 @@ use crate::ast::Ast;
|
||||||
mod base;
|
mod base;
|
||||||
|
|
||||||
mod world;
|
mod world;
|
||||||
use crate::world::World;
|
use crate::world::{World, Zoo};
|
||||||
|
|
||||||
mod spans;
|
mod spans;
|
||||||
use crate::spans::Spanned;
|
use crate::spans::Spanned;
|
||||||
|
@ -42,10 +44,11 @@ mod value;
|
||||||
use value::Value;
|
use value::Value;
|
||||||
|
|
||||||
mod vm;
|
mod vm;
|
||||||
|
use vm::Creature;
|
||||||
|
|
||||||
const PRELUDE: &str = include_str!("../assets/test_prelude.ld");
|
const PRELUDE: &str = include_str!("../assets/test_prelude.ld");
|
||||||
|
|
||||||
async fn prelude() -> HashMap<&'static str, Value> {
|
fn prelude() -> HashMap<&'static str, Value> {
|
||||||
let tokens = lexer().parse(PRELUDE).into_output_errors().0.unwrap();
|
let tokens = lexer().parse(PRELUDE).into_output_errors().0.unwrap();
|
||||||
let (parsed, parse_errors) = parser()
|
let (parsed, parse_errors) = parser()
|
||||||
.parse(Stream::from_iter(tokens).map((0..PRELUDE.len()).into(), |(t, s)| (t, s)))
|
.parse(Stream::from_iter(tokens).map((0..PRELUDE.len()).into(), |(t, s)| (t, s)))
|
||||||
|
@ -71,7 +74,7 @@ async fn prelude() -> HashMap<&'static str, Value> {
|
||||||
if !validator.errors.is_empty() {
|
if !validator.errors.is_empty() {
|
||||||
println!("VALIDATION ERRORS IN PRLUDE:");
|
println!("VALIDATION ERRORS IN PRLUDE:");
|
||||||
report_invalidation(validator.errors);
|
report_invalidation(validator.errors);
|
||||||
panic!();
|
panic!("validator errors in prelude");
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parsed));
|
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parsed));
|
||||||
|
@ -88,25 +91,37 @@ async fn prelude() -> HashMap<&'static str, Value> {
|
||||||
compiler.compile();
|
compiler.compile();
|
||||||
|
|
||||||
let chunk = compiler.chunk;
|
let chunk = compiler.chunk;
|
||||||
let mut world = World::new(chunk, DEBUG_PRELUDE_RUN);
|
log("compiled prelude");
|
||||||
let stub_console = Value::r#box(Value::new_list());
|
let stub_zoo = Rc::new(RefCell::new(Zoo::new()));
|
||||||
world.run(stub_console).await;
|
let mut prld_sync = Creature::new(chunk, stub_zoo, DEBUG_PRELUDE_RUN);
|
||||||
let prelude = world.result.unwrap().unwrap();
|
prld_sync.interpret();
|
||||||
|
log("run prelude synchronously");
|
||||||
|
let prelude = prld_sync.result.unwrap().unwrap();
|
||||||
match prelude {
|
match prelude {
|
||||||
Value::Dict(hashmap) => *hashmap,
|
Value::Dict(hashmap) => *hashmap,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
|
fn log(s: &str);
|
||||||
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn ludus(src: String) -> String {
|
pub async fn ludus(src: String) -> String {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
log("successfully entered ludus fn in Rust");
|
||||||
let src = src.to_string().leak();
|
let src = src.to_string().leak();
|
||||||
|
log(src);
|
||||||
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
|
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
|
||||||
if !lex_errs.is_empty() {
|
if !lex_errs.is_empty() {
|
||||||
return format!("{:?}", lex_errs);
|
return format!("{:?}", lex_errs);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tokens = tokens.unwrap();
|
let tokens = tokens.unwrap();
|
||||||
|
log("successfully tokenized source");
|
||||||
|
|
||||||
let (parse_result, parse_errors) = parser()
|
let (parse_result, parse_errors) = parser()
|
||||||
.parse(Stream::from_iter(tokens).map((0..src.len()).into(), |(t, s)| (t, s)))
|
.parse(Stream::from_iter(tokens).map((0..src.len()).into(), |(t, s)| (t, s)))
|
||||||
|
@ -119,8 +134,10 @@ pub async fn ludus(src: String) -> String {
|
||||||
// This simplifies lifetimes, and
|
// This simplifies lifetimes, and
|
||||||
// in any event, the AST should live forever
|
// in any event, the AST should live forever
|
||||||
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap()));
|
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap()));
|
||||||
|
log("successfully parsed source");
|
||||||
|
|
||||||
let prelude = prelude().await;
|
let prelude = prelude();
|
||||||
|
log("successfully loaded prelude");
|
||||||
let postlude = prelude.clone();
|
let postlude = prelude.clone();
|
||||||
// let prelude = imbl::HashMap::new();
|
// let prelude = imbl::HashMap::new();
|
||||||
|
|
||||||
|
@ -146,6 +163,7 @@ pub async fn ludus(src: String) -> String {
|
||||||
// compiler.bind("base");
|
// compiler.bind("base");
|
||||||
|
|
||||||
compiler.compile();
|
compiler.compile();
|
||||||
|
log("successfully compiled source");
|
||||||
if DEBUG_SCRIPT_COMPILE {
|
if DEBUG_SCRIPT_COMPILE {
|
||||||
println!("=== source code ===");
|
println!("=== source code ===");
|
||||||
println!("{src}");
|
println!("{src}");
|
||||||
|
@ -159,13 +177,16 @@ pub async fn ludus(src: String) -> String {
|
||||||
|
|
||||||
let vm_chunk = compiler.chunk;
|
let vm_chunk = compiler.chunk;
|
||||||
|
|
||||||
let mut world = World::new(vm_chunk, DEBUG_SCRIPT_RUN);
|
let mut world = World::new(vm_chunk, prelude.clone(), DEBUG_SCRIPT_RUN);
|
||||||
let console = prelude
|
let console = prelude
|
||||||
.get("console")
|
.get("console")
|
||||||
.expect("prelude must have a console")
|
.expect("prelude must have a console")
|
||||||
.clone();
|
.clone();
|
||||||
world.run(console).await;
|
log("loaded world and console");
|
||||||
|
world.run().await;
|
||||||
let result = world.result.clone().unwrap();
|
let result = world.result.clone().unwrap();
|
||||||
|
log("ran script");
|
||||||
|
log(format!("{:?}", result).as_str());
|
||||||
|
|
||||||
let console = postlude.get("console").unwrap();
|
let console = postlude.get("console").unwrap();
|
||||||
let Value::Box(console) = console else {
|
let Value::Box(console) = console else {
|
||||||
|
|
|
@ -2,10 +2,9 @@ use rudus::ludus;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
#[tokio::main]
|
pub fn main() {
|
||||||
pub async fn main() {
|
|
||||||
env::set_var("RUST_BACKTRACE", "1");
|
env::set_var("RUST_BACKTRACE", "1");
|
||||||
let src = fs::read_to_string("sandbox.ld").unwrap();
|
let src = fs::read_to_string("sandbox.ld").unwrap();
|
||||||
let json = ludus(src).await;
|
let json = ludus(src);
|
||||||
println!("{json}");
|
// println!("{json}");
|
||||||
}
|
}
|
||||||
|
|
|
@ -259,7 +259,9 @@ impl Value {
|
||||||
pub fn to_json(&self) -> Option<String> {
|
pub fn to_json(&self) -> Option<String> {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
match self {
|
match self {
|
||||||
True | False | String(..) | Interned(..) | Number(..) => Some(self.show()),
|
True | False | Number(..) => Some(self.show()),
|
||||||
|
String(string) => Some(serde_json::to_string(string.as_ref()).unwrap()),
|
||||||
|
Interned(str) => Some(serde_json::to_string(str).unwrap()),
|
||||||
Keyword(str) => Some(format!("\"{str}\"")),
|
Keyword(str) => Some(format!("\"{str}\"")),
|
||||||
List(members) => {
|
List(members) => {
|
||||||
let mut joined = "".to_string();
|
let mut joined = "".to_string();
|
||||||
|
|
|
@ -339,7 +339,7 @@ impl Creature {
|
||||||
let Value::Number(ms) = args[1] else {
|
let Value::Number(ms) = args[1] else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
self.zoo.as_ref().borrow_mut().sleep(self.pid, ms as usize);
|
self.zoo.as_ref().borrow_mut().sleep(self.pid, ms);
|
||||||
self.r#yield = true;
|
self.r#yield = true;
|
||||||
self.push(Value::Keyword("ok"));
|
self.push(Value::Keyword("ok"));
|
||||||
}
|
}
|
||||||
|
|
259
src/world.rs
259
src/world.rs
|
@ -2,14 +2,26 @@ use crate::chunk::Chunk;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::vm::{Creature, Panic};
|
use crate::vm::{Creature, Panic};
|
||||||
use crate::io::{MsgOut, MsgIn, do_io};
|
use crate::io::{MsgOut, MsgIn, do_io};
|
||||||
use ran::ran_u8;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::mem::swap;
|
use std::mem::swap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::{Duration, Instant};
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
const ANIMALS: [&str; 24] = [
|
// Grab some JS stuff
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
|
fn log(s: &str);
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = Math)]
|
||||||
|
fn random() -> f64;
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = Date)]
|
||||||
|
fn now() -> f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ANIMALS: [&str; 32] = [
|
||||||
"tortoise",
|
"tortoise",
|
||||||
"hare",
|
"hare",
|
||||||
"squirrel",
|
"squirrel",
|
||||||
|
@ -31,9 +43,17 @@ const ANIMALS: [&str; 24] = [
|
||||||
"zebra",
|
"zebra",
|
||||||
"hyena",
|
"hyena",
|
||||||
"giraffe",
|
"giraffe",
|
||||||
"leopard",
|
|
||||||
"lion",
|
|
||||||
"hippopotamus",
|
"hippopotamus",
|
||||||
|
"capybara",
|
||||||
|
"python",
|
||||||
|
"gopher",
|
||||||
|
"crab",
|
||||||
|
"trout",
|
||||||
|
"osprey",
|
||||||
|
"lemur",
|
||||||
|
"wobbegong",
|
||||||
|
"walrus",
|
||||||
|
"opossum",
|
||||||
];
|
];
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -70,7 +90,7 @@ pub struct Zoo {
|
||||||
ids: HashMap<&'static str, usize>,
|
ids: HashMap<&'static str, usize>,
|
||||||
dead: HashSet<&'static str>,
|
dead: HashSet<&'static str>,
|
||||||
kill_list: Vec<&'static str>,
|
kill_list: Vec<&'static str>,
|
||||||
sleeping: HashMap<&'static str, (Instant, Duration)>,
|
sleeping: HashMap<&'static str, f64>,
|
||||||
active_idx: usize,
|
active_idx: usize,
|
||||||
active_id: &'static str,
|
active_id: &'static str,
|
||||||
}
|
}
|
||||||
|
@ -90,20 +110,27 @@ impl Zoo {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_id(&self) -> String {
|
fn random_id(&self) -> String {
|
||||||
let rand = ran_u8() as usize % 24;
|
log("generating random id");
|
||||||
|
let rand_idx = (random() * 32.0) as usize;
|
||||||
|
log("random number!");
|
||||||
let idx = self.procs.len();
|
let idx = self.procs.len();
|
||||||
format!("{}_{idx}", ANIMALS[rand])
|
log("procs len");
|
||||||
|
format!("{}_{idx}", ANIMALS[rand_idx])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_id(&self) -> &'static str {
|
fn new_id(&self) -> &'static str {
|
||||||
|
log("creating new id");
|
||||||
let mut new = self.random_id();
|
let mut new = self.random_id();
|
||||||
|
log("got new ramdom id");
|
||||||
while self.dead.iter().any(|old| *old == new) {
|
while self.dead.iter().any(|old| *old == new) {
|
||||||
new = self.random_id();
|
new = self.random_id();
|
||||||
}
|
}
|
||||||
|
log(format!("got new id: {}", new).as_str());
|
||||||
new.leak()
|
new.leak()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put(&mut self, mut proc: Creature) -> &'static str {
|
pub fn put(&mut self, mut proc: Creature) -> &'static str {
|
||||||
|
log("putting creature");
|
||||||
if self.empty.is_empty() {
|
if self.empty.is_empty() {
|
||||||
let id = self.new_id();
|
let id = self.new_id();
|
||||||
let idx = self.procs.len();
|
let idx = self.procs.len();
|
||||||
|
@ -113,7 +140,7 @@ impl Zoo {
|
||||||
id
|
id
|
||||||
} else {
|
} else {
|
||||||
let idx = self.empty.pop().unwrap();
|
let idx = self.empty.pop().unwrap();
|
||||||
let rand = ran_u8() as usize % 24;
|
let rand = (random() * 32.0) as usize;
|
||||||
let id = format!("{}_{idx}", ANIMALS[rand]).leak();
|
let id = format!("{}_{idx}", ANIMALS[rand]).leak();
|
||||||
proc.pid = id;
|
proc.pid = id;
|
||||||
self.ids.insert(id, idx);
|
self.ids.insert(id, idx);
|
||||||
|
@ -126,9 +153,9 @@ impl Zoo {
|
||||||
self.kill_list.push(id);
|
self.kill_list.push(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sleep(&mut self, id: &'static str, ms: usize) {
|
pub fn sleep(&mut self, id: &'static str, ms: f64) {
|
||||||
self.sleeping
|
self.sleeping
|
||||||
.insert(id, (Instant::now(), Duration::from_millis(ms as u64)));
|
.insert(id, now() + ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_alive(&self, id: &'static str) -> bool {
|
pub fn is_alive(&self, id: &'static str) -> bool {
|
||||||
|
@ -161,7 +188,7 @@ impl Zoo {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sleeping
|
self.sleeping
|
||||||
.retain(|_, (instant, duration)| instant.elapsed() < *duration);
|
.retain(|_, wakeup_time| now() < *wakeup_time);
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"currently sleeping processes: {}",
|
"currently sleeping processes: {}",
|
||||||
|
@ -232,25 +259,72 @@ impl Zoo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Buffers {
|
||||||
|
console: Value,
|
||||||
|
// commands: Value,
|
||||||
|
// fetch_outbox: Value,
|
||||||
|
// fetch_inbox: Value,
|
||||||
|
input: Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Buffers {
|
||||||
|
pub fn new (prelude: imbl::HashMap<&'static str, Value>) -> Buffers {
|
||||||
|
Buffers {
|
||||||
|
console: prelude.get("console").unwrap().clone(),
|
||||||
|
// commands: prelude.get("commands").unwrap().clone(),
|
||||||
|
// fetch_outbox: prelude.get("fetch_outbox").unwrap().clone(),
|
||||||
|
// fetch_inbox: prelude.get("fetch_inbox").unwrap().clone(),
|
||||||
|
input: prelude.get("input").unwrap().clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn console (&self) -> Rc<RefCell<Value>> {
|
||||||
|
self.console.as_box()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn input (&self) -> Rc<RefCell<Value>> {
|
||||||
|
self.input.as_box()
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn commands (&self) -> Rc<RefCell<Value>> {
|
||||||
|
// self.commands.as_box()
|
||||||
|
// }
|
||||||
|
// pub fn fetch_outbox (&self) -> Rc<RefCell<Value>> {
|
||||||
|
// self.fetch_outbox.as_box()
|
||||||
|
// }
|
||||||
|
// pub fn fetch_inbox (&self) -> Rc<RefCell<Value>> {
|
||||||
|
// self.fetch_inbox.as_box()
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct World {
|
pub struct World {
|
||||||
zoo: Rc<RefCell<Zoo>>,
|
zoo: Rc<RefCell<Zoo>>,
|
||||||
active: Option<Creature>,
|
active: Option<Creature>,
|
||||||
main: &'static str,
|
main: &'static str,
|
||||||
pub result: Option<Result<Value, Panic>>,
|
pub result: Option<Result<Value, Panic>>,
|
||||||
|
buffers: Buffers,
|
||||||
|
last_io: f64,
|
||||||
|
kill_signal: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
pub fn new(chunk: Chunk, debug: bool) -> World {
|
pub fn new(chunk: Chunk, prelude: imbl::HashMap<&'static str, Value>, debug: bool) -> World {
|
||||||
let zoo = Rc::new(RefCell::new(Zoo::new()));
|
let zoo = Rc::new(RefCell::new(Zoo::new()));
|
||||||
let main = Creature::new(chunk, zoo.clone(), debug);
|
let main = Creature::new(chunk, zoo.clone(), debug);
|
||||||
let id = zoo.as_ref().borrow_mut().put(main);
|
let id = zoo.borrow_mut().put(main);
|
||||||
|
let buffers = Buffers::new(prelude);
|
||||||
|
|
||||||
World {
|
World {
|
||||||
zoo,
|
zoo,
|
||||||
active: None,
|
active: None,
|
||||||
main: id,
|
main: id,
|
||||||
result: None,
|
result: None,
|
||||||
|
buffers,
|
||||||
|
last_io: 0.0,
|
||||||
|
kill_signal: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,111 +340,100 @@ impl World {
|
||||||
swap(&mut new_active_opt, &mut self.active);
|
swap(&mut new_active_opt, &mut self.active);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate_main(&mut self) {
|
fn activate_main(&mut self) {
|
||||||
let main = self.zoo.as_ref().borrow_mut().catch(self.main);
|
let main = self.zoo.borrow_mut().catch(self.main);
|
||||||
self.active = Some(main);
|
self.active = Some(main);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn active_id(&mut self) -> &'static str {
|
fn active_id(&mut self) -> &'static str {
|
||||||
self.active.as_ref().unwrap().pid
|
self.active.as_ref().unwrap().pid
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kill_active(&mut self) {
|
fn kill_active(&mut self) {
|
||||||
let id = self.active_id();
|
let id = self.active_id();
|
||||||
self.zoo.as_ref().borrow_mut().kill(id);
|
self.zoo.as_ref().borrow_mut().kill(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn active_result(&mut self) -> &Option<Result<Value, Panic>> {
|
fn active_result(&mut self) -> &Option<Result<Value, Panic>> {
|
||||||
&self.active.as_ref().unwrap().result
|
&self.active.as_ref().unwrap().result
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add memory io places to this signature
|
fn flush_buffers(&mut self) -> Vec<MsgOut> {
|
||||||
// * console
|
|
||||||
// * input
|
|
||||||
// * commands
|
|
||||||
// * slurp
|
|
||||||
pub async fn run(
|
|
||||||
&mut self,
|
|
||||||
console: Value,
|
|
||||||
// input: Value,
|
|
||||||
// commands: Value,
|
|
||||||
// slurp_out: Value,
|
|
||||||
// slurp_in: Value,
|
|
||||||
) {
|
|
||||||
self.activate_main();
|
|
||||||
// let Value::Box(input) = input else {unreachable!()};
|
|
||||||
// let Value::Box(commands) = commands else {unreachable!()};
|
|
||||||
// let Value::Box(slurp) = slurp else {
|
|
||||||
// unreachable!()};
|
|
||||||
let mut last_io = Instant::now();
|
|
||||||
let mut kill_signal = false;
|
|
||||||
loop {
|
|
||||||
if kill_signal {
|
|
||||||
// TODO: send a last message to the console
|
|
||||||
println!("received KILL signal");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
println!(
|
|
||||||
"entering world loop; active process is {}",
|
|
||||||
self.active_id()
|
|
||||||
);
|
|
||||||
self.active.as_mut().unwrap().interpret();
|
|
||||||
println!("yielded from {}", self.active_id());
|
|
||||||
match self.active_result() {
|
|
||||||
None => (),
|
|
||||||
Some(_) => {
|
|
||||||
if self.active_id() == self.main {
|
|
||||||
let result = self.active_result().clone().unwrap();
|
|
||||||
self.result = Some(result.clone());
|
|
||||||
|
|
||||||
//TODO: capture any remaining console or command values
|
|
||||||
do_io(vec![MsgOut::Complete(result)]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
println!(
|
|
||||||
"process {} died with {:?}",
|
|
||||||
self.active_id(),
|
|
||||||
self.active_result().clone()
|
|
||||||
);
|
|
||||||
self.kill_active();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("getting next process");
|
|
||||||
self.next();
|
|
||||||
// TODO:: if enough time has elapsed (how much?) run i/o
|
|
||||||
// 10 ms is 100hz, so that's a nice refresh rate
|
|
||||||
if Instant::now().duration_since(last_io) > Duration::from_millis(10) {
|
|
||||||
// gather io
|
|
||||||
// compile it into messages
|
|
||||||
// serialize it
|
|
||||||
let mut outbox = vec![];
|
let mut outbox = vec![];
|
||||||
if let Some(console) = flush_console(&console) {
|
if let Some(console) = self.flush_console() {
|
||||||
outbox.push(console);
|
outbox.push(console);
|
||||||
};
|
|
||||||
// TODO: slurp
|
|
||||||
// TODO: commands
|
|
||||||
// send it
|
|
||||||
// await the response
|
|
||||||
let inbox = do_io(outbox).await;
|
|
||||||
// unpack the response into messages
|
|
||||||
for msg in inbox {
|
|
||||||
match msg {
|
|
||||||
MsgIn::Kill => kill_signal = true,
|
|
||||||
_ => todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// update
|
|
||||||
last_io = Instant::now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
outbox
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush_console(console: &Value) -> Option<MsgOut> {
|
fn flush_console(&self) -> Option<MsgOut> {
|
||||||
let console = console.as_box();
|
let console = self.buffers.console();
|
||||||
let working_copy = RefCell::new(Value::new_list());
|
let working_copy = RefCell::new(Value::new_list());
|
||||||
console.swap(&working_copy);
|
console.swap(&working_copy);
|
||||||
let working_value = working_copy.borrow();
|
let working_value = working_copy.borrow();
|
||||||
if working_value.as_list().is_empty() { return None; }
|
if working_value.as_list().is_empty() { return None; }
|
||||||
Some(MsgOut::Console(working_value.clone()))
|
Some(MsgOut::Console(working_value.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn complete_main(&mut self) -> Vec<MsgOut> {
|
||||||
|
let mut outbox = self.flush_buffers();
|
||||||
|
// TODO: if we have a panic, actually add the panic message to the console
|
||||||
|
let result = self.active_result().clone().unwrap();
|
||||||
|
self.result = Some(result.clone());
|
||||||
|
outbox.push(MsgOut::Complete(result));
|
||||||
|
outbox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interpret_active(&mut self) {
|
||||||
|
self.active.as_mut().unwrap().interpret();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn maybe_do_io(&mut self) {
|
||||||
|
if self.last_io + 10.0 > now () {
|
||||||
|
let outbox = self.flush_buffers();
|
||||||
|
let inbox = do_io(outbox).await;
|
||||||
|
self.fill_buffers(inbox);
|
||||||
|
}
|
||||||
|
self.last_io = now();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_input(&mut self, str: String) {
|
||||||
|
let value = Value::string(str);
|
||||||
|
let working = RefCell::new(value);
|
||||||
|
let input = self.buffers.input();
|
||||||
|
input.swap(&working);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_buffers(&mut self, inbox: Vec<MsgIn>) {
|
||||||
|
for msg in inbox {
|
||||||
|
match msg {
|
||||||
|
MsgIn::Input(str) => self.fill_input(str),
|
||||||
|
MsgIn::Kill => self.kill_signal = true,
|
||||||
|
_ => todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(&mut self) {
|
||||||
|
self.activate_main();
|
||||||
|
loop {
|
||||||
|
if self.kill_signal {
|
||||||
|
let outbox = self.flush_buffers();
|
||||||
|
do_io(outbox).await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.interpret_active();
|
||||||
|
if self.active_result().is_some() {
|
||||||
|
if self.active_id() == self.main {
|
||||||
|
let outbox = self.complete_main();
|
||||||
|
do_io(outbox).await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.kill_active();
|
||||||
|
}
|
||||||
|
self.next();
|
||||||
|
self.maybe_do_io().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user