hook the things up and discover a possible stop-the-world bug

This commit is contained in:
Scott Richmond 2025-07-01 00:43:01 -04:00
parent 4eceb62ce5
commit 2f3f362f49
14 changed files with 147 additions and 145 deletions

View File

@ -19,3 +19,4 @@ 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"
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1.7"
# talc = "4.4.3"

31
assets/agent.ld Normal file
View File

@ -0,0 +1,31 @@
fn agent (val) -> receive {
(: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!

View File

@ -416,7 +416,7 @@ fn print! {
"Sends a text representation of Ludus values to the console." "Sends a text representation of Ludus values to the console."
(...args) -> { (...args) -> {
let line = do args > map (string, _) > join (_, " ") let line = do args > map (string, _) > join (_, " ")
base :print! (args) & base :print! (args)
update! (console, append (_, line)) update! (console, append (_, line))
:ok :ok
} }

View File

@ -1175,3 +1175,4 @@ That leaves the following list:
* One thing I hadn't quite grokked before is that we need to have a way of running the i/o events. Perhaps the simplest way to do this is to just to do it every so often, regardless of how long the ludus event loop is taking. That way even if things are getting weird in the VM, i/o still happens regularly. * One thing I hadn't quite grokked before is that we need to have a way of running the i/o events. Perhaps the simplest way to do this is to just to do it every so often, regardless of how long the ludus event loop is taking. That way even if things are getting weird in the VM, i/o still happens regularly.
* The return to a `slurp` call is interesting. * The return to a `slurp` call is interesting.
* I think the thing to do is to write to a slurp buffer/box as well. * I think the thing to do is to write to a slurp buffer/box as well.

View File

@ -13,18 +13,15 @@ worker.onmessage = async (e) => {
throw Error("bad json from Ludus") throw Error("bad json from Ludus")
} }
for (const msg of msgs) { for (const msg of msgs) {
console.log("Main: message received from worker:", msg);
switch (msg.verb) { switch (msg.verb) {
case "complete": { case "complete": {
console.log("completed ludus run!") console.log("ludus completed with => ", msg.data)
console.log("with", msg.data)
res = msg.data res = msg.data
running = false running = false
break break
} }
case "console": { case "console": {
console.log("console msg from msg.data") console.log("ludus says => ", msg.data)
console.log(msg.data)
break break
} }
} }

4
pkg/rudus.d.ts vendored
View File

@ -14,8 +14,8 @@ export interface InitOutput {
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_export_6: WebAssembly.Table; readonly __wbindgen_export_6: WebAssembly.Table;
readonly closure347_externref_shim: (a: number, b: number, c: any) => void; readonly closure327_externref_shim: (a: number, b: number, c: any) => void;
readonly closure371_externref_shim: (a: number, b: number, c: any, d: any) => void; readonly closure340_externref_shim: (a: number, b: number, c: any, d: any) => void;
readonly __wbindgen_start: () => void; readonly __wbindgen_start: () => void;
} }

View File

@ -17,22 +17,6 @@ function handleError(f, args) {
} }
} }
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') } } ); 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(); }; if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
@ -70,8 +54,6 @@ 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;
@ -100,7 +82,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;
} }
@ -122,12 +104,6 @@ function isLikeNone(x) {
return x === undefined || x === null; return x === undefined || x === null;
} }
function _assertBoolean(n) {
if (typeof(n) !== 'boolean') {
throw new Error(`expected a boolean argument, found ${typeof(n)}`);
}
}
const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} } ? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(state => { : new FinalizationRegistry(state => {
@ -234,19 +210,12 @@ export function ludus(src) {
return ret; return ret;
} }
function _assertNum(n) {
if (typeof(n) !== 'number') throw new Error(`expected a number argument, found ${typeof(n)}`);
}
function __wbg_adapter_22(arg0, arg1, arg2) { function __wbg_adapter_22(arg0, arg1, arg2) {
_assertNum(arg0); wasm.closure327_externref_shim(arg0, arg1, arg2);
_assertNum(arg1);
wasm.closure347_externref_shim(arg0, arg1, arg2);
} }
function __wbg_adapter_50(arg0, arg1, arg2, arg3) { function __wbg_adapter_52(arg0, arg1, arg2, arg3) {
_assertNum(arg0); wasm.closure340_externref_shim(arg0, arg1, arg2, arg3);
_assertNum(arg1);
wasm.closure371_externref_shim(arg0, arg1, arg2, arg3);
} }
async function __wbg_load(module, imports) { async function __wbg_load(module, imports) {
@ -291,7 +260,7 @@ function __wbg_get_imports() {
const ret = arg0.call(arg1, arg2); const ret = arg0.call(arg1, arg2);
return ret; return ret;
}, arguments) }; }, arguments) };
imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function() { return logError(function (arg0, arg1) { imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function(arg0, arg1) {
let deferred0_0; let deferred0_0;
let deferred0_1; let deferred0_1;
try { try {
@ -301,8 +270,8 @@ function __wbg_get_imports() {
} finally { } finally {
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
} }
}, arguments) }; };
imports.wbg.__wbg_io_4b41f8089de924df = function() { return logError(function (arg0, arg1) { imports.wbg.__wbg_io_4b41f8089de924df = function(arg0, arg1) {
let deferred0_0; let deferred0_0;
let deferred0_1; let deferred0_1;
try { try {
@ -313,8 +282,8 @@ function __wbg_get_imports() {
} finally { } finally {
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
} }
}, arguments) }; };
imports.wbg.__wbg_log_86d603e98cc11395 = function() { return logError(function (arg0, arg1) { imports.wbg.__wbg_log_86d603e98cc11395 = function(arg0, arg1) {
let deferred0_0; let deferred0_0;
let deferred0_1; let deferred0_1;
try { try {
@ -324,18 +293,29 @@ function __wbg_get_imports() {
} finally { } finally {
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
} }
}, arguments) }; };
imports.wbg.__wbg_log_9e426f8e841e42d3 = function() { return logError(function (arg0, arg1) { imports.wbg.__wbg_log_9e426f8e841e42d3 = function(arg0, arg1) {
console.log(getStringFromWasm0(arg0, arg1)); console.log(getStringFromWasm0(arg0, arg1));
}, arguments) }; };
imports.wbg.__wbg_new_23a2665fac83c611 = function() { return logError(function (arg0, arg1) { imports.wbg.__wbg_log_edeb598b620f1ba2 = 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);
}
};
imports.wbg.__wbg_new_23a2665fac83c611 = function(arg0, arg1) {
try { try {
var state0 = {a: arg0, b: arg1}; var state0 = {a: arg0, b: arg1};
var cb0 = (arg0, arg1) => { var cb0 = (arg0, arg1) => {
const a = state0.a; const a = state0.a;
state0.a = 0; state0.a = 0;
try { try {
return __wbg_adapter_50(a, state0.b, arg0, arg1); return __wbg_adapter_52(a, state0.b, arg0, arg1);
} finally { } finally {
state0.a = a; state0.a = a;
} }
@ -345,65 +325,65 @@ function __wbg_get_imports() {
} finally { } finally {
state0.a = state0.b = 0; state0.a = state0.b = 0;
} }
}, arguments) }; };
imports.wbg.__wbg_new_8a6f238a6ece86ea = function() { return logError(function () { imports.wbg.__wbg_new_8a6f238a6ece86ea = function() {
const ret = new Error(); const ret = new Error();
return ret; return ret;
}, arguments) }; };
imports.wbg.__wbg_newnoargs_105ed471475aaf50 = function() { return logError(function (arg0, arg1) { imports.wbg.__wbg_newnoargs_105ed471475aaf50 = function(arg0, arg1) {
const ret = new Function(getStringFromWasm0(arg0, arg1)); const ret = new Function(getStringFromWasm0(arg0, arg1));
return ret; return ret;
}, arguments) }; };
imports.wbg.__wbg_now_8dddb61fa4928554 = function() { return logError(function () { imports.wbg.__wbg_now_8dddb61fa4928554 = function() {
const ret = Date.now(); const ret = Date.now();
return ret; return ret;
}, arguments) }; };
imports.wbg.__wbg_queueMicrotask_97d92b4fcc8a61c5 = function() { return logError(function (arg0) { imports.wbg.__wbg_queueMicrotask_97d92b4fcc8a61c5 = function(arg0) {
queueMicrotask(arg0); queueMicrotask(arg0);
}, arguments) }; };
imports.wbg.__wbg_queueMicrotask_d3219def82552485 = function() { return logError(function (arg0) { imports.wbg.__wbg_queueMicrotask_d3219def82552485 = function(arg0) {
const ret = arg0.queueMicrotask; const ret = arg0.queueMicrotask;
return ret; return ret;
}, arguments) }; };
imports.wbg.__wbg_random_57c118f142535bb6 = function() { return logError(function () { imports.wbg.__wbg_random_57c118f142535bb6 = function() {
const ret = Math.random(); const ret = Math.random();
return ret; return ret;
}, arguments) }; };
imports.wbg.__wbg_resolve_4851785c9c5f573d = function() { return logError(function (arg0) { imports.wbg.__wbg_resolve_4851785c9c5f573d = function(arg0) {
const ret = Promise.resolve(arg0); const ret = Promise.resolve(arg0);
return ret; return ret;
}, arguments) }; };
imports.wbg.__wbg_stack_0ed75d68575b0f3c = function() { return logError(function (arg0, arg1) { imports.wbg.__wbg_stack_0ed75d68575b0f3c = function(arg0, arg1) {
const ret = arg1.stack; const ret = arg1.stack;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN; const len1 = WASM_VECTOR_LEN;
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
}, arguments) }; };
imports.wbg.__wbg_static_accessor_GLOBAL_88a902d13a557d07 = function() { return logError(function () { imports.wbg.__wbg_static_accessor_GLOBAL_88a902d13a557d07 = function() {
const ret = typeof global === 'undefined' ? null : global; const ret = typeof global === 'undefined' ? null : global;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
}, arguments) }; };
imports.wbg.__wbg_static_accessor_GLOBAL_THIS_56578be7e9f832b0 = function() { return logError(function () { imports.wbg.__wbg_static_accessor_GLOBAL_THIS_56578be7e9f832b0 = function() {
const ret = typeof globalThis === 'undefined' ? null : globalThis; const ret = typeof globalThis === 'undefined' ? null : globalThis;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
}, arguments) }; };
imports.wbg.__wbg_static_accessor_SELF_37c5d418e4bf5819 = function() { return logError(function () { imports.wbg.__wbg_static_accessor_SELF_37c5d418e4bf5819 = function() {
const ret = typeof self === 'undefined' ? null : self; const ret = typeof self === 'undefined' ? null : self;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
}, arguments) }; };
imports.wbg.__wbg_static_accessor_WINDOW_5de37043a91a9c40 = function() { return logError(function () { imports.wbg.__wbg_static_accessor_WINDOW_5de37043a91a9c40 = function() {
const ret = typeof window === 'undefined' ? null : window; const ret = typeof window === 'undefined' ? null : window;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
}, arguments) }; };
imports.wbg.__wbg_then_44b73946d2fb3e7d = function() { return logError(function (arg0, arg1) { imports.wbg.__wbg_then_44b73946d2fb3e7d = function(arg0, arg1) {
const ret = arg0.then(arg1); const ret = arg0.then(arg1);
return ret; return ret;
}, arguments) }; };
imports.wbg.__wbg_then_48b406749878a531 = function() { return logError(function (arg0, arg1, arg2) { imports.wbg.__wbg_then_48b406749878a531 = function(arg0, arg1, arg2) {
const ret = arg0.then(arg1, arg2); const ret = arg0.then(arg1, arg2);
return ret; return ret;
}, arguments) }; };
imports.wbg.__wbindgen_cb_drop = function(arg0) { imports.wbg.__wbindgen_cb_drop = function(arg0) {
const obj = arg0.original; const obj = arg0.original;
if (obj.cnt-- == 1) { if (obj.cnt-- == 1) {
@ -411,13 +391,12 @@ function __wbg_get_imports() {
return true; return true;
} }
const ret = false; const ret = false;
_assertBoolean(ret);
return ret; return ret;
}; };
imports.wbg.__wbindgen_closure_wrapper7663 = function() { return logError(function (arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper974 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 348, __wbg_adapter_22); const ret = makeMutClosure(arg0, arg1, 328, __wbg_adapter_22);
return ret; return ret;
}, arguments) }; };
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
const ret = debugString(arg1); const ret = debugString(arg1);
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@ -437,12 +416,10 @@ function __wbg_get_imports() {
}; };
imports.wbg.__wbindgen_is_function = function(arg0) { imports.wbg.__wbindgen_is_function = function(arg0) {
const ret = typeof(arg0) === 'function'; const ret = typeof(arg0) === 'function';
_assertBoolean(ret);
return ret; return ret;
}; };
imports.wbg.__wbindgen_is_undefined = function(arg0) { imports.wbg.__wbindgen_is_undefined = function(arg0) {
const ret = arg0 === undefined; const ret = arg0 === undefined;
_assertBoolean(ret);
return ret; return ret;
}; };
imports.wbg.__wbindgen_string_get = function(arg0, arg1) { imports.wbg.__wbindgen_string_get = function(arg0, arg1) {

Binary file not shown.

View File

@ -9,6 +9,6 @@ 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_export_6: WebAssembly.Table; export const __wbindgen_export_6: WebAssembly.Table;
export const closure347_externref_shim: (a: number, b: number, c: any) => void; export const closure327_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 closure340_externref_shim: (a: number, b: number, c: any, d: any) => void;
export const __wbindgen_start: () => void; export const __wbindgen_start: () => void;

View File

@ -1,12 +1,11 @@
import init, {ludus} from "./rudus.js"; import init, {ludus} from "./rudus.js";
console.log("Worker: starting Ludus VM.")
export function io (out) { export function io (out) {
if (out.length > 0) postMessage(out) if (out.length > 0) postMessage(out)
return new Promise((resolve, _) => { return new Promise((resolve, _) => {
onmessage = (e) => { onmessage = (e) => {
console.log(e.data) // console.log("Worker: from Ludus:", e.data)
resolve(JSON.stringify(e.data)) resolve(JSON.stringify(e.data))
} }
}) })
@ -17,18 +16,19 @@ let loaded_wasm = false
async function run(e) { async function run(e) {
if (!loaded_wasm) { if (!loaded_wasm) {
await init() await init()
console.log("Worker: Ludus has been initialized.")
loaded_wasm = true loaded_wasm = true
} }
let msgs = e.data let msgs = e.data
for (const msg of msgs) { for (const msg of msgs) {
if (msg.verb === "run" && typeof msg.data === 'string') { if (msg.verb === "run" && typeof msg.data === 'string') {
console.log("running ludus!") // console.log("running ludus!")
onmessage = () => {} onmessage = () => {}
let result = await ludus(msg.data) console.log("Worker: Beginning new Ludus run.")
console.log(result) await ludus(msg.data)
onmessage = run onmessage = run
} else { } else {
console.log("Did not get valid startup message. Instead got:") console.log("Worker: Did not get valid startup message. Instead got:")
console.log(e.data) console.log(e.data)
} }
} }
@ -36,5 +36,4 @@ async function run(e) {
onmessage = run onmessage = run
console.log("Worker: Ludus VM is running.")

View File

@ -242,7 +242,12 @@ pub fn last(ordered: &Value) -> Value {
} }
} }
// TODO: fix this: x is a list of all the args passed to Ludus's print! #[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(msg: String);
}
pub fn print(x: &Value) -> Value { pub fn print(x: &Value) -> Value {
let Value::List(args) = x else { let Value::List(args) = x else {
unreachable!("internal Ludus error") unreachable!("internal Ludus error")
@ -252,7 +257,8 @@ pub fn print(x: &Value) -> Value {
.map(|val| format!("{val}")) .map(|val| format!("{val}"))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(" "); .join(" ");
println!("{out}"); // println!("{out}");
log(out);
Value::Keyword("ok") Value::Keyword("ok")
} }

View File

@ -118,10 +118,9 @@ 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() { if !inbox.is_empty() {
log("got messages in ludus!".to_string()); log("ludus received messages".to_string());
for msg in inbox.iter() { for msg in inbox.iter() {
log(format!("{}", msg)); log(format!("{}", msg));
} }

View File

@ -4,11 +4,16 @@ use wasm_bindgen::prelude::*;
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; 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;
const DEBUG_PRELUDE_COMPILE: bool = false; const DEBUG_PRELUDE_COMPILE: bool = false;
const DEBUG_PRELUDE_RUN: bool = false; const DEBUG_PRELUDE_RUN: bool = false;
// #[cfg(target_family = "wasm")]
// #[global_allocator]
// static ALLOCATOR: talc::TalckWasm = unsafe { talc::TalckWasm::new_global() };
mod io; mod io;
mod ast; mod ast;
@ -91,11 +96,9 @@ fn prelude() -> HashMap<&'static str, Value> {
compiler.compile(); compiler.compile();
let chunk = compiler.chunk; let chunk = compiler.chunk;
log("compiled prelude");
let stub_zoo = Rc::new(RefCell::new(Zoo::new())); let stub_zoo = Rc::new(RefCell::new(Zoo::new()));
let mut prld_sync = Creature::new(chunk, stub_zoo, DEBUG_PRELUDE_RUN); let mut prld_sync = Creature::new(chunk, stub_zoo, DEBUG_PRELUDE_RUN);
prld_sync.interpret(); prld_sync.interpret();
log("run prelude synchronously");
let prelude = prld_sync.result.unwrap().unwrap(); let prelude = prld_sync.result.unwrap().unwrap();
match prelude { match prelude {
Value::Dict(hashmap) => *hashmap, Value::Dict(hashmap) => *hashmap,
@ -111,17 +114,15 @@ extern "C" {
#[wasm_bindgen] #[wasm_bindgen]
pub async fn ludus(src: String) -> String { pub async fn ludus(src: String) -> String {
log("Ludus: starting ludus run.");
console_error_panic_hook::set_once(); 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)))
@ -130,14 +131,9 @@ pub async fn ludus(src: String) -> String {
return format!("{:?}", parse_errors); return format!("{:?}", parse_errors);
} }
// ::sigh:: The AST should be 'static
// This simplifies lifetimes, and
// 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(); 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();
@ -158,12 +154,8 @@ pub async fn ludus(src: String) -> String {
prelude.clone(), prelude.clone(),
DEBUG_SCRIPT_COMPILE, DEBUG_SCRIPT_COMPILE,
); );
// let base = base::make_base();
// compiler.emit_constant(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}");
@ -178,15 +170,8 @@ pub async fn ludus(src: String) -> String {
let vm_chunk = compiler.chunk; let vm_chunk = compiler.chunk;
let mut world = World::new(vm_chunk, prelude.clone(), DEBUG_SCRIPT_RUN); let mut world = World::new(vm_chunk, prelude.clone(), DEBUG_SCRIPT_RUN);
let console = prelude
.get("console")
.expect("prelude must have a console")
.clone();
log("loaded world and console");
world.run().await; 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 {

View File

@ -110,27 +110,20 @@ impl Zoo {
} }
fn random_id(&self) -> String { fn random_id(&self) -> String {
log("generating random id");
let rand_idx = (random() * 32.0) as usize; let rand_idx = (random() * 32.0) as usize;
log("random number!");
let idx = self.procs.len(); let idx = self.procs.len();
log("procs len");
format!("{}_{idx}", ANIMALS[rand_idx]) 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();
@ -236,14 +229,14 @@ impl Zoo {
} }
pub fn next(&mut self) -> &'static str { pub fn next(&mut self) -> &'static str {
self.clean_up();
let starting_idx = self.active_idx;
self.active_idx = (self.active_idx + 1) % self.procs.len(); self.active_idx = (self.active_idx + 1) % self.procs.len();
while !self.is_available() { while !self.is_available() {
self.clean_up(); if self.active_idx == starting_idx {
return ""
}
self.active_idx = (self.active_idx + 1) % self.procs.len(); self.active_idx = (self.active_idx + 1) % self.procs.len();
println!(
"testing process availability: {}",
self.procs[self.active_idx]
);
} }
match &self.procs[self.active_idx] { match &self.procs[self.active_idx] {
Status::Empty | Status::Borrowed => unreachable!(), Status::Empty | Status::Borrowed => unreachable!(),
@ -331,9 +324,15 @@ impl World {
fn next(&mut self) { fn next(&mut self) {
let mut active = None; let mut active = None;
swap(&mut active, &mut self.active); swap(&mut active, &mut self.active);
let mut zoo = self.zoo.as_ref().borrow_mut(); let mut zoo = self.zoo.borrow_mut();
zoo.release(active.unwrap()); if let Some(active) = active {
zoo.release(active);
}
let new_active_id = zoo.next(); let new_active_id = zoo.next();
if new_active_id.is_empty() {
self.active = None;
return;
}
let mut new_active_proc = zoo.catch(new_active_id); let mut new_active_proc = zoo.catch(new_active_id);
new_active_proc.reset_reductions(); new_active_proc.reset_reductions();
let mut new_active_opt = Some(new_active_proc); let mut new_active_opt = Some(new_active_proc);
@ -345,16 +344,21 @@ impl World {
self.active = Some(main); self.active = Some(main);
} }
fn active_id(&mut self) -> &'static str { fn active_id(&mut self) -> Option<&'static str> {
self.active.as_ref().unwrap().pid match &self.active {
Some(creature) => Some(creature.pid),
None => None,
}
} }
fn kill_active(&mut self) { fn kill_active(&mut self) {
let id = self.active_id(); if let Some(pid) = self.active_id() {
self.zoo.as_ref().borrow_mut().kill(id); self.zoo.borrow_mut().kill(pid);
}
} }
fn active_result(&mut self) -> &Option<Result<Value, Panic>> { fn active_result(&mut self) -> &Option<Result<Value, Panic>> {
if self.active.is_none() { return &None; }
&self.active.as_ref().unwrap().result &self.active.as_ref().unwrap().result
} }
@ -389,12 +393,12 @@ impl World {
} }
async fn maybe_do_io(&mut self) { async fn maybe_do_io(&mut self) {
if self.last_io + 10.0 > now () { if self.last_io + 10.0 < now() {
let outbox = self.flush_buffers(); let outbox = self.flush_buffers();
let inbox = do_io(outbox).await; let inbox = do_io(outbox).await;
self.fill_buffers(inbox); self.fill_buffers(inbox);
self.last_io = now();
} }
self.last_io = now();
} }
fn fill_input(&mut self, str: String) { fn fill_input(&mut self, str: String) {
@ -417,14 +421,17 @@ impl World {
pub async fn run(&mut self) { pub async fn run(&mut self) {
self.activate_main(); self.activate_main();
loop { loop {
self.maybe_do_io().await;
if self.kill_signal { if self.kill_signal {
let outbox = self.flush_buffers(); let outbox = self.flush_buffers();
do_io(outbox).await; do_io(outbox).await;
return; return;
} }
self.interpret_active(); if self.active.is_some() {
self.interpret_active();
}
if self.active_result().is_some() { if self.active_result().is_some() {
if self.active_id() == self.main { if self.active_id().unwrap() == self.main {
let outbox = self.complete_main(); let outbox = self.complete_main();
do_io(outbox).await; do_io(outbox).await;
return; return;
@ -432,7 +439,6 @@ impl World {
self.kill_active(); self.kill_active();
} }
self.next(); self.next();
self.maybe_do_io().await;
} }
} }
} }