diff --git a/examples/cat.cxpr b/examples/cat.cxpr new file mode 100644 index 0000000..d46429d --- /dev/null +++ b/examples/cat.cxpr @@ -0,0 +1,3 @@ +while true { + print(input()); +} \ No newline at end of file diff --git a/examples/collatz.cxpr b/examples/collatz.cxpr new file mode 100644 index 0000000..f2fb77a --- /dev/null +++ b/examples/collatz.cxpr @@ -0,0 +1,10 @@ +let n = 29; +while n != 1 { + println(n); + if n % 2 == 0 { + n = n/2; + } else { + n = 3*n+1; + } +} +println(n); \ No newline at end of file diff --git a/examples/fib.cxpr b/examples/fib.cxpr new file mode 100644 index 0000000..4e39cd3 --- /dev/null +++ b/examples/fib.cxpr @@ -0,0 +1,9 @@ +let x = 0; +let y = 0; +let z = 1; +while z < 1000000 { + x = y + z; + z = y; + y = x; + println(z); +} \ No newline at end of file diff --git a/examples/helloworld.cxpr b/examples/helloworld.cxpr new file mode 100644 index 0000000..ce50212 --- /dev/null +++ b/examples/helloworld.cxpr @@ -0,0 +1,2 @@ +# Prints the string "Hello, world!" followed by a newline +println("Hello, world!"); \ No newline at end of file diff --git a/src/bin/main.rs b/src/bin/main.rs index db593f9..249f231 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,6 +1,6 @@ -use std::{rc::Rc, cell::RefCell, fs::{File, self}, io::{BufReader, Read}}; +use std::{rc::Rc, cell::RefCell, fs}; -use complexpr::{eval::Environment, interpreter::interpret, value::Value}; +use complexpr::{eval::Environment, interpreter::interpret, value::Value, stdlib}; use rustyline::{self, error::ReadlineError}; const C_RESET: &'static str = "\x1b[0m"; @@ -26,6 +26,7 @@ fn repl() -> Result<(), Box> { println!("Press {}Ctrl+D{} to exit.", C_BLUE, C_RESET); let mut rl = rustyline::Editor::<()>::new()?; let env = Rc::new(RefCell::new(Environment::new())); + stdlib::load(&mut env.borrow_mut()); loop { let readline = rl.readline(PROMPT); match readline { @@ -33,7 +34,7 @@ fn repl() -> Result<(), Box> { let result = interpret(&line, None, Some(env.clone()), true); match result { Ok(Value::Nil) => (), - Ok(value) => println!("{:?}", value), + Ok(value) => println!("{}", value.repr()), Err(e) => println!("{}", e) } } diff --git a/src/eval.rs b/src/eval.rs index 14ba74d..d733d7e 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,17 +1,14 @@ use std::{collections::HashMap, rc::Rc, cell::RefCell}; -use num_complex::Complex64; - -use crate::{value::Value, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError}; - +use crate::{value::{Value, Complex}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError}; #[derive(Debug)] pub struct Environment { parent: Option, - map: HashMap, Value> + map: HashMap, Value>, } -type EnvRef = Rc>; +pub type EnvRef = Rc>; impl Environment { pub fn new() -> Self { @@ -27,24 +24,20 @@ impl Environment { } pub fn get(&self, name: &str) -> Result { - let x = match self.map.get(name) { + match self.map.get(name) { Some(v) => Ok(v.clone()), None => match self.parent { Some(ref p) => p.borrow().get(name), None => Err(()) } - }; - println!("get {}: {:?}", name, x); - x + } } pub fn declare(&mut self, name: Rc, value: Value) { - println!("declare {}: {:?}", name, value); self.map.insert(name, value); } pub fn set(&mut self, name: Rc, value: Value) -> Result<(),()> { - println!("set {}: {:?}", name, value); match self.map.contains_key(&name) { true => { self.map.insert(name, value); Ok(()) }, false => match self.parent { @@ -104,7 +97,15 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), RuntimeError> { return Err(RuntimeError { message: "Cannot iterate this type".into(), pos: var.pos.clone() }) }; }, - _ => unreachable!() + Stmt::While { expr, stmt } => { + loop { + let cond = eval_expr(expr, env.clone())?; + if !cond.truthy() { + break + } + eval_stmt(&stmt, env.clone())?; + } + }, } Ok(()) } @@ -118,8 +119,26 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result { => eval_assignment(lhs, rhs, op, env), Some(OpType::Additive) | Some(OpType::Multiplicative) => eval_binary(lhs, rhs, op, env), + Some(OpType::Comparison) + => eval_comp(lhs, rhs, op, env), o => todo!("{:?}", o) // TODO other operations }, + Expr::List { items } => { + let mut list = Vec::with_capacity(items.len()); + for item in items { + list.push(eval_expr(item, env.clone())?); + } + Ok(Value::List(Rc::new(list))) + }, + Expr::FuncCall { func, args, pos } => { + let func = eval_expr(&func, env.clone())?; + let mut arg_values = Vec::with_capacity(args.len()); + for arg in args { + let result = eval_expr(arg, env.clone())?; + arg_values.push(result); + } + func.call(arg_values, pos) + } e => todo!("{:?}", e) // TODO other expression types } } @@ -131,7 +150,7 @@ pub fn eval_literal(token: &Token) -> Value { TokenType::False => Value::Bool(false), TokenType::Int(n) => Value::Int(*n), TokenType::Float(f) => Value::Float(*f), - TokenType::ImFloat(f) => Value::Complex(Complex64::new(0.0, *f)), + TokenType::ImFloat(f) => Value::Complex(Complex::new(0.0, *f)), TokenType::String(s) => Value::String(s.clone()), TokenType::Char(c) => Value::Char(*c), _ => todo!() @@ -194,4 +213,33 @@ pub fn eval_binary(lhs: &Box, rhs: &Box, op: &Token, env: EnvRef) -> TokenType::Percent => &l % &r, _ => todo!() // TODO other operations }.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() }) +} + +pub fn eval_comp(lhs: &Box, rhs: &Box, op: &Token, env: EnvRef) -> Result { + let l = eval_expr(lhs, env.clone())?; + let r = eval_expr(rhs, env)?; + let mk_err = || RuntimeError { + message: format!("Cannot compare {:?} with {:?}", l, r), + pos: op.pos.clone() + }; + match op.ty { + TokenType::DoubleEqual => Ok(Value::Bool(l == r)), + TokenType::BangEqual => Ok(Value::Bool(l != r)), + TokenType::Greater => l.partial_cmp(&r) + .ok_or_else(mk_err) + .map(|o| Value::from(o.is_gt())), + TokenType::Less => l.partial_cmp(&r) + .ok_or_else(mk_err) + .map(|o| Value::from(o.is_lt())), + TokenType::GreaterEqual => l.partial_cmp(&r) + .ok_or_else(mk_err) + .map(|o| Value::from(o.is_ge())), + TokenType::LessEqual => l.partial_cmp(&r) + .ok_or_else(mk_err) + .map(|o| Value::from(o.is_le())), + TokenType::Spaceship => l.partial_cmp(&r) + .ok_or_else(mk_err) + .map(|o| Value::from(o as i8)), + _ => unreachable!() + } } \ No newline at end of file diff --git a/src/expr.rs b/src/expr.rs index 75006dc..632d41c 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1,13 +1,14 @@ use std::fmt; -use crate::{token::{Token, OpType}}; +use crate::{token::{Token, OpType}, Position}; pub enum Stmt { Expr { expr: Expr }, Let { lhs: Token, rhs: Option }, Block { stmts: Vec }, If { if_clauses: Vec<(Expr, Stmt)>, else_clause: Option> }, - For { var: Token, expr: Expr, stmt: Box } + For { var: Token, expr: Expr, stmt: Box }, + While { expr: Expr, stmt: Box }, } impl fmt::Debug for Stmt { @@ -26,18 +27,18 @@ pub enum Expr { Ident { value: Token }, Literal { value: Token }, List { items: Vec }, - FuncCall { func: Box, args: Vec } + FuncCall { func: Box, args: Vec, pos: Position } } impl fmt::Debug for Expr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::Binary { lhs: left, rhs: right, op} => write!(f, "({:?} {:?} {:?})", op, left, right), - Self::Unary { arg, op} => write!(f, "({:?} {:?})", op, arg), - Self::Ident { value, .. } => write!(f, "(ident {:?})", value), - Self::Literal { value, .. } => write!(f, "(lit {:?})", value), + Self::Unary { arg, op } => write!(f, "({:?} {:?})", op, arg), + Self::Ident { value } => write!(f, "(ident {:?})", value), + Self::Literal { value } => write!(f, "(lit {:?})", value), Self::List { items } => write!(f, "(list {:?})", items), - Self::FuncCall { func, args } => write!(f, "(call {:?} {:?})", func, args), + Self::FuncCall { func, args, .. } => write!(f, "(call {:?} {:?})", func, args), } } } diff --git a/src/interpreter.rs b/src/interpreter.rs index 172601f..025979d 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,8 +1,8 @@ use std::{cell::RefCell, rc::Rc}; -use crate::{value::Value, lexer::Lexer, parser::Parser, eval::{Environment, eval_stmt, eval_expr}, expr::Stmt}; +use crate::{value::Value, lexer::Lexer, parser::Parser, eval::{Environment, eval_stmt, eval_expr, EnvRef}, expr::Stmt, stdlib}; -pub fn interpret(src: &str, fname: Option, env: Option>>, repl: bool) -> Result> { +pub fn interpret(src: &str, fname: Option, env: Option, repl: bool) -> Result> { let mut lexer = Lexer::new(src, fname); lexer.lex()?; let mut parser = Parser::new(lexer.into_tokens(), repl); @@ -11,8 +11,10 @@ pub fn interpret(src: &str, fname: Option, env: Option Option { + if self.at_end() { return None } for c in chars { if self.code[self.current] == *c { self.advance(*c == '\n'); @@ -126,7 +127,10 @@ impl Lexer { _ => self.add_token(TokenType::Greater, ">") }, '<' => match self.expect(&['=']) { - Some('=') => self.add_token(TokenType::LessEqual, "<="), + Some('=') => match self.expect(&['>']) { + Some('>') => self.add_token(TokenType::Spaceship, "<=>"), + _ => self.add_token(TokenType::LessEqual, "<="), + } _ => self.add_token(TokenType::Less, "<") }, '&' => match self.expect(&['&']) { @@ -176,6 +180,7 @@ impl Lexer { if self.at_end() { return Err(self.mk_error("Unexpected EOF in character literal")) } // TODO Escapes } + if self.at_end() { return Err(self.mk_error("Unexpected EOF in character literal")) } self.expect(&['\'']).ok_or(self.mk_error("Expected ' to terminate character literal"))?; self.add_token(TokenType::Char(c), self.collect_literal()); Ok(()) @@ -186,6 +191,9 @@ impl Lexer { while !self.at_end() && self.peek() != '"' { if self.peek() == '\\' { self.advance(false); + if self.at_end() { + return Err(self.mk_error("Unexpected EOF in string literal")) + } // TODO more escape codes! \xHH, \u{HH..} or maybe \uhhhh \Uhhhhhhhh match self.peek() { '0' => s.push('\0'), @@ -205,7 +213,7 @@ impl Lexer { } } if self.at_end() { - return Err(self.mk_error("Unexpected EOF while parsing string")) + return Err(self.mk_error("Unexpected EOF in string literal")) } self.advance(false); self.add_token(TokenType::String(Rc::from(s)), self.collect_literal()); @@ -217,7 +225,7 @@ impl Lexer { while !self.at_end() && (self.peek().is_numeric() || self.peek() == '.') { if self.peek() == '.' { if has_dot { - return Err(self.mk_error("Numeric literals cannot contain two decimal points")) + break; } else { has_dot = true; } diff --git a/src/lib.rs b/src/lib.rs index 09a2a83..6bf012b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ pub mod parser; pub mod value; pub mod eval; pub mod interpreter; +pub mod stdlib; #[derive(Clone, Debug)] pub struct Position { diff --git a/src/parser.rs b/src/parser.rs index 7547373..41fec52 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -70,10 +70,15 @@ impl Parser { self.ifstmt() }, TokenType::For => { - // for statement + // for loop self.next(); self.forstmt() }, + TokenType::While => { + // while loop + self.next(); + self.whilestmt() + }, _ => { // fallback to an expression terminated with a semicolon let expr = self.assignment()?; @@ -177,6 +182,14 @@ impl Parser { } } + fn whilestmt(&mut self) -> Result { + self.err_on_eof()?; + let expr = self.assignment()?; + self.err_on_eof()?; + let stmt = self.statement()?; + Ok(Stmt::While{ expr, stmt: Box::new(stmt) }) + } + fn block(&mut self) -> Result { let mut stmts = vec![]; while !self.at_end() && self.peek().ty != TokenType::RBrace { @@ -200,9 +213,10 @@ impl Parser { fn commalist(&mut self, terminator: TokenType, parse_item: fn(&mut Parser) -> Result) -> Result, ParserError> { let mut items = vec![]; - while self.peek().ty != terminator { + 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 { @@ -211,6 +225,7 @@ impl Parser { return Err(self.mk_error(format!("Expected Comma or {:?} after list", terminator))) } } + self.err_on_eof()?; self.next(); Ok(items) } @@ -233,7 +248,11 @@ impl Parser { } fn pipeline(&mut self) -> Result { - self.expr(OpType::Pipeline, Self::additive) + self.expr(OpType::Pipeline, Self::comparison) + } + + fn comparison(&mut self) -> Result { + self.expr(OpType::Comparison, Self::additive) } fn additive(&mut self) -> Result { @@ -272,9 +291,9 @@ impl Parser { fn fncall(&mut self) -> Result { let expr = self.expr_base()?; if !self.at_end() && self.peek().ty == TokenType::LParen { - self.next(); + let lparen = self.next(); let args = self.commalist(TokenType::RParen, Self::assignment)?; - Ok(Expr::FuncCall { func: Box::new(expr), args }) + Ok(Expr::FuncCall { func: Box::new(expr), args, pos: lparen.pos.clone() }) } else { Ok(expr) } @@ -298,6 +317,9 @@ impl Parser { } else { Ok(expr) } + } else if next.ty == TokenType::LBrack { + let items = self.commalist(TokenType::RBrack, Self::assignment)?; + Ok(Expr::List { items }) } else { Err(self.mk_error(format!("Unexpected token: {:?}", next.ty))) } diff --git a/src/stdlib/mod.rs b/src/stdlib/mod.rs new file mode 100644 index 0000000..6c4b3e8 --- /dev/null +++ b/src/stdlib/mod.rs @@ -0,0 +1,43 @@ +use std::{rc::Rc, io::Write}; + +use crate::{value::{Value, BuiltinFn}, eval::Environment}; + +pub fn load(env: &mut Environment) { + let mut name: Rc; + name = Rc::from("str"); + env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_str, arg_count: 1, name })); + name = Rc::from("repr"); + env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_repr, arg_count: 1, name })); + name = Rc::from("print"); + env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_print, arg_count: 1, name })); + name = Rc::from("println"); + env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_println, arg_count: 1, name })); + name = Rc::from("input"); + env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_input, arg_count: 0, name })); +} + +fn fn_str(args: Vec) -> Result { + Ok(Value::String(args[0].to_string())) +} + +fn fn_repr(args: Vec) -> Result { + Ok(Value::String(args[0].repr())) +} + +fn fn_print(args: Vec) -> Result { + print!("{}", args[0].to_string()); + std::io::stdout().flush().map_err(|e| e.to_string())?; + Ok(Value::Nil) +} + +fn fn_println(args: Vec) -> Result { + println!("{}", args[0].to_string()); + Ok(Value::Nil) +} + +fn fn_input(_: Vec) -> Result { + let mut buffer = String::new(); + let stdin = std::io::stdin(); + stdin.read_line(&mut buffer).map_err(|e| e.to_string())?; + Ok(Value::from(buffer)) +} \ No newline at end of file diff --git a/src/token.rs b/src/token.rs index 4d7232d..1d71204 100644 --- a/src/token.rs +++ b/src/token.rs @@ -25,7 +25,7 @@ pub enum TokenType { Bang, Amper, Pipe, DoubleAmper, DoublePipe, Equal, PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual, DoubleSlashEqual, CaretEqual, - DoubleEqual, BangEqual, Greater, GreaterEqual, Less, LessEqual, + DoubleEqual, BangEqual, Greater, GreaterEqual, Less, LessEqual, Spaceship, Arrow, PipeColon, PipePoint, PipeQuestion, PipeAmper, @@ -49,6 +49,9 @@ impl TokenType { Self::PipeColon | Self::PipeAmper | Self::PipePoint | Self::PipeQuestion => Some(OpType::Pipeline), + Self::Greater | Self::GreaterEqual | Self::Less | Self::LessEqual + | Self::DoubleEqual | Self::BangEqual | Self::Spaceship => Some(OpType::Comparison), + Self::Equal | Self::PlusEqual | Self::MinusEqual | Self::StarEqual | Self::SlashEqual | Self::DoubleSlashEqual | Self::CaretEqual | Self::PercentEqual => Some(OpType::Assignment), @@ -60,7 +63,7 @@ impl TokenType { #[derive(Clone,Copy,Debug,PartialEq)] pub enum OpType { - Assignment, Pipeline, Additive, Multiplicative, Exponential + Assignment, Comparison, Pipeline, Additive, Multiplicative, Exponential } impl OpType { diff --git a/src/value.rs b/src/value.rs index 82589aa..f68d48c 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,62 +1,49 @@ -use std::{rc::Rc, collections::HashMap, ops::*, ascii::AsciiExt}; +use std::{rc::Rc, collections::HashMap, ops::*, fmt, cmp::Ordering}; -use num_traits::Zero; +use num_traits::{Zero, ToPrimitive}; -type Rational = num_rational::Ratio; -type Complex = num_complex::Complex64; +use crate::{RuntimeError, Position}; -macro_rules! value_from { - ($variant:ident, $($kind:ty)*) => { - $( - impl From<$kind> for Value { - fn from(x: $kind) -> Self { - Self::$variant(x.into()) - } - } - )* - }; +pub type Rational = num_rational::Ratio; +pub type Complex = num_complex::Complex64; + +#[derive(Clone)] +pub struct BuiltinFn { + pub name: Rc, + pub func: fn(Vec) -> Result, + pub arg_count: usize } -macro_rules! impl_numeric_op { - ($optrait:ty, $fnname:ident, { $($bonus:tt)* }) => { - impl $optrait for &Value { - type Output = Result; - fn $fnname(self, other: Self) -> Self::Output { - use Value::*; - use num_traits::ToPrimitive; - const RATIO_CAST_FAIL: &'static str = "Failed to cast Rational to Float"; - match (self, other) { - (Int(a), Int(b)) => Ok(a.$fnname(b).into()), - (Rational(a), Int(b)) => Ok(a.$fnname(b).into()), - (Int(a), Rational(b)) => Ok(self::Rational::from(*a).$fnname(b).into()), - (Float(a), Int(b)) => Ok(a.$fnname(*b as f64).into()), - (Int(a), Float(b)) => Ok((*a as f64).$fnname(b).into()), - (Float(a), Rational(b)) => Ok(a.$fnname(b.to_f64().ok_or(RATIO_CAST_FAIL)?).into()), - (Rational(a), Float(b)) => Ok(a.to_f64().ok_or(RATIO_CAST_FAIL)?.$fnname(b).into()), - (Float(a), Float(b)) => Ok(a.$fnname(b).into()), - (Int(a), Complex(b)) => Ok(self::Complex::from(*a as f64).$fnname(b).into()), - (Complex(a), Int(b)) => Ok(a.$fnname(self::Complex::from(*b as f64)).into()), - (Float(a), Complex(b)) => Ok(self::Complex::from(a).$fnname(b).into()), - (Complex(a), Float(b)) => Ok(a.$fnname(self::Complex::from(b)).into()), - (Rational(a), Complex(b)) => Ok(self::Complex::from(a.to_f64().ok_or(RATIO_CAST_FAIL)?).$fnname(b).into()), - (Complex(a), Rational(b)) => Ok(a.$fnname(self::Complex::from(b.to_f64().ok_or(RATIO_CAST_FAIL)?)).into()), - (Complex(a), Complex(b)) => Ok(a.$fnname(b).into()), - $($bonus)* - (lhs, rhs) => Err(format!("Unsupported operation '{}' between {:?} and {:?}", stringify!($fnname), lhs, rhs)) - } - } - } +impl fmt::Debug for BuiltinFn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BuiltinFn").field("name", &self.name).field("arg_count", &self.arg_count).finish() } } #[derive(Clone, Debug)] +pub struct Data { + pub ty: usize, + // TODO user-defined data types +} + +#[derive(Clone, Debug)] +pub struct Type { + pub name: Rc, + pub id: usize +} + +#[derive(Clone, Debug)] +#[repr(u8)] pub enum Value { Nil, + Type(usize), Int(i64), Float(f64), Complex(Complex), Rational(Rational), Bool(bool), Char(char), String(Rc), List(Rc>), Map(Rc>), + BuiltinFn(BuiltinFn), + Data(Data), } impl Value { @@ -83,8 +70,170 @@ impl Value { _ => Err(()) } } + + pub fn call(&self, args: Vec, pos: &Position) -> Result { + match self { + Value::BuiltinFn(f) => { + if args.len() == f.arg_count { + (f.func)(args).map_err(|e| RuntimeError { message: e, pos: pos.clone() }) + } else if args.len() < f.arg_count { + Err(RuntimeError { + message: format!("Not enough arguments for function: expected {}, got {}", f.arg_count, args.len()), + pos: pos.clone() + }) + } else { + Err(RuntimeError { + message: format!("Too many arguments for function: expected {}, got {}", f.arg_count, args.len()), + pos: pos.clone() + }) + } + } + _ => Err(RuntimeError { message: "Cannot call".into(), pos: pos.clone() }) + } + } + + pub fn to_string(&self) -> Rc { + 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::Char(c) => Rc::from(c.to_string()), + 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::BuiltinFn(bf) => Rc::from(format!("", bf.name, bf.func as *const ())), + Self::Data(_) => todo!(), + } + } + + pub fn repr(&self) -> Rc { + 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::Char(c) => Rc::from(format!("'{}'", c)), // TODO escaping + Self::String(s) => Rc::from(format!("\"{}\"", s)), // TODO escaping + Self::List(l) => Rc::from(format!("{:?}", l)), // TODO fix + Self::Map(m) => Rc::from(format!("{:?}", m)), // TODO fix + Self::Type(_) => todo!(), + Self::BuiltinFn(bf) => Rc::from(format!("", bf.name, bf.func as *const ())), + Self::Data(_) => todo!(), + } + } } +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::Int(a), Self::Int(b)) => a == b, + (Self::Rational(a), Self::Int(b)) => *a == Rational::from(*b), + (Self::Int(a), Self::Rational(b)) => Rational::from(*a) == *b, + (Self::Rational(a), Self::Rational(b)) => a == b, + (Self::Float(a), Self::Int(b)) => *a == *b as f64, + (Self::Int(a), Self::Float(b)) => *a as f64 == *b, + (Self::Float(a), Self::Rational(b)) => *a == b.to_f64().unwrap(), + (Self::Rational(a), Self::Float(b)) => a.to_f64().unwrap() == *b, + (Self::Float(a), Self::Float(b)) => a == b, + + (Self::Complex(a), Self::Int(b)) => *a == Complex::from(*b as f64), + (Self::Int(a), Self::Complex(b)) => Complex::from(*a as f64) == *b, + (Self::Complex(a), Self::Rational(b)) => *a == Complex::from(b.to_f64().unwrap()), + (Self::Rational(a), Self::Complex(b)) => Complex::from(a.to_f64().unwrap()) == *b, + (Self::Complex(a), Self::Float(b)) => *a == Complex::from(*b), + (Self::Float(a), Self::Complex(b)) => Complex::from(*a) == *b, + (Self::Complex(a), Self::Complex(b)) => a == b, + + (Self::Bool(a), Self::Bool(b)) => a == b, + (Self::Char(a), Self::Char(b)) => a == b, + (Self::String(a), Self::String(b)) => a == b, + (Self::List(a), Self::List(b)) => a == b, + (Self::Map(_), Self::Map(_)) => todo!("Can't test maps for equality yet"), + (Self::BuiltinFn(a), Self::BuiltinFn(b)) + => (a.func as *const ()) == (b.func as *const ()) && a.arg_count == b.arg_count, + (Self::Data(_), Self::Data(_)) => todo!("Can't compare data yet"), + _ => false + } + } +} + +impl PartialOrd for Value { + fn partial_cmp(&self, other: &Self) -> Option { + if self == other { + return Some(Ordering::Equal) + } + match (self, other) { + (Self::Int(a), Self::Int(b)) => a.partial_cmp(b), + (Self::Int(a), Self::Float(b)) => (*a as f64).partial_cmp(b), + (Self::Float(a), Self::Int(b)) => a.partial_cmp(&(*b as f64)), + (Self::Float(a), Self::Float(b)) => a.partial_cmp(b), + (Self::Int(a), Self::Rational(b)) => Rational::from(*a).partial_cmp(b), + (Self::Rational(a), Self::Int(b)) => a.partial_cmp(&Rational::from(*b)), + (Self::Float(a), Self::Rational(b)) => a.partial_cmp(&b.to_f64().unwrap()), + (Self::Rational(a), Self::Float(b)) => a.to_f64().unwrap().partial_cmp(b), + (Self::Rational(a), Self::Rational(b)) => a.partial_cmp(b), + + (Self::Char(a), Self::Char(b)) => a.partial_cmp(b), + (Self::String(a), Self::String(b)) => a.partial_cmp(b), + (Self::List(a), Self::List(b)) => a.partial_cmp(b), + _ => None + } + } +} + +macro_rules! value_from { + ($variant:ident, $($kind:ty)*) => { + $( + impl From<$kind> for Value { + fn from(x: $kind) -> Self { + Self::$variant(x.into()) + } + } + )* + }; +} + +macro_rules! impl_numeric_op { + ($optrait:ty, $fnname:ident, { $($bonus:tt)* }) => { + impl $optrait for &Value { + type Output = Result; + fn $fnname(self, other: Self) -> Self::Output { + use Value::*; + use num_traits::ToPrimitive; + const RATIO_CAST_FAIL: &'static str = "Failed to cast Rational to Float"; + match (self, other) { + (Int(a), Int(b)) => Ok(a.$fnname(b).into()), + (Rational(a), Int(b)) => Ok(a.$fnname(b).into()), + (Int(a), Rational(b)) => Ok(self::Rational::from(*a).$fnname(b).into()), + (Rational(a), Rational(b)) => Ok(a.$fnname(b).into()), + (Float(a), Int(b)) => Ok(a.$fnname(*b as f64).into()), + (Int(a), Float(b)) => Ok((*a as f64).$fnname(b).into()), + (Float(a), Rational(b)) => Ok(a.$fnname(b.to_f64().ok_or(RATIO_CAST_FAIL)?).into()), + (Rational(a), Float(b)) => Ok(a.to_f64().ok_or(RATIO_CAST_FAIL)?.$fnname(b).into()), + (Float(a), Float(b)) => Ok(a.$fnname(b).into()), + (Int(a), Complex(b)) => Ok(self::Complex::from(*a as f64).$fnname(b).into()), + (Complex(a), Int(b)) => Ok(a.$fnname(self::Complex::from(*b as f64)).into()), + (Float(a), Complex(b)) => Ok(self::Complex::from(a).$fnname(b).into()), + (Complex(a), Float(b)) => Ok(a.$fnname(self::Complex::from(b)).into()), + (Rational(a), Complex(b)) => Ok(self::Complex::from(a.to_f64().ok_or(RATIO_CAST_FAIL)?).$fnname(b).into()), + (Complex(a), Rational(b)) => Ok(a.$fnname(self::Complex::from(b.to_f64().ok_or(RATIO_CAST_FAIL)?)).into()), + (Complex(a), Complex(b)) => Ok(a.$fnname(b).into()), + $($bonus)* + (lhs, rhs) => Err(format!("Unsupported operation '{}' between {:?} and {:?}", stringify!($fnname), lhs, rhs)) + } + } + } + } +} value_from!(Int, u8 u16 u32 i8 i16 i32 i64); value_from!(Float, f32 f64); diff --git a/tests/code.cxpr b/tests/code.cxpr deleted file mode 100644 index 22e8a87..0000000 --- a/tests/code.cxpr +++ /dev/null @@ -1,4 +0,0 @@ - -for c: "asdfghjkl" { - let b = 'q' + c; -} \ No newline at end of file diff --git a/tests/test.rs b/tests/test.rs deleted file mode 100644 index b06f238..0000000 --- a/tests/test.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![cfg(test)] - -use std::{cell::RefCell, rc::Rc}; - -use complexpr::{lexer::Lexer, parser::Parser, eval::{Environment, eval_stmt}}; - -#[test] -pub fn test() { - let mut lexer = Lexer::new("let a = 1 + 1; let b = a + 1;", None); - lexer.lex().unwrap(); - let mut parser = Parser::new(lexer.into_tokens(), false); - let ast = parser.parse().unwrap(); - let env = Rc::new(RefCell::new(Environment::new())); - for stmt in ast { - eval_stmt(&stmt, env.clone()).unwrap(); - } - println!("{:?}", env); - todo!("end of tests") -} \ No newline at end of file