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
|
// * [ ] wire up Ariadne parsing errors
|
||||||
// * [ ] validation
|
// * [ ] validation
|
||||||
// * [x] break this out into multiple files
|
// * [x] break this out into multiple files
|
||||||
// * [ ] write a tree-walk VM
|
// * [x] write a tree-walk VM
|
||||||
// - [ ] learn how to deal with lifetimes
|
// - [x] learn how to deal with lifetimes
|
||||||
// - [ ] with stack mechanics and refcounting
|
// - [x] with stack mechanics and refcounting
|
||||||
// - [ ] with tail-call optimization
|
// - [ ] 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
|
// * [ ] write `base` in Rust
|
||||||
// * [ ] turn this into a library function
|
// * [ ] turn this into a library function
|
||||||
// * [ ] compile this into WASM
|
// * [ ] compile this into WASM
|
||||||
// * [ ] perf testing
|
// * [ ] perf testing
|
||||||
|
|
||||||
use chumsky::{input::Stream, prelude::*};
|
use chumsky::{input::Stream, prelude::*};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
mod spans;
|
mod spans;
|
||||||
|
|
||||||
|
@ -33,6 +40,7 @@ mod lexer;
|
||||||
use crate::lexer::*;
|
use crate::lexer::*;
|
||||||
|
|
||||||
mod value;
|
mod value;
|
||||||
|
use crate::value::*;
|
||||||
|
|
||||||
mod parser;
|
mod parser;
|
||||||
use crate::parser::*;
|
use crate::parser::*;
|
||||||
|
@ -40,10 +48,12 @@ use crate::parser::*;
|
||||||
mod vm;
|
mod vm;
|
||||||
use crate::vm::*;
|
use crate::vm::*;
|
||||||
|
|
||||||
|
mod base;
|
||||||
|
use crate::base::*;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let src = "
|
let src = "
|
||||||
#{:a 1, :b 2}
|
eq
|
||||||
|
|
||||||
";
|
";
|
||||||
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
|
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
|
||||||
if lex_errs.len() > 0 {
|
if lex_errs.len() > 0 {
|
||||||
|
@ -60,7 +70,7 @@ pub fn main() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// println!("{}", ast);
|
// println!("{}", ast);
|
||||||
|
|
||||||
let mut ctx = vec![];
|
let mut ctx = base();
|
||||||
|
|
||||||
let result = eval(&ast, &mut ctx).unwrap();
|
let result = eval(&ast, &mut ctx).unwrap();
|
||||||
|
|
||||||
|
|
|
@ -172,7 +172,17 @@ impl<'src> fmt::Display for Ast<'src> {
|
||||||
}
|
}
|
||||||
Ast::FnDeclaration(_name) => todo!(),
|
Ast::FnDeclaration(_name) => todo!(),
|
||||||
Ast::Panic(_expr) => 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::Repeat(_times, _body) => todo!(),
|
||||||
// Ast::Loop(init, body) => todo!(),
|
// Ast::Loop(init, body) => todo!(),
|
||||||
// Ast::Recur(args) => todo!(),
|
// Ast::Recur(args) => todo!(),
|
||||||
|
|
66
src/value.rs
66
src/value.rs
|
@ -1,16 +1,17 @@
|
||||||
|
use crate::base::*;
|
||||||
use crate::parser::*;
|
use crate::parser::*;
|
||||||
use imbl::*;
|
use imbl::*;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Fn<'src> {
|
pub struct Fn<'src> {
|
||||||
pub name: &'src str,
|
pub name: &'src str,
|
||||||
pub body: &'src Vec<MatchClause<'src>>,
|
pub body: &'src Vec<MatchClause<'src>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug)]
|
||||||
pub enum Value<'src> {
|
pub enum Value<'src> {
|
||||||
Nil,
|
Nil,
|
||||||
Placeholder,
|
Placeholder,
|
||||||
|
@ -27,6 +28,7 @@ pub enum Value<'src> {
|
||||||
Dict(HashMap<&'src str, Self>),
|
Dict(HashMap<&'src str, Self>),
|
||||||
Box(&'src str, Rc<RefCell<Self>>),
|
Box(&'src str, Rc<RefCell<Self>>),
|
||||||
Fn(Rc<Fn<'src>>),
|
Fn(Rc<Fn<'src>>),
|
||||||
|
Base(&Base<'src>),
|
||||||
// Set(HashSet<Self>),
|
// Set(HashSet<Self>),
|
||||||
// Sets are hard
|
// Sets are hard
|
||||||
// Sets require Eq
|
// Sets require Eq
|
||||||
|
@ -54,6 +56,7 @@ impl<'src> Clone for Value<'src> {
|
||||||
Value::Dict(d) => Value::Dict(d.clone()),
|
Value::Dict(d) => Value::Dict(d.clone()),
|
||||||
Value::Box(name, b) => Value::Box(name, b.clone()),
|
Value::Box(name, b) => Value::Box(name, b.clone()),
|
||||||
Value::Placeholder => Value::Placeholder,
|
Value::Placeholder => Value::Placeholder,
|
||||||
|
Value::Base(b) => Value::Base(b.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,8 +94,23 @@ impl<'src> fmt::Display for Value<'src> {
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.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::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,
|
_ => 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::parser::*;
|
||||||
use crate::value::*;
|
use crate::value::*;
|
||||||
use imbl::HashMap;
|
use imbl::HashMap;
|
||||||
|
@ -7,7 +8,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct LudusError {
|
pub struct LudusError {
|
||||||
msg: String,
|
pub msg: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
// oy
|
// oy
|
||||||
|
@ -66,6 +67,10 @@ pub fn match_pattern<'src, 'a>(
|
||||||
ctx.push((w, val.clone()));
|
ctx.push((w, val.clone()));
|
||||||
Some(ctx)
|
Some(ctx)
|
||||||
}
|
}
|
||||||
|
(Pattern::As(word, type_kw), value) => {
|
||||||
|
let ludus_type = value.ludus_type();
|
||||||
|
None
|
||||||
|
}
|
||||||
// todo: add splats to these match clauses
|
// todo: add splats to these match clauses
|
||||||
(Pattern::Tuple(x), Value::Tuple(y)) => {
|
(Pattern::Tuple(x), Value::Tuple(y)) => {
|
||||||
if x.len() != y.len() {
|
if x.len() != y.len() {
|
||||||
|
@ -97,7 +102,25 @@ pub fn match_pattern<'src, 'a>(
|
||||||
}
|
}
|
||||||
Some(ctx)
|
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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,7 +158,13 @@ pub fn apply<'src, 'a>(
|
||||||
Ok(Value::Nil)
|
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)) => {
|
(Value::Fn(f), Value::Tuple(args)) => {
|
||||||
let args = Value::Tuple(args);
|
let args = Value::Tuple(args);
|
||||||
match_clauses(&args, f.body, ctx)
|
match_clauses(&args, f.body, ctx)
|
||||||
|
@ -276,7 +305,7 @@ pub fn eval<'src, 'a>(
|
||||||
ctx.push((name, the_fn.clone()));
|
ctx.push((name, the_fn.clone()));
|
||||||
Ok(the_fn)
|
Ok(the_fn)
|
||||||
}
|
}
|
||||||
Ast::FnDeclaration(_) => todo!(),
|
Ast::FnDeclaration(_name) => todo!(),
|
||||||
Ast::Panic(msg) => {
|
Ast::Panic(msg) => {
|
||||||
let msg = eval(&msg.0, ctx)?;
|
let msg = eval(&msg.0, ctx)?;
|
||||||
Err(LudusError {
|
Err(LudusError {
|
||||||
|
@ -300,8 +329,15 @@ pub fn eval<'src, 'a>(
|
||||||
}
|
}
|
||||||
Ok(Value::Nil)
|
Ok(Value::Nil)
|
||||||
}
|
}
|
||||||
Ast::Do(_) => todo!(),
|
Ast::Do(terms) => {
|
||||||
// Ast::Loop(_, _) => todo!(),
|
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!(),
|
// Ast::Recur(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user