hash maps
This commit is contained in:
parent
9c2a24c2d9
commit
ccbc6c32f3
8 changed files with 179 additions and 42 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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
36
examples/fib_recur.cxpr
Normal 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));
|
||||
}
|
|
@ -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());
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) })
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
|
|
111
src/value.rs
111
src/value.rs
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue