lots of work, need to fix cloning closures in base
This commit is contained in:
parent
9f0cef5207
commit
26ff15cae8
117
src/base.rs
Normal file
117
src/base.rs
Normal 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
|
24
src/main.rs
24
src/main.rs
|
@ -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();
|
||||
|
||||
|
|
|
@ -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!(),
|
||||
|
|
66
src/value.rs
66
src/value.rs
|
@ -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> {}
|
||||
|
|
48
src/vm.rs
48
src/vm.rs
|
@ -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!(),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user