diff --git a/src/ast.rs b/src/ast.rs new file mode 100644 index 0000000..e15dbf5 --- /dev/null +++ b/src/ast.rs @@ -0,0 +1,459 @@ +use crate::parser::StringMatcher; +use crate::spans::*; +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>), + Receive(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>), + 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("") + ), + } + } +} diff --git a/src/lexer.rs b/src/lexer.rs index 50ca0ec..4ed7811 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -56,7 +56,9 @@ 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" => Token::Reserved(word), + | "recur" | "repeat" | "then" | "when" | "with" | "or" | "and" | "receive" => { + Token::Reserved(word) + } _ => Token::Word(word), }); diff --git a/src/parser.rs b/src/parser.rs index 38f4c4b..80f931e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -8,7 +8,7 @@ use crate::spans::*; use chumsky::{input::ValueInput, prelude::*, recursive::Recursive}; use std::fmt; -pub struct StringMatcher(pub Box Option>>); +pub struct StringMatcher(); impl PartialEq for StringMatcher { fn eq(&self, _other: &StringMatcher) -> bool { @@ -122,54 +122,6 @@ fn parse_string(s: &'static str, span: SimpleSpan) -> Result>) -> StringMatcher { - StringMatcher(Box::new(move |scrutinee| { - let mut last_match = 0; - let mut parts_iter = parts.iter(); - let mut matches = vec![]; - while let Some((part, _)) = parts_iter.next() { - match part { - StringPart::Data(string) => match scrutinee.find(string.as_str()) { - Some(i) => { - // if i = 0, we're at the beginning - if i == 0 && last_match == 0 { - last_match = i + string.len(); - continue; - } - // in theory, we only hit this branch if the first part is Data - unreachable!("internal Ludus error: bad string pattern") - } - None => return None, - }, - StringPart::Word(word) => { - let to_test = scrutinee.get(last_match..scrutinee.len()).unwrap(); - match parts_iter.next() { - None => matches.push((word.clone(), to_test.to_string())), - Some(part) => { - let (StringPart::Data(part), _) = part else { - unreachable!("internal Ludus error: bad string pattern") - }; - match to_test.find(part) { - None => return None, - Some(i) => { - matches.push(( - word.clone(), - to_test.get(last_match..i).unwrap().to_string(), - )); - last_match = i + part.len(); - continue; - } - } - } - } - } - _ => unreachable!("internal Ludus error"), - } - } - Some(matches) - })) -} - pub fn parser( ) -> impl Parser<'static, I, Spanned, extra::Err>> + Clone where @@ -215,10 +167,7 @@ where match parsed { Ok(parts) => match parts[0] { (StringPart::Inline(_), _) => Ok((StringPattern(s), e.span())), - _ => Ok(( - InterpolatedPattern(parts.clone(), compile_string_pattern(parts)), - e.span(), - )), + _ => Ok((InterpolatedPattern(parts.clone()), e.span())), }, Err(msg) => Err(Rich::custom(e.span(), msg)), } @@ -428,7 +377,7 @@ where |span| (Error, span), ))); - let if_ = just(Token::Reserved("if")) + let r#if = just(Token::Reserved("if")) .ignore_then(simple.clone()) .then_ignore(terminators.clone().or_not()) .then_ignore(just(Token::Reserved("then"))) @@ -494,7 +443,7 @@ where .then( match_clause .clone() - .or(guarded_clause) + .or(guarded_clause.clone()) .separated_by(terminators.clone()) .allow_leading() .allow_trailing() @@ -503,7 +452,20 @@ where ) .map_with(|(expr, clauses), e| (Match(Box::new(expr), clauses), e.span())); - let conditional = when.or(if_).or(r#match); + let receive = just(Token::Reserved("receive")) + .ignore_then( + match_clause + .clone() + .or(guarded_clause) + .separated_by(terminators.clone()) + .allow_leading() + .allow_trailing() + .collect() + .delimited_by(just(Token::Punctuation("{")), just(Token::Punctuation("}"))), + ) + .map_with(|clauses, e| (Receive(clauses), e.span())); + + let conditional = when.or(r#if).or(r#match).or(receive); let panic = just(Token::Reserved("panic!")) .ignore_then(nonbinding.clone())