project reorganization, general improvements
This commit is contained in:
parent
172d7b10f9
commit
19b7215234
26 changed files with 139 additions and 125 deletions
29
Cargo.lock
generated
29
Cargo.lock
generated
|
@ -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"
|
||||
|
|
14
Cargo.toml
14
Cargo.toml
|
@ -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
11
complexpr-bin/Cargo.toml
Normal 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"
|
|
@ -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
10
complexpr/Cargo.toml
Normal 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
55
complexpr/src/env.rs
Normal 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()
|
||||
}
|
||||
}
|
|
@ -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()))
|
||||
}
|
||||
|
|
@ -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>") };
|
|
@ -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(&['=']) {
|
|
@ -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)]
|
|
@ -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>;
|
|
@ -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,
|
||||
|
|
@ -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 {
|
Loading…
Reference in a new issue