From 7173c5dda1d42f31653ad2042f5c0eb7c6b39b4c Mon Sep 17 00:00:00 2001 From: TriMill Date: Fri, 23 Sep 2022 14:55:18 -0400 Subject: [PATCH] refactoring, extensible types, basic file i/o --- complexpr/Cargo.toml | 1 + complexpr/src/eval.rs | 12 ++-- complexpr/src/parser.rs | 4 +- complexpr/src/stdlib/io.rs | 96 +++++++++++++++++++++++++++++-- complexpr/src/stdlib/math.rs | 4 +- complexpr/src/stdlib/mod.rs | 22 ++++++- complexpr/src/value/mod.rs | 108 +++++++++++++++++++++-------------- 7 files changed, 187 insertions(+), 60 deletions(-) diff --git a/complexpr/Cargo.toml b/complexpr/Cargo.toml index 8321d4c..1d1825c 100644 --- a/complexpr/Cargo.toml +++ b/complexpr/Cargo.toml @@ -9,3 +9,4 @@ num-rational = "0.4.1" num-traits = "0.2.15" strum = { version = "0.24.1", features = ["derive"] } paste = "1.0.9" +lazy_static = "1.4.0" diff --git a/complexpr/src/eval.rs b/complexpr/src/eval.rs index 14f2659..6ca7251 100644 --- a/complexpr/src/eval.rs +++ b/complexpr/src/eval.rs @@ -224,7 +224,7 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result { Ok(Value::Func(func)) }, Expr::StructInit { ty, args, .. } => { - let ty_val = eval_expr(&ty, env.clone())?; + let ty_val = eval_expr(ty, env.clone())?; let ty = match ty_val { Value::Type(ty) => ty, _ => return Err(format!("'{}' is not a type", ty_val.repr()).into()) @@ -294,7 +294,7 @@ pub fn eval_assignment(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Resul // plain assignment let r = eval_expr(rhs, env.clone())?; env.borrow_mut() - .set(name.clone(), r.clone()) + .set(name, r.clone()) .map_err(|_| RuntimeError::new("Variable not declared before assignment", op.pos.clone()))?; Ok(r) } else { @@ -306,7 +306,7 @@ pub fn eval_assignment(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Resul let result = compound_assignment_inner(&prev_value, &r, op)?; - env.borrow_mut().set(name, result.clone()).expect("unreachable"); + env.borrow_mut().set(name, result).expect("unreachable"); Ok(Value::Nil) } }, @@ -321,7 +321,7 @@ pub fn eval_assignment(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Resul let prev_value = l.index(&idx).map_err(|e| RuntimeError::new(e, pos.clone()))?; let r = eval_expr(rhs, env)?; let result = compound_assignment_inner(&prev_value, &r, op)?; - l.assign_index(&idx, result.clone()).map_err(|e| RuntimeError::new(e, pos.clone()))?; + l.assign_index(&idx, result).map_err(|e| RuntimeError::new(e, pos.clone()))?; Ok(Value::Nil) } }, @@ -403,7 +403,7 @@ pub fn eval_boolean(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result Func { let it = RefCell::new(it); - return Func::BuiltinClosure{ + Func::BuiltinClosure{ arg_count: 0, func: Rc::new(move |_| { if let Some(next) = it.borrow_mut().next() { @@ -417,7 +417,7 @@ fn mk_pipecolon_inner(f: Func, it: CIterator) -> Func { fn mk_pipequestion_inner(f: Func, it: CIterator) -> Func { let it = RefCell::new(it); - return Func::BuiltinClosure { + Func::BuiltinClosure { arg_count: 0, func: Rc::new(move |_| { loop { diff --git a/complexpr/src/parser.rs b/complexpr/src/parser.rs index 6f6f759..8d76019 100644 --- a/complexpr/src/parser.rs +++ b/complexpr/src/parser.rs @@ -410,8 +410,8 @@ impl Parser { }; Ok(Expr::Range { start: Box::new(start), - end: end.map(|x| Box::new(x)), - step: step.map(|x| Box::new(x)), + end: end.map(Box::new), + step: step.map(Box::new), incl }) } else { diff --git a/complexpr/src/stdlib/io.rs b/complexpr/src/stdlib/io.rs index 84f760e..679a179 100644 --- a/complexpr/src/stdlib/io.rs +++ b/complexpr/src/stdlib/io.rs @@ -1,21 +1,24 @@ -use std::io::Write; +use std::{io::{Write, Read}, fs::{OpenOptions, File}, rc::Rc, cell::RefCell, fmt}; -use crate::{env::Environment, declare_fn, value::Value, RuntimeError}; +use crate::{env::Environment, declare_fn, value::{Value, Native, TypeData}, RuntimeError}; pub fn load(env: &mut Environment) { declare_fn!(env, print, 1); declare_fn!(env, println, 1); declare_fn!(env, input, 0); + declare_fn!(env, open, 1); + declare_fn!(env, close, 1); + declare_fn!(env, read, 1); } fn fn_print(args: Vec) -> Result { - print!("{}", args[0].to_string()); + print!("{}", args[0]); std::io::stdout().flush().map_err(|e| e.to_string())?; Ok(Value::Nil) } fn fn_println(args: Vec) -> Result { - println!("{}", args[0].to_string()); + println!("{}", args[0]); Ok(Value::Nil) } @@ -28,3 +31,88 @@ fn fn_input(_: Vec) -> Result { } Ok(Value::from(buffer)) } + +struct FileBox { + f: Option +} + +lazy_static::lazy_static! { + static ref FILE_TYPE_ID: usize = crate::value::generate_type_id(); +} +thread_local!(static FILE_TYPE_NAME: Rc = Rc::from("File")); + +impl Native for FileBox { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + fn get_type(&self) -> crate::value::Type { + crate::value::Type { name: FILE_TYPE_NAME.with(Rc::clone), id: *FILE_TYPE_ID, typedata: TypeData::None } + + } +} + +impl fmt::Debug for FileBox { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Display for FileBox { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "", self.f) + } +} + +fn fn_open(args: Vec) -> Result { + let fname = match &args[0] { + Value::String(s) => s, + _ => return Err(format!("Expected filename, got {}", args[0]).into()) + }; + let f = match OpenOptions::new().read(true).open(fname.as_ref()) { + Ok(f) => f, + Err(e) => return Err(format!("Could not open file '{}': {}", fname, e).into()) + }; + Ok(Value::Native(Rc::new(RefCell::new(FileBox { f: Some(f) })))) +} + +fn fn_close(args: Vec) -> Result { + match &args[0] { + Value::Native(s) => { + let mut bs = s.borrow_mut(); + let f: &mut FileBox = match bs.as_any_mut().downcast_mut() { + Some(f) => f, + None => return Err(format!("Expected a file, got {}", args[0]).into()) + }; + drop(f.f.take()); + Ok(Value::Nil) + }, + _ => return Err(format!("Expected a file, got {}", args[0]).into()) + } +} + +fn fn_read(args: Vec) -> Result { + match &args[0] { + Value::Native(s) => { + let mut bs = s.borrow_mut(); + let f: &mut FileBox = match bs.as_any_mut().downcast_mut() { + Some(f) => f, + None => return Err(format!("Expected a file, got {}", args[0]).into()) + }; + if let Some(file) = &mut f.f { + let mut buf = String::new(); + match file.read_to_string(&mut buf) { + Ok(_) => return Ok(Value::from(buf)), + Err(e) => return Err(format!("Error reading file {}: {}", f, e).into()) + } + } else { + Err("Attempt to read file that has been closed".into()) + } + }, + _ => return Err(format!("Expected a file, got {}", args[0]).into()) + } +} diff --git a/complexpr/src/stdlib/math.rs b/complexpr/src/stdlib/math.rs index fcab6f6..01c09d6 100644 --- a/complexpr/src/stdlib/math.rs +++ b/complexpr/src/stdlib/math.rs @@ -56,9 +56,9 @@ fn try_into_floaty(v: &Value, name: &'static str) -> Result { match v { Value::Int(n) => Ok((*n as f64).into()), Value::Float(f) => Ok((*f).into()), - Value::Rational(r) => Ok((r.to_f64().ok_or_else(|| "Could not convert rational to float")?).into()), + Value::Rational(r) => Ok((r.to_f64().ok_or("Could not convert rational to float")?).into()), Value::Complex(z) => Ok((*z).into()), - _ => Err(format!("Argument to {} must be numeric", name).into()) + _ => Err(format!("Argument to {} must be numeric", name)) } } diff --git a/complexpr/src/stdlib/mod.rs b/complexpr/src/stdlib/mod.rs index ef5d702..be8eb8f 100644 --- a/complexpr/src/stdlib/mod.rs +++ b/complexpr/src/stdlib/mod.rs @@ -2,9 +2,9 @@ pub mod io; pub mod iter; pub mod math; -use std::time::{SystemTime, UNIX_EPOCH}; +use std::{time::{SystemTime, UNIX_EPOCH}, rc::Rc, cell::RefCell}; -use crate::{value::Value, RuntimeError, env::Environment}; +use crate::{value::{Value, func::Func}, RuntimeError, env::Environment}; #[macro_export] macro_rules! declare_fn { @@ -28,6 +28,7 @@ pub fn load(env: &mut Environment) { declare_fn!(env, chr, 1); declare_fn!(env, has, 2); declare_fn!(env, len, 1); + declare_fn!(env, args, 0); declare_fn!(env, time, 0); declare_fn!(env, list, 1); declare_fn!(env, push, 2); @@ -99,6 +100,21 @@ fn fn_has(args: Vec) -> Result { } } +fn fn_args(_: Vec) -> Result { + let mut args = std::env::args(); + args.next(); + let args = RefCell::new(args); + Ok(Value::Func(Func::BuiltinClosure { + arg_count: 0, + func: Rc::new(move |_| { + match args.borrow_mut().next() { + Some(s) => Ok(Value::from(s)), + None => Ok(Value::Nil) + } + }) + })) +} + fn fn_time(_: Vec) -> Result { let time = SystemTime::now().duration_since(UNIX_EPOCH).map_err(|e| e.to_string())?; Ok(Value::from(time.as_secs_f64())) @@ -122,7 +138,7 @@ fn fn_push(args: Vec) -> Result { fn fn_pop(args: Vec) -> Result { if let Value::List(l) = &args[0] { - l.as_ref().borrow_mut().pop().ok_or("Pop on empty list".into()) + l.as_ref().borrow_mut().pop().ok_or_else(|| "Pop on empty list".into()) } else{ Err("First argument to pop must be a list".into()) } diff --git a/complexpr/src/value/mod.rs b/complexpr/src/value/mod.rs index 778a4e4..2ccfde9 100644 --- a/complexpr/src/value/mod.rs +++ b/complexpr/src/value/mod.rs @@ -1,4 +1,4 @@ -use std::{rc::Rc, collections::HashMap, ops::*, cmp::Ordering, cell::RefCell, hash::Hash, sync::atomic::{AtomicUsize, self}}; +use std::{rc::Rc, collections::HashMap, ops::*, cmp::Ordering, cell::RefCell, hash::Hash, sync::atomic::{AtomicUsize, self}, fmt::{self, Write}, any::Any}; use num_traits::{Zero, ToPrimitive, Pow}; use strum::{EnumCount, EnumDiscriminants, EnumIter, IntoEnumIterator, AsRefStr}; @@ -10,6 +10,12 @@ pub mod func; pub type Rational = num_rational::Ratio; pub type Complex = num_complex::Complex64; +pub trait Native: Any + std::fmt::Debug + std::fmt::Display { + fn as_any(&self) -> &dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; + fn get_type(&self) -> Type; +} + static TYPE_COUNTER: AtomicUsize = AtomicUsize::new(Value::COUNT); #[derive(Clone, Debug)] @@ -33,6 +39,10 @@ impl PartialEq for Type { impl Eq for Type {} +pub fn generate_type_id() -> usize { + TYPE_COUNTER.fetch_add(1, atomic::Ordering::Relaxed) +} + pub fn generate_struct_type(name: Rc, fields: Vec) -> Type { Type { name, @@ -55,21 +65,25 @@ pub fn generate_builtin_types() -> Vec { fn fmt_list(list: &Vec) -> String { let mut result: String = "[".into(); - for v in list { - result += &(v.repr() + ", "); + if list.len() > 0 { + for v in list { + result += &(v.repr() + ", "); + } + result.pop(); + result.pop(); } - result.pop(); - result.pop(); result + "]" } fn fmt_map(map: &HashMap) -> String { let mut result: String = "{".into(); - for (k, v) in map { - result += &(k.repr() + ": " + &v.repr() + ", "); + if map.len() > 0 { + for (k, v) in map { + result += &(k.repr() + ": " + &v.repr() + ", "); + } + result.pop(); + result.pop(); } - result.pop(); - result.pop(); result + "}" } @@ -140,6 +154,7 @@ pub enum Value { Map(Rc>>), Func(Func), Struct(CxprStruct), + Native(Rc>), } impl Value { @@ -150,9 +165,9 @@ impl Value { Float(f) => *f != 0.0, Complex(z) => !z.is_zero(), Rational(r) => !r.is_zero(), - String(s) => !s.len() == 0, - List(l) => !l.borrow().len() == 0, - Map(m) => !m.borrow().len() == 0, + String(s) => s.len() != 0, + List(l) => l.borrow().len() != 0, + Map(m) => m.borrow().len() != 0, Char(c) => *c != '\0', _ => true } @@ -180,35 +195,6 @@ impl Value { } } - pub fn to_string(&self) -> String { - match self { - Self::Nil => "nil".into(), - Self::Bool(b) => b.to_string(), - Self::Int(n) => n.to_string(), - Self::Float(f) => f.to_string(), - Self::Rational(r) => r.to_string(), - Self::Complex(z) => z.to_string(), - Self::Char(c) => c.to_string(), - Self::String(s) => s.as_ref().to_owned(), - Self::List(l) => fmt_list(&l.borrow()), - Self::Map(m) => fmt_map(&m.borrow()), - Self::Type(t) => format!("", t.name), - Self::Func(Func::Builtin { name, func, .. }) => format!("", name, *func as *const ()), - Self::Func(Func::BuiltinClosure { .. }) => format!(""), - Self::Func(f @ Func::Partial { .. }) => match f.name() { - Some(name) => format!("", name), - None => "".into(), - } - Self::Func(Func::Func { name, .. }) => match name { - Some(name) => format!("", name), - None => "".into(), - }, - Self::Struct(CxprStruct { ty, data }) - => format!("{} {{ {} }}", ty.name, - data.borrow().iter().map(|(k, v)| format!("{}: {}", k, v.repr())) - .collect::>().join(", ")) - } - } pub fn repr(&self) -> String { match self { @@ -290,8 +276,10 @@ impl Value { pub fn get_type(&self) -> Type { let discr = ValueDiscriminants::from(self); - if let Self::Struct(_) = self { - todo!() + if let Self::Struct(s) = self { + s.ty.clone() + } else if let Self::Native(n) = self { + n.borrow().get_type() } else { Type { name: Rc::from(discr.as_ref()), @@ -302,6 +290,39 @@ impl Value { } } +impl fmt::Display for Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Nil => f.write_str("nil"), + Self::Bool(b) => f.write_str(&b.to_string()), + Self::Int(n) => f.write_str(&n.to_string()), + Self::Float(x) => f.write_str(&x.to_string()), + Self::Rational(r) => f.write_str(&r.to_string()), + Self::Complex(z) => f.write_str(&z.to_string()), + Self::Char(c) => f.write_char(*c), + Self::String(s) => f.write_str(s.as_ref()), + Self::List(l) => f.write_str(&fmt_list(&l.borrow())), + Self::Map(m) => f.write_str(&fmt_map(&m.borrow())), + Self::Type(t) => write!(f, "", t.name), + Self::Func(Func::Builtin { name, func, .. }) => write!(f, "", name, *func as *const ()), + Self::Func(Func::BuiltinClosure { .. }) => f.write_str(""), + Self::Func(func @ Func::Partial { .. }) => match func.name() { + Some(name) => write!(f, "", name), + None => f.write_str(""), + } + Self::Func(Func::Func { name, .. }) => match name { + Some(name) => write!(f, "", name), + None => f.write_str(""), + }, + Self::Struct(CxprStruct { ty, data }) + => write!(f, "{} {{ {} }}", ty.name, + data.borrow().iter().map(|(k, v)| format!("{}: {}", k, v.repr())) + .collect::>().join(", ")), + Self::Native(x) => f.write_str(&x.borrow().to_string()), + } + } +} + #[allow(clippy::ptr_eq)] // provided fix does not work impl PartialEq for Value { fn eq(&self, other: &Self) -> bool { @@ -415,6 +436,7 @@ impl Hash for Value { Self::Map(_) => todo!(), Self::Func(f) => f.hash(state), Self::Struct(_) => todo!(), + Self::Native(_) => todo!(), } }