lots of work, need to fix cloning closures in base

This commit is contained in:
Scott Richmond 2024-11-11 17:50:58 -05:00
parent 9f0cef5207
commit 26ff15cae8
5 changed files with 251 additions and 18 deletions

117
src/base.rs Normal file
View File

@ -0,0 +1,117 @@
use crate::value::*;
use crate::vm::*;
use imbl::*;
use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;
pub enum Base<'src> {
Unary(
&'src str,
Box<dyn std::ops::Fn(Value<'src>) -> Result<Value<'src>, LudusError>>,
),
Binary(
&'src str,
Box<dyn std::ops::Fn(Value<'src>, Value<'src>) -> Result<Value<'src>, LudusError>>,
),
Ternary(
&'src str,
Box<
dyn std::ops::Fn(
Value<'src>,
Value<'src>,
Value<'src>,
) -> Result<Value<'src>, LudusError>,
>,
),
}
impl<'src> Base<'src> {
pub fn name(&self) -> &'src str {
match self {
Base::Unary(name, _) => name,
Base::Binary(name, _) => name,
Base::Ternary(name, _) => name,
}
}
}
impl<'src> fmt::Debug for Base<'src> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Base").field(&self.name()).finish()
}
}
pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> {
let mut base = vec![];
let eq = Base::Binary("eq", Box::new(|x, y| Ok(Value::Boolean(x == y))));
base.push(("eq", Value::Base(&eq)));
let add = Base::Binary(
"add",
Box::new(|x, y| match (x, y) {
(Value::Number(x), Value::Number(y)) => Ok(Value::Number(x + y)),
_ => Err(LudusError {
msg: "I can only add numbers".to_string(),
}),
}),
);
base.push(("add", Value::Base(&add)));
base
}
// add (x, y) -> number
// and (x, y) -> value
// assoc (x, y) -> dict
// atan_2 (x) -> number
// bool (x) -> bool
// ceil (x) -> number
// chars (x) -> list
// concat (x, y) -> value
// conj (x, y) -> list
// cos (x) -> number
// dec (x) -> number
// disj (x) -> set
// dissoc (x, y) -> dict
// div (x, y) -> number
// doc (x) -> string
// downcase (x) -> string
// first (x) -> value
// floor (x) -> number
// get (x, y) -> value
// gt (x, y) -> bool
// gte! (x, y) -> bool
// inc (x) -> number
// last (x) -> value
// lt (x) -> bool
// lte (x) -> bool
// mod (x, y) -> number
// or (x, y) -> value
// pi
// print! (x) -> :ok
// prn (x) -> value
// push (x) -> list
// random () -> number
// range () -> list
// rest (x) -> coll
// round (x) -> number
// show (x) -> string
// sin (x) -> number
// slice (x, y, z) -> list
// split (x, y) -> list(string)
// sqrt (x) -> number
// store! (x, y) -> value
// str_slice (x, y, z) -> string
// stringify (x) -> string
// sub (x, y) -> number
// tan (x) -> number
// to_list (x) -> list
// to_number (x) -> number
// trim (x) -> string
// triml (x) -> string
// trimr (x) -> string
// type (x) -> keyword
// unbox (x) -> value
// upcase (x) -> string

View File

@ -16,16 +16,23 @@
// * [ ] wire up Ariadne parsing errors
// * [ ] validation
// * [x] break this out into multiple files
// * [ ] write a tree-walk VM
// - [ ] learn how to deal with lifetimes
// - [ ] with stack mechanics and refcounting
// - [ ] with tail-call optimization
// * [x] write a tree-walk VM
// - [x] learn how to deal with lifetimes
// - [x] with stack mechanics and refcounting
// - [ ] with tail-call optimization (nb: this may not be possible w/ a TW-VM)
// - [ ] with all the necessary forms for current Ludus
// * [ ] guards in match clauses
// * [ ] `as` patterns
// * [ ] splat patterns in tuples, lists, dicts
// * [ ] splats in list and dict literals
// * [ ] `loop` and `recur`
// * [ ] write `base` in Rust
// * [ ] turn this into a library function
// * [ ] compile this into WASM
// * [ ] perf testing
use chumsky::{input::Stream, prelude::*};
use std::rc::Rc;
mod spans;
@ -33,6 +40,7 @@ mod lexer;
use crate::lexer::*;
mod value;
use crate::value::*;
mod parser;
use crate::parser::*;
@ -40,10 +48,12 @@ use crate::parser::*;
mod vm;
use crate::vm::*;
mod base;
use crate::base::*;
pub fn main() {
let src = "
#{:a 1, :b 2}
eq
";
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
if lex_errs.len() > 0 {
@ -60,7 +70,7 @@ pub fn main() {
.unwrap();
// println!("{}", ast);
let mut ctx = vec![];
let mut ctx = base();
let result = eval(&ast, &mut ctx).unwrap();

View File

@ -172,7 +172,17 @@ impl<'src> fmt::Display for Ast<'src> {
}
Ast::FnDeclaration(_name) => todo!(),
Ast::Panic(_expr) => todo!(),
Ast::Do(_exprs) => todo!(),
Ast::Do(terms) => {
write!(
f,
"do: {}",
terms
.iter()
.map(|(term, _)| term.to_string())
.collect::<Vec<_>>()
.join(" > ")
)
}
Ast::Repeat(_times, _body) => todo!(),
// Ast::Loop(init, body) => todo!(),
// Ast::Recur(args) => todo!(),

View File

@ -1,16 +1,17 @@
use crate::base::*;
use crate::parser::*;
use imbl::*;
use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug)]
pub struct Fn<'src> {
pub name: &'src str,
pub body: &'src Vec<MatchClause<'src>>,
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub enum Value<'src> {
Nil,
Placeholder,
@ -27,6 +28,7 @@ pub enum Value<'src> {
Dict(HashMap<&'src str, Self>),
Box(&'src str, Rc<RefCell<Self>>),
Fn(Rc<Fn<'src>>),
Base(&Base<'src>),
// Set(HashSet<Self>),
// Sets are hard
// Sets require Eq
@ -54,6 +56,7 @@ impl<'src> Clone for Value<'src> {
Value::Dict(d) => Value::Dict(d.clone()),
Value::Box(name, b) => Value::Box(name, b.clone()),
Value::Placeholder => Value::Placeholder,
Value::Base(b) => Value::Base(b.clone()),
}
}
}
@ -91,8 +94,23 @@ impl<'src> fmt::Display for Value<'src> {
.collect::<Vec<_>>()
.join(", ")
),
Value::Box(name, b) => todo!(),
Value::Box(name, value) => {
write!(
f,
"box {}: [{}]",
name,
&value.try_borrow().unwrap().to_string()
)
}
Value::Placeholder => write!(f, "_"),
Value::Base(base) => {
let name = match base {
Base::Unary(name, _) => name,
Base::Binary(name, _) => name,
Base::Ternary(name, _) => name,
};
write!(f, "base fn {}", name)
}
}
}
}
@ -104,4 +122,46 @@ impl<'src> Value<'src> {
_ => true,
}
}
pub fn ludus_type(&self) -> Value {
match self {
Value::Nil => Value::Keyword("nil"),
Value::Number(_) => Value::Keyword("number"),
Value::Boolean(_) => Value::Keyword("boolean"),
Value::Keyword(_) => Value::Keyword("keyword"),
Value::Tuple(_) => Value::Keyword("tuple"),
Value::String(_) => Value::Keyword("string"),
Value::List(_) => Value::Keyword("list"),
Value::Dict(_) => Value::Keyword("dict"),
Value::Fn(_) => Value::Keyword("fn"),
Value::Box(_, _) => Value::Keyword("box"),
Value::Placeholder => unreachable!(),
Value::Args(_) => unreachable!(),
Value::Base(_) => Value::Keyword("fn"),
}
}
}
impl<'src> PartialEq for Value<'src> {
fn eq(&self, other: &Value<'src>) -> bool {
match (self, other) {
// value equality types
(Value::Nil, Value::Nil) => true,
(Value::Boolean(x), Value::Boolean(y)) => x == y,
(Value::Number(x), Value::Number(y)) => x == y,
(Value::String(x), Value::String(y)) => x == y,
(Value::Keyword(x), Value::Keyword(y)) => x == y,
(Value::Tuple(x), Value::Tuple(y)) => x == y,
(Value::List(x), Value::List(y)) => x == y,
(Value::Dict(x), Value::Dict(y)) => x == y,
// reference equality types
(Value::Fn(x), Value::Fn(y)) => Rc::<Fn<'_>>::as_ptr(x) == Rc::<Fn<'_>>::as_ptr(y),
(Value::Box(_, x), Value::Box(_, y)) => {
Rc::<RefCell<Value<'_>>>::as_ptr(x) == Rc::<RefCell<Value<'_>>>::as_ptr(y)
}
_ => false,
}
}
}
impl<'src> Eq for Value<'src> {}

View File

@ -1,3 +1,4 @@
use crate::base::*;
use crate::parser::*;
use crate::value::*;
use imbl::HashMap;
@ -7,7 +8,7 @@ use std::rc::Rc;
#[derive(Clone, Debug)]
pub struct LudusError {
msg: String,
pub msg: String,
}
// oy
@ -66,6 +67,10 @@ pub fn match_pattern<'src, 'a>(
ctx.push((w, val.clone()));
Some(ctx)
}
(Pattern::As(word, type_kw), value) => {
let ludus_type = value.ludus_type();
None
}
// todo: add splats to these match clauses
(Pattern::Tuple(x), Value::Tuple(y)) => {
if x.len() != y.len() {
@ -97,7 +102,25 @@ pub fn match_pattern<'src, 'a>(
}
Some(ctx)
}
(Pattern::Dict(_), _) => todo!("dictionary patterns still to do"),
(Pattern::Dict(x), Value::Dict(y)) => {
if x.len() != y.len() {
return None;
};
let to = ctx.len();
for (PairPattern { key, patt }, _) in x {
if let Some(val) = y.get(key) {
if let None = match_pattern(&patt.0, val, ctx) {
while ctx.len() > to {
ctx.pop();
return None;
}
}
} else {
return None;
};
}
Some(ctx)
}
_ => None,
}
}
@ -135,7 +158,13 @@ pub fn apply<'src, 'a>(
Ok(Value::Nil)
}
}
(Value::Dict(_dict), Value::Keyword(_kw)) => todo!(),
(Value::Dict(dict), Value::Keyword(kw)) => {
if let Some(val) = dict.get(kw) {
Ok(val.clone())
} else {
Ok(Value::Nil)
}
}
(Value::Fn(f), Value::Tuple(args)) => {
let args = Value::Tuple(args);
match_clauses(&args, f.body, ctx)
@ -276,7 +305,7 @@ pub fn eval<'src, 'a>(
ctx.push((name, the_fn.clone()));
Ok(the_fn)
}
Ast::FnDeclaration(_) => todo!(),
Ast::FnDeclaration(_name) => todo!(),
Ast::Panic(msg) => {
let msg = eval(&msg.0, ctx)?;
Err(LudusError {
@ -300,8 +329,15 @@ pub fn eval<'src, 'a>(
}
Ok(Value::Nil)
}
Ast::Do(_) => todo!(),
// Ast::Loop(_, _) => todo!(),
Ast::Do(terms) => {
let mut result = eval(&terms[0].0, ctx)?;
for i in 1..terms.len() {
let next = eval(&terms[i].0, ctx)?;
let arg = Value::Tuple(Rc::new(vec![result]));
result = apply(next, arg, ctx)?;
}
Ok(result)
} // Ast::Loop(_, _) => todo!(),
// Ast::Recur(_) => todo!(),
}
}