project reorganization, general improvements

This commit is contained in:
TriMill 2022-09-16 23:06:49 -04:00
parent 172d7b10f9
commit 19b7215234
26 changed files with 139 additions and 125 deletions

29
Cargo.lock generated
View File

@ -71,11 +71,18 @@ dependencies = [
name = "complexpr"
version = "0.1.0"
dependencies = [
"backtrace",
"lazy_static",
"num-complex",
"num-rational",
"num-traits",
]
[[package]]
name = "complexpr-bin"
version = "0.1.0"
dependencies = [
"backtrace",
"complexpr",
"rustyline",
]
@ -415,18 +422,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.34"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1b05ca9d106ba7d2e31a9dab4a64e7be2cce415321966ea3132c49a656e252"
checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.34"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487"
checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783"
dependencies = [
"proc-macro2",
"quote",
@ -435,21 +442,21 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.3"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
[[package]]
name = "unicode-segmentation"
version = "1.9.0"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
[[package]]
name = "unicode-width"
version = "0.1.9"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "utf8parse"

View File

@ -1,12 +1,2 @@
[package]
name = "complexpr"
version = "0.1.0"
edition = "2021"
[dependencies]
lazy_static = "1.4.0"
num-complex = "0.4.2"
num-rational = "0.4.1"
num-traits = "0.2.15"
rustyline = "10.0.0"
backtrace = "0.3.66"
[workspace]
members = [ "complexpr-bin", "complexpr" ]

11
complexpr-bin/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "complexpr-bin"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
complexpr = { path = "../complexpr" }
rustyline = "10.0.0"
backtrace = "0.3.66"

View File

@ -1,7 +1,7 @@
use std::{rc::Rc, cell::RefCell, fs, panic::{self, PanicInfo}};
use backtrace::Backtrace;
use complexpr::{eval::Environment, interpreter::interpret, value::Value, stdlib};
use complexpr::{env::Environment, interpreter::interpret, value::Value, stdlib};
use rustyline::{self, error::ReadlineError};
const C_RESET: &str = "\x1b[0m";
@ -25,7 +25,6 @@ fn panic_hook(info: &PanicInfo) {
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("{}", std::mem::size_of::<Value>());
panic::set_hook(Box::new(panic_hook));
let args: Vec<String> = std::env::args().collect();
if args.len() == 2 {

10
complexpr/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "complexpr"
version = "0.1.0"
edition = "2021"
[dependencies]
lazy_static = "1.4.0"
num-complex = "0.4.2"
num-rational = "0.4.1"
num-traits = "0.2.15"

55
complexpr/src/env.rs Normal file
View File

@ -0,0 +1,55 @@
use std::{collections::HashMap, rc::Rc, cell::RefCell};
use crate::value::Value;
#[derive(Debug)]
pub struct Environment {
parent: Option<EnvRef>,
map: HashMap<Rc<str>, Value>,
}
pub type EnvRef = Rc<RefCell<Environment>>;
impl Environment {
pub fn new() -> Self {
Self { parent: None, map: HashMap::new() }
}
pub fn wrap(self) -> EnvRef {
Rc::new(RefCell::new(self))
}
pub fn extend(parent: EnvRef) -> Self {
Self { parent: Some(parent), map: HashMap::new() }
}
pub fn get(&self, name: &str) -> Option<Value> {
match self.map.get(name) {
Some(v) => Some(v.clone()),
None => match self.parent {
Some(ref p) => p.borrow().get(name),
None => None
}
}
}
pub fn declare(&mut self, name: Rc<str>, value: Value) {
self.map.insert(name, value);
}
pub fn set(&mut self, name: Rc<str>, value: Value) -> Result<(),()> {
match self.map.contains_key(&name) {
true => { self.map.insert(name, value); Ok(()) },
false => match self.parent {
Some(ref mut p) => p.borrow_mut().set(name, value),
None => Err(())
}
}
}
}
impl Default for Environment {
fn default() -> Self {
Self::new()
}
}

View File

@ -1,60 +1,9 @@
use std::{collections::HashMap, rc::Rc, cell::RefCell};
use num_traits::{Pow, Zero};
use num_traits::Pow;
use crate::{value::{Value, Complex, Func, Rational, CIterator}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError, Position};
use crate::{value::{Value, Complex, Func, CIterator}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError, Position, env::{EnvRef, Environment}};
#[derive(Debug)]
pub struct Environment {
parent: Option<EnvRef>,
map: HashMap<Rc<str>, Value>,
}
pub type EnvRef = Rc<RefCell<Environment>>;
impl Environment {
pub fn new() -> Self {
Self { parent: None, map: HashMap::new() }
}
pub fn wrap(self) -> EnvRef {
Rc::new(RefCell::new(self))
}
pub fn extend(parent: EnvRef) -> Self {
Self { parent: Some(parent), map: HashMap::new() }
}
pub fn get(&self, name: &str) -> Option<Value> {
match self.map.get(name) {
Some(v) => Some(v.clone()),
None => match self.parent {
Some(ref p) => p.borrow().get(name),
None => None
}
}
}
pub fn declare(&mut self, name: Rc<str>, value: Value) {
self.map.insert(name, value);
}
pub fn set(&mut self, name: Rc<str>, value: Value) -> Result<(),()> {
match self.map.contains_key(&name) {
true => { self.map.insert(name, value); Ok(()) },
false => match self.parent {
Some(ref mut p) => p.borrow_mut().set(name, value),
None => Err(())
}
}
}
}
impl Default for Environment {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub enum Unwind {
@ -256,6 +205,19 @@ pub fn eval_ident(token: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
}
}
fn compound_assignment_inner(l: Value, r: Value, op: &Token) -> Result<Value, RuntimeError> {
match op.ty {
TokenType::PlusEqual => &l + &r,
TokenType::MinusEqual => &l - &r,
TokenType::StarEqual => &l * &r,
TokenType::SlashEqual => &l / &r,
TokenType::PercentEqual => &l % &r,
TokenType::CaretEqual => l.pow(&r),
TokenType::DoubleSlashEqual => l.fracdiv(&r),
_ => todo!() // TODO more operations
}.map_err(|e| RuntimeError::new(e, op.pos.clone()))
}
pub fn eval_assignment(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
// lhs must be an identifier (checked in parser)
if let Expr::Ident{value: Token{ty: TokenType::Ident(name),..}} = lhs {
@ -273,14 +235,7 @@ pub fn eval_assignment(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Resul
.ok_or_else(|| RuntimeError::new("Variable not defined in scope", op.pos.clone()))?;
let r = eval_expr(rhs, env.clone())?;
let result = match op.ty {
TokenType::PlusEqual => &prev_value + &r,
TokenType::MinusEqual => &prev_value - &r,
TokenType::StarEqual => &prev_value * &r,
TokenType::SlashEqual => &prev_value / &r,
TokenType::PercentEqual => &prev_value % &r,
_ => todo!() // TODO more operations
}.map_err(|e| RuntimeError::new(e, op.pos.clone()))?;
let result = compound_assignment_inner(prev_value, r, op)?;
env.borrow_mut()
.set(name.clone(), result.clone()).expect("unreachable");
@ -296,14 +251,7 @@ pub fn eval_assignment(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Resul
} else {
let prev_value = l.index(&idx).map_err(|e| RuntimeError::new(e, pos.clone()))?;
let r = eval_expr(rhs, env)?;
let result = match op.ty {
TokenType::PlusEqual => &prev_value + &r,
TokenType::MinusEqual => &prev_value - &r,
TokenType::StarEqual => &prev_value * &r,
TokenType::SlashEqual => &prev_value / &r,
TokenType::PercentEqual => &prev_value % &r,
_ => todo!() // TODO more operations
}.map_err(|e| RuntimeError::new(e, op.pos.clone()))?;
let result = compound_assignment_inner(prev_value, r, op)?;
l.assign_index(&idx, result.clone()).map_err(|e| RuntimeError::new(e, pos.clone()))?;
Ok(result)
}
@ -322,18 +270,8 @@ pub fn eval_arith(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Val
TokenType::Slash => &l / &r,
TokenType::Percent => &l % &r,
TokenType::Caret => l.pow(&r),
TokenType::DoubleSlash => match (l, r) {
(Value::Int(_), Value::Int(b)) if b == 0 => Err("Integer division by zero".into()),
(Value::Rational(_), Value::Int(b)) if b == 0 => Err("Rational division by zero".into()),
(Value::Int(_), Value::Rational(b)) if b.is_zero() => Err("Rational division by zero".into()),
(Value::Rational(_), Value::Rational(b)) if b.is_zero() => Err("Rational division by zero".into()),
(Value::Int(a), Value::Int(b)) => Ok(Value::Rational(Rational::new(a, b))),
(Value::Rational(a), Value::Int(b)) => Ok(Value::from(a/b)),
(Value::Int(a), Value::Rational(b)) => Ok(Value::from(b.recip()*a)),
(Value::Rational(a), Value::Rational(b)) => Ok(Value::from(a/b)),
(x,y) => Err(format!("Unsupported operation 'fracdiv' between {:?} and {:?}", x, y))
},
_ => todo!() // TODO other operations
TokenType::DoubleSlash => l.fracdiv(&r),
_ => todo!()
}.map_err(|e| RuntimeError::new(e, op.pos.clone()))
}

View File

@ -1,6 +1,6 @@
use std::{cell::RefCell, rc::Rc};
use crate::{value::Value, lexer::Lexer, parser::Parser, eval::{Environment, eval_stmt, eval_expr, EnvRef}, expr::Stmt, stdlib};
use crate::{value::Value, lexer::Lexer, parser::Parser, eval::{eval_stmt, eval_expr}, expr::Stmt, stdlib, env::{EnvRef, Environment}};
pub fn interpret(src: &str, fname: Option<String>, env: Option<EnvRef>, repl: bool) -> Result<Value, Box<dyn std::error::Error>> {
let ctx_name = if repl { "<interactive input>" } else { fname.as_ref().map(|s| s.as_ref()).unwrap_or("<unknown>") };

View File

@ -146,7 +146,6 @@ impl Lexer {
},
'-' => match self.expect(&['=', '>']) {
Some('=') => self.add_token(TokenType::MinusEqual, "-="),
Some('>') => self.add_token(TokenType::Arrow, "->"),
_ => self.add_token(TokenType::Minus, "-"),
},
'*' => match self.expect(&['=']) {

View File

@ -7,6 +7,7 @@ pub mod parser;
pub mod value;
pub mod eval;
pub mod interpreter;
pub mod env;
pub mod stdlib;
#[derive(Clone, Debug)]

View File

@ -2,7 +2,7 @@ use std::{rc::Rc, io::Write, cmp::Ordering, time::{SystemTime, UNIX_EPOCH}, cell
use num_traits::ToPrimitive;
use crate::{value::{Value, Func, CIterator}, eval::Environment, RuntimeError};
use crate::{value::{Value, Func, CIterator}, RuntimeError, env::Environment};
pub fn load(env: &mut Environment) {
let mut name: Rc<str>;

View File

@ -27,7 +27,7 @@ pub enum TokenType {
Equal, PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual, DoubleSlashEqual, CaretEqual,
DoubleEqual, BangEqual, Greater, GreaterEqual, Less, LessEqual, Spaceship,
Arrow, PipeColon, PipePoint, PipeQuestion, PipeAmper,
PipeColon, PipePoint, PipeQuestion, PipeAmper,
Comma, Semicolon, Colon,

View File

@ -2,7 +2,7 @@ use std::{rc::Rc, collections::HashMap, ops::*, fmt, cmp::Ordering, cell::RefCel
use num_traits::{Zero, ToPrimitive, Pow};
use crate::{RuntimeError, eval::{EnvRef, eval_stmt, Environment, Unwind, eval_expr}, expr::Stmt};
use crate::{RuntimeError, eval::{eval_stmt, Unwind, eval_expr}, expr::Stmt, env::{EnvRef, Environment}};
pub type Rational = num_rational::Ratio<i64>;
pub type Complex = num_complex::Complex64;
@ -231,24 +231,13 @@ impl Value {
pub fn repr(&self) -> Rc<str> {
match self {
Self::Nil => Rc::from("nil"),
Self::Bool(b) => Rc::from(b.to_string()),
Self::Int(n) => Rc::from(n.to_string()),
Self::Float(f) => Rc::from(f.to_string()),
Self::Rational(r) => Rc::from(r.to_string()),
Self::Complex(z) => Rc::from(z.to_string()),
Self::Float(f) => Rc::from(format!("{:?}",f)),
Self::Rational(r) => Rc::from(r.numer().to_string() + "//" + &r.denom().to_string()),
Self::Char(c) => Rc::from(format!("'{}'", c)), // TODO escaping
Self::String(s) => Rc::from(format!("\"{}\"", s)), // TODO escaping
Self::List(l) => Rc::from(format!("{:?}", l.borrow())), // TODO fix
Self::Map(m) => Rc::from(format!("{:?}", m)), // TODO fix
Self::Type(_) => todo!(),
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(Func::Func { name, .. }) => match name {
Some(name) => Rc::from(format!("<fn {}>", name)),
None => Rc::from("<anonymous fn>"),
},
Self::Data(_) => todo!(),
_ => self.to_string(),
}
}
@ -300,6 +289,21 @@ impl Value {
v => Err(format!("{:?} has no length", v).into())
}
}
pub fn fracdiv(&self, other: &Value) -> Result<Value, String> {
use Value::*;
match (self, other) {
(Int(_), Int(b)) if *b == 0 => Err("Integer division by zero".into()),
(Rational(_), Int(b)) if *b == 0 => Err("Rational division by zero".into()),
(Int(_), Rational(b)) if b.is_zero() => Err("Rational division by zero".into()),
(Rational(_), Rational(b)) if b.is_zero() => Err("Rational division by zero".into()),
(Int(a), Int(b)) => Ok(Value::from(crate::value::Rational::new(*a, *b))),
(Rational(a), Int(b)) => Ok(Value::from(a/b)),
(Int(a), Rational(b)) => Ok(Value::from(b.recip()*a)),
(Rational(a), Rational(b)) => Ok(Value::from(a/b)),
(x,y) => Err(format!("Unsupported operation 'fracdiv' between {:?} and {:?}", x, y))
}
}
}
impl PartialEq for Value {