struct syntax, math functions, refactoring
This commit is contained in:
parent
674d2ced7d
commit
8a62d46bcf
15 changed files with 713 additions and 394 deletions
43
Cargo.lock
generated
43
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -61,14 +61,20 @@ fn repl() -> Result<(), Box<dyn std::error::Error>> {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<Rc<str>> = Some(Rc::from("<pipeline>")));
|
||||
thread_local!(static FORLOOP_NAME: Option<Rc<str>> = Some(Rc::from("<for loop>")));
|
||||
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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<Token>, body: Box<Stmt> },
|
||||
Struct { name: Token, ty: Type, items: Vec<Token> }
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ pub fn interpret(src: &str, fname: Option<String>, env: Option<EnvRef>, 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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<Token>,
|
||||
|
@ -11,6 +11,20 @@ impl Parser {
|
|||
Self { tokens, repl, idx: 0 }
|
||||
}
|
||||
|
||||
pub fn parse(&mut self) -> Result<Vec<Stmt>, 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<S>(&self, msg: S) -> ParserError where S: Into<String> {
|
||||
let token = if self.at_end() {
|
||||
self.tokens.last().unwrap()
|
||||
|
@ -42,14 +61,39 @@ impl Parser {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse(&mut self) -> Result<Vec<Stmt>, ParserError> {
|
||||
let mut stmts = vec![];
|
||||
while !self.at_end() {
|
||||
stmts.push(self.statement(!self.repl)?);
|
||||
fn ident(&mut self) -> Result<Token, ParserError> {
|
||||
let next = self.next();
|
||||
match next.ty {
|
||||
TokenType::Ident(_) => Ok(next),
|
||||
_ => Err(ParserError { message: "Expected identifier".into(), pos: next.pos })
|
||||
}
|
||||
Ok(stmts)
|
||||
}
|
||||
|
||||
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)?;
|
||||
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<Stmt, ParserError> {
|
||||
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<Token, ParserError> {
|
||||
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<Stmt, ParserError> {
|
||||
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<Stmt, ParserError> {
|
||||
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<Expr, ParserError>) -> Result<Expr, ParserError> {
|
||||
let mut expr = next_level(self)?;
|
||||
|
@ -235,24 +292,6 @@ impl Parser {
|
|||
Ok(expr)
|
||||
}
|
||||
|
||||
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)?;
|
||||
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<Expr, ParserError> {
|
||||
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)?;
|
||||
|
|
87
complexpr/src/stdlib/iter.rs
Normal file
87
complexpr/src/stdlib/iter.rs
Normal file
|
@ -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<Value>, data: Rc<RefCell<Vec<Value>>>, iter_data: Rc<RefCell<Vec<CIterator>>>) -> Result<Value, RuntimeError> {
|
||||
// 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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>, data: Rc<RefCell<Vec<Value>>>, iter_data: Rc<RefCell<Vec<CIterator>>>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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))
|
||||
}
|
197
complexpr/src/stdlib/math.rs
Normal file
197
complexpr/src/stdlib/math.rs
Normal file
|
@ -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<f64> for Floaty {
|
||||
fn from(f: f64) -> Self {
|
||||
Self::Real(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Complex> 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<Floaty, String> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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 [<fn_ $func>](args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
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 }
|
|
@ -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<str> = Rc::from(stringify!($name));
|
||||
$env.declare(s.clone(), Value::Func(Func::Builtin { func: [<fn_ $name>], arg_count: $arg_count, name: s }));
|
||||
}}};
|
||||
($env:ident, $name:literal, $rust_name:ident, $arg_count:literal) => {{
|
||||
let s: Rc<str> = 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<str>;
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
Ok(Value::Type(args[0].get_type()))
|
||||
}
|
||||
|
||||
fn fn_type_eq(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
Ok(Value::Bool(if args[0].get_type() != args[1].get_type() {
|
||||
false
|
||||
} else {
|
||||
args[0] == args[1]
|
||||
}))
|
||||
}
|
||||
|
||||
fn fn_str(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
|
@ -141,25 +139,6 @@ fn fn_has(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
|||
}
|
||||
}
|
||||
|
||||
fn fn_re(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
|||
for v in a { res.push(v?); }
|
||||
Ok(Value::from(res))
|
||||
}
|
||||
|
||||
fn take_inner(_: Vec<Value>, data: Rc<RefCell<Vec<Value>>>, iter_data: Rc<RefCell<Vec<CIterator>>>) -> Result<Value, RuntimeError> {
|
||||
// 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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>, data: Rc<RefCell<Vec<Value>>>, iter_data: Rc<RefCell<Vec<CIterator>>>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
178
complexpr/src/value/func.rs
Normal file
178
complexpr/src/value/func.rs
Normal file
|
@ -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<RefCell<Vec<Value>>>;
|
||||
pub type ClosureIterData = Rc<RefCell<Vec<CIterator>>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Func {
|
||||
Func {
|
||||
name: Option<Rc<str>>,
|
||||
args: Vec<Rc<str>>,
|
||||
env: EnvRef,
|
||||
func: Box<Stmt>,
|
||||
},
|
||||
Builtin {
|
||||
name: Rc<str>,
|
||||
func: fn(Vec<Value>) -> Result<Value, RuntimeError>,
|
||||
arg_count: usize,
|
||||
},
|
||||
BuiltinClosure {
|
||||
func: fn(Vec<Value>, ClosureData, ClosureIterData) -> Result<Value, RuntimeError>,
|
||||
data: ClosureData,
|
||||
iter_data: ClosureIterData,
|
||||
arg_count: usize,
|
||||
},
|
||||
Partial {
|
||||
inner: Box<Func>,
|
||||
filled_args: Vec<Value>,
|
||||
}
|
||||
}
|
||||
|
||||
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<Rc<str>> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<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);
|
||||
},
|
||||
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<Value, RuntimeError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<i64>;
|
||||
pub type Complex = num_complex::Complex64;
|
||||
pub type ClosureData = Rc<RefCell<Vec<Value>>>;
|
||||
pub type ClosureIterData = Rc<RefCell<Vec<CIterator>>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Func {
|
||||
Func {
|
||||
name: Option<Rc<str>>,
|
||||
args: Vec<Rc<str>>,
|
||||
env: EnvRef,
|
||||
func: Box<Stmt>,
|
||||
},
|
||||
Builtin {
|
||||
name: Rc<str>,
|
||||
func: fn(Vec<Value>) -> Result<Value, RuntimeError>,
|
||||
arg_count: usize,
|
||||
},
|
||||
BuiltinClosure {
|
||||
func: fn(Vec<Value>, ClosureData, ClosureIterData) -> Result<Value, RuntimeError>,
|
||||
data: ClosureData,
|
||||
iter_data: ClosureIterData,
|
||||
arg_count: usize,
|
||||
},
|
||||
Partial {
|
||||
inner: Box<Func>,
|
||||
filled_args: Vec<Value>,
|
||||
}
|
||||
}
|
||||
|
||||
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<Rc<str>> {
|
||||
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<Value>) -> Result<Value, RuntimeError> {
|
||||
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<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);
|
||||
},
|
||||
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<Value, RuntimeError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<str>,
|
||||
pub id: usize
|
||||
}
|
||||
|
||||
impl PartialEq for Type {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn generate_type(name: Rc<str>) -> Type {
|
||||
Type {
|
||||
name,
|
||||
id: TYPE_COUNTER.fetch_add(1, atomic::Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_builtin_types() -> Vec<Type> {
|
||||
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<Value>,
|
||||
}
|
||||
|
||||
#[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<RefCell<Vec<Value>>>),
|
||||
Map(Rc<RefCell<HashMap<Value,Value>>>),
|
||||
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!("<type {}>", t.name)),
|
||||
Self::Func(Func::Builtin { name, func, .. }) => Rc::from(format!("<builtin fn {} at {:?}>", name, *func as *const ())),
|
||||
Self::Func(Func::BuiltinClosure { func, .. }) => Rc::from(format!("<builtin anonymous fn at {:?}>", *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<H: std::hash::Hasher>(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<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Self::Nil => "nil".hash(state),
|
||||
Self::Type(_) => todo!(),
|
||||
Self::Type(t) => { "<type>".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); }
|
Loading…
Reference in a new issue