diff --git a/pkg/rudus.js b/pkg/rudus.js index 3cdd292..284525a 100644 --- a/pkg/rudus.js +++ b/pkg/rudus.js @@ -425,7 +425,7 @@ function __wbg_get_imports() { _assertBoolean(ret); return ret; }; - imports.wbg.__wbindgen_closure_wrapper7701 = function() { return logError(function (arg0, arg1, arg2) { + imports.wbg.__wbindgen_closure_wrapper7779 = function() { return logError(function (arg0, arg1, arg2) { const ret = makeMutClosure(arg0, arg1, 347, __wbg_adapter_22); return ret; }, arguments) }; diff --git a/pkg/rudus_bg.wasm b/pkg/rudus_bg.wasm index 201e08b..855a541 100644 Binary files a/pkg/rudus_bg.wasm and b/pkg/rudus_bg.wasm differ diff --git a/src/ast.rs b/src/ast.rs index 628dbd6..30e28c8 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -34,6 +34,7 @@ pub enum Ast { Boolean(bool), Number(f64), Keyword(&'static str), + Method(&'static str, Box>), Word(&'static str), String(&'static str), Interpolated(Vec>), @@ -103,6 +104,7 @@ impl Ast { Boolean(b) | BooleanPattern(b) => b.to_string(), Number(n) | NumberPattern(n) => n.to_string(), Keyword(k) | KeywordPattern(k) => format!(":{k}"), + Method(m, args) => format!("::{m} {}", args.0), Word(w) | WordPattern(w) => w.to_string(), Block(lines) => { let mut out = "{\n".to_string(); @@ -260,6 +262,7 @@ impl fmt::Display for Ast { Boolean(b) => write!(f, "Boolean: {}", b), Number(n) => write!(f, "Number: {}", n), Keyword(k) => write!(f, "Keyword: :{}", k), + Method(m, args) => write!(f, "Method: ::{m} ({})", args.0), Word(w) => write!(f, "Word: {}", w), Block(b) => write!( f, diff --git a/src/chunk.rs b/src/chunk.rs index 57c81c1..e86826d 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -37,7 +37,8 @@ impl Chunk { | Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString | ConcatStrings | Stringify | MatchType | Return | UnconditionalMatch | Print | AppendList | ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing - | PushGlobal | SetUpvalue | LoadMessage | NextMessage | MatchMessage | ClearMessage => { + | PushGlobal | SetUpvalue | LoadMessage | NextMessage | MatchMessage | ClearMessage + | SendMethod => { println!("{i:04}: {op}") } Constant | MatchConstant => { diff --git a/src/compiler.rs b/src/compiler.rs index 134726c..483b2a4 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -860,6 +860,14 @@ impl Compiler { self.stack_depth -= 1; self.report_depth("after keyword access"); } + (Keyword(_), Method(str, args)) | (Word(_), Method(str, args)) => { + self.visit(first); + self.emit_constant(Value::Keyword(str)); + self.visit(args); + self.emit_op(Op::SendMethod); + // target, method, args -> result + self.stack_depth -= 2; + } (Keyword(_), Arguments(args)) => { self.visit(&args[0]); self.visit(first); @@ -954,8 +962,16 @@ impl Compiler { Keyword(str) => { self.emit_constant(Value::Keyword(str)); self.emit_op(Op::GetKey); + // target, keyword -> value self.stack_depth -= 1; } + Method(str, args) => { + self.emit_constant(Value::Keyword(str)); + self.visit(args); + self.emit_op(Op::SendMethod); + // target, method, args -> result + self.stack_depth -= 2; + } Arguments(args) => { self.store(); let arity = args.len(); @@ -1475,7 +1491,7 @@ impl Compiler { Placeholder => { self.emit_op(Op::Nothing); } - And | Or | Arguments(..) => unreachable!(), + And | Or | Arguments(..) | Method(..) => unreachable!(), } } diff --git a/src/lexer.rs b/src/lexer.rs index 4ed7811..d89ea1d 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -13,6 +13,7 @@ pub enum Token { // todo: hard code these types Reserved(&'static str), Punctuation(&'static str), + Method(&'static str), } impl fmt::Display for Token { @@ -26,6 +27,7 @@ impl fmt::Display for Token { Token::Reserved(r) => write!(f, "[Reserved {}]", r), Token::Nil => write!(f, "[nil]"), Token::Punctuation(p) => write!(f, "[Punctuation {}]", p), + Token::Method(m) => write!(f, "[Method {m}]"), } } } @@ -62,6 +64,8 @@ pub fn lexer( _ => Token::Word(word), }); + let method = just("::").ignore_then(word).map(Token::Method); + let keyword = just(':').ignore_then(word).map(Token::Keyword); let string = just('"') @@ -81,6 +85,7 @@ pub fn lexer( let token = number .or(reserved_or_word) .or(keyword) + .or(method) .or(string) .or(punctuation); diff --git a/src/op.rs b/src/op.rs index 3a9911f..7dbae4f 100644 --- a/src/op.rs +++ b/src/op.rs @@ -94,6 +94,7 @@ pub enum Op { NextMessage, MatchMessage, ClearMessage, + SendMethod, // Inc, // Dec, // Gt, @@ -230,6 +231,7 @@ impl std::fmt::Display for Op { NextMessage => "next_message", MatchMessage => "match_message", ClearMessage => "clear_message", + SendMethod => "send_method", }; write!(f, "{rep}") } diff --git a/src/parser.rs b/src/parser.rs index 517ef53..5ba3d1d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -233,7 +233,7 @@ where ) .map_with(|dict, e| (DictPattern(dict), e.span())); - let keyword = select! {Token::Keyword(k) => Keyword(k),}.map_with(|k, e| (k, e.span())); + let keyword = select! {Token::Keyword(k) => Keyword(k)}.map_with(|k, e| (k, e.span())); let as_pattern = select! {Token::Word(w) => w} .then_ignore(just(Token::Reserved("as"))) @@ -300,9 +300,13 @@ where let and = just(Token::Reserved("and")).map_with(|_, e| (And, e.span())); + let method = select!(Token::Method(m) => m) + .then(tuple.clone()) + .map_with(|(m, t), e| (Ast::Method(m, Box::new(t)), e.span())); + let synth_root = or.or(and).or(word).or(keyword); - let synth_term = keyword.or(args); + let synth_term = keyword.or(args).or(method); let synthetic = synth_root .then(synth_term.clone()) diff --git a/src/validator.rs b/src/validator.rs index 22a2e62..9174b79 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -284,6 +284,13 @@ impl<'a> Validator<'a> { // check arity against fn info if first term is word and second term is args Synthetic(first, second, rest) => { match (&first.0, &second.0) { + (Ast::Word(_), Ast::Method(_, args)) => { + self.visit(first.as_ref()); + self.visit(args); + } + (Ast::Keyword(_), Ast::Method(_, args)) => { + self.visit(args); + } (Ast::And, Ast::Arguments(_)) | (Ast::Or, Ast::Arguments(_)) => { self.visit(second.as_ref()) } @@ -587,7 +594,7 @@ impl<'a> Validator<'a> { } PairPattern(_, patt) => self.visit(patt.as_ref()), // terminals can never be invalid - Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) | And | Or => (), + Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) | And | Or | Method(..) => (), // terminal patterns can never be invalid NilPattern | BooleanPattern(..) | NumberPattern(..) | StringPattern(..) | KeywordPattern(..) | PlaceholderPattern => (), diff --git a/src/vm.rs b/src/vm.rs index 8ad7e86..a3d6bd5 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -234,12 +234,14 @@ impl Creature { let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack()); println!("process {} panicked!\n{msg}", self.pid); self.result = Some(Err(Panic::String(msg))); + self.r#yield = true; } pub fn panic_with(&mut self, msg: String) { let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack()); println!("process {} panicked!\n{msg}", self.pid); self.result = Some(Err(Panic::String(msg))); + self.r#yield = true; } fn get_value_at(&mut self, idx: u8) -> Value { @@ -269,6 +271,18 @@ impl Creature { self.ip >= self.chunk().bytecode.len() } + fn send_msg(&mut self, pid: Value, msg: Value) { + let Value::Keyword(pid) = pid else { + return self.panic_with(format!("Ludus expected pid keyword, and instead got {pid}")); + }; + if self.pid == pid { + self.mbx.push_back(msg.clone()); + } else { + self.zoo.as_ref().borrow_mut().send_msg(pid, msg); + } + self.push(Value::Keyword("ok")); + } + fn handle_msg(&mut self, args: Vec) { println!("message received by {}: {}", self.pid, args[0]); let Value::Keyword(msg) = args.first().unwrap() else { @@ -277,24 +291,25 @@ impl Creature { match *msg { "self" => self.push(Value::Keyword(self.pid)), "send" => { - let Value::Keyword(pid) = args[1] else { - return self.panic("malformed pid"); - }; - println!( - "sending msg from {} to {} of {}", - self.pid, - pid, - args[2].show() - ); - if self.pid == pid { - self.mbx.push_back(args[2].clone()); - } else { - self.zoo - .as_ref() - .borrow_mut() - .send_msg(pid, args[2].clone()); - } - self.push(Value::Keyword("ok")); + self.send_msg(args[1].clone(), args[2].clone()) + // let Value::Keyword(pid) = args[1] else { + // return self.panic("malformed pid"); + // }; + // println!( + // "sending msg from {} to {} of {}", + // self.pid, + // pid, + // args[2].show() + // ); + // if self.pid == pid { + // self.mbx.push_back(args[2].clone()); + // } else { + // self.zoo + // .as_ref() + // .borrow_mut() + // .send_msg(pid, args[2].clone()); + // } + // self.push(Value::Keyword("ok")); } "spawn" => { let f = args[1].clone(); @@ -376,19 +391,19 @@ impl Creature { loop { if self.at_end() { let result = self.stack.pop().unwrap(); - println!("process {} has returned {result}", self.pid); + // println!("process {} has returned {result}", self.pid); self.result = Some(Ok(result)); return; } if self.r#yield { - println!("process {} has explicitly yielded", self.pid); + // println!("process {} has explicitly yielded", self.pid); return; } if self.reductions >= MAX_REDUCTIONS { - println!( - "process {} is yielding after {MAX_REDUCTIONS} reductions", - self.pid - ); + // println!( + // "process {} is yielding after {MAX_REDUCTIONS} reductions", + // self.pid + // ); return; } let code = self.read(); @@ -1231,11 +1246,23 @@ impl Creature { } }, MatchMessage => { - let matched = self.mbx.remove(self.msg_idx).unwrap(); + self.mbx.remove(self.msg_idx).unwrap(); } ClearMessage => { self.msg_idx = 0; } + SendMethod => { + let Value::Tuple(args) = self.pop() else { + unreachable!("method args should be a tuple"); + }; + let method = self.pop(); + let target = self.pop(); + let mut msg = vec![method]; + for arg in args.as_ref() { + msg.push(arg.clone()); + } + self.send_msg(target, Value::tuple(msg)); + } } } }