spawn is now a special form
This commit is contained in:
parent
8ce6a33573
commit
bbdab93cf0
|
@ -1247,10 +1247,10 @@ fn send {
|
|||
}
|
||||
}
|
||||
|
||||
fn spawn! {
|
||||
"Spawns a new process running the function passed in."
|
||||
(f as :fn) -> base :process (:spawn, f)
|
||||
}
|
||||
& fn spawn! {
|
||||
& "Spawns a new process running the function passed in."
|
||||
& (f as :fn) -> base :process (:spawn, f)
|
||||
& }
|
||||
|
||||
fn yield! {
|
||||
"Forces a process to yield."
|
||||
|
@ -1303,7 +1303,7 @@ fn fetch {
|
|||
"Requests the contents of the URL passed in. Returns a result tuple of `(:ok, {contents})` or `(:err, {status code})`."
|
||||
(url) -> {
|
||||
let pid = self ()
|
||||
spawn! (fn () -> request_fetch! (pid, url))
|
||||
spawn! request_fetch! (pid, url)
|
||||
receive {
|
||||
(:reply, response) -> response
|
||||
}
|
||||
|
@ -1312,7 +1312,7 @@ fn fetch {
|
|||
|
||||
fn input_reader! {
|
||||
(pid as :keyword) -> {
|
||||
if not (unbox (input))
|
||||
if do input > unbox > not
|
||||
then {
|
||||
yield! ()
|
||||
input_reader! (pid)
|
||||
|
@ -1328,7 +1328,7 @@ fn read_input {
|
|||
"Waits until there is input in the input buffer, and returns it once there is."
|
||||
() -> {
|
||||
let pid = self ()
|
||||
spawn! (fn () -> input_reader! (pid))
|
||||
spawn! input_reader! (pid)
|
||||
receive {
|
||||
(:reply, response) -> response
|
||||
}
|
||||
|
@ -1339,7 +1339,7 @@ fn read_input {
|
|||
& completed actor functions
|
||||
self
|
||||
send
|
||||
spawn!
|
||||
& spawn!
|
||||
yield!
|
||||
sleep!
|
||||
alive?
|
||||
|
|
27
pkg/ludus.js
27
pkg/ludus.js
|
@ -13,18 +13,6 @@ let ready = false
|
|||
let io_interval_id = null
|
||||
let keys_down = new Set();
|
||||
|
||||
function reset_ludus () {
|
||||
outbox = []
|
||||
ludus_console = ""
|
||||
ludus_commands = []
|
||||
ludus_result = null
|
||||
code = null
|
||||
running = false
|
||||
ready = false
|
||||
io_interval_id = null
|
||||
keys_down = new Set()
|
||||
}
|
||||
|
||||
worker.onmessage = handle_messages
|
||||
|
||||
async function handle_messages (e) {
|
||||
|
@ -99,17 +87,28 @@ function io_poller () {
|
|||
}
|
||||
|
||||
function start_io_polling () {
|
||||
io_interval_id = setInterval(io_poller, 10)
|
||||
io_interval_id = setInterval(io_poller, 100)
|
||||
}
|
||||
|
||||
// runs a ludus script; does not return the result
|
||||
// the result must be explicitly polled with `result`
|
||||
export function run (source) {
|
||||
if (running || ready) {
|
||||
console.log("Main: received bouncy `run` call");
|
||||
return "TODO: handle this? should not be running"
|
||||
}
|
||||
// start the vm
|
||||
worker.postMessage([{verb: "Run", data: source}])
|
||||
reset_ludus()
|
||||
// reset all my state
|
||||
outbox = []
|
||||
ludus_console = ""
|
||||
ludus_commands = []
|
||||
ludus_result = null
|
||||
code = null
|
||||
running = true
|
||||
ready = false
|
||||
keys_down = new Set();
|
||||
// start the polling loop loop
|
||||
start_io_polling()
|
||||
}
|
||||
|
||||
|
|
|
@ -403,7 +403,7 @@ function __wbg_get_imports() {
|
|||
_assertBoolean(ret);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper8065 = function() { return logError(function (arg0, arg1, arg2) {
|
||||
imports.wbg.__wbindgen_closure_wrapper8087 = function() { return logError(function (arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 354, __wbg_adapter_20);
|
||||
return ret;
|
||||
}, arguments) };
|
||||
|
|
Binary file not shown.
|
@ -18,7 +18,7 @@ export function io (out) {
|
|||
resolve(JSON.stringify(e.data))
|
||||
}
|
||||
// cancel the response if it takes too long
|
||||
setTimeout(() => reject("io took too long"), 500)
|
||||
setTimeout(() => reject("io took too long"), 1000)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ pub enum Ast {
|
|||
WhenClause(Box<Spanned<Self>>, Box<Spanned<Self>>),
|
||||
Match(Box<Spanned<Self>>, Vec<Spanned<Self>>),
|
||||
Receive(Vec<Spanned<Self>>),
|
||||
Spawn(Box<Spanned<Self>>),
|
||||
MatchClause(
|
||||
Box<Spanned<Self>>,
|
||||
Box<Option<Spanned<Self>>>,
|
||||
|
@ -210,6 +211,7 @@ impl Ast {
|
|||
.join(" > ")
|
||||
)
|
||||
}
|
||||
Spawn(body) => format!("spawn! {}", body.0.show()),
|
||||
Repeat(times, body) => format!("repeat {} {{\n{}\n}}", times.0.show(), body.0.show()),
|
||||
Splat(word) => format!("...{}", word),
|
||||
Splattern(pattern) => format!("...{}", pattern.0.show()),
|
||||
|
@ -380,6 +382,7 @@ impl fmt::Display for Ast {
|
|||
.join(" > ")
|
||||
)
|
||||
}
|
||||
Spawn(body) => write!(f, "spawn: {}", body.0),
|
||||
Repeat(_times, _body) => todo!(),
|
||||
Splat(word) => {
|
||||
write!(f, "splat: {}", word)
|
||||
|
|
|
@ -43,7 +43,7 @@ impl Chunk {
|
|||
| Not | Panic | EmptyString | ConcatStrings | Stringify | MatchType | Return
|
||||
| UnconditionalMatch | Print | AppendList | ConcatList | PushList | PushDict
|
||||
| AppendDict | ConcatDict | Nothing | PushGlobal | SetUpvalue | LoadMessage
|
||||
| NextMessage | MatchMessage | ClearMessage | SendMethod | LoadScrutinee => {
|
||||
| NextMessage | MatchMessage | ClearMessage | SendMethod | LoadScrutinee | Spawn => {
|
||||
console_log!("{i:04}: {op}")
|
||||
}
|
||||
Constant | MatchConstant => {
|
||||
|
|
|
@ -1059,6 +1059,54 @@ impl Compiler {
|
|||
self.emit_op(Op::Load);
|
||||
self.stack_depth += 1;
|
||||
}
|
||||
Spawn(body) => {
|
||||
// we compile this identically to a function
|
||||
// but it's a single, nullary fn
|
||||
// no match at the beginning, just go
|
||||
let name = "_spawned";
|
||||
let mut compiler = Compiler::new(
|
||||
body,
|
||||
self.input,
|
||||
self.src,
|
||||
self.depth + 1,
|
||||
self.chunk.env.clone(),
|
||||
self.debug,
|
||||
);
|
||||
compiler.tail_pos = true;
|
||||
compiler.visit(body);
|
||||
compiler.store();
|
||||
compiler.scope_depth -= 1;
|
||||
while let Some(binding) = compiler.bindings.last() {
|
||||
if binding.depth > compiler.scope_depth {
|
||||
compiler.bindings.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
compiler.pop_n(compiler.stack_depth);
|
||||
compiler.stack_depth = 0;
|
||||
compiler.emit_op(Op::Return);
|
||||
let arities = vec![0];
|
||||
let chunks = vec![compiler.chunk];
|
||||
let lfn = crate::value::LFn::Defined {
|
||||
name,
|
||||
doc: None,
|
||||
arities,
|
||||
chunks,
|
||||
splat: 0,
|
||||
closed: RefCell::new(vec![]),
|
||||
};
|
||||
|
||||
let the_fn = Value::Fn(Rc::new(lfn));
|
||||
|
||||
self.emit_constant(the_fn);
|
||||
for upvalue in compiler.upvalues {
|
||||
self.resolve_binding(upvalue);
|
||||
self.emit_op(Op::SetUpvalue);
|
||||
self.stack_depth -= 1;
|
||||
}
|
||||
self.emit_op(Op::Spawn);
|
||||
}
|
||||
Receive(clauses) => {
|
||||
let tail_pos = self.tail_pos;
|
||||
self.emit_op(Op::ClearMessage);
|
||||
|
|
|
@ -82,10 +82,13 @@ pub async fn send_err_to_ludus_console(msg: String) {
|
|||
|
||||
pub async fn do_io (msgs: Vec<MsgOut>) -> Vec<MsgIn> {
|
||||
let json = serde_json::to_string(&msgs).unwrap();
|
||||
let inbox = io (json).await;
|
||||
let inbox = io(json).await;
|
||||
let inbox = match inbox {
|
||||
Ok(msgs) => msgs,
|
||||
Err(_) => return vec![]
|
||||
Err(errs) => {
|
||||
console_log!("error receiving messages in io; {:?}", errs);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
let inbox = inbox.as_string().expect("response should be a string");
|
||||
let inbox: Vec<MsgIn> = serde_json::from_str(inbox.as_str()).expect("response from js should be valid");
|
||||
|
|
|
@ -76,7 +76,7 @@ pub fn lexer(
|
|||
"nil" => Token::Nil,
|
||||
// todo: hard code these as type constructors
|
||||
"as" | "box" | "do" | "else" | "fn" | "if" | "let" | "loop" | "match" | "panic!"
|
||||
| "recur" | "repeat" | "then" | "when" | "with" | "or" | "and" | "receive" => {
|
||||
| "recur" | "repeat" | "then" | "when" | "with" | "or" | "and" | "receive" | "spawn!" => {
|
||||
Token::Reserved(word)
|
||||
}
|
||||
_ => Token::Word(word),
|
||||
|
|
|
@ -106,6 +106,7 @@ pub enum Op {
|
|||
MatchMessage,
|
||||
ClearMessage,
|
||||
SendMethod,
|
||||
Spawn,
|
||||
|
||||
LoadScrutinee,
|
||||
}
|
||||
|
@ -208,6 +209,7 @@ impl std::fmt::Display for Op {
|
|||
MatchMessage => "match_message",
|
||||
ClearMessage => "clear_message",
|
||||
SendMethod => "send_method",
|
||||
Spawn => "spawn",
|
||||
|
||||
LoadScrutinee => "load_scrutinee",
|
||||
};
|
||||
|
|
|
@ -512,6 +512,10 @@ where
|
|||
.then(block.clone())
|
||||
.map_with(|(count, body), e| (Repeat(Box::new(count), Box::new(body)), e.span()));
|
||||
|
||||
let spawn = just(Token::Reserved("spawn!"))
|
||||
.ignore_then(nonbinding.clone())
|
||||
.map_with(|body, e| (Spawn(Box::new(body)), e.span()));
|
||||
|
||||
let fn_guarded = tuple_pattern
|
||||
.clone()
|
||||
.then_ignore(just(Token::Reserved("if")))
|
||||
|
@ -590,6 +594,7 @@ where
|
|||
.or(conditional)
|
||||
.or(block)
|
||||
.or(repeat)
|
||||
.or(spawn)
|
||||
.or(r#loop)
|
||||
.labelled("nonbinding expression"),
|
||||
);
|
||||
|
|
|
@ -164,6 +164,7 @@ impl<'a> Validator<'a> {
|
|||
let root = self.ast;
|
||||
match root {
|
||||
Error => unreachable!(),
|
||||
Spawn(body) => self.visit(body),
|
||||
Word(name) | Splat(name) => {
|
||||
if !self.resolved(name) {
|
||||
self.err(format!("unbound name `{name}`"))
|
||||
|
|
Loading…
Reference in New Issue
Block a user