diff --git a/assets/test_prelude.ld b/assets/test_prelude.ld index 189d58c..5e24eef 100644 --- a/assets/test_prelude.ld +++ b/assets/test_prelude.ld @@ -1218,8 +1218,11 @@ fn self { } fn send { - "Sends a message to the specified process." - (pid as :keyword, msg) -> base :process (:send, pid, msg) + "Sends a message to the specified process and returns the message." + (pid as :keyword, msg) -> { + base :process (:send, pid, msg) + msg + } } fn spawn! { @@ -1245,21 +1248,11 @@ fn link! { (pid1 as :keyword, pid2 as :keyword, :enforce) -> base :process (:link_enforce, pid1, pid2) } -fn msgs { - "Returns the entire contents of the current process as a list. Leaves all messages in the process mailbox." - () -> base :process (:msgs) -} - fn flush! { - "Clears the current process's mailbox." + "Clears the current process's mailbox and returns all the messages." () -> base :process (:flush) } -fn flush_i! { - "Flushes the message at the indicated index in the current process's mailbox." - (i as :number) -> base :process (:flush_i, i) -} - fn sleep! { "Puts the current process to sleep for at least the specified number of milliseconds." (ms as :number) -> base :process (:sleep, ms) diff --git a/may_2025_thoughts.md b/may_2025_thoughts.md index e4b19e9..bc7eb26 100644 --- a/may_2025_thoughts.md +++ b/may_2025_thoughts.md @@ -969,7 +969,8 @@ match (nil) with { In particular, function arities need to be changed, flushes need to be inserted, anonymous lambdas need to be created (which can't be multi-clause), etc. -* [ ] There was another bug that I was going to write down and fix, but I forgot what it was. Something with processes. +~* [ ] There was another bug that I was going to write down and fix, but I forgot what it was. Something with processes.~ +* [ ] I remembered: I got some weird behaviour when `MAX_REDUCTIONS` was set to 100; I've increased it to 1000, but now need to test what happens when we yield because of reductions. * [ ] Also: the `butlast` bug is still outstanding: `base :slice` causes a panic in that function, but not the call to the Ludus function. Still have to investigate that one. diff --git a/src/compiler.rs b/src/compiler.rs index 7a03288..a59581a 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,7 +1,6 @@ +use crate::ast::{Ast, StringPart}; use crate::chunk::{Chunk, StrPattern}; use crate::op::Op; -use crate::parser::Ast; -use crate::parser::StringPart; use crate::spans::Spanned; use crate::value::*; use chumsky::prelude::SimpleSpan; diff --git a/src/lib.rs b/src/lib.rs index f1a02c8..f5beeb2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,9 @@ const DEBUG_SCRIPT_RUN: bool = false; const DEBUG_PRELUDE_COMPILE: bool = false; const DEBUG_PRELUDE_RUN: bool = false; +mod ast; +use crate::ast::Ast; + mod base; mod world; @@ -19,7 +22,7 @@ mod lexer; use crate::lexer::lexer; mod parser; -use crate::parser::{parser, Ast}; +use crate::parser::parser; mod validator; use crate::validator::Validator; diff --git a/src/parser.rs b/src/parser.rs index e13bc5c..38f4c4b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2,466 +2,12 @@ // TODO: remove StringMatcher cruft // TODO: good error messages? +use crate::ast::{Ast, StringPart}; use crate::lexer::*; use crate::spans::*; use chumsky::{input::ValueInput, prelude::*, recursive::Recursive}; use std::fmt; -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum StringPart { - Data(String), - Word(String), - Inline(String), -} - -impl fmt::Display for StringPart { - fn fmt(self: &StringPart, f: &mut fmt::Formatter) -> fmt::Result { - let rep = match self { - StringPart::Word(s) => format!("{{{s}}}"), - StringPart::Data(s) => s.to_string(), - StringPart::Inline(s) => s.to_string(), - }; - write!(f, "{}", rep) - } -} - -#[derive(Clone, Debug, PartialEq)] -pub enum Ast { - // a special Error node - // may come in handy? - Error, - - And, - Or, - - // expression nodes - Placeholder, - Nil, - Boolean(bool), - Number(f64), - Keyword(&'static str), - Word(&'static str), - String(&'static str), - Interpolated(Vec>), - Block(Vec>), - If(Box>, Box>, Box>), - Tuple(Vec>), - Arguments(Vec>), - List(Vec>), - Dict(Vec>), - Let(Box>, Box>), - LBox(&'static str, Box>), - Synthetic(Box>, Box>, Vec>), - When(Vec>), - WhenClause(Box>, Box>), - Match(Box>, Vec>), - MatchClause( - Box>, - Box>>, - Box>, - ), - Fn(&'static str, Box>, Option<&'static str>), - FnBody(Vec>), - FnDeclaration(&'static str), - Panic(Box>), - Do(Vec>), - Repeat(Box>, Box>), - Splat(&'static str), - Pair(&'static str, Box>), - Loop(Box>, Vec>), - Recur(Vec>), - - // pattern nodes - NilPattern, - BooleanPattern(bool), - NumberPattern(f64), - StringPattern(&'static str), - InterpolatedPattern(Vec>, StringMatcher), - KeywordPattern(&'static str), - WordPattern(&'static str), - AsPattern(&'static str, &'static str), - Splattern(Box>), - PlaceholderPattern, - TuplePattern(Vec>), - ListPattern(Vec>), - PairPattern(&'static str, Box>), - DictPattern(Vec>), -} - -impl Ast { - pub fn show(&self) -> String { - use Ast::*; - match self { - And => "and".to_string(), - Or => "or".to_string(), - Error => unreachable!(), - Nil | NilPattern => "nil".to_string(), - String(s) | StringPattern(s) => format!("\"{s}\""), - Interpolated(strs) | InterpolatedPattern(strs, _) => { - let mut out = "".to_string(); - out = format!("\"{out}"); - for (part, _) in strs { - out = format!("{out}{part}"); - } - format!("{out}\"") - } - Boolean(b) | BooleanPattern(b) => b.to_string(), - Number(n) | NumberPattern(n) => n.to_string(), - Keyword(k) | KeywordPattern(k) => format!(":{k}"), - Word(w) | WordPattern(w) => w.to_string(), - Block(lines) => { - let mut out = "{\n".to_string(); - for (line, _) in lines { - out = format!("{out}\n {}", line.show()); - } - format!("{out}\n}}") - } - If(cond, then, r#else) => format!( - "if {}\n then {}\n else {}", - cond.0.show(), - then.0.show(), - r#else.0.show() - ), - Let(pattern, expression) => { - format!("let {} = {}", pattern.0.show(), expression.0.show()) - } - Dict(entries) | DictPattern(entries) => { - format!( - "#{{{}}}", - entries - .iter() - .map(|(pair, _)| pair.show()) - .collect::>() - .join(", ") - ) - } - List(members) | ListPattern(members) => format!( - "[{}]", - members - .iter() - .map(|(member, _)| member.show()) - .collect::>() - .join(", ") - ), - Arguments(members) => format!( - "({})", - members - .iter() - .map(|(member, _)| member.show()) - .collect::>() - .join(", ") - ), - Tuple(members) | TuplePattern(members) => format!( - "({})", - members - .iter() - .map(|(member, _)| member.show()) - .collect::>() - .join(", ") - ), - Synthetic(root, first, rest) => format!( - "{} {} {}", - root.0.show(), - first.0.show(), - rest.iter() - .map(|(term, _)| term.show()) - .collect::>() - .join(" ") - ), - When(clauses) => format!( - "when {{\n {}\n}}", - clauses - .iter() - .map(|(clause, _)| clause.show()) - .collect::>() - .join("\n ") - ), - Placeholder | PlaceholderPattern => "_".to_string(), - LBox(name, rhs) => format!("box {name} = {}", rhs.0.show()), - Match(scrutinee, clauses) => format!( - "match {} with {{\n {}\n}}", - scrutinee.0.show(), - clauses - .iter() - .map(|(clause, _)| clause.show()) - .collect::>() - .join("\n ") - ), - FnBody(clauses) => clauses - .iter() - .map(|(clause, _)| clause.show()) - .collect::>() - .join("\n "), - Fn(name, body, doc) => { - let mut out = format!("fn {name} {{\n"); - if let Some(doc) = doc { - out = format!("{out} {doc}\n"); - } - format!("{out} {}\n}}", body.0.show()) - } - FnDeclaration(name) => format!("fn {name}"), - Panic(expr) => format!("panic! {}", expr.0.show()), - Do(terms) => { - format!( - "do {}", - terms - .iter() - .map(|(term, _)| term.show()) - .collect::>() - .join(" > ") - ) - } - Repeat(times, body) => format!("repeat {} {{\n{}\n}}", times.0.show(), body.0.show()), - Splat(word) => format!("...{}", word), - Splattern(pattern) => format!("...{}", pattern.0.show()), - AsPattern(word, type_keyword) => format!("{word} as :{type_keyword}"), - Pair(key, value) | PairPattern(key, value) => format!(":{key} {}", value.0.show()), - Loop(init, body) => format!( - "loop {} with {{\n {}\n}}", - init.0.show(), - body.iter() - .map(|(clause, _)| clause.show()) - .collect::>() - .join("\n ") - ), - Recur(args) => format!( - "recur ({})", - args.iter() - .map(|(arg, _)| arg.show()) - .collect::>() - .join(", ") - ), - MatchClause(pattern, guard, body) => { - let mut out = pattern.0.show(); - if let Some(guard) = guard.as_ref() { - out = format!("{out} if {}", guard.0.show()); - } - format!("{out} -> {}", body.0.show()) - } - WhenClause(cond, body) => format!("{} -> {}", cond.0.show(), body.0.show()), - } - } -} - -impl fmt::Display for Ast { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use Ast::*; - match self { - And => write!(f, "And"), - Or => write!(f, "Or"), - Error => write!(f, "Error"), - Nil => write!(f, "nil"), - String(s) => write!(f, "String: \"{}\"", s), - Interpolated(strs) => { - write!( - f, - "Interpolated: \"{}\"", - strs.iter() - .map(|(s, _)| s.to_string()) - .collect::>() - .join("") - ) - } - Boolean(b) => write!(f, "Boolean: {}", b), - Number(n) => write!(f, "Number: {}", n), - Keyword(k) => write!(f, "Keyword: :{}", k), - Word(w) => write!(f, "Word: {}", w), - Block(b) => write!( - f, - "Block: <{}>", - b.iter() - .map(|(line, _)| line.to_string()) - .collect::>() - .join("\n") - ), - If(cond, then_branch, else_branch) => write!( - f, - "If: {} Then: {} Else: {}", - cond.0, then_branch.0, else_branch.0 - ), - Let(pattern, expression) => { - write!(f, "Let: {} = {}", pattern.0, expression.0) - } - Dict(entries) => write!( - f, - "#{{{}}}", - entries - .iter() - .map(|pair| pair.0.to_string()) - .collect::>() - .join(", ") - ), - List(l) => write!( - f, - "List: [{}]", - l.iter() - .map(|(line, _)| line.to_string()) - .collect::>() - .join("\n") - ), - Arguments(a) => write!( - f, - "Arguments: ({})", - a.iter() - .map(|(line, _)| line.to_string()) - .collect::>() - .join("\n") - ), - Tuple(t) => write!( - f, - "Tuple: ({})", - t.iter() - .map(|(line, _)| line.to_string()) - .collect::>() - .join("\n") - ), - Synthetic(root, first, rest) => write!( - f, - "Synth: [{}, {}, {}]", - root.0, - first.0, - rest.iter() - .map(|(term, _)| term.to_string()) - .collect::>() - .join("\n") - ), - When(clauses) => write!( - f, - "When: [{}]", - clauses - .iter() - .map(|clause| clause.0.to_string()) - .collect::>() - .join("\n") - ), - Placeholder => write!(f, "Placeholder"), - LBox(_name, _rhs) => todo!(), - Match(value, clauses) => { - write!( - f, - "match: {} with {}", - &value.0.to_string(), - clauses - .iter() - .map(|clause| clause.0.to_string()) - .collect::>() - .join("\n") - ) - } - FnBody(clauses) => { - write!( - f, - "{}", - clauses - .iter() - .map(|clause| clause.0.to_string()) - .collect::>() - .join("\n") - ) - } - Fn(name, body, ..) => { - write!(f, "fn: {name}\n{}", body.0) - } - FnDeclaration(_name) => todo!(), - Panic(_expr) => todo!(), - Do(terms) => { - write!( - f, - "do: {}", - terms - .iter() - .map(|(term, _)| term.to_string()) - .collect::>() - .join(" > ") - ) - } - Repeat(_times, _body) => todo!(), - Splat(word) => { - write!(f, "splat: {}", word) - } - Pair(k, v) => { - write!(f, "pair: {} {}", k, v.0) - } - Loop(init, body) => { - write!( - f, - "loop: {} with {}", - init.0, - body.iter() - .map(|clause| clause.0.to_string()) - .collect::>() - .join("\n") - ) - } - Recur(args) => { - write!( - f, - "recur: {}", - args.iter() - .map(|(arg, _)| arg.to_string()) - .collect::>() - .join(", ") - ) - } - MatchClause(pattern, guard, body) => { - write!( - f, - "match clause: {} if {:?} -> {}", - pattern.0, guard, body.0 - ) - } - WhenClause(cond, body) => { - write!(f, "when clause: {} -> {}", cond.0, body.0) - } - - NilPattern => write!(f, "nil"), - BooleanPattern(b) => write!(f, "{}", b), - NumberPattern(n) => write!(f, "{}", n), - StringPattern(s) => write!(f, "{}", s), - KeywordPattern(k) => write!(f, ":{}", k), - WordPattern(w) => write!(f, "{}", w), - AsPattern(w, t) => write!(f, "{} as :{}", w, t), - Splattern(p) => write!(f, "...{}", p.0), - PlaceholderPattern => write!(f, "_"), - TuplePattern(t) => write!( - f, - "({})", - t.iter() - .map(|x| x.0.to_string()) - .collect::>() - .join(", ") - ), - ListPattern(l) => write!( - f, - "({})", - l.iter() - .map(|x| x.0.to_string()) - .collect::>() - .join(", ") - ), - DictPattern(entries) => write!( - f, - "#{{{}}}", - entries - .iter() - .map(|(pair, _)| pair.to_string()) - .collect::>() - .join(", ") - ), - PairPattern(key, value) => write!(f, ":{} {}", key, value.0), - InterpolatedPattern(strprts, _) => write!( - f, - "interpolated: \"{}\"", - strprts - .iter() - .map(|part| part.0.to_string()) - .collect::>() - .join("") - ), - } - } -} - pub struct StringMatcher(pub Box Option>>); impl PartialEq for StringMatcher { diff --git a/src/validator.rs b/src/validator.rs index c8467e9..7833365 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -4,7 +4,7 @@ // * [ ] ensure loops have fixed arity (no splats) // * [ ] ensure fn pattern splats are always highest (and same) arity -use crate::parser::*; +use crate::ast::{Ast, StringPart}; use crate::spans::{Span, Spanned}; use crate::value::Value; use std::collections::{HashMap, HashSet}; diff --git a/src/vm.rs b/src/vm.rs index 3234f71..118b6a1 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,7 +1,7 @@ +use crate::ast::Ast; use crate::base::BaseFn; use crate::chunk::Chunk; use crate::op::Op; -use crate::parser::Ast; use crate::spans::Spanned; use crate::value::{LFn, Value}; use crate::world::Zoo;