method syntax sugar achieved

This commit is contained in:
Scott Richmond 2025-07-02 19:29:49 -04:00
parent 12389ae371
commit 0cd682de21
10 changed files with 96 additions and 31 deletions

View File

@ -425,7 +425,7 @@ function __wbg_get_imports() {
_assertBoolean(ret); _assertBoolean(ret);
return 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); const ret = makeMutClosure(arg0, arg1, 347, __wbg_adapter_22);
return ret; return ret;
}, arguments) }; }, arguments) };

Binary file not shown.

View File

@ -34,6 +34,7 @@ pub enum Ast {
Boolean(bool), Boolean(bool),
Number(f64), Number(f64),
Keyword(&'static str), Keyword(&'static str),
Method(&'static str, Box<Spanned<Self>>),
Word(&'static str), Word(&'static str),
String(&'static str), String(&'static str),
Interpolated(Vec<Spanned<StringPart>>), Interpolated(Vec<Spanned<StringPart>>),
@ -103,6 +104,7 @@ impl Ast {
Boolean(b) | BooleanPattern(b) => b.to_string(), Boolean(b) | BooleanPattern(b) => b.to_string(),
Number(n) | NumberPattern(n) => n.to_string(), Number(n) | NumberPattern(n) => n.to_string(),
Keyword(k) | KeywordPattern(k) => format!(":{k}"), Keyword(k) | KeywordPattern(k) => format!(":{k}"),
Method(m, args) => format!("::{m} {}", args.0),
Word(w) | WordPattern(w) => w.to_string(), Word(w) | WordPattern(w) => w.to_string(),
Block(lines) => { Block(lines) => {
let mut out = "{\n".to_string(); let mut out = "{\n".to_string();
@ -260,6 +262,7 @@ impl fmt::Display for Ast {
Boolean(b) => write!(f, "Boolean: {}", b), Boolean(b) => write!(f, "Boolean: {}", b),
Number(n) => write!(f, "Number: {}", n), Number(n) => write!(f, "Number: {}", n),
Keyword(k) => write!(f, "Keyword: :{}", k), Keyword(k) => write!(f, "Keyword: :{}", k),
Method(m, args) => write!(f, "Method: ::{m} ({})", args.0),
Word(w) => write!(f, "Word: {}", w), Word(w) => write!(f, "Word: {}", w),
Block(b) => write!( Block(b) => write!(
f, f,

View File

@ -37,7 +37,8 @@ impl Chunk {
| Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString | Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString
| ConcatStrings | Stringify | MatchType | Return | UnconditionalMatch | Print | ConcatStrings | Stringify | MatchType | Return | UnconditionalMatch | Print
| AppendList | ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing | AppendList | ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing
| PushGlobal | SetUpvalue | LoadMessage | NextMessage | MatchMessage | ClearMessage => { | PushGlobal | SetUpvalue | LoadMessage | NextMessage | MatchMessage | ClearMessage
| SendMethod => {
println!("{i:04}: {op}") println!("{i:04}: {op}")
} }
Constant | MatchConstant => { Constant | MatchConstant => {

View File

@ -860,6 +860,14 @@ impl Compiler {
self.stack_depth -= 1; self.stack_depth -= 1;
self.report_depth("after keyword access"); 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)) => { (Keyword(_), Arguments(args)) => {
self.visit(&args[0]); self.visit(&args[0]);
self.visit(first); self.visit(first);
@ -954,8 +962,16 @@ impl Compiler {
Keyword(str) => { Keyword(str) => {
self.emit_constant(Value::Keyword(str)); self.emit_constant(Value::Keyword(str));
self.emit_op(Op::GetKey); self.emit_op(Op::GetKey);
// target, keyword -> value
self.stack_depth -= 1; 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) => { Arguments(args) => {
self.store(); self.store();
let arity = args.len(); let arity = args.len();
@ -1475,7 +1491,7 @@ impl Compiler {
Placeholder => { Placeholder => {
self.emit_op(Op::Nothing); self.emit_op(Op::Nothing);
} }
And | Or | Arguments(..) => unreachable!(), And | Or | Arguments(..) | Method(..) => unreachable!(),
} }
} }

View File

@ -13,6 +13,7 @@ pub enum Token {
// todo: hard code these types // todo: hard code these types
Reserved(&'static str), Reserved(&'static str),
Punctuation(&'static str), Punctuation(&'static str),
Method(&'static str),
} }
impl fmt::Display for Token { impl fmt::Display for Token {
@ -26,6 +27,7 @@ impl fmt::Display for Token {
Token::Reserved(r) => write!(f, "[Reserved {}]", r), Token::Reserved(r) => write!(f, "[Reserved {}]", r),
Token::Nil => write!(f, "[nil]"), Token::Nil => write!(f, "[nil]"),
Token::Punctuation(p) => write!(f, "[Punctuation {}]", p), Token::Punctuation(p) => write!(f, "[Punctuation {}]", p),
Token::Method(m) => write!(f, "[Method {m}]"),
} }
} }
} }
@ -62,6 +64,8 @@ pub fn lexer(
_ => Token::Word(word), _ => Token::Word(word),
}); });
let method = just("::").ignore_then(word).map(Token::Method);
let keyword = just(':').ignore_then(word).map(Token::Keyword); let keyword = just(':').ignore_then(word).map(Token::Keyword);
let string = just('"') let string = just('"')
@ -81,6 +85,7 @@ pub fn lexer(
let token = number let token = number
.or(reserved_or_word) .or(reserved_or_word)
.or(keyword) .or(keyword)
.or(method)
.or(string) .or(string)
.or(punctuation); .or(punctuation);

View File

@ -94,6 +94,7 @@ pub enum Op {
NextMessage, NextMessage,
MatchMessage, MatchMessage,
ClearMessage, ClearMessage,
SendMethod,
// Inc, // Inc,
// Dec, // Dec,
// Gt, // Gt,
@ -230,6 +231,7 @@ impl std::fmt::Display for Op {
NextMessage => "next_message", NextMessage => "next_message",
MatchMessage => "match_message", MatchMessage => "match_message",
ClearMessage => "clear_message", ClearMessage => "clear_message",
SendMethod => "send_method",
}; };
write!(f, "{rep}") write!(f, "{rep}")
} }

View File

@ -233,7 +233,7 @@ where
) )
.map_with(|dict, e| (DictPattern(dict), e.span())); .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} let as_pattern = select! {Token::Word(w) => w}
.then_ignore(just(Token::Reserved("as"))) .then_ignore(just(Token::Reserved("as")))
@ -300,9 +300,13 @@ where
let and = just(Token::Reserved("and")).map_with(|_, e| (And, e.span())); 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_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 let synthetic = synth_root
.then(synth_term.clone()) .then(synth_term.clone())

View File

@ -284,6 +284,13 @@ impl<'a> Validator<'a> {
// check arity against fn info if first term is word and second term is args // check arity against fn info if first term is word and second term is args
Synthetic(first, second, rest) => { Synthetic(first, second, rest) => {
match (&first.0, &second.0) { 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(_)) => { (Ast::And, Ast::Arguments(_)) | (Ast::Or, Ast::Arguments(_)) => {
self.visit(second.as_ref()) self.visit(second.as_ref())
} }
@ -587,7 +594,7 @@ impl<'a> Validator<'a> {
} }
PairPattern(_, patt) => self.visit(patt.as_ref()), PairPattern(_, patt) => self.visit(patt.as_ref()),
// terminals can never be invalid // 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 // terminal patterns can never be invalid
NilPattern | BooleanPattern(..) | NumberPattern(..) | StringPattern(..) NilPattern | BooleanPattern(..) | NumberPattern(..) | StringPattern(..)
| KeywordPattern(..) | PlaceholderPattern => (), | KeywordPattern(..) | PlaceholderPattern => (),

View File

@ -234,12 +234,14 @@ impl Creature {
let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack()); let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack());
println!("process {} panicked!\n{msg}", self.pid); println!("process {} panicked!\n{msg}", self.pid);
self.result = Some(Err(Panic::String(msg))); self.result = Some(Err(Panic::String(msg)));
self.r#yield = true;
} }
pub fn panic_with(&mut self, msg: String) { pub fn panic_with(&mut self, msg: String) {
let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack()); let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack());
println!("process {} panicked!\n{msg}", self.pid); println!("process {} panicked!\n{msg}", self.pid);
self.result = Some(Err(Panic::String(msg))); self.result = Some(Err(Panic::String(msg)));
self.r#yield = true;
} }
fn get_value_at(&mut self, idx: u8) -> Value { fn get_value_at(&mut self, idx: u8) -> Value {
@ -269,6 +271,18 @@ impl Creature {
self.ip >= self.chunk().bytecode.len() 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<Value>) { fn handle_msg(&mut self, args: Vec<Value>) {
println!("message received by {}: {}", self.pid, args[0]); println!("message received by {}: {}", self.pid, args[0]);
let Value::Keyword(msg) = args.first().unwrap() else { let Value::Keyword(msg) = args.first().unwrap() else {
@ -277,24 +291,25 @@ impl Creature {
match *msg { match *msg {
"self" => self.push(Value::Keyword(self.pid)), "self" => self.push(Value::Keyword(self.pid)),
"send" => { "send" => {
let Value::Keyword(pid) = args[1] else { self.send_msg(args[1].clone(), args[2].clone())
return self.panic("malformed pid"); // let Value::Keyword(pid) = args[1] else {
}; // return self.panic("malformed pid");
println!( // };
"sending msg from {} to {} of {}", // println!(
self.pid, // "sending msg from {} to {} of {}",
pid, // self.pid,
args[2].show() // pid,
); // args[2].show()
if self.pid == pid { // );
self.mbx.push_back(args[2].clone()); // if self.pid == pid {
} else { // self.mbx.push_back(args[2].clone());
self.zoo // } else {
.as_ref() // self.zoo
.borrow_mut() // .as_ref()
.send_msg(pid, args[2].clone()); // .borrow_mut()
} // .send_msg(pid, args[2].clone());
self.push(Value::Keyword("ok")); // }
// self.push(Value::Keyword("ok"));
} }
"spawn" => { "spawn" => {
let f = args[1].clone(); let f = args[1].clone();
@ -376,19 +391,19 @@ impl Creature {
loop { loop {
if self.at_end() { if self.at_end() {
let result = self.stack.pop().unwrap(); 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)); self.result = Some(Ok(result));
return; return;
} }
if self.r#yield { if self.r#yield {
println!("process {} has explicitly yielded", self.pid); // println!("process {} has explicitly yielded", self.pid);
return; return;
} }
if self.reductions >= MAX_REDUCTIONS { if self.reductions >= MAX_REDUCTIONS {
println!( // println!(
"process {} is yielding after {MAX_REDUCTIONS} reductions", // "process {} is yielding after {MAX_REDUCTIONS} reductions",
self.pid // self.pid
); // );
return; return;
} }
let code = self.read(); let code = self.read();
@ -1231,11 +1246,23 @@ impl Creature {
} }
}, },
MatchMessage => { MatchMessage => {
let matched = self.mbx.remove(self.msg_idx).unwrap(); self.mbx.remove(self.msg_idx).unwrap();
} }
ClearMessage => { ClearMessage => {
self.msg_idx = 0; 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));
}
} }
} }
} }