struct syntax, math functions, refactoring

This commit is contained in:
TriMill 2022-09-19 22:59:27 -04:00
parent 674d2ced7d
commit 8a62d46bcf
15 changed files with 713 additions and 394 deletions

43
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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)
}
}

View file

@ -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"

View file

@ -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(())
}

View file

@ -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),
}
}
}

View file

@ -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;

View file

@ -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,

View file

@ -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>,
@ -10,6 +10,20 @@ impl Parser {
pub fn new(tokens: Vec<Token>, repl: bool) -> Self {
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)?;

View 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))
}

View 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 }

View file

@ -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())
}
}

View file

@ -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
View 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)
},
}
}
}

View file

@ -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); }