integration work continues
This commit is contained in:
parent
173fdb913c
commit
4eceb62ce5
|
@ -11,7 +11,6 @@ crate-type = ["cdylib", "rlib"]
|
|||
[dependencies]
|
||||
chumsky = "0.10.1"
|
||||
imbl = "3.0.0"
|
||||
ran = "2.0.1"
|
||||
num-derive = "0.4.2"
|
||||
num-traits = "0.2.19"
|
||||
regex = "1.11.1"
|
||||
|
@ -19,5 +18,4 @@ wasm-bindgen = "0.2"
|
|||
wasm-bindgen-futures = "0.4.50"
|
||||
serde = {version = "1.0", features = ["derive"]}
|
||||
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
|
||||
fn type {
|
||||
"Returns a keyword representing the type of the value passed in."
|
||||
|
@ -408,8 +412,6 @@ fn to_number {
|
|||
(num as :string) -> base :number (num)
|
||||
}
|
||||
|
||||
box console = []
|
||||
|
||||
fn print! {
|
||||
"Sends a text representation of Ludus values to the console."
|
||||
(...args) -> {
|
||||
|
@ -1269,6 +1271,9 @@ fn sleep! {
|
|||
|
||||
link!
|
||||
|
||||
console
|
||||
input
|
||||
|
||||
abs
|
||||
abs
|
||||
add
|
||||
|
|
24
justfile
24
justfile
|
@ -1,11 +1,23 @@
|
|||
wasm:
|
||||
wasm: && clean-wasm-pack
|
||||
# build with wasm-pack
|
||||
wasm-pack build --target web
|
||||
rm pkg/.gitignore
|
||||
cp pkg/rudus.js pkg/rudus.js.backup
|
||||
echo 'import {io} from "../worker.js"' > rudus.js
|
||||
cat rudus.js.backup | tail -n+2>> rudus.js
|
||||
rm rudus.js.backup
|
||||
|
||||
wasm-dev: && clean-wasm-pack
|
||||
wasm-pack build --dev --target web
|
||||
|
||||
clean-wasm-pack:
|
||||
# delete cruft from wasm-pack
|
||||
rm pkg/.gitignore pkg/package.json pkg/README.md
|
||||
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:
|
||||
@just --list
|
||||
|
|
0
pkg/.gitignore
vendored
0
pkg/.gitignore
vendored
|
@ -6,13 +6,7 @@
|
|||
|
||||
</head>
|
||||
<body>
|
||||
<script type="module">
|
||||
import {run} from "./ludus.js";
|
||||
|
||||
window.ludus = run;
|
||||
|
||||
console.log(run(":foobar"));
|
||||
</script>
|
||||
<script src="./ludus.js" type="module"></script>
|
||||
<p>
|
||||
Open the console. All the action's in there.
|
||||
</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
|
||||
|
||||
export function run (source) {
|
||||
code = source
|
||||
const output = ludus(source)
|
||||
res = JSON.parse(output)
|
||||
return res
|
||||
worker.onmessage = async (e) => {
|
||||
let msgs
|
||||
try {
|
||||
msgs = JSON.parse(e.data)
|
||||
} catch {
|
||||
console.log(e.data)
|
||||
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 ""
|
||||
return res.io.stdout.data
|
||||
}
|
||||
|
@ -375,25 +429,4 @@ export function p5 (commands) {
|
|||
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 */
|
||||
/* 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 interface InitOutput {
|
||||
readonly memory: WebAssembly.Memory;
|
||||
readonly ludus: (a: number, b: number) => [number, number];
|
||||
readonly __wbindgen_export_0: WebAssembly.Table;
|
||||
readonly ludus: (a: number, b: number) => any;
|
||||
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_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;
|
||||
}
|
||||
|
||||
|
|
359
pkg/rudus.js
359
pkg/rudus.js
|
@ -1,6 +1,41 @@
|
|||
import { io } from "./worker.js"
|
||||
|
||||
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;
|
||||
|
||||
|
@ -11,6 +46,13 @@ function getUint8ArrayMemory0() {
|
|||
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 encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
||||
|
@ -28,6 +70,8 @@ const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
|||
|
||||
function passStringToWasm0(arg, malloc, realloc) {
|
||||
|
||||
if (typeof(arg) !== 'string') throw new Error(`expected a string argument, found ${typeof(arg)}`);
|
||||
|
||||
if (realloc === undefined) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
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;
|
||||
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
||||
const ret = encodeString(arg, view);
|
||||
|
||||
if (ret.read !== arg.length) throw new Error('failed to pass whole string');
|
||||
offset += ret.written;
|
||||
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
||||
}
|
||||
|
@ -65,31 +109,144 @@ function passStringToWasm0(arg, malloc, realloc) {
|
|||
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) {
|
||||
ptr = ptr >>> 0;
|
||||
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
||||
function isLikeNone(x) {
|
||||
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')
|
||||
? { 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
|
||||
* @returns {string}
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
export function ludus(src) {
|
||||
let deferred2_0;
|
||||
let deferred2_1;
|
||||
try {
|
||||
const ptr0 = passStringToWasm0(src, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.ludus(ptr0, len0);
|
||||
deferred2_0 = ret[0];
|
||||
deferred2_1 = ret[1];
|
||||
return getStringFromWasm0(ret[0], ret[1]);
|
||||
} finally {
|
||||
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
||||
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) {
|
||||
_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) {
|
||||
|
@ -126,8 +283,150 @@ async function __wbg_load(module, imports) {
|
|||
function __wbg_get_imports() {
|
||||
const imports = {};
|
||||
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() {
|
||||
const table = wasm.__wbindgen_export_0;
|
||||
const table = wasm.__wbindgen_export_2;
|
||||
const offset = table.grow(4);
|
||||
table.set(0, undefined);
|
||||
table.set(offset + 0, undefined);
|
||||
|
@ -136,6 +435,31 @@ function __wbg_get_imports() {
|
|||
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;
|
||||
}
|
||||
|
@ -147,6 +471,7 @@ function __wbg_init_memory(imports, memory) {
|
|||
function __wbg_finalize_init(instance, module) {
|
||||
wasm = instance.exports;
|
||||
__wbg_init.__wbindgen_wasm_module = module;
|
||||
cachedDataViewMemory0 = 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 */
|
||||
/* eslint-disable */
|
||||
export const memory: WebAssembly.Memory;
|
||||
export const ludus: (a: number, b: number) => [number, number];
|
||||
export const __wbindgen_export_0: WebAssembly.Table;
|
||||
export const ludus: (a: number, b: number) => any;
|
||||
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_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;
|
||||
|
|
|
@ -1,15 +1,40 @@
|
|||
import init from "./rudus.js";
|
||||
import init, {ludus} from "./rudus.js";
|
||||
|
||||
console.log("Worker: starting Ludus VM.")
|
||||
|
||||
export function io (out) {
|
||||
if (Object.keys(out).length > 0) postMessage(out)
|
||||
if (out.length > 0) postMessage(out)
|
||||
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()
|
||||
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.")
|
||||
|
||||
|
|
32
sandbox.ld
32
sandbox.ld
|
@ -1,31 +1 @@
|
|||
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!
|
||||
:foobar
|
||||
|
|
17
src/base.rs
17
src/base.rs
|
@ -1,7 +1,7 @@
|
|||
use crate::value::*;
|
||||
use imbl::*;
|
||||
use ran::ran_f64;
|
||||
use std::rc::Rc;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum BaseFn {
|
||||
|
@ -481,8 +481,14 @@ pub fn floor(x: &Value) -> Value {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn random() -> Value {
|
||||
Value::Number(ran_f64())
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = Math)]
|
||||
fn random() -> f64;
|
||||
}
|
||||
|
||||
pub fn base_random() -> Value {
|
||||
Value::Number(random())
|
||||
}
|
||||
|
||||
pub fn round(x: &Value) -> Value {
|
||||
|
@ -610,7 +616,10 @@ pub fn make_base() -> Value {
|
|||
("pi", Value::Number(std::f64::consts::PI)),
|
||||
("print!", Value::BaseFn(BaseFn::Unary("print!", print))),
|
||||
("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))),
|
||||
("rest", Value::BaseFn(BaseFn::Unary("rest", rest))),
|
||||
("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;
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(s: String);
|
||||
}
|
||||
|
||||
type Lines = Value; // expect a list of values
|
||||
type Commands = Value; // expect a list of values
|
||||
type Url = Value; // expect a string representing a URL
|
||||
|
@ -25,7 +31,7 @@ pub enum MsgOut {
|
|||
Console(Lines),
|
||||
Commands(Commands),
|
||||
SlurpRequest(Url),
|
||||
Complete(Result<Value, Panic>),
|
||||
Complete(FinalValue),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MsgOut {
|
||||
|
@ -38,7 +44,10 @@ impl MsgOut {
|
|||
pub fn to_json(&self) -> String {
|
||||
match self {
|
||||
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())
|
||||
},
|
||||
MsgOut::Commands(commands) => {
|
||||
|
@ -55,7 +64,7 @@ impl MsgOut {
|
|||
}
|
||||
MsgOut::Console(lines) => {
|
||||
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}\"");
|
||||
make_json_payload("console", json_lines)
|
||||
}
|
||||
|
@ -72,6 +81,16 @@ pub enum MsgIn {
|
|||
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 {
|
||||
pub fn to_value(self) -> Value {
|
||||
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 inbox = io (outbox).await;
|
||||
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");
|
||||
if !inbox.is_empty() {
|
||||
log("got messages in ludus!".to_string());
|
||||
for msg in inbox.iter() {
|
||||
log(format!("{}", msg));
|
||||
}
|
||||
}
|
||||
inbox
|
||||
|
||||
}
|
||||
|
||||
|
|
41
src/lib.rs
41
src/lib.rs
|
@ -1,6 +1,8 @@
|
|||
use chumsky::{input::Stream, prelude::*};
|
||||
use imbl::HashMap;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
const DEBUG_SCRIPT_COMPILE: bool = false;
|
||||
const DEBUG_SCRIPT_RUN: bool = false;
|
||||
|
@ -15,7 +17,7 @@ use crate::ast::Ast;
|
|||
mod base;
|
||||
|
||||
mod world;
|
||||
use crate::world::World;
|
||||
use crate::world::{World, Zoo};
|
||||
|
||||
mod spans;
|
||||
use crate::spans::Spanned;
|
||||
|
@ -42,10 +44,11 @@ mod value;
|
|||
use value::Value;
|
||||
|
||||
mod vm;
|
||||
use vm::Creature;
|
||||
|
||||
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 (parsed, parse_errors) = parser()
|
||||
.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() {
|
||||
println!("VALIDATION ERRORS IN PRLUDE:");
|
||||
report_invalidation(validator.errors);
|
||||
panic!();
|
||||
panic!("validator errors in prelude");
|
||||
}
|
||||
|
||||
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parsed));
|
||||
|
@ -88,25 +91,37 @@ async fn prelude() -> HashMap<&'static str, Value> {
|
|||
compiler.compile();
|
||||
|
||||
let chunk = compiler.chunk;
|
||||
let mut world = World::new(chunk, DEBUG_PRELUDE_RUN);
|
||||
let stub_console = Value::r#box(Value::new_list());
|
||||
world.run(stub_console).await;
|
||||
let prelude = world.result.unwrap().unwrap();
|
||||
log("compiled prelude");
|
||||
let stub_zoo = Rc::new(RefCell::new(Zoo::new()));
|
||||
let mut prld_sync = Creature::new(chunk, stub_zoo, DEBUG_PRELUDE_RUN);
|
||||
prld_sync.interpret();
|
||||
log("run prelude synchronously");
|
||||
let prelude = prld_sync.result.unwrap().unwrap();
|
||||
match prelude {
|
||||
Value::Dict(hashmap) => *hashmap,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(s: &str);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
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();
|
||||
log(src);
|
||||
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
|
||||
if !lex_errs.is_empty() {
|
||||
return format!("{:?}", lex_errs);
|
||||
}
|
||||
|
||||
let tokens = tokens.unwrap();
|
||||
log("successfully tokenized source");
|
||||
|
||||
let (parse_result, parse_errors) = parser()
|
||||
.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
|
||||
// in any event, the AST should live forever
|
||||
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 prelude = imbl::HashMap::new();
|
||||
|
||||
|
@ -146,6 +163,7 @@ pub async fn ludus(src: String) -> String {
|
|||
// compiler.bind("base");
|
||||
|
||||
compiler.compile();
|
||||
log("successfully compiled source");
|
||||
if DEBUG_SCRIPT_COMPILE {
|
||||
println!("=== source code ===");
|
||||
println!("{src}");
|
||||
|
@ -159,13 +177,16 @@ pub async fn ludus(src: String) -> String {
|
|||
|
||||
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
|
||||
.get("console")
|
||||
.expect("prelude must have a console")
|
||||
.clone();
|
||||
world.run(console).await;
|
||||
log("loaded world and console");
|
||||
world.run().await;
|
||||
let result = world.result.clone().unwrap();
|
||||
log("ran script");
|
||||
log(format!("{:?}", result).as_str());
|
||||
|
||||
let console = postlude.get("console").unwrap();
|
||||
let Value::Box(console) = console else {
|
||||
|
|
|
@ -2,10 +2,9 @@ use rudus::ludus;
|
|||
use std::env;
|
||||
use std::fs;
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() {
|
||||
pub fn main() {
|
||||
env::set_var("RUST_BACKTRACE", "1");
|
||||
let src = fs::read_to_string("sandbox.ld").unwrap();
|
||||
let json = ludus(src).await;
|
||||
println!("{json}");
|
||||
let json = ludus(src);
|
||||
// println!("{json}");
|
||||
}
|
||||
|
|
|
@ -259,7 +259,9 @@ impl Value {
|
|||
pub fn to_json(&self) -> Option<String> {
|
||||
use Value::*;
|
||||
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}\"")),
|
||||
List(members) => {
|
||||
let mut joined = "".to_string();
|
||||
|
|
|
@ -339,7 +339,7 @@ impl Creature {
|
|||
let Value::Number(ms) = args[1] else {
|
||||
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.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::vm::{Creature, Panic};
|
||||
use crate::io::{MsgOut, MsgIn, do_io};
|
||||
use ran::ran_u8;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::mem::swap;
|
||||
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",
|
||||
"hare",
|
||||
"squirrel",
|
||||
|
@ -31,9 +43,17 @@ const ANIMALS: [&str; 24] = [
|
|||
"zebra",
|
||||
"hyena",
|
||||
"giraffe",
|
||||
"leopard",
|
||||
"lion",
|
||||
"hippopotamus",
|
||||
"capybara",
|
||||
"python",
|
||||
"gopher",
|
||||
"crab",
|
||||
"trout",
|
||||
"osprey",
|
||||
"lemur",
|
||||
"wobbegong",
|
||||
"walrus",
|
||||
"opossum",
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
@ -70,7 +90,7 @@ pub struct Zoo {
|
|||
ids: HashMap<&'static str, usize>,
|
||||
dead: HashSet<&'static str>,
|
||||
kill_list: Vec<&'static str>,
|
||||
sleeping: HashMap<&'static str, (Instant, Duration)>,
|
||||
sleeping: HashMap<&'static str, f64>,
|
||||
active_idx: usize,
|
||||
active_id: &'static str,
|
||||
}
|
||||
|
@ -90,20 +110,27 @@ impl Zoo {
|
|||
}
|
||||
|
||||
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();
|
||||
format!("{}_{idx}", ANIMALS[rand])
|
||||
log("procs len");
|
||||
format!("{}_{idx}", ANIMALS[rand_idx])
|
||||
}
|
||||
|
||||
fn new_id(&self) -> &'static str {
|
||||
log("creating new id");
|
||||
let mut new = self.random_id();
|
||||
log("got new ramdom id");
|
||||
while self.dead.iter().any(|old| *old == new) {
|
||||
new = self.random_id();
|
||||
}
|
||||
log(format!("got new id: {}", new).as_str());
|
||||
new.leak()
|
||||
}
|
||||
|
||||
pub fn put(&mut self, mut proc: Creature) -> &'static str {
|
||||
log("putting creature");
|
||||
if self.empty.is_empty() {
|
||||
let id = self.new_id();
|
||||
let idx = self.procs.len();
|
||||
|
@ -113,7 +140,7 @@ impl Zoo {
|
|||
id
|
||||
} else {
|
||||
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();
|
||||
proc.pid = id;
|
||||
self.ids.insert(id, idx);
|
||||
|
@ -126,9 +153,9 @@ impl Zoo {
|
|||
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
|
||||
.insert(id, (Instant::now(), Duration::from_millis(ms as u64)));
|
||||
.insert(id, now() + ms);
|
||||
}
|
||||
|
||||
pub fn is_alive(&self, id: &'static str) -> bool {
|
||||
|
@ -161,7 +188,7 @@ impl Zoo {
|
|||
}
|
||||
|
||||
self.sleeping
|
||||
.retain(|_, (instant, duration)| instant.elapsed() < *duration);
|
||||
.retain(|_, wakeup_time| now() < *wakeup_time);
|
||||
|
||||
println!(
|
||||
"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)]
|
||||
pub struct World {
|
||||
zoo: Rc<RefCell<Zoo>>,
|
||||
active: Option<Creature>,
|
||||
main: &'static str,
|
||||
pub result: Option<Result<Value, Panic>>,
|
||||
buffers: Buffers,
|
||||
last_io: f64,
|
||||
kill_signal: bool,
|
||||
}
|
||||
|
||||
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 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 {
|
||||
zoo,
|
||||
active: None,
|
||||
main: id,
|
||||
result: None,
|
||||
buffers,
|
||||
last_io: 0.0,
|
||||
kill_signal: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,111 +340,100 @@ impl World {
|
|||
swap(&mut new_active_opt, &mut self.active);
|
||||
}
|
||||
|
||||
pub fn activate_main(&mut self) {
|
||||
let main = self.zoo.as_ref().borrow_mut().catch(self.main);
|
||||
fn activate_main(&mut self) {
|
||||
let main = self.zoo.borrow_mut().catch(self.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
|
||||
}
|
||||
|
||||
pub fn kill_active(&mut self) {
|
||||
fn kill_active(&mut self) {
|
||||
let id = self.active_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
|
||||
}
|
||||
|
||||
// TODO: add memory io places to this signature
|
||||
// * 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
|
||||
fn flush_buffers(&mut self) -> Vec<MsgOut> {
|
||||
let mut outbox = vec![];
|
||||
if let Some(console) = flush_console(&console) {
|
||||
if let Some(console) = self.flush_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> {
|
||||
let console = console.as_box();
|
||||
fn flush_console(&self) -> Option<MsgOut> {
|
||||
let console = self.buffers.console();
|
||||
let working_copy = RefCell::new(Value::new_list());
|
||||
console.swap(&working_copy);
|
||||
let working_value = working_copy.borrow();
|
||||
if working_value.as_list().is_empty() { return None; }
|
||||
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