hash maps

This commit is contained in:
TriMill 2022-09-15 00:01:57 -04:00
parent 9c2a24c2d9
commit ccbc6c32f3
8 changed files with 179 additions and 42 deletions

8
Cargo.lock generated
View file

@ -246,9 +246,9 @@ dependencies = [
[[package]]
name = "num-complex"
version = "0.4.1"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fbc387afefefd5e9e39493299f3069e14a140dd34dc19b4c1c1a8fddb6a790"
checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
dependencies = [
"num-traits",
]
@ -265,9 +265,9 @@ dependencies = [
[[package]]
name = "num-rational"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint",

View file

@ -5,8 +5,8 @@ edition = "2021"
[dependencies]
lazy_static = "1.4.0"
num-complex = "0.4.1"
num-rational = "0.4.0"
num-complex = "0.4.2"
num-rational = "0.4.1"
num-traits = "0.2.15"
rustyline = "10.0.0"
backtrace = "0.3.66"

36
examples/fib_recur.cxpr Normal file
View file

@ -0,0 +1,36 @@
fn memoize(f) {
let map = {};
return fn(x) {
if has(map, x) {
return map[x];
} else {
let result = f(x);
map[x] = result;
return result;
}
};
}
fn fib(n) {
if n <= 1 {
return n;
} else {
return fib(n-1) + fib(n-2);
}
}
for n: [10,20,25] {
let start_time = time();
let result = fib(n);
let elapsed = time() - start_time;
println("time for fib(" + str(n) + ")=" + str(result) + ": " + str(elapsed));
}
let fib = memoize(fib);
for n: [10,20,25] {
let start_time = time();
let result = fib(n);
let elapsed = time() - start_time;
println("time for memoized fib(" + str(n) + ")=" + str(result) + ": " + str(elapsed));
}

View file

@ -193,6 +193,15 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
}
Ok(Value::from(list))
},
Expr::Map { items } => {
let mut map = HashMap::with_capacity(items.len());
for (k, v) in items {
let key = eval_expr(k, env.clone())?;
let value = eval_expr(v, env.clone())?;
map.insert(key, value);
}
Ok(Value::from(map))
},
Expr::FuncCall { func, args, pos } => {
let func = eval_expr(func, env.clone())?;
let mut arg_values = Vec::with_capacity(args.len());

View file

@ -40,6 +40,7 @@ pub enum Expr {
Ident { value: Token },
Literal { value: Token },
List { items: Vec<Expr> },
Map { items: Vec<(Expr,Expr)> },
FuncCall { func: Box<Expr>, args: Vec<Expr>, pos: Position },
Index { lhs: Box<Expr>, index: Box<Expr>, pos: Position },
Fn { args: Vec<Token>, body: Box<Stmt> },
@ -53,6 +54,7 @@ impl fmt::Debug for Expr {
Self::Ident { value } => write!(f, "(ident {:?})", value),
Self::Literal { value } => write!(f, "(lit {:?})", value),
Self::List { items } => write!(f, "(list {:?})", items),
Self::Map { items } => write!(f, "(map {:?})", items),
Self::FuncCall { func, args, .. } => write!(f, "(call {:?} {:?})", func, args),
Self::Index { lhs, index, .. } => write!(f, "(index {:?} {:?})", lhs, index),
Self::Fn { args, body } => write!(f, "(fn {:?} {:?})", args, body)

View file

@ -223,19 +223,16 @@ impl Parser {
if let TokenType::LParen = next.ty {} else {
return Err(ParserError { message: "Expected left parenthesis to start arguments list".into(), pos: next.pos })
}
let args = self.commalist(TokenType::RParen, Self::ident)?.into_iter().map(|e| {
if let Expr::Ident { value: i } = e { i }
else { panic!() }
}).collect();
let args = self.commalist(TokenType::RParen, Self::ident)?;
self.err_on_eof()?;
let body = self.statement()?;
Ok(Stmt::Fn { name, args, body: Box::new(body) })
}
fn ident(&mut self) -> Result<Expr, ParserError> {
fn ident(&mut self) -> Result<Token, ParserError> {
let next = self.next();
match next.ty {
TokenType::Ident(_) => Ok(Expr::Ident{ value: next }),
TokenType::Ident(_) => Ok(next),
_ => Err(ParserError { message: "Expected identifier".into(), pos: next.pos })
}
}
@ -261,7 +258,7 @@ impl Parser {
Ok(expr)
}
fn commalist(&mut self, terminator: TokenType, parse_item: fn(&mut Parser) -> Result<Expr, ParserError>) -> Result<Vec<Expr>, ParserError> {
fn commalist<T>(&mut self, terminator: TokenType, parse_item: fn(&mut Parser) -> Result<T, ParserError>) -> Result<Vec<T>, ParserError> {
let mut items = vec![];
while !self.at_end() && self.peek().ty != terminator {
let expr = parse_item(self)?;
@ -370,6 +367,18 @@ impl Parser {
Ok(Expr::Index { lhs: Box::new(expr), index: Box::new(index), pos: lbrack.pos })
}
fn kv_pair(&mut self) -> Result<(Expr, Expr), ParserError> {
let key = self.assignment()?;
self.err_on_eof()?;
let next = self.next();
if next.ty != TokenType::Colon {
return Err(ParserError { message: "Expected colon in key-value pair".into(), pos: next.pos })
}
self.err_on_eof()?;
let value = self.assignment()?;
Ok((key, value))
}
fn expr_base(&mut self) -> Result<Expr, ParserError> {
self.err_on_eof()?;
let next = self.next();
@ -391,16 +400,16 @@ impl Parser {
} else if next.ty == TokenType::LBrack {
let items = self.commalist(TokenType::RBrack, Self::assignment)?;
Ok(Expr::List { items })
} else if next.ty == TokenType::LBrace {
let items = self.commalist(TokenType::RBrace, Self::kv_pair)?;
Ok(Expr::Map { items })
} else if next.ty == TokenType::Fn {
self.err_on_eof()?;
let next = self.next();
if let TokenType::LParen = next.ty {} else {
return Err(ParserError { message: "Expected left parenthesis to start arguments list".into(), pos: next.pos })
}
let args = self.commalist(TokenType::RParen, Self::ident)?.into_iter().map(|e| {
if let Expr::Ident { value: i } = e { i }
else { panic!() }
}).collect();
let args = self.commalist(TokenType::RParen, Self::ident)?;
self.err_on_eof()?;
let body = self.statement()?;
Ok(Expr::Fn { args, body: Box::new(body) })

View file

@ -1,4 +1,4 @@
use std::{rc::Rc, io::Write, cmp::Ordering};
use std::{rc::Rc, io::Write, cmp::Ordering, time::{SystemTime, UNIX_EPOCH}};
use num_traits::ToPrimitive;
@ -22,12 +22,16 @@ pub fn load(env: &mut Environment) {
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_chr, arg_count: 1, name }));
name = Rc::from("range");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_range, arg_count: 2, name }));
name = Rc::from("has");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_has, arg_count: 2, name }));
name = Rc::from("len");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_len, arg_count: 1, name }));
name = Rc::from("re");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_re, arg_count: 1, name }));
name = Rc::from("im");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_im, arg_count: 1, name }));
name = Rc::from("time");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_time, arg_count: 0, name }));
}
fn fn_str(args: Vec<Value>) -> Result<Value, String> {
@ -99,11 +103,18 @@ fn fn_len(args: Vec<Value>) -> Result<Value, String> {
match &args[0] {
Value::String(s) => Ok(Value::Int(s.len() as i64)),
Value::List(l) => Ok(Value::Int(l.borrow().len() as i64)),
Value::Map(m) => Ok(Value::Int(m.len() as i64)),
Value::Map(m) => Ok(Value::Int(m.borrow().len() as i64)),
v => Err(format!("{:?} has no length", v))
}
}
fn fn_has(args: Vec<Value>) -> Result<Value, String> {
match &args[0] {
Value::Map(m) => Ok(Value::from(m.borrow().contains_key(&args[1]))),
v => Err(format!("Argument to has must be a map, got {:?} ", v))
}
}
fn fn_re(args: Vec<Value>) -> Result<Value, String> {
match &args[0] {
Value::Int(x) => Ok(Value::Float(*x as f64)),
@ -122,3 +133,8 @@ fn fn_im(args: Vec<Value>) -> Result<Value, String> {
x => Err(format!("Cannot get real part of {:?}", x))
}
}
fn fn_time(_: Vec<Value>) -> Result<Value, String> {
let time = SystemTime::now().duration_since(UNIX_EPOCH).map_err(|e| e.to_string())?;
Ok(Value::from(time.as_secs_f64()))
}

View file

@ -1,4 +1,4 @@
use std::{rc::Rc, collections::HashMap, ops::*, fmt, cmp::Ordering, cell::RefCell};
use std::{rc::Rc, collections::HashMap, ops::*, fmt, cmp::Ordering, cell::RefCell, hash::Hash};
use num_traits::{Zero, ToPrimitive};
@ -77,6 +77,22 @@ impl Func {
}
}
impl Hash for Func {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Builtin { name, arg_count, func } => {
name.hash(state);
arg_count.hash(state);
func.hash(state);
},
Self::Func { name, args, .. } => {
name.hash(state);
args.hash(state);
}
}
}
}
pub enum EitherRteOrString {
Rte(RuntimeError), String(String)
}
@ -122,20 +138,6 @@ impl Iterator for Func {
}
}
}
//
//#[derive(Clone)]
//pub struct BuiltinFunc {
// pub name: Rc<str>,
// pub func: fn(Vec<Value>) -> Result<Value, String>,
// pub arg_count: usize
//}
//
//impl fmt::Debug for BuiltinFunc {
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// f.debug_struct("BuiltinFn").field("name", &self.name).field("arg_count", &self.arg_count).finish()
// }
//}
//
#[derive(Clone, Debug)]
pub struct Data {
@ -158,7 +160,8 @@ pub enum Value {
Bool(bool),
Char(char),
String(Rc<str>),
List(Rc<RefCell<Vec<Value>>>), Map(Rc<HashMap<Value,Value>>),
List(Rc<RefCell<Vec<Value>>>),
Map(Rc<RefCell<HashMap<Value,Value>>>),
Func(Func),
Data(Data),
}
@ -173,7 +176,7 @@ impl Value {
Rational(r) => !r.is_zero(),
String(s) => !s.len() == 0,
List(l) => !l.borrow().len() == 0,
Map(m) => !m.len() == 0,
Map(m) => !m.borrow().len() == 0,
_ => true
}
}
@ -263,14 +266,13 @@ impl Value {
Value::Int(i) => Err(format!("List index {} cannot be negative", i)),
_ => Err(format!("Cannot index {:?} with {:?}", self, idx))
}
Self::Map(_) => todo!(),
Self::Map(m) => m.borrow().get(idx).cloned().ok_or_else(|| format!("Map does not contain key {:?}", idx)),
v => Err(format!("Cannot index into {:?}", v))
}
}
pub fn assign_index(&self, idx: &Value, value: Value) -> Result<(), String> {
match self {
Self::String(_) => todo!("Can't mutate strings yet"),
Self::List(l) => match idx {
Value::Int(i) if *i >= 0 && (*i as usize) < l.borrow().len() => {
l.borrow_mut()[*i as usize] = value;
@ -280,8 +282,11 @@ impl Value {
Value::Int(i) => Err(format!("List index {} cannot be negative", i)),
_ => Err(format!("Cannot index {:?} with {:?}", self, idx))
}
Self::Map(_) => todo!(),
v => Err(format!("Cannot index into {:?}", v))
Self::Map(m) => {
m.borrow_mut().insert(idx.clone(), value);
Ok(())
}
v => Err(format!("Cannot assign to index in {:?}", v))
}
}
}
@ -314,7 +319,19 @@ impl PartialEq for Value {
(Self::Char(a), Self::Char(b)) => a == b,
(Self::String(a), Self::String(b)) => a == b,
(Self::List(a), Self::List(b)) => a == b,
(Self::Map(_), Self::Map(_)) => todo!("Can't test maps for equality yet"),
(Self::Map(a), Self::Map(b)) => {
// prevent double borrow
if a.as_ref().as_ptr() == b.as_ref().as_ptr() { return true }
if a.borrow().len() != b.borrow().len() { return false }
for (k, v1) in a.borrow().iter() {
let bb = b.borrow();
let v2 = bb.get(k);
if v2 != Some(v1) {
return false
}
}
return true
}
(Self::Func(f1), Self::Func(f2)) => match (f1, f2) {
(
Func::Builtin { func: f1, arg_count: c1, .. },
@ -328,6 +345,8 @@ impl PartialEq for Value {
}
}
impl Eq for Value {}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self == other {
@ -352,6 +371,43 @@ impl PartialOrd for Value {
}
}
fn hash_f64<H: std::hash::Hasher>(f: f64, state: &mut H) {
if f.is_nan() {
"NaN".hash(state)
} else if f == 0.0 {
"0.0".hash(state)
} else if f.is_infinite() {
if f > 0.0 {
"+inf".hash(state)
} else{
"-inf".hash(state)
}
} else{
unsafe { std::mem::transmute::<f64,u64>(f).hash(state) }
}
}
impl Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Nil => "nil".hash(state),
Self::Type(_) => todo!(),
Self::Int(i) => i.hash(state),
Self::Float(f) => hash_f64(*f, state),
Self::Complex(z) => { hash_f64(z.re, state); hash_f64(z.im, state); }
Self::Rational(r) => { r.numer().hash(state); r.denom().hash(state); }
Self::Bool(b) => b.hash(state),
Self::Char(c) => c.hash(state),
Self::String(s) => s.hash(state),
Self::List(l) => l.borrow().hash(state),
Self::Map(_) => todo!(),
Self::Func(f) => f.hash(state),
Self::Data(_) => todo!(),
}
}
}
macro_rules! value_from {
($variant:ident, $($kind:ty)*) => {
$(
@ -370,6 +426,12 @@ impl From<Vec<Value>> for Value {
}
}
impl From<HashMap<Value,Value>> for Value {
fn from(x: HashMap<Value,Value>) -> Self {
Self::Map(RefCell::new(x).into())
}
}
value_from!(Int, u8 u16 u32 i8 i16 i32 i64);
value_from!(Float, f32 f64);
value_from!(Complex, Complex);
@ -378,7 +440,7 @@ value_from!(Bool, bool);
value_from!(String, String Rc<str>);
value_from!(List, RefCell<Vec<Value>>);
value_from!(Char, char);
value_from!(Map, HashMap<Value,Value>);
value_from!(Map, RefCell<HashMap<Value,Value>>);
macro_rules! impl_numeric_op {
@ -419,6 +481,9 @@ impl Neg for Value {
fn neg(self) -> Self::Output {
match self {
Value::Int(a) => Ok(Value::Int(-a)),
Value::Float(a) => Ok(Value::Float(-a)),
Value::Rational(a) => Ok(Value::Rational(-a)),
Value::Complex(a) => Ok(Value::Complex(-a)),
_ => Err(format!("Unsupported operation 'neg' on {:?}", self))
}
}