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]]
|
[[package]]
|
||||||
name = "num-complex"
|
name = "num-complex"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97fbc387afefefd5e9e39493299f3069e14a140dd34dc19b4c1c1a8fddb6a790"
|
checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
@ -265,9 +265,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-rational"
|
name = "num-rational"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
|
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
|
|
|
@ -5,8 +5,8 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
num-complex = "0.4.1"
|
num-complex = "0.4.2"
|
||||||
num-rational = "0.4.0"
|
num-rational = "0.4.1"
|
||||||
num-traits = "0.2.15"
|
num-traits = "0.2.15"
|
||||||
rustyline = "10.0.0"
|
rustyline = "10.0.0"
|
||||||
backtrace = "0.3.66"
|
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))
|
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 } => {
|
Expr::FuncCall { func, args, pos } => {
|
||||||
let func = eval_expr(func, env.clone())?;
|
let func = eval_expr(func, env.clone())?;
|
||||||
let mut arg_values = Vec::with_capacity(args.len());
|
let mut arg_values = Vec::with_capacity(args.len());
|
||||||
|
|
|
@ -40,6 +40,7 @@ pub enum Expr {
|
||||||
Ident { value: Token },
|
Ident { value: Token },
|
||||||
Literal { value: Token },
|
Literal { value: Token },
|
||||||
List { items: Vec<Expr> },
|
List { items: Vec<Expr> },
|
||||||
|
Map { items: Vec<(Expr,Expr)> },
|
||||||
FuncCall { func: Box<Expr>, args: Vec<Expr>, pos: Position },
|
FuncCall { func: Box<Expr>, args: Vec<Expr>, pos: Position },
|
||||||
Index { lhs: Box<Expr>, index: Box<Expr>, pos: Position },
|
Index { lhs: Box<Expr>, index: Box<Expr>, pos: Position },
|
||||||
Fn { args: Vec<Token>, body: Box<Stmt> },
|
Fn { args: Vec<Token>, body: Box<Stmt> },
|
||||||
|
@ -53,6 +54,7 @@ impl fmt::Debug for Expr {
|
||||||
Self::Ident { value } => write!(f, "(ident {:?})", value),
|
Self::Ident { value } => write!(f, "(ident {:?})", value),
|
||||||
Self::Literal { value } => write!(f, "(lit {:?})", value),
|
Self::Literal { value } => write!(f, "(lit {:?})", value),
|
||||||
Self::List { items } => write!(f, "(list {:?})", items),
|
Self::List { items } => write!(f, "(list {:?})", items),
|
||||||
|
Self::Map { items } => write!(f, "(map {:?})", items),
|
||||||
Self::FuncCall { func, args, .. } => write!(f, "(call {:?} {:?})", func, args),
|
Self::FuncCall { func, args, .. } => write!(f, "(call {:?} {:?})", func, args),
|
||||||
Self::Index { lhs, index, .. } => write!(f, "(index {:?} {:?})", lhs, index),
|
Self::Index { lhs, index, .. } => write!(f, "(index {:?} {:?})", lhs, index),
|
||||||
Self::Fn { args, body } => write!(f, "(fn {:?} {:?})", args, body)
|
Self::Fn { args, body } => write!(f, "(fn {:?} {:?})", args, body)
|
||||||
|
|
|
@ -223,19 +223,16 @@ impl Parser {
|
||||||
if let TokenType::LParen = next.ty {} else {
|
if let TokenType::LParen = next.ty {} else {
|
||||||
return Err(ParserError { message: "Expected left parenthesis to start arguments list".into(), pos: next.pos })
|
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| {
|
let args = self.commalist(TokenType::RParen, Self::ident)?;
|
||||||
if let Expr::Ident { value: i } = e { i }
|
|
||||||
else { panic!() }
|
|
||||||
}).collect();
|
|
||||||
self.err_on_eof()?;
|
self.err_on_eof()?;
|
||||||
let body = self.statement()?;
|
let body = self.statement()?;
|
||||||
Ok(Stmt::Fn { name, args, body: Box::new(body) })
|
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();
|
let next = self.next();
|
||||||
match next.ty {
|
match next.ty {
|
||||||
TokenType::Ident(_) => Ok(Expr::Ident{ value: next }),
|
TokenType::Ident(_) => Ok(next),
|
||||||
_ => Err(ParserError { message: "Expected identifier".into(), pos: next.pos })
|
_ => Err(ParserError { message: "Expected identifier".into(), pos: next.pos })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,7 +258,7 @@ impl Parser {
|
||||||
Ok(expr)
|
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![];
|
let mut items = vec![];
|
||||||
while !self.at_end() && self.peek().ty != terminator {
|
while !self.at_end() && self.peek().ty != terminator {
|
||||||
let expr = parse_item(self)?;
|
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 })
|
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> {
|
fn expr_base(&mut self) -> Result<Expr, ParserError> {
|
||||||
self.err_on_eof()?;
|
self.err_on_eof()?;
|
||||||
let next = self.next();
|
let next = self.next();
|
||||||
|
@ -391,16 +400,16 @@ impl Parser {
|
||||||
} else if next.ty == TokenType::LBrack {
|
} else if next.ty == TokenType::LBrack {
|
||||||
let items = self.commalist(TokenType::RBrack, Self::assignment)?;
|
let items = self.commalist(TokenType::RBrack, Self::assignment)?;
|
||||||
Ok(Expr::List { items })
|
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 {
|
} else if next.ty == TokenType::Fn {
|
||||||
self.err_on_eof()?;
|
self.err_on_eof()?;
|
||||||
let next = self.next();
|
let next = self.next();
|
||||||
if let TokenType::LParen = next.ty {} else {
|
if let TokenType::LParen = next.ty {} else {
|
||||||
return Err(ParserError { message: "Expected left parenthesis to start arguments list".into(), pos: next.pos })
|
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| {
|
let args = self.commalist(TokenType::RParen, Self::ident)?;
|
||||||
if let Expr::Ident { value: i } = e { i }
|
|
||||||
else { panic!() }
|
|
||||||
}).collect();
|
|
||||||
self.err_on_eof()?;
|
self.err_on_eof()?;
|
||||||
let body = self.statement()?;
|
let body = self.statement()?;
|
||||||
Ok(Expr::Fn { args, body: Box::new(body) })
|
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;
|
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 }));
|
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_chr, arg_count: 1, name }));
|
||||||
name = Rc::from("range");
|
name = Rc::from("range");
|
||||||
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_range, arg_count: 2, name }));
|
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");
|
name = Rc::from("len");
|
||||||
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_len, arg_count: 1, name }));
|
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_len, arg_count: 1, name }));
|
||||||
name = Rc::from("re");
|
name = Rc::from("re");
|
||||||
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_re, arg_count: 1, name }));
|
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_re, arg_count: 1, name }));
|
||||||
name = Rc::from("im");
|
name = Rc::from("im");
|
||||||
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_im, arg_count: 1, name }));
|
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> {
|
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] {
|
match &args[0] {
|
||||||
Value::String(s) => Ok(Value::Int(s.len() as i64)),
|
Value::String(s) => Ok(Value::Int(s.len() as i64)),
|
||||||
Value::List(l) => Ok(Value::Int(l.borrow().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))
|
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> {
|
fn fn_re(args: Vec<Value>) -> Result<Value, String> {
|
||||||
match &args[0] {
|
match &args[0] {
|
||||||
Value::Int(x) => Ok(Value::Float(*x as f64)),
|
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))
|
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};
|
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 {
|
pub enum EitherRteOrString {
|
||||||
Rte(RuntimeError), String(String)
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
|
@ -158,7 +160,8 @@ pub enum Value {
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Char(char),
|
Char(char),
|
||||||
String(Rc<str>),
|
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),
|
Func(Func),
|
||||||
Data(Data),
|
Data(Data),
|
||||||
}
|
}
|
||||||
|
@ -173,7 +176,7 @@ impl Value {
|
||||||
Rational(r) => !r.is_zero(),
|
Rational(r) => !r.is_zero(),
|
||||||
String(s) => !s.len() == 0,
|
String(s) => !s.len() == 0,
|
||||||
List(l) => !l.borrow().len() == 0,
|
List(l) => !l.borrow().len() == 0,
|
||||||
Map(m) => !m.len() == 0,
|
Map(m) => !m.borrow().len() == 0,
|
||||||
_ => true
|
_ => true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,14 +266,13 @@ impl Value {
|
||||||
Value::Int(i) => Err(format!("List index {} cannot be negative", i)),
|
Value::Int(i) => Err(format!("List index {} cannot be negative", i)),
|
||||||
_ => Err(format!("Cannot index {:?} with {:?}", self, idx))
|
_ => 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))
|
v => Err(format!("Cannot index into {:?}", v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assign_index(&self, idx: &Value, value: Value) -> Result<(), String> {
|
pub fn assign_index(&self, idx: &Value, value: Value) -> Result<(), String> {
|
||||||
match self {
|
match self {
|
||||||
Self::String(_) => todo!("Can't mutate strings yet"),
|
|
||||||
Self::List(l) => match idx {
|
Self::List(l) => match idx {
|
||||||
Value::Int(i) if *i >= 0 && (*i as usize) < l.borrow().len() => {
|
Value::Int(i) if *i >= 0 && (*i as usize) < l.borrow().len() => {
|
||||||
l.borrow_mut()[*i as usize] = value;
|
l.borrow_mut()[*i as usize] = value;
|
||||||
|
@ -280,8 +282,11 @@ impl Value {
|
||||||
Value::Int(i) => Err(format!("List index {} cannot be negative", i)),
|
Value::Int(i) => Err(format!("List index {} cannot be negative", i)),
|
||||||
_ => Err(format!("Cannot index {:?} with {:?}", self, idx))
|
_ => Err(format!("Cannot index {:?} with {:?}", self, idx))
|
||||||
}
|
}
|
||||||
Self::Map(_) => todo!(),
|
Self::Map(m) => {
|
||||||
v => Err(format!("Cannot index into {:?}", v))
|
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::Char(a), Self::Char(b)) => a == b,
|
||||||
(Self::String(a), Self::String(b)) => a == b,
|
(Self::String(a), Self::String(b)) => a == b,
|
||||||
(Self::List(a), Self::List(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) {
|
(Self::Func(f1), Self::Func(f2)) => match (f1, f2) {
|
||||||
(
|
(
|
||||||
Func::Builtin { func: f1, arg_count: c1, .. },
|
Func::Builtin { func: f1, arg_count: c1, .. },
|
||||||
|
@ -328,6 +345,8 @@ impl PartialEq for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Eq for Value {}
|
||||||
|
|
||||||
impl PartialOrd for Value {
|
impl PartialOrd for Value {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
if self == other {
|
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 {
|
macro_rules! value_from {
|
||||||
($variant:ident, $($kind:ty)*) => {
|
($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!(Int, u8 u16 u32 i8 i16 i32 i64);
|
||||||
value_from!(Float, f32 f64);
|
value_from!(Float, f32 f64);
|
||||||
value_from!(Complex, Complex);
|
value_from!(Complex, Complex);
|
||||||
|
@ -378,7 +440,7 @@ value_from!(Bool, bool);
|
||||||
value_from!(String, String Rc<str>);
|
value_from!(String, String Rc<str>);
|
||||||
value_from!(List, RefCell<Vec<Value>>);
|
value_from!(List, RefCell<Vec<Value>>);
|
||||||
value_from!(Char, char);
|
value_from!(Char, char);
|
||||||
value_from!(Map, HashMap<Value,Value>);
|
value_from!(Map, RefCell<HashMap<Value,Value>>);
|
||||||
|
|
||||||
|
|
||||||
macro_rules! impl_numeric_op {
|
macro_rules! impl_numeric_op {
|
||||||
|
@ -419,6 +481,9 @@ impl Neg for Value {
|
||||||
fn neg(self) -> Self::Output {
|
fn neg(self) -> Self::Output {
|
||||||
match self {
|
match self {
|
||||||
Value::Int(a) => Ok(Value::Int(-a)),
|
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))
|
_ => Err(format!("Unsupported operation 'neg' on {:?}", self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue