diff --git a/Cargo.lock b/Cargo.lock index da9c4bb..e4dbc94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,6 +74,9 @@ dependencies = [ "num-complex", "num-rational", "num-traits", + "paste", + "strum", + "strum_macros", ] [[package]] @@ -172,6 +175,12 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "io-lifetimes" version = "0.7.3" @@ -294,6 +303,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "paste" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" + [[package]] name = "proc-macro2" version = "1.0.43" @@ -362,6 +377,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + [[package]] name = "rustyline" version = "10.0.0" @@ -414,6 +435,28 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "syn" version = "1.0.99" diff --git a/complexpr-bin/Cargo.toml b/complexpr-bin/Cargo.toml index 830640a..8458b38 100644 --- a/complexpr-bin/Cargo.toml +++ b/complexpr-bin/Cargo.toml @@ -10,3 +10,7 @@ complexpr = { path = "../complexpr" } rustyline = "10.0.0" backtrace = "0.3.66" rustyline-derive = "0.7.0" + +[[bin]] +name = "complexpr" +path = "src/main.rs" diff --git a/complexpr-bin/src/main.rs b/complexpr-bin/src/main.rs index 0d3e3b9..01eb9db 100644 --- a/complexpr-bin/src/main.rs +++ b/complexpr-bin/src/main.rs @@ -61,14 +61,20 @@ fn repl() -> Result<(), Box> { rl.set_helper(Some(h)); println!("Press {}Ctrl+D{} to exit.", C_BLUE, C_RESET); stdlib::load(&mut env.borrow_mut()); + stdlib::iter::load(&mut env.borrow_mut()); + stdlib::math::load(&mut env.borrow_mut()); loop { let readline = rl.readline(">> "); match readline { Ok(line) => { let result = interpret(&line, None, Some(env.clone()), true); match result { - Ok(Value::Nil) => (), - Ok(value) => println!("{}", value.repr()), + Ok(value) => { + if value != Value::Nil { + println!("{}", value.repr()); + } + env.borrow_mut().declare("_".into(), value); + } Err(e) => eprintln!("{}Error: {}{}", C_RED, C_RESET, e) } } diff --git a/complexpr/Cargo.toml b/complexpr/Cargo.toml index 137e594..02ff50c 100644 --- a/complexpr/Cargo.toml +++ b/complexpr/Cargo.toml @@ -7,3 +7,6 @@ edition = "2021" num-complex = "0.4.2" num-rational = "0.4.1" num-traits = "0.2.15" +strum_macros = "0.24" +strum = { version = "0.24", features = ["derive"] } +paste = "1.0.9" diff --git a/complexpr/src/eval.rs b/complexpr/src/eval.rs index e8bd258..8ac43c2 100644 --- a/complexpr/src/eval.rs +++ b/complexpr/src/eval.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, rc::Rc, cell::RefCell}; use num_traits::Pow; -use crate::{value::{Value, Complex, Func, CIterator}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError, Position, env::{EnvRef, Environment}}; +use crate::{value::{Value, Complex, func::{Func, CIterator}}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError, Position, env::{EnvRef, Environment}}; thread_local!(static PIPE_NAME: Option> = Some(Rc::from(""))); thread_local!(static FORLOOP_NAME: Option> = Some(Rc::from(""))); @@ -122,7 +122,8 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), Unwind> { Stmt::Return { pos, expr } => { let value = eval_expr(expr, env)?; return Err(Unwind::Return { pos: pos.clone(), value }) - } + }, + Stmt::Struct { .. } => todo!(), } Ok(()) } diff --git a/complexpr/src/expr.rs b/complexpr/src/expr.rs index 5ef40d5..9fbed3e 100644 --- a/complexpr/src/expr.rs +++ b/complexpr/src/expr.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::{token::{Token, OpType}, Position}; +use crate::{token::{Token, OpType}, Position, value::Type}; #[derive(Clone)] pub enum Stmt { @@ -14,6 +14,7 @@ pub enum Stmt { Continue { pos: Position }, Return { pos: Position, expr: Expr }, Fn { name: Token, args: Vec, body: Box }, + Struct { name: Token, ty: Type, items: Vec } } impl fmt::Debug for Stmt { @@ -29,6 +30,7 @@ impl fmt::Debug for Stmt { Self::Continue { .. } => write!(f, "(continue)"), Self::Return { expr, .. } => write!(f, "(return {:?})", expr), Self::Fn { name, args, body } => write!(f, "(fn {:?} {:?} {:?})", name, args, body), + Self::Struct { name, ty, items } => write!(f, "(struct {:?} #{:?} {:?})", name, ty.id, items), } } } diff --git a/complexpr/src/interpreter.rs b/complexpr/src/interpreter.rs index 4f62508..2e1681d 100644 --- a/complexpr/src/interpreter.rs +++ b/complexpr/src/interpreter.rs @@ -14,6 +14,8 @@ pub fn interpret(src: &str, fname: Option, env: Option, repl: bo } else { environ = Rc::new(RefCell::new(Environment::new())); stdlib::load(&mut environ.borrow_mut()); + stdlib::iter::load(&mut environ.borrow_mut()); + stdlib::math::load(&mut environ.borrow_mut()); } let mut result = Value::Nil; diff --git a/complexpr/src/lexer.rs b/complexpr/src/lexer.rs index effb2f7..82be629 100644 --- a/complexpr/src/lexer.rs +++ b/complexpr/src/lexer.rs @@ -347,6 +347,7 @@ impl Lexer { "for" => TokenType::For, "fn" => TokenType::Fn, "let" => TokenType::Let, + "struct" => TokenType::Struct, "break" => TokenType::Break, "continue" => TokenType::Continue, "return" => TokenType::Return, diff --git a/complexpr/src/parser.rs b/complexpr/src/parser.rs index 434dd5f..aacbc69 100644 --- a/complexpr/src/parser.rs +++ b/complexpr/src/parser.rs @@ -1,4 +1,4 @@ -use crate::{token::{Token, TokenType, OpType}, ParserError, expr::{Stmt, Expr}}; +use crate::{token::{Token, TokenType, OpType}, ParserError, expr::{Stmt, Expr}, value}; pub struct Parser { tokens: Vec, @@ -10,6 +10,20 @@ impl Parser { pub fn new(tokens: Vec, repl: bool) -> Self { Self { tokens, repl, idx: 0 } } + + pub fn parse(&mut self) -> Result, ParserError> { + let mut stmts = vec![]; + while !self.at_end() { + stmts.push(self.statement(!self.repl)?); + } + Ok(stmts) + } + + //////////////////////// + // // + // Helper functions // + // // + //////////////////////// fn at_end(&self) -> bool { self.idx >= self.tokens.len() @@ -25,6 +39,11 @@ impl Parser { t } + fn expect(&mut self, tokty: TokenType) -> (bool, Token) { + let next = self.next(); + (tokty == next.ty, next) + } + fn mk_error(&self, msg: S) -> ParserError where S: Into { let token = if self.at_end() { self.tokens.last().unwrap() @@ -42,14 +61,39 @@ impl Parser { } } - pub fn parse(&mut self) -> Result, ParserError> { - let mut stmts = vec![]; - while !self.at_end() { - stmts.push(self.statement(!self.repl)?); + fn ident(&mut self) -> Result { + let next = self.next(); + match next.ty { + TokenType::Ident(_) => Ok(next), + _ => Err(ParserError { message: "Expected identifier".into(), pos: next.pos }) } - Ok(stmts) } + fn commalist(&mut self, terminator: TokenType, parse_item: fn(&mut Parser) -> Result) -> Result, ParserError> { + let mut items = vec![]; + while !self.at_end() && self.peek().ty != terminator { + let expr = parse_item(self)?; + items.push(expr); + self.err_on_eof()?; + if self.peek().ty == TokenType::Comma { + self.next(); + } else if self.peek().ty == terminator { + break; + } else { + return Err(self.mk_error(format!("Expected Comma or {:?} after list", terminator))) + } + } + self.err_on_eof()?; + self.next(); + Ok(items) + } + + ////////////////// + // // + // Statements // + // // + ////////////////// + fn statement(&mut self, req_semicolon: bool) -> Result { let next_ty = &self.peek().ty; @@ -96,6 +140,10 @@ impl Parser { self.next(); self.fndef() }, + TokenType::Struct => { + self.next(); + self.structstmt() + }, _ => { // fallback to an expression terminated with a semicolon let expr = self.assignment()?; @@ -112,11 +160,9 @@ impl Parser { self.err_on_eof()?; } - let next = self.next(); - - match next.ty { - TokenType::Semicolon => Ok(stmt), - _ => Err(self.mk_error("Missing semicolon after statement")) + match self.expect(TokenType::Semicolon) { + (true, _) => Ok(stmt), + (false, _) => Err(self.mk_error("Missing semicolon after statement")) } } @@ -196,9 +242,8 @@ impl Parser { return Err(ParserError { message: "Expected identifer in function declaration".into(), pos: name.pos }) }; 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 }) + if !self.expect(TokenType::LParen).0 { + return Err(self.mk_error("Expected left parenthesis to start arguments list")) } let args = self.commalist(TokenType::RParen, Self::ident)?; self.err_on_eof()?; @@ -206,14 +251,6 @@ impl Parser { Ok(Stmt::Fn { name, args, body: Box::new(body) }) } - fn ident(&mut self) -> Result { - let next = self.next(); - match next.ty { - TokenType::Ident(_) => Ok(next), - _ => Err(ParserError { message: "Expected identifier".into(), pos: next.pos }) - } - } - fn block(&mut self) -> Result { let mut stmts = vec![]; while !self.at_end() && self.peek().ty != TokenType::RBrace { @@ -224,6 +261,26 @@ impl Parser { Ok(Stmt::Block{ stmts }) } + fn structstmt(&mut self) -> Result { + self.err_on_eof()?; + let tok_name = self.ident()?; + let name = tok_name.ty.clone().as_ident().unwrap(); + self.err_on_eof()?; + if !self.expect(TokenType::LBrace).0 { + return Err(self.mk_error("Expected left brace in struct definition")) + } + self.err_on_eof()?; + let items = self.commalist(TokenType::RBrace, Self::ident)?; + let ty = value::generate_type(name); + Ok(Stmt::Struct { name: tok_name, ty, items }) + } + + /////////////////// + // // + // Expressions // + // // + /////////////////// + // Generic method for left-associative operators fn expr(&mut self, op_type: OpType, next_level: fn(&mut Parser) -> Result) -> Result { let mut expr = next_level(self)?; @@ -235,24 +292,6 @@ impl Parser { Ok(expr) } - fn commalist(&mut self, terminator: TokenType, parse_item: fn(&mut Parser) -> Result) -> Result, ParserError> { - let mut items = vec![]; - while !self.at_end() && self.peek().ty != terminator { - let expr = parse_item(self)?; - items.push(expr); - self.err_on_eof()?; - if self.peek().ty == TokenType::Comma { - self.next(); - } else if self.peek().ty == terminator { - break; - } else { - return Err(self.mk_error(format!("Expected Comma or {:?} after list", terminator))) - } - } - self.err_on_eof()?; - self.next(); - Ok(items) - } fn assignment(&mut self) -> Result { let mut stack= vec![]; @@ -278,8 +317,7 @@ impl Parser { let right = self.logical_or()?; if op.ty == TokenType::PipeSlash || op.ty == TokenType::PipeBackslash { self.err_on_eof()?; - let next = self.next(); - if next.ty != TokenType::Comma { + if !self.expect(TokenType::Comma).0 { return Err(self.mk_error("Expected comma after first argument")) } let right2 = self.logical_or()?; @@ -358,7 +396,7 @@ impl Parser { let lbrack = self.next(); let index = self.assignment()?; self.err_on_eof()?; - if self.next().ty != TokenType::RBrack { + if !self.expect(TokenType::RBrack).0 { return Err(self.mk_error("Expected RBrack after collection index")) } Ok(Expr::Index { lhs: Box::new(expr), index: Box::new(index), pos: lbrack.pos }) @@ -367,8 +405,7 @@ impl Parser { 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 { + if !self.expect(TokenType::Colon).0 { return Err(self.mk_error("Expected colon in key-value pair")) } self.err_on_eof()?; @@ -402,8 +439,7 @@ impl Parser { 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 { + if !self.expect(TokenType::LParen).0 { return Err(self.mk_error("Expected left parenthesis to start arguments list")) } let args = self.commalist(TokenType::RParen, Self::ident)?; diff --git a/complexpr/src/stdlib/iter.rs b/complexpr/src/stdlib/iter.rs new file mode 100644 index 0000000..fbd2f03 --- /dev/null +++ b/complexpr/src/stdlib/iter.rs @@ -0,0 +1,87 @@ +use std::{rc::Rc, cell::RefCell}; + +use crate::{value::{Value, func::{Func, CIterator}}, RuntimeError, env::Environment, declare_fn}; + +pub fn load(env: &mut Environment) { + declare_fn!(env, take, 2); + declare_fn!(env, skip, 2); + declare_fn!(env, forall, 2); + declare_fn!(env, exists, 2); +} + + +fn take_inner(_: Vec, data: Rc>>, iter_data: Rc>>) -> Result { + // 0: current index + // 1: target index + let mut d = data.borrow_mut(); + if d[0] >= d[1] { + Ok(Value::Nil) + } else { + d[0] = (&d[0] + &Value::Int(1))?; + match iter_data.borrow_mut()[0].next() { + None => Ok(Value::Nil), + Some(x) => x + } + } +} + +fn fn_take(args: Vec) -> Result { + Ok(Value::Func(Func::BuiltinClosure { + arg_count: 0, + data: Rc::new(RefCell::new(vec![Value::Int(0), args[0].clone()])), + iter_data: Rc::new(RefCell::new(vec![args[1].iter()?])), + func: take_inner + })) +} + +fn skip_inner(_: Vec, data: Rc>>, iter_data: Rc>>) -> Result { + let mut d = if let Value::Int(d) = data.borrow()[0] { d } else { + unreachable!() // checked by fn_skip() + }; + while d > 0 { + iter_data.borrow_mut()[0].next(); + d -= 1; + } + data.borrow_mut()[0] = Value::Int(d); + match iter_data.borrow_mut()[0].next() { + None => Ok(Value::Nil), + Some(x) => x + } +} + +fn fn_skip(args: Vec) -> Result { + let n = match args[0] { + Value::Int(n) if n <= 0 => return Err(RuntimeError::new_no_pos("First argument to skip must be nonnegative")), + Value::Int(n) => n, + _ => return Err(RuntimeError::new_no_pos("First argument to skip must be an integer")) + }; + let it = args[1].iter()?; + Ok(Value::Func(Func::BuiltinClosure { + arg_count: 0, + data: Rc::new(RefCell::new(vec![Value::Int(n)])), + iter_data: Rc::new(RefCell::new(vec![it])), + func: skip_inner + })) +} + +fn fn_forall(args: Vec) -> Result { + let func = &args[0].as_func()?; + for item in args[1].iter()? { + let item = item?; + if !func.call(vec![item])?.truthy() { + return Ok(Value::Bool(false)) + } + } + Ok(Value::Bool(true)) +} + +fn fn_exists(args: Vec) -> Result { + let func = &args[0].as_func()?; + for item in args[1].iter()? { + let item = item?; + if func.call(vec![item])?.truthy() { + return Ok(Value::Bool(true)) + } + } + Ok(Value::Bool(false)) +} diff --git a/complexpr/src/stdlib/math.rs b/complexpr/src/stdlib/math.rs new file mode 100644 index 0000000..cdae4cd --- /dev/null +++ b/complexpr/src/stdlib/math.rs @@ -0,0 +1,197 @@ +use std::{rc::Rc, cmp::Ordering}; + +use num_traits::{ToPrimitive, Pow}; + +use crate::{value::{Value, func::Func, Complex, Rational}, RuntimeError, env::Environment, declare_fn}; + +enum Floaty { + Real(f64), Complex(Complex) +} + +impl From for Floaty { + fn from(f: f64) -> Self { + Self::Real(f) + } +} + +impl From for Floaty { + fn from(z: Complex) -> Self { + Self::Complex(z) + } +} + +pub fn load(env: &mut Environment) { + declare_fn!(env, re, 1); + declare_fn!(env, im, 1); + declare_fn!(env, min, 2); + declare_fn!(env, max, 2); + declare_fn!(env, floor, 1); + declare_fn!(env, ceil, 1); + declare_fn!(env, round, 1); + declare_fn!(env, round_to, 2); + declare_fn!(env, sin, 1); + declare_fn!(env, cos, 1); + declare_fn!(env, tan, 1); + declare_fn!(env, asin, 1); + declare_fn!(env, acos, 1); + declare_fn!(env, atan, 1); + declare_fn!(env, sinh, 1); + declare_fn!(env, cosh, 1); + declare_fn!(env, tanh, 1); + declare_fn!(env, asinh, 1); + declare_fn!(env, acosh, 1); + declare_fn!(env, atanh, 1); + declare_fn!(env, exp, 1); + declare_fn!(env, "log", fn_ln, 1); +} + +// +// Helper functions +// + +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::Complex(z) => Ok((*z).into()), + _ => Err(format!("Argument to {} must be numeric", name).into()) + } +} + +// +// Misc functions +// + +fn fn_re(args: Vec) -> Result { + match &args[0] { + Value::Int(x) => Ok(Value::Float(*x as f64)), + Value::Float(x) => Ok(Value::Float(*x)), + Value::Rational(x) => Ok(Value::Float(x.to_f64().unwrap())), + Value::Complex(x) => Ok(Value::Float(x.re)), + x => Err(format!("Cannot get real part of {:?}", x).into()) + } +} + +fn fn_im(args: Vec) -> Result { + match &args[0] { + Value::Int(_) | Value::Float(_) | Value::Rational(_) + => Ok(Value::Float(0.0)), + Value::Complex(x) => Ok(Value::Float(x.im)), + x => Err(format!("Cannot get real part of {:?}", x).into()) + } +} + +fn fn_min(args: Vec) -> Result { + match args[0].partial_cmp(&args[1]) { + None => Err("Arguments to min must be comparable".into()), + Some(Ordering::Greater) => Ok(args[1].clone()), + _ => Ok(args[0].clone()) + } +} + +fn fn_max(args: Vec) -> Result { + match args[0].partial_cmp(&args[1]) { + None => Err("Arguments to max must be comparable".into()), + Some(Ordering::Less) => Ok(args[1].clone()), + _ => Ok(args[0].clone()) + } +} + +fn fn_floor(args: Vec) -> Result { + match args[0] { + Value::Int(n) => Ok(Value::Int(n)), + Value::Float(f) => Ok(Value::Float(f.floor())), + Value::Complex(c) => Ok(Value::Complex(Complex::new(c.re.floor(), c.im.floor()))), + Value::Rational(r) => Ok(Value::Rational(r.floor())), + _ => Err("Argument to floor must be numeric".into()), + } +} + +fn fn_ceil(args: Vec) -> Result { + match args[0] { + Value::Int(n) => Ok(Value::Int(n)), + Value::Float(f) => Ok(Value::Float(f.ceil())), + Value::Complex(c) => Ok(Value::Complex(Complex::new(c.re.ceil(), c.im.ceil()))), + Value::Rational(r) => Ok(Value::Rational(r.ceil())), + _ => Err("Argument to ceil must be numeric".into()), + } +} + +fn fn_round(args: Vec) -> Result { + match args[0] { + Value::Int(n) => Ok(Value::Int(n)), + Value::Float(f) => Ok(Value::Float(f.round())), + Value::Complex(c) => Ok(Value::Complex(Complex::new(c.re.round(), c.im.round()))), + Value::Rational(r) => Ok(Value::Rational(r.round())), + _ => Err("Argument to round must be numeric".into()), + } +} + +fn fn_round_to(args: Vec) -> Result { + let places = if let Value::Int(x) = args[1] { + x as i32 + } else { + return Err("Second argument to round_to must be an integer".into()) + }; + match args[0] { + Value::Int(n) if places >= 0 => Ok(Value::Int(n)), + Value::Int(n) if places < 0 => { + let factor = 10i64.pow((-places) as u32); + Ok(Value::Int( + (n / factor) * factor + )) + } + Value::Float(f) => { + let factor = 10.0_f64.pow(places); + Ok(Value::Float( + (f * factor).round() / factor + )) + }, + Value::Complex(c) => { + let factor = 10.0_f64.pow(places); + Ok(Value::Complex(Complex::new( + (c.re * factor).round() / factor, + (c.im * factor).round() / factor, + ))) + }, + Value::Rational(r) => { + let factor = Rational::from(10).pow(places); + Ok(Value::Rational((r * factor).round() / factor)) + }, + _ => Err("First argument to round_to must be numeric".into()), + } +} + +// +// Transcendental functions +// + +macro_rules! transcendental { + ($func:ident) => { + paste::paste! { + fn [](args: Vec) -> Result { + let val = try_into_floaty(&args[0], stringify!($func))?; + match val { + Floaty::Real(f) => Ok(Value::Float(f.$func())), + Floaty::Complex(f) => Ok(Value::Complex(f.$func())), + } + } + } + }; +} + +transcendental!{ sin } +transcendental!{ cos } +transcendental!{ tan } +transcendental!{ asin } +transcendental!{ acos } +transcendental!{ atan } +transcendental!{ sinh } +transcendental!{ cosh } +transcendental!{ tanh } +transcendental!{ asinh } +transcendental!{ acosh } +transcendental!{ atanh } +transcendental!{ exp } +transcendental!{ ln } diff --git a/complexpr/src/stdlib/mod.rs b/complexpr/src/stdlib/mod.rs index 4470330..76611b2 100644 --- a/complexpr/src/stdlib/mod.rs +++ b/complexpr/src/stdlib/mod.rs @@ -1,51 +1,49 @@ +pub mod math; +pub mod iter; + use std::{rc::Rc, io::Write, cmp::Ordering, time::{SystemTime, UNIX_EPOCH}, cell::RefCell}; -use num_traits::ToPrimitive; +use crate::{value::{Value, func::{Func, CIterator}}, RuntimeError, env::Environment}; -use crate::{value::{Value, Func, CIterator}, RuntimeError, env::Environment}; +#[macro_export] +macro_rules! declare_fn { + ($env:ident, $name:ident, $arg_count:literal) => {paste::paste!{{ + let s: Rc = Rc::from(stringify!($name)); + $env.declare(s.clone(), Value::Func(Func::Builtin { func: [], arg_count: $arg_count, name: s })); + }}}; + ($env:ident, $name:literal, $rust_name:ident, $arg_count:literal) => {{ + let s: Rc = Rc::from($name); + $env.declare(s.clone(), Value::Func(Func::Builtin { func: $rust_name, arg_count: $arg_count, name: s })); + }}; +} pub fn load(env: &mut Environment) { - let mut name: Rc; - name = Rc::from("str"); - env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_str, arg_count: 1, name })); - name = Rc::from("repr"); - env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_repr, arg_count: 1, name })); - name = Rc::from("print"); - env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_print, arg_count: 1, name })); - name = Rc::from("println"); - env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_println, arg_count: 1, name })); - name = Rc::from("input"); - env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_input, arg_count: 0, name })); - name = Rc::from("ord"); - env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_ord, arg_count: 1, name })); - name = Rc::from("chr"); - 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 })); - name = Rc::from("list"); - env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_list, arg_count: 1, name })); - name = Rc::from("take"); - env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_take, arg_count: 2, name })); - name = Rc::from("skip"); - env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_skip, arg_count: 2, name })); - name = Rc::from("forall"); - env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_forall, arg_count: 2, name })); - name = Rc::from("exists"); - env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_exists, arg_count: 2, name })); - name = Rc::from("min"); - env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_min, arg_count: 2, name })); - name = Rc::from("max"); - env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_max, arg_count: 2, name })); + declare_fn!(env, "type", fn_type, 1); + declare_fn!(env, type_eq, 1); + declare_fn!(env, str, 1); + declare_fn!(env, repr, 1); + declare_fn!(env, print, 1); + declare_fn!(env, println, 1); + declare_fn!(env, input, 0); + declare_fn!(env, ord, 1); + declare_fn!(env, chr, 1); + declare_fn!(env, range, 2); + declare_fn!(env, has, 2); + declare_fn!(env, len, 1); + declare_fn!(env, time, 0); + declare_fn!(env, list, 1); +} + +fn fn_type(args: Vec) -> Result { + Ok(Value::Type(args[0].get_type())) +} + +fn fn_type_eq(args: Vec) -> Result { + Ok(Value::Bool(if args[0].get_type() != args[1].get_type() { + false + } else { + args[0] == args[1] + })) } fn fn_str(args: Vec) -> Result { @@ -141,25 +139,6 @@ fn fn_has(args: Vec) -> Result { } } -fn fn_re(args: Vec) -> Result { - match &args[0] { - Value::Int(x) => Ok(Value::Float(*x as f64)), - Value::Float(x) => Ok(Value::Float(*x)), - Value::Rational(x) => Ok(Value::Float(x.to_f64().unwrap())), - Value::Complex(x) => Ok(Value::Float(x.re)), - x => Err(format!("Cannot get real part of {:?}", x).into()) - } -} - -fn fn_im(args: Vec) -> Result { - match &args[0] { - Value::Int(_) | Value::Float(_) | Value::Rational(_) - => Ok(Value::Float(0.0)), - Value::Complex(x) => Ok(Value::Float(x.im)), - x => Err(format!("Cannot get real part of {:?}", x).into()) - } -} - 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())) @@ -171,95 +150,3 @@ fn fn_list(args: Vec) -> Result { for v in a { res.push(v?); } Ok(Value::from(res)) } - -fn take_inner(_: Vec, data: Rc>>, iter_data: Rc>>) -> Result { - // 0: current index - // 1: target index - let mut d = data.borrow_mut(); - if d[0] >= d[1] { - Ok(Value::Nil) - } else { - d[0] = (&d[0] + &Value::Int(1))?; - match iter_data.borrow_mut()[0].next() { - None => Ok(Value::Nil), - Some(x) => x - } - } -} - -fn fn_take(args: Vec) -> Result { - Ok(Value::Func(Func::BuiltinClosure { - arg_count: 0, - data: Rc::new(RefCell::new(vec![Value::Int(0), args[0].clone()])), - iter_data: Rc::new(RefCell::new(vec![args[1].iter()?])), - func: take_inner - })) -} - -fn skip_inner(_: Vec, data: Rc>>, iter_data: Rc>>) -> Result { - let mut d = if let Value::Int(d) = data.borrow()[0] { d } else { - unreachable!() // checked by fn_skip() - }; - while d > 0 { - iter_data.borrow_mut()[0].next(); - d -= 1; - } - data.borrow_mut()[0] = Value::Int(d); - match iter_data.borrow_mut()[0].next() { - None => Ok(Value::Nil), - Some(x) => x - } -} - -fn fn_skip(args: Vec) -> Result { - let n = match args[0] { - Value::Int(n) if n <= 0 => return Err(RuntimeError::new_no_pos("First argument to skip must be nonnegative")), - Value::Int(n) => n, - _ => return Err(RuntimeError::new_no_pos("First argument to skip must be an integer")) - }; - let it = args[1].iter()?; - Ok(Value::Func(Func::BuiltinClosure { - arg_count: 0, - data: Rc::new(RefCell::new(vec![Value::Int(n)])), - iter_data: Rc::new(RefCell::new(vec![it])), - func: skip_inner - })) -} - -fn fn_forall(args: Vec) -> Result { - let func = &args[0].as_func()?; - for item in args[1].iter()? { - let item = item?; - if !func.call(vec![item])?.truthy() { - return Ok(Value::Bool(false)) - } - } - Ok(Value::Bool(true)) -} - -fn fn_exists(args: Vec) -> Result { - let func = &args[0].as_func()?; - for item in args[1].iter()? { - let item = item?; - if func.call(vec![item])?.truthy() { - return Ok(Value::Bool(true)) - } - } - Ok(Value::Bool(false)) -} - -fn fn_min(args: Vec) -> Result { - match args[0].partial_cmp(&args[1]) { - None => Err("Arguments to min must be comparable".into()), - Some(Ordering::Greater) => Ok(args[1].clone()), - _ => Ok(args[0].clone()) - } -} - -fn fn_max(args: Vec) -> Result { - match args[0].partial_cmp(&args[1]) { - None => Err("Arguments to max must be comparable".into()), - Some(Ordering::Less) => Ok(args[1].clone()), - _ => Ok(args[0].clone()) - } -} diff --git a/complexpr/src/token.rs b/complexpr/src/token.rs index 7e1e3f0..95655e8 100644 --- a/complexpr/src/token.rs +++ b/complexpr/src/token.rs @@ -37,7 +37,8 @@ pub enum TokenType { True, False, Nil, If, Elif, Else, For, While, - Fn, Let, Break, Continue, Return + Fn, Let, Struct, + Break, Continue, Return } impl TokenType { diff --git a/complexpr/src/value/func.rs b/complexpr/src/value/func.rs new file mode 100644 index 0000000..859b555 --- /dev/null +++ b/complexpr/src/value/func.rs @@ -0,0 +1,178 @@ +use std::{rc::Rc, fmt, cmp::Ordering, cell::RefCell, hash::Hash}; + +use crate::{RuntimeError, eval::{eval_stmt, Unwind, eval_expr}, expr::Stmt, env::{EnvRef, Environment}}; + +use super::Value; + +pub type ClosureData = Rc>>; +pub type ClosureIterData = Rc>>; + +#[derive(Clone)] +pub enum Func { + Func { + name: Option>, + args: Vec>, + env: EnvRef, + func: Box, + }, + Builtin { + name: Rc, + func: fn(Vec) -> Result, + arg_count: usize, + }, + BuiltinClosure { + func: fn(Vec, ClosureData, ClosureIterData) -> Result, + data: ClosureData, + iter_data: ClosureIterData, + arg_count: usize, + }, + Partial { + inner: Box, + filled_args: Vec, + } +} + +impl fmt::Debug for Func { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Func { name, args, .. } + => f.debug_struct("Func::Func") + .field("name", name) + .field("args", args) + .finish_non_exhaustive(), + Self::Builtin { name, arg_count, .. } + => f.debug_struct("Func::Builtin") + .field("name", name) + .field("arg_count", arg_count) + .finish_non_exhaustive(), + Self::BuiltinClosure { arg_count, data, .. } + => f.debug_struct("Func::BuiltinClosure") + .field("arg_count", arg_count) + .field("data", data) + .finish_non_exhaustive(), + Self::Partial { inner, filled_args } + => f.debug_struct("Func::Partial") + .field("inner", inner) + .field("filled_args", filled_args) + .finish(), + } + } + +} + +impl Func { + pub fn arg_count(&self) -> usize { + match self { + Self::Builtin { arg_count, .. } => *arg_count, + Self::BuiltinClosure { arg_count, .. } => *arg_count, + Self::Func { args, .. } => args.len(), + Self::Partial { inner, filled_args } => inner.arg_count() - filled_args.len(), + } + } + + pub fn name(&self) -> Option> { + match self { + Self::Builtin { name, .. } => Some(name.clone()), + Self::BuiltinClosure { .. } => None, + Self::Func { name, .. } => name.clone(), + Self::Partial { inner, .. } => inner.name() + } + } + + pub fn call(&self, mut arg_values: Vec) -> Result { + match arg_values.len().cmp(&self.arg_count()) { + Ordering::Equal => match self { + Self::Builtin { func, .. } + => func(arg_values), + Self::BuiltinClosure { func, data, iter_data, .. } + => func(arg_values, data.clone(), iter_data.clone()), + Self::Func { args, func, env, .. } => { + let mut env = Environment::extend(env.clone()); + for (k, v) in args.iter().zip(arg_values.iter()) { + env.declare(k.clone(), v.clone()); + } + match func.as_ref() { + Stmt::Expr { expr } => eval_expr(expr, env.wrap()), + stmt => match eval_stmt(stmt, env.wrap()) { + Ok(()) => Ok(Value::Nil), + Err(Unwind::Return{ value, .. }) => Ok(value), + Err(e) => Err(e.as_error()), + } + + } + }, + Self::Partial { inner, filled_args } => { + let mut filled_args = filled_args.clone(); + filled_args.append(&mut arg_values); + inner.call(filled_args) + } + }, + Ordering::Less if arg_values.is_empty() => Err(RuntimeError::new_no_pos( + format!("Cannot call this function with zero arguments: expected {}", self.arg_count()) + )), + Ordering::Less => match self { + Self::Partial { inner, filled_args } => { + let mut args = filled_args.clone(); + args.append(&mut arg_values); + Ok(Value::Func(Func::Partial { inner: inner.clone(), filled_args: args })) + } + f => Ok(Value::Func(Func::Partial { inner: Box::new(f.clone()), filled_args: arg_values })) + }, + Ordering::Greater => Err(RuntimeError::new_no_pos( + format!("Too many arguments for function: expected {}, got {}", self.arg_count(), arg_values.len()) + )) + } + } +} + +impl Hash for Func { + fn hash(&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); + }, + Self::BuiltinClosure { arg_count, data, .. } => { + arg_count.hash(state); + data.borrow().hash(state); + }, + Self::Partial { inner, filled_args } => { + filled_args.hash(state); + inner.hash(state); + } + } + } +} + +pub enum CIterator { + // precondition: value must be len()able + Indexable{ value: Value, idx: i64 }, + Func(Func) +} + +impl Iterator for CIterator { + type Item = Result; + + fn next(&mut self) -> Option { + match self { + Self::Indexable{ value, ref mut idx } => { + if *idx >= value.len().unwrap() as i64 { + None + } else { + let result = value.index(&Value::Int(*idx)).unwrap(); + *idx += 1; + Some(Ok(result)) + } + }, + Self::Func(f) => match f.call(vec![]) { + Ok(Value::Nil) => None, + x => Some(x) + }, + } + } +} diff --git a/complexpr/src/value.rs b/complexpr/src/value/mod.rs similarity index 74% rename from complexpr/src/value.rs rename to complexpr/src/value/mod.rs index e90016c..2f45225 100644 --- a/complexpr/src/value.rs +++ b/complexpr/src/value/mod.rs @@ -1,200 +1,59 @@ -use std::{rc::Rc, collections::HashMap, ops::*, fmt, cmp::Ordering, cell::RefCell, hash::Hash}; +use std::{rc::Rc, collections::HashMap, ops::*, cmp::Ordering, cell::RefCell, hash::Hash, sync::atomic::{AtomicUsize, self}}; use num_traits::{Zero, ToPrimitive, Pow}; +use strum::{EnumCount, EnumDiscriminants, EnumIter, IntoEnumIterator, AsRefStr}; -use crate::{RuntimeError, eval::{eval_stmt, Unwind, eval_expr}, expr::Stmt, env::{EnvRef, Environment}}; +use self::func::{Func, CIterator}; + +pub mod func; pub type Rational = num_rational::Ratio; pub type Complex = num_complex::Complex64; -pub type ClosureData = Rc>>; -pub type ClosureIterData = Rc>>; -#[derive(Clone)] -pub enum Func { - Func { - name: Option>, - args: Vec>, - env: EnvRef, - func: Box, - }, - Builtin { - name: Rc, - func: fn(Vec) -> Result, - arg_count: usize, - }, - BuiltinClosure { - func: fn(Vec, ClosureData, ClosureIterData) -> Result, - data: ClosureData, - iter_data: ClosureIterData, - arg_count: usize, - }, - Partial { - inner: Box, - filled_args: Vec, - } -} - -impl fmt::Debug for Func { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Func { name, args, .. } - => f.debug_struct("Func::Func") - .field("name", name) - .field("args", args) - .finish_non_exhaustive(), - Self::Builtin { name, arg_count, .. } - => f.debug_struct("Func::Builtin") - .field("name", name) - .field("arg_count", arg_count) - .finish_non_exhaustive(), - Self::BuiltinClosure { arg_count, data, .. } - => f.debug_struct("Func::BuiltinClosure") - .field("arg_count", arg_count) - .field("data", data) - .finish_non_exhaustive(), - Self::Partial { inner, filled_args } - => f.debug_struct("Func::Partial") - .field("inner", inner) - .field("filled_args", filled_args) - .finish(), - } - } - -} - -impl Func { - pub fn arg_count(&self) -> usize { - match self { - Self::Builtin { arg_count, .. } => *arg_count, - Self::BuiltinClosure { arg_count, .. } => *arg_count, - Self::Func { args, .. } => args.len(), - Self::Partial { inner, filled_args } => inner.arg_count() - filled_args.len(), - } - } - - pub fn name(&self) -> Option> { - match self { - Self::Builtin { name, .. } => Some(name.clone()), - Self::BuiltinClosure { .. } => None, - Self::Func { name, .. } => name.clone(), - Self::Partial { inner, .. } => inner.name() - } - } - - pub fn call(&self, mut arg_values: Vec) -> Result { - match arg_values.len().cmp(&self.arg_count()) { - Ordering::Equal => match self { - Self::Builtin { func, .. } - => func(arg_values), - Self::BuiltinClosure { func, data, iter_data, .. } - => func(arg_values, data.clone(), iter_data.clone()), - Self::Func { args, func, env, .. } => { - let mut env = Environment::extend(env.clone()); - for (k, v) in args.iter().zip(arg_values.iter()) { - env.declare(k.clone(), v.clone()); - } - match func.as_ref() { - Stmt::Expr { expr } => eval_expr(expr, env.wrap()), - stmt => match eval_stmt(stmt, env.wrap()) { - Ok(()) => Ok(Value::Nil), - Err(Unwind::Return{ value, .. }) => Ok(value), - Err(e) => Err(e.as_error()), - } - - } - }, - Self::Partial { inner, filled_args } => { - let mut filled_args = filled_args.clone(); - filled_args.append(&mut arg_values); - inner.call(filled_args) - } - }, - Ordering::Less if arg_values.is_empty() => Err(RuntimeError::new_no_pos( - format!("Cannot call this function with zero arguments: expected {}", self.arg_count()) - )), - Ordering::Less => match self { - Self::Partial { inner, filled_args } => { - let mut args = filled_args.clone(); - args.append(&mut arg_values); - Ok(Value::Func(Func::Partial { inner: inner.clone(), filled_args: args })) - } - f => Ok(Value::Func(Func::Partial { inner: Box::new(f.clone()), filled_args: arg_values })) - }, - Ordering::Greater => Err(RuntimeError::new_no_pos( - format!("Too many arguments for function: expected {}, got {}", self.arg_count(), arg_values.len()) - )) - } - } -} - -impl Hash for Func { - fn hash(&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); - }, - Self::BuiltinClosure { arg_count, data, .. } => { - arg_count.hash(state); - data.borrow().hash(state); - }, - Self::Partial { inner, filled_args } => { - filled_args.hash(state); - inner.hash(state); - } - } - } -} - -pub enum CIterator { - // precondition: value must be len()able - Indexable{ value: Value, idx: i64 }, - Func(Func) -} - -impl Iterator for CIterator { - type Item = Result; - - fn next(&mut self) -> Option { - match self { - Self::Indexable{ value, ref mut idx } => { - if *idx >= value.len().unwrap() as i64 { - None - } else { - let result = value.index(&Value::Int(*idx)).unwrap(); - *idx += 1; - Some(Ok(result)) - } - }, - Self::Func(f) => match f.call(vec![]) { - Ok(Value::Nil) => None, - x => Some(x) - }, - } - } -} - -#[derive(Clone, Debug)] -pub struct Data { - pub ty: usize, - // TODO user-defined data types -} - -#[derive(Clone, Debug)] +static TYPE_COUNTER: AtomicUsize = AtomicUsize::new(Value::COUNT); +#[derive(Clone, Debug, Eq)] pub struct Type { pub name: Rc, pub id: usize } +impl PartialEq for Type { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + + +pub fn generate_type(name: Rc) -> Type { + Type { + name, + id: TYPE_COUNTER.fetch_add(1, atomic::Ordering::Relaxed) + } +} + +pub fn generate_builtin_types() -> Vec { + let mut types = vec![]; + for x in ValueDiscriminants::iter() { + types.push(Type { + name: Rc::from(x.as_ref()), + id: x as usize, + }) + } + types +} + #[derive(Clone, Debug)] +pub struct CxprStruct { + pub ty: Type, + pub data: Vec, +} + +#[derive(Clone, Debug, EnumCount, EnumDiscriminants)] +#[strum_discriminants(derive(EnumIter, AsRefStr))] +#[repr(u8)] pub enum Value { Nil, - Type(usize), + Type(Type), Int(i64), Float(f64), Complex(Complex), Rational(Rational), Bool(bool), Char(char), @@ -202,7 +61,7 @@ pub enum Value { List(Rc>>), Map(Rc>>), Func(Func), - Data(Data), + Data(CxprStruct), } impl Value { @@ -255,7 +114,7 @@ impl Value { Self::String(s) => s.clone(), Self::List(l) => Rc::from(format!("{:?}", l)), // TODO fix Self::Map(m) => Rc::from(format!("{:?}", m)), // TODO fix - Self::Type(_) => todo!(), + Self::Type(t) => Rc::from(format!("", t.name)), Self::Func(Func::Builtin { name, func, .. }) => Rc::from(format!("", name, *func as *const ())), Self::Func(Func::BuiltinClosure { func, .. }) => Rc::from(format!("", *func as *const ())), Self::Func(f @ Func::Partial { .. }) => match f.name() { @@ -349,6 +208,18 @@ impl Value { (x,y) => Err(format!("Unsupported operation 'fracdiv' between {:?} and {:?}", x, y)) } } + + pub fn get_type(&self) -> Type { + let discr = ValueDiscriminants::from(self); + if let Self::Data(_) = self { + todo!() + } else { + Type { + name: Rc::from(discr.as_ref()), + id: discr as usize + } + } + } } #[allow(clippy::ptr_eq)] // provided fix does not work @@ -356,7 +227,7 @@ impl PartialEq for Value { fn eq(&self, other: &Self) -> bool { match (self, other) { (Self::Nil, Self::Nil) => true, - (Self::Type(a), Self::Type(b)) => a == b, + (Self::Type(a), Self::Type(b)) => a.id == b.id, (Self::Int(a), Self::Int(b)) => a == b, (Self::Rational(a), Self::Int(b)) => *a == Rational::from(*b), @@ -443,7 +314,7 @@ fn hash_f64(f: f64, state: &mut H) { } else{ "-inf".hash(state) } - } else{ + } else { f.to_bits().hash(state); } } @@ -452,7 +323,7 @@ impl Hash for Value { fn hash(&self, state: &mut H) { match self { Self::Nil => "nil".hash(state), - Self::Type(_) => todo!(), + Self::Type(t) => { "".hash(state); t.id.hash(state); } 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); }