From 9f0cef5207ee212c33b92a3d70e9773456671904 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Sun, 10 Nov 2024 20:12:19 -0500 Subject: [PATCH] lots of new things! almost a language --- Cargo.toml | 1 + src/main.rs | 17 ++++--- src/parser.rs | 116 ++++++++++++++++++++++++++++++-------------- src/value.rs | 34 +++++++------ src/vm.rs | 131 +++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 223 insertions(+), 76 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3dad161..ed08903 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" ariadne = { git = "https://github.com/zesterer/ariadne" } chumsky = { git = "https://github.com/zesterer/chumsky", features = ["label"] } imbl = "3.0.0" +tailcall = "1.0.1" diff --git a/src/main.rs b/src/main.rs index ff568b8..7f96eaa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,6 @@ // * [ ] perf testing use chumsky::{input::Stream, prelude::*}; -use std::rc::Rc; mod spans; @@ -34,7 +33,6 @@ mod lexer; use crate::lexer::*; mod value; -use crate::value::*; mod parser; use crate::parser::*; @@ -43,7 +41,10 @@ mod vm; use crate::vm::*; pub fn main() { - let src = "when { 0 -> :false; true -> :true}"; + let src = " +#{:a 1, :b 2} + + "; let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); if lex_errs.len() > 0 { println!("{:?}", lex_errs); @@ -51,15 +52,15 @@ pub fn main() { } let tokens = tokens.unwrap(); let to_parse = tokens.clone(); - for (token, _) in tokens { - println!("{}", token) - } + // for (token, _) in tokens { + // println!("{}", token) + // } let (ast, _) = parser() .parse(Stream::from_iter(to_parse).map((0..src.len()).into(), |(t, s)| (t, s))) .unwrap(); - println!("{}", ast); + // println!("{}", ast); - let mut ctx = vec![("foo", Value::Number(42.0))]; + let mut ctx = vec![]; let result = eval(&ast, &mut ctx).unwrap(); diff --git a/src/parser.rs b/src/parser.rs index b244d26..611dddb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -32,6 +32,18 @@ impl<'src> fmt::Display for MatchClause<'src> { } } +#[derive(Clone, Debug, PartialEq)] +pub struct Pair<'src> { + pub key: &'src str, + pub value: Spanned>, +} + +impl<'src> fmt::Display for Pair<'src> { + fn fmt(self: &Pair<'src>, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "pair: {}: {}", self.key, self.value.0) + } +} + #[derive(Clone, Debug, PartialEq)] pub enum Ast<'src> { Error, @@ -47,20 +59,19 @@ pub enum Ast<'src> { Tuple(Vec>), Arguments(Vec>), List(Vec>), - Pair(&'src str, Box>), - Dict(Vec>), + Dict(Vec>), Let(Box>>, Box>), Box(&'src str, Box>), Synthetic(Box>, Box>, Vec>), When(Vec>>), - Match(Box>, Vec>>), - Fn(&'src str, Vec>>), + Match(Box>, Vec>), + Fn(&'src str, Vec>), FnDeclaration(&'src str), Panic(Box>), Do(Vec>), Repeat(Box>, Box>), - Loop(Box>, Vec>), - Recur(Vec>), + // Loop(Box>, Vec>), + // Recur(Vec>), } impl<'src> fmt::Display for Ast<'src> { @@ -89,13 +100,12 @@ impl<'src> fmt::Display for Ast<'src> { Ast::Let(pattern, expression) => { write!(f, "Let: {} = {}", pattern.0, expression.0) } - Ast::Pair(kw, expr) => write!(f, "{} {}", kw, expr.0), Ast::Dict(entries) => write!( f, "#{{{}}}", entries .iter() - .map(|(pair, _)| pair.to_string()) + .map(|pair| pair.to_string()) .collect::>() .join(", ") ), @@ -134,11 +144,54 @@ impl<'src> fmt::Display for Ast<'src> { .collect::>() .join("\n") ), - _ => unimplemented!(), + Ast::Placeholder => todo!(), + Ast::Box(_name, _rhs) => todo!(), + Ast::Match(value, clauses) => { + write!( + f, + "match: {} with {}", + &value.0.to_string(), + clauses + .iter() + .map(|clause| clause.to_string()) + .collect::>() + .join("\n") + ) + } + Ast::Fn(name, clauses) => { + write!( + f, + "fn: {}\n{}", + name, + clauses + .iter() + .map(|clause| clause.to_string()) + .collect::>() + .join("\n") + ) + } + Ast::FnDeclaration(_name) => todo!(), + Ast::Panic(_expr) => todo!(), + Ast::Do(_exprs) => todo!(), + Ast::Repeat(_times, _body) => todo!(), + // Ast::Loop(init, body) => todo!(), + // Ast::Recur(args) => todo!(), } } } +#[derive(Clone, Debug, PartialEq)] +pub struct PairPattern<'src> { + pub key: &'src str, + pub patt: Spanned>, +} + +impl<'src> fmt::Display for PairPattern<'src> { + fn fmt(self: &PairPattern<'src>, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "pair pattern: {}: {}", self.key, self.patt.0) + } +} + #[derive(Clone, Debug, PartialEq)] pub enum Pattern<'src> { Nil, @@ -153,8 +206,7 @@ pub enum Pattern<'src> { List(Vec>), // is this the right representation for Dicts? // Could/should this also be a Vec? - Pair(&'src str, Box>), - Dict(Vec>), + Dict(Vec>>), } impl<'src> fmt::Display for Pattern<'src> { @@ -184,7 +236,6 @@ impl<'src> fmt::Display for Pattern<'src> { .collect::>() .join(", ") ), - Pattern::Pair(kw, expr) => write!(f, "{} {}", kw, expr.0), Pattern::Dict(entries) => write!( f, "#{{{}}}", @@ -260,11 +311,14 @@ where let pair_pattern = select! {Token::Keyword(k) => k} .then(pattern.clone()) - .map_with(|(kw, patt), e| (Pattern::Pair(kw, Box::new(patt)), e.span())); + .map_with(|(key, patt), e| (PairPattern { key, patt }, e.span())); let shorthand_pattern = select! {Token::Word(w) => w}.map_with(|w, e| { ( - Pattern::Pair(w, Box::new((Pattern::Word(w), e.span()))), + PairPattern { + key: w, + patt: ((Pattern::Word(w), e.span())), + }, e.span(), ) }); @@ -358,10 +412,12 @@ where let pair = select! {Token::Keyword(k) => k} .then(simple.clone()) - .map_with(|(kw, expr), e| (Ast::Pair(kw, Box::new(expr)), e.span())); + .map_with(|(key, value), e| Pair { key, value }); - let shorthand = select! {Token::Word(w) => w} - .map_with(|w, e| (Ast::Pair(w, Box::new((Ast::Word(w), e.span()))), e.span())); + let shorthand = select! {Token::Word(w) => w}.map_with(|w, e| Pair { + key: w, + value: ((Ast::Word(w), e.span())), + }); let dict = pair .or(shorthand) @@ -442,15 +498,10 @@ where .clone() .then_ignore(just(Token::Punctuation("->"))) .then(expr.clone()) - .map_with(|(patt, body), e| { - ( - MatchClause { - patt, - guard: None, - body, - }, - e.span(), - ) + .map_with(|(patt, body), _| MatchClause { + patt, + guard: None, + body, }); let match_ = just(Token::Reserved("match")) @@ -499,15 +550,10 @@ where .clone() .then_ignore(just(Token::Punctuation("->"))) .then(nonbinding.clone()) - .map_with(|(patt, body), e| { - ( - MatchClause { - patt, - body, - guard: None, - }, - e.span(), - ) + .map_with(|(patt, body), _| MatchClause { + patt, + body, + guard: None, }) .labelled("function clause"); diff --git a/src/value.rs b/src/value.rs index 3956c49..cbb7282 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,39 +1,32 @@ use crate::parser::*; -use crate::spans::*; use imbl::*; use std::cell::RefCell; use std::fmt; use std::rc::Rc; -#[derive(Clone, Debug, PartialEq)] -pub struct Clause<'src> { - patt: Pattern<'src>, - span: Span, - body: Ast<'src>, -} - #[derive(Clone, Debug, PartialEq)] pub struct Fn<'src> { - name: &'src str, - span: Span, - body: Vec>, + pub name: &'src str, + pub body: &'src Vec>, } #[derive(Debug, PartialEq)] pub enum Value<'src> { Nil, + Placeholder, Boolean(bool), Number(f64), Keyword(&'src str), String(&'src str), // on the heap for now Tuple(Rc>), + Args(Rc>), // ref-counted, immutable, persistent List(Vector), // ref-counted, immutable, persistent Dict(HashMap<&'src str, Self>), Box(&'src str, Rc>), - // Fn(Rc>), + Fn(Rc>), // Set(HashSet), // Sets are hard // Sets require Eq @@ -55,9 +48,12 @@ impl<'src> Clone for Value<'src> { Value::Keyword(s) => Value::Keyword(s), Value::Number(n) => Value::Number(n.clone()), Value::Tuple(t) => Value::Tuple(t.clone()), + Value::Args(a) => Value::Args(a.clone()), + Value::Fn(f) => Value::Fn(f.clone()), Value::List(l) => Value::List(l.clone()), Value::Dict(d) => Value::Dict(d.clone()), Value::Box(name, b) => Value::Box(name, b.clone()), + Value::Placeholder => Value::Placeholder, } } } @@ -70,8 +66,8 @@ impl<'src> fmt::Display for Value<'src> { Value::Number(n) => write!(f, "{}", n), Value::Keyword(k) => write!(f, ":{}", k), Value::String(s) => write!(f, "\"{}\"", s), - // Value::Fn(fun) => write!(f, "fn {}", fun.name), - Value::Tuple(t) => write!( + Value::Fn(fun) => write!(f, "fn {}", fun.name), + Value::Tuple(t) | Value::Args(t) => write!( f, "({})", t.iter() @@ -87,8 +83,16 @@ impl<'src> fmt::Display for Value<'src> { .collect::>() .join(", ") ), - Value::Dict(d) => write!(f, "#{{{:?}}}", d), + Value::Dict(d) => write!( + f, + "#{{{}}}", + d.iter() + .map(|(k, v)| format!(":{k} {v}")) + .collect::>() + .join(", ") + ), Value::Box(name, b) => todo!(), + Value::Placeholder => write!(f, "_"), } } } diff --git a/src/vm.rs b/src/vm.rs index 9ecb382..7f8c1b8 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,6 +1,8 @@ use crate::parser::*; use crate::value::*; +use imbl::HashMap; use imbl::Vector; +use std::cell::RefCell; use std::rc::Rc; #[derive(Clone, Debug)] @@ -48,7 +50,7 @@ where } } -pub fn matchh<'src, 'a>( +pub fn match_pattern<'src, 'a>( patt: &Pattern<'src>, val: &Value<'src>, ctx: &'a mut Vec<(&'src str, Value<'src>)>, @@ -71,7 +73,7 @@ pub fn matchh<'src, 'a>( }; let to = ctx.len(); for i in 0..x.len() { - if let None = matchh(&x[i].0, &y[i], ctx) { + if let None = match_pattern(&x[i].0, &y[i], ctx) { while ctx.len() > to { ctx.pop(); } @@ -86,7 +88,7 @@ pub fn matchh<'src, 'a>( }; let to = ctx.len(); for i in 0..x.len() { - if let None = matchh(&x[i].0, y.get(i).unwrap(), ctx) { + if let None = match_pattern(&x[i].0, y.get(i).unwrap(), ctx) { while ctx.len() > to { ctx.pop(); } @@ -95,14 +97,60 @@ pub fn matchh<'src, 'a>( } Some(ctx) } - (Pattern::Pair(_, _), _) => todo!("dictionary patterns still to do"), (Pattern::Dict(_), _) => todo!("dictionary patterns still to do"), _ => None, } } +pub fn match_clauses<'src, 'a>( + value: &Value<'src>, + clauses: &'src Vec>, + ctx: &'a mut Vec<(&'src str, Value<'src>)>, +) -> Result, LudusError> { + let to = ctx.len(); + for MatchClause { patt, body, .. } in clauses.iter() { + if let Some(ctx) = match_pattern(&patt.0, value, ctx) { + let res = eval(&body.0, ctx); + while ctx.len() > to { + ctx.pop(); + } + return res; + } + } + Err(LudusError { + msg: "no match".to_string(), + }) +} + +pub fn apply<'src, 'a>( + callee: Value<'src>, + caller: Value<'src>, + ctx: &'a mut Vec<(&'src str, Value<'src>)>, +) -> Result, LudusError> { + match (callee, caller) { + (Value::Keyword(kw), Value::Dict(dict)) => { + if let Some(val) = dict.get(kw) { + Ok(val.clone()) + } else { + Ok(Value::Nil) + } + } + (Value::Dict(_dict), Value::Keyword(_kw)) => todo!(), + (Value::Fn(f), Value::Tuple(args)) => { + let args = Value::Tuple(args); + match_clauses(&args, f.body, ctx) + } + (Value::Fn(_f), Value::Args(_args)) => todo!(), + (_, Value::Keyword(_)) => Ok(Value::Nil), + (_, Value::Args(_)) => Err(LudusError { + msg: "you may only call a function".to_string(), + }), + _ => unreachable!(), + } +} + pub fn eval<'src, 'a>( - ast: &Ast<'src>, + ast: &'src Ast<'src>, ctx: &'a mut Vec<(&'src str, Value<'src>)>, ) -> Result, LudusError> { match ast { @@ -154,20 +202,57 @@ pub fn eval<'src, 'a>( } Ast::Let(patt, expr) => { let val = eval(&expr.0, ctx)?; - match matchh(&patt.0, &val, ctx) { + match match_pattern(&patt.0, &val, ctx) { Some(_) => Ok(val), None => Err(LudusError { msg: "No match".to_string(), }), } } - Ast::Placeholder => todo!(), - Ast::Error => todo!(), - Ast::Dict(_) => todo!(), - Ast::Arguments(_) => todo!(), - Ast::Pair(_, _) => todo!(), - Ast::Box(_, _) => todo!(), - Ast::Synthetic(_, _, _) => todo!(), + Ast::Placeholder => Ok(Value::Placeholder), + Ast::Error => unreachable!(), + Ast::Arguments(a) => { + let mut args = vec![]; + for (arg, _) in a.iter() { + let arg = eval(&arg, ctx)?; + args.push(arg); + } + if args.iter().any(|arg| { + if let Value::Placeholder = arg { + true + } else { + false + } + }) { + Ok(Value::Args(Rc::new(args))) + } else { + Ok(Value::Tuple(Rc::new(args))) + } + } + Ast::Dict(pairs) => { + let mut dict = HashMap::new(); + for Pair { key, value } in pairs { + let value = eval(&value.0, ctx)?; + dict.insert(*key, value); + } + Ok(Value::Dict(dict)) + } + Ast::Box(name, expr) => { + let val = eval(&expr.0, ctx)?; + let boxed = Value::Box(name, Rc::new(RefCell::new(val))); + ctx.push((name, boxed.clone())); + Ok(boxed) + } + Ast::Synthetic(root, first, rest) => { + let root = eval(&root.0, ctx)?; + let first = eval(&first.0, ctx)?; + let mut curr = apply(root, first, ctx)?; + for term in rest.iter() { + let next = eval(&term.0, ctx)?; + curr = apply(curr, next, ctx)?; + } + Ok(curr) + } Ast::When(clauses) => { for clause in clauses.iter() { let WhenClause { cond, body } = &clause.0; @@ -179,8 +264,18 @@ pub fn eval<'src, 'a>( msg: "no match".to_string(), }); } - Ast::Match(_, _) => todo!(), - Ast::Fn(_, _) => todo!(), + Ast::Match(value, clauses) => { + let value = eval(&value.0, ctx)?; + match_clauses(&value, clauses, ctx) + } + Ast::Fn(name, clauses) => { + let the_fn = Value::Fn::<'src>(Rc::new(Fn::<'src> { + name, + body: clauses, + })); + ctx.push((name, the_fn.clone())); + Ok(the_fn) + } Ast::FnDeclaration(_) => todo!(), Ast::Panic(msg) => { let msg = eval(&msg.0, ctx)?; @@ -201,12 +296,12 @@ pub fn eval<'src, 'a>( } } for _ in 0..times_num { - let _ = eval(&body.0, ctx)?; + eval(&body.0, ctx)?; } Ok(Value::Nil) } Ast::Do(_) => todo!(), - Ast::Loop(_, _) => todo!(), - Ast::Recur(_) => todo!(), + // Ast::Loop(_, _) => todo!(), + // Ast::Recur(_) => todo!(), } }