From f6909e5e23ee931582830e6cbfd8c28eaec37452 Mon Sep 17 00:00:00 2001 From: Snorre Date: Thu, 20 Mar 2025 12:27:27 +0100 Subject: [PATCH] did some grinding off camera --- README.md | 206 +++++++++++++++++++++++++++++++ src/cli-repl.rs | 218 ++++++++++++++++++++------------- src/lib/environment.rs | 46 ++++--- src/lib/node/add.rs | 72 ++++++++++- src/lib/node/assign.rs | 50 +++++--- src/lib/node/call.rs | 14 +-- src/lib/node/closure.rs | 4 +- src/lib/node/comparison.rs | 22 ++-- src/lib/node/constant.rs | 4 +- src/lib/node/divide.rs | 6 +- src/lib/node/empty.rs | 4 +- src/lib/node/equals.rs | 4 +- src/lib/node/exponent.rs | 4 +- src/lib/node/function.rs | 6 +- src/lib/node/if_else.rs | 26 ++-- src/lib/node/loop.rs | 78 ++++++++++++ src/lib/node/mod.rs | 81 ++++++++++-- src/lib/node/multiply.rs | 237 ++++++++++++++++-------------------- src/lib/node/node_ref.rs | 4 +- src/lib/node/range.rs | 43 +++++++ src/lib/node/set.rs | 17 ++- src/lib/node/string_node.rs | 4 +- src/lib/node/subtract.rs | 99 ++++++++------- src/lib/node/symbol.rs | 16 ++- src/lib/parser/mod.rs | 193 +++++++++++++++++++++-------- test.txt | 33 +++++ 26 files changed, 1070 insertions(+), 421 deletions(-) create mode 100644 README.md create mode 100644 src/lib/node/loop.rs create mode 100644 src/lib/node/range.rs create mode 100644 test.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..7525beb --- /dev/null +++ b/README.md @@ -0,0 +1,206 @@ +# Openbirch Rust + +Openbirch rewritten in rust. + +Very messy and bad, im gonna start the umpteenth rewrite soon. + +# Features + +[x] Constants (numbers) + [x] Addition + [x] Subtraction + [x] Multiplication + [x] Division + [x] Arbitrary float precision +[ ] Sets (or lists, arrays, etc) + [x] Definition + [x] Multiplication with constants + [x] Concattenation + [ ] Index operator +[x] Functions + [x] Native functions + [x] User defined functions + [x] Currying + [x] Closures + [x] Calling +[x] Scopes + [x] Shadowing variables +[ ] Ranges + [x] Defining ranges (`1..5` is a range of `[1..5)`) + [ ] Iterating ranges + [x] Evaluating ranges (very hacky, but defining a Set of length 1 with a range expands said range into the set, eg `[1..3]` becomes `[1,2]`) +[ ] Loops + [x] Infinite loops (`loop ... end` block) + [ ] For loops (`for x in y ... end` block) + [x] Break keyword +[ ] Match expression +[x] If expression + [x] If expression + [x] If-else expression + +## Wanted + +[ ] More concise syntax, i dont like how it is right now +[ ] Fix stack overflow with recursive functions (internally convert to loops) +[ ] Generally implement more +[ ] Implicit multiplication (may not be possible to implement non-ambiguously) +[ ] Differentiation +[ ] Integration +[ ] Better error handling (i was lazy and skimped on errors, bad move) + +# Syntax + +Every Openbirch program is made up of 0 or more *expressions*. + +An expression is anything from a binary operation like `2+2` to function calls `f(x)` and +even assignments `x := 5`. + +All expressions return a value. For expressions that "dont" they return an `Empty`, which cant +be used for anything and will error if used in other operations (eg. `2+{Empty}`). + +## Definition + +Variables can be defined with a `define` expression. + +``` +x := 5 # define x as 5 + +print(x+x) # prints 10 +``` + +As openbirch is a symbolic language variables can be used before assignment + +``` +y := x^2 + +print(y) # prints x^2 + +x := 5 + +print(y) # prints 25 +``` + +### Scopes + +Some expressions define a *scope*, where all defined variables inside will be deleted once the scope ends. + +Expressions that create a scope include, but are not limited to, +- If-else +- Loop +- Functions +- Closures + +If a variable is defined outside a scope it will be *shadowed* inside the scope. What this means +is that inside the scope the variable will have the shadowed value, while outside the scope it +will have the previous value. + +``` +x := 5 # Global scope + +print(x) # prints 5 + +if true then # Define a new scope + x := 5000 # Shadow the value of x + print(x) # prints 5000 +end # Scope ends here + +print(x) # prints 5 +``` + +### Assignment + +Since you may want to change the value of a variable outside the current scope you can use an `assignment` rather than a definition. + +``` +x := 5 + +if true then + x <- 500 # Assign to x rather than defining it and shadowing it +end + +print(x) # prints 500 +``` + +## If-else + +If expressions are defined as such + +``` +if {condition} then + # 0 or more statements +end +``` + +and if-else as + +``` +if {condition} then + # 0 or more statements +else + {expression} +end +``` + +Since `if` is an expression the body of the else branch can be another if expression, in which +case only 1 `end` keyword is needed. + +``` +if {condition} then + # 0 or more statements +else if {condition} then + # 0 or more statements +end +``` + +Since all expressions evaluate to a value the value of the if expression is the resulting value +of the last statement in the chosen branch, eg. + +``` +x := if true then + # Evaluate some expressions + 2+2 + f(x) + y := f(f(x)) + # Last expression is returned + 5 +else + 0 +end + +print(x) # prints 5 +``` + +if a branch is empty then `Empty` is returned. + +## Loops + +Currently the only type of loop is an infinite loop. + +``` +loop + # This will repeat forever +end +``` + +# Running + +## Linux + +```sh +$ cargo run +``` + +### Nixos + +```sh +$ nix develop +$ cargo run +``` + +## Windows + +idfk, havent used that shit in years, but probably just `cargo run`. + +## MacOS + +never used, but again probably `cargo run` diff --git a/src/cli-repl.rs b/src/cli-repl.rs index 5579d10..62291ff 100644 --- a/src/cli-repl.rs +++ b/src/cli-repl.rs @@ -1,6 +1,7 @@ -use std::f64; -use std::io::{self, StdoutLock, Write, stdout}; +use std::fs::File; +use std::io::{self, Read, StdoutLock, Write, stdout}; use std::rc::Rc; +use std::{env, f64}; use libopenbirch::environment::{Environment, EnvironmentInternalSymbolKey}; use libopenbirch::node::call::Call; @@ -8,7 +9,7 @@ use libopenbirch::node::constant::Constant; use libopenbirch::node::empty::Empty; use libopenbirch::node::set::Set; use libopenbirch::node::string_node::StringNode; -use libopenbirch::node::{Node, NodeEnum}; +use libopenbirch::node::{Error, Node, NodeEnum}; use libopenbirch::parser::{Lexer, LexerError, Parser, ParserError}; use termion::color; use termion::event::Key; @@ -73,7 +74,9 @@ impl Input { return Err(io::Error::from(io::ErrorKind::Interrupted)); } - self.history.insert(0, self.buffer.clone()); + if self.buffer.trim().len() > 0 { + self.history.insert(0, self.buffer.clone()); + } let r = self.buffer.clone(); self.buffer.clear(); self.current_char = 0; @@ -200,7 +203,7 @@ fn print_err(i: usize, len: usize, exp: String) { ); } -fn print(args: &Vec>, env: &mut Environment) -> Result, String> { +fn print(args: &Vec>, env: &mut Environment) -> Result, Error> { for expr in args { println!("\r{}", expr.as_string(Some(env))); } @@ -208,17 +211,17 @@ fn print(args: &Vec>, env: &mut Environment) -> Result Ok(Empty::new("")) } -fn unset(args: &Vec>, env: &mut Environment) -> Result, String> { +fn unset(args: &Vec>, env: &mut Environment) -> Result, Error> { for arg in args { if let NodeEnum::StringNode(string) = arg.as_ref() { if let Some(id) = env.str_to_id(string.get_value()) { env.undefine(*id); } } else { - return Err(format!( + return Err(Error::MismatchedType(format!( "Expected strings, but one of the arguments was a {}", arg.type_str() - )); + ))); } } @@ -228,7 +231,7 @@ fn unset(args: &Vec>, env: &mut Environment) -> Result fn get_float_precision( args: &Vec>, env: &mut Environment, -) -> Result, String> { +) -> Result, Error> { match args.len() { 0 => Ok(Rc::new( Constant::new_from_float(env.get_float_precision() as f64, &env).into(), @@ -240,22 +243,25 @@ fn get_float_precision( Ok(Rc::new(Constant::new_from_float(prec, &env).into())) } else { - Err(format!( + Err(Error::MismatchedType(format!( "Expected argument of type Constant, but got {}", arg.type_str() - )) + ))) } } - _ => Err(format!("Expected 0 or 1 arguments, got {}", args.len()))?, + _ => Err(Error::Other(format!( + "Expected 0 or 1 arguments, got {}", + args.len() + )))?, } } fn set_float_precision( args: &Vec>, env: &mut Environment, -) -> Result, String> { +) -> Result, Error> { if args.len() != 1 { - Err(format!("Expected 1 arguments, got {}", args.len()))? + Err(Error::ArgumentCount(1, args.len()))? } let arg = args.first().unwrap(); @@ -263,10 +269,10 @@ fn set_float_precision( let precision: u64 = if let NodeEnum::Constant(value) = arg.as_ref() { value.get_value().to_u32_saturating().unwrap().into() } else { - Err(format!( + Err(Error::MismatchedType(format!( "Expected argument of type Constant, but got {}", arg.type_str() - ))? + )))? }; env.set_float_precision(precision); @@ -274,28 +280,28 @@ fn set_float_precision( Ok(Empty::new(format!("Set float precision to {precision}"))) } -fn map(args: &Vec>, env: &mut Environment) -> Result, String> { +fn map(args: &Vec>, env: &mut Environment) -> Result, Error> { if args.len() != 2 { - Err(format!("Expected 2 argument but got {}", args.len()))? + Err(Error::ArgumentCount(2, args.len()))? } let arg = args.first().unwrap(); let func = match arg.as_ref() { NodeEnum::Function(_) | NodeEnum::Closure(_) => arg, - _ => Err(format!( + _ => Err(Error::MismatchedType(format!( "Argument 1 expected a Function but got {}", arg.type_str() - ))?, + )))?, }; let arg = args.get(1).unwrap().as_ref(); let set = if let NodeEnum::Set(set) = arg { set.get_values() } else { - return Err(format!( + return Err(Error::MismatchedType(format!( "Argument 1 expected a Set but got {}", arg.type_str() - ))?; + )))?; }; let mut out = Vec::with_capacity(set.len()); @@ -307,51 +313,52 @@ fn map(args: &Vec>, env: &mut Environment) -> Result, Ok(Set::new(out)) } -fn get(args: &Vec>, _env: &mut Environment) -> Result, String> { +fn get(args: &Vec>, _env: &mut Environment) -> Result, Error> { if args.len() != 2 { - Err(format!("Expected 2 argument but got {}", args.len()))? + Err(Error::ArgumentCount(2, args.len()))? } let set = if let NodeEnum::Set(set) = args.first().unwrap().as_ref() { set.get_values() } else { - return Err(format!( + return Err(Error::MismatchedType(format!( "Argument 1 expected Set but got {}", args.first().unwrap().type_str() - )); + ))); }; let idx = if let NodeEnum::Constant(c) = args.get(1).unwrap().as_ref() { if let Some(u) = c.get_value().to_u32_saturating() { u as usize } else { - return Err( + return Err(Error::ConversionError( "Error occured while trying to convert second argument to integer".to_owned(), - ); + )); } } else { - return Err("Argument 1 is expected to be a Set".to_owned()); + return Err(Error::MismatchedType( + "Argument 1 is expected to be a Set".to_owned(), + )); }; if let Some(v) = set.get(idx) { return Ok(v.clone()); } - return Err(format!( - "Index was out of bounds for Set of length {}", - set.len() - )); + return Err(Error::IndexOutOfBounds(set.len(), idx)); } -fn length(args: &Vec>, env: &mut Environment) -> Result, String> { +fn length(args: &Vec>, env: &mut Environment) -> Result, Error> { if args.len() != 1 { - Err(format!("Expected 1 argument but got {}", args.len()))? + Err(Error::ArgumentCount(1, args.len()))? } let set = if let NodeEnum::Set(set) = args.first().unwrap().as_ref() { set.get_values() } else { - return Err("Argument 1 is expected to be a Set".to_owned()); + return Err(Error::MismatchedType( + "Argument 1 is expected to be a Set".to_owned(), + )); }; Ok(Rc::new( @@ -359,9 +366,9 @@ fn length(args: &Vec>, env: &mut Environment) -> Result>, env: &mut Environment) -> Result, String> { +fn benchmark(args: &Vec>, env: &mut Environment) -> Result, Error> { if args.len() != 1 { - Err(format!("Expected 1 argument but got {}", args.len()))? + Err(Error::ArgumentCount(1, args.len()))? } use std::time::Instant; @@ -377,18 +384,18 @@ fn benchmark(args: &Vec>, env: &mut Environment) -> Result>, _env: &mut Environment) -> Result, String> { +fn head(args: &Vec>, _env: &mut Environment) -> Result, Error> { if args.len() != 1 { - Err(format!("Expected 1 argument but got {}", args.len()))? + Err(Error::ArgumentCount(1, args.len()))? } let set = if let NodeEnum::Set(set) = args.first().unwrap().as_ref() { set.get_values() } else { - return Err(format!( + return Err(Error::MismatchedType(format!( "Expected a Set but got a {}", args.first().unwrap().type_str() - )); + ))); }; let head = set.first().cloned().unwrap_or(Set::new(vec![])); @@ -402,9 +409,30 @@ fn head(args: &Vec>, _env: &mut Environment) -> Result Ok(Set::new(vec![head, Set::new(tail)])) } -fn diff(args: &Vec>, _env: &mut Environment) -> Result, String> { +fn mult_equal(args: &Vec>, env: &mut Environment) -> Result, Error> { + let a1 = if let NodeEnum::Multiply(m) = args.get(0).ok_or(Error::ArgumentCount(1, 0))?.as_ref() + { + m + } else { + return Err(Error::MismatchedType( + "Argument 1 is not a multiply".to_owned(), + )); + }; + let a2 = if let NodeEnum::Multiply(m) = args.get(1).ok_or(Error::ArgumentCount(2, 1))?.as_ref() + { + m + } else { + return Err(Error::MismatchedType( + "Argument 2 is not a multiply".to_owned(), + )); + }; + + Ok(Rc::new(NodeEnum::Bool(a1.symbols_match(a2).into()))) +} + +fn diff(args: &Vec>, _env: &mut Environment) -> Result, Error> { if args.len() != 2 { - return Err(format!("Expected 1-2 argument but got {}", args.len())); + return Err(Error::ArgumentCount(2, args.len())); } let expr = args.first().unwrap().clone(); @@ -412,10 +440,10 @@ fn diff(args: &Vec>, _env: &mut Environment) -> Result let var = if let NodeEnum::Symbol(symbol) = args.get(1).unwrap().as_ref() { symbol.get_value() } else { - return Err(format!( + return Err(Error::MismatchedType(format!( "Argument 2 is expected to be a Symbol but got a {}", args.get(1).unwrap().type_str() - )); + ))); }; todo!() @@ -429,9 +457,48 @@ fn diff_internally( todo!() } -fn main() -> Result<(), io::Error> { - let mut input = Input::new(); +fn evaluate(source: &String, mut env: &mut Environment, verbose: bool) { + let mut lexer = Lexer::new(&source); + let tokens_result = lexer.lex(); + if tokens_result.is_err() { + match tokens_result.err().unwrap() { + LexerError::UnexpectedChar(i, exp) => print_err(i, 1, exp), + } + return; + } + + let tokens = tokens_result.unwrap(); + let mut parser = Parser::new(tokens, &mut env); + + let nodes = match parser.parse() { + Ok(nodes) => nodes, + Err(err) => { + match err { + ParserError::UnexpectedEndOfTokens(exp) => print_err(source.len(), 1, exp), + ParserError::UnexpectedToken(i, len, exp) + | ParserError::Unimplemented(i, len, exp) => print_err(i, len, exp), + ParserError::UnexpectedNode(i, exp) | ParserError::NumberParse(i, exp) => { + print_err(i, 1, exp) + } + } + return; + } + }; + + print!("{}", color::Fg(color::Blue)); + + for node in nodes { + let evaluated = node.evaluate(&mut env); + match evaluated { + Ok(result) if verbose => println!("\r\t{}", result.as_string(Some(&env))), + Err(exp) => print_err(0, 1, exp.to_string()), + _ => {} + } + } +} + +fn main() -> Result<(), io::Error> { let mut env = Environment::new(); env.set_float_precision(128); @@ -445,52 +512,33 @@ fn main() -> Result<(), io::Error> { env.define_native_function("get", get); env.define_native_function("length", length); env.define_native_function("benchmark", benchmark); + env.define_native_function("mult_equals", mult_equal); env.define( "pi", Rc::new(Constant::new_from_float(std::f64::consts::PI, &env).into()), ); + let args: Vec = std::env::args().collect(); + + if args.len() > 1 { + let file = args.get(1).unwrap(); + + let mut file = File::open(file)?; + let mut src = String::new(); + let _ = file.read_to_string(&mut src); + + evaluate(&src, &mut env, false); + + return Ok(()); + } + + let mut input = Input::new(); + while let Some(source) = input.get()? { input.disable_raw()?; - let mut lexer = Lexer::new(&source); - let tokens_result = lexer.lex(); - - if tokens_result.is_err() { - match tokens_result.err().unwrap() { - LexerError::UnexpectedChar(i, exp) => print_err(i, 1, exp), - } - continue; - } - - let tokens = tokens_result.unwrap(); - let mut parser = Parser::new(tokens, &mut env); - - let nodes = match parser.parse() { - Ok(nodes) => nodes, - Err(err) => { - match err { - ParserError::UnexpectedEndOfTokens(exp) => print_err(source.len(), 1, exp), - ParserError::UnexpectedToken(i, len, exp) - | ParserError::Unimplemented(i, len, exp) => print_err(i, len, exp), - ParserError::UnexpectedNode(i, exp) | ParserError::NumberParse(i, exp) => { - print_err(i, 1, exp) - } - } - continue; - } - }; - - print!("{}", color::Fg(color::Blue)); - - for node in nodes { - let evaluated = node.evaluate(&mut env); - match evaluated { - Ok(result) => println!("\r\t{}", result.as_string(Some(&env))), - Err(exp) => print_err(0, 1, exp), - } - } + evaluate(&source, &mut env, true); input.enable_raw()?; diff --git a/src/lib/environment.rs b/src/lib/environment.rs index 8c99173..2566e67 100644 --- a/src/lib/environment.rs +++ b/src/lib/environment.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, rc::Rc}; use crate::node::{ - NodeEnum, + Error, NodeEnum, function::{Function, FunctionType, NativeFunctionType}, }; @@ -90,11 +90,11 @@ impl Environment { self.enable_closures } - pub fn push_stack(&mut self) -> Result<(), String> { - if self.stack_depth == self.max_stack_depth { - self.unwind_stack(); - return Err("Max Stack depth exceeded".to_owned()); - } + pub fn push_stack(&mut self) -> Result<(), Error> { + // if self.stack_depth == self.max_stack_depth { + // // self.unwind_stack(); + // return Err("Max Stack depth exceeded".to_owned()); + // } self.stack_depth += 1; self.stack_sizes.push(Scope { @@ -107,9 +107,9 @@ impl Environment { Ok(()) } - pub fn pop_stack(&mut self) -> Result<(), String> { + pub fn pop_stack(&mut self) -> Result<(), Error> { if self.stack_depth == 0 { - return Err("Trying to pop empty stack".to_owned()); + return Err(Error::StackUnderflow); } self.stack_depth -= 1; @@ -126,15 +126,20 @@ impl Environment { } fn unwind_stack(&mut self) { - for scope in &self.stack_sizes { - for _ in 0..scope.stack_len { - self.map.remove(&self.stack.pop().unwrap()); - } - } + // for scope in &self.stack_sizes { + // for _ in 0..scope.stack_len { + // self.map.remove(&self.stack.pop().unwrap()); + // } + // } + // + // self.stack_sizes = vec![]; + // self.stack_shadows = vec![]; + // self.stack_depth = 0; - self.stack_sizes = vec![]; - self.stack_shadows = vec![]; - self.stack_depth = 0; + let depth = self.stack_depth - 1; + for _ in 0..depth { + let _ = self.pop_stack(); + } } pub fn define(&mut self, name: impl Into, value: Rc) { @@ -174,8 +179,13 @@ impl Environment { self.stack.get(start..len).unwrap() } - pub fn insert(&mut self, key: EnvironmentInternalSymbolKey, value: Rc) { - if self.stack_depth != 0 { + pub fn insert( + &mut self, + key: EnvironmentInternalSymbolKey, + value: Rc, + dont_shadow: bool, + ) { + if self.stack_depth != 0 && !dont_shadow { let mut shadow = false; if self.map.get(&key).is_some() { if !self.get_current_scope().contains(&key) { diff --git a/src/lib/node/add.rs b/src/lib/node/add.rs index c013e2d..f2e8e87 100644 --- a/src/lib/node/add.rs +++ b/src/lib/node/add.rs @@ -1,9 +1,9 @@ -use std::rc::Rc; +use std::{cmp::Ordering, rc::Rc}; use rug::Float; use super::{ - Environment, Node, NodeEnum, Precedence, constant::Constant, set::Set, string_node::StringNode, + constant::Constant, set::Set, string_node::StringNode, Environment, Error, Node, NodeEnum, Precedence }; #[derive(Clone, Debug, PartialEq, PartialOrd)] @@ -13,7 +13,7 @@ pub struct Add { } impl Node for Add { - fn evaluate(&self, env: &mut Environment) -> Result, String> { + fn evaluate(&self, env: &mut Environment) -> Result, Error> { let evaluated_left = self.left.evaluate(env)?; let evaluated_right = self.right.evaluate(env)?; @@ -51,17 +51,79 @@ impl Node for Add { Ok(StringNode::new(s1.get_value().clone() + s2.get_value())) } + (NodeEnum::Add(add), o) | (o, NodeEnum::Add(add)) => { + // Aggregate into a single vector for easy sorting + let mut aggregate = vec![]; + let mut queue = vec![]; + queue.push(Rc::new(o.clone())); + let mut c: Rc = Rc::new(add.clone().into()); + + loop { + match c.as_ref() { + NodeEnum::Add(m) => { + queue.push(m.get_left()); + queue.push(m.get_right()); + } + _ => { + aggregate.push(c.clone()); + } + } + + if !queue.is_empty() { + c = queue.pop().unwrap(); + } else { + break; + } + } + + aggregate.sort_by(|a, b| match (a.as_ref(), b.as_ref()) { + (_, NodeEnum::Constant(_)) => Ordering::Less, + (NodeEnum::Constant(_), _) => Ordering::Greater, + (NodeEnum::Symbol(s1), NodeEnum::Symbol(s2)) => s1.cmp(s2), + _ => a.partial_cmp(b).unwrap_or(Ordering::Equal) + }); + + let mut constant = Rc::new(Constant::new_from_float(0.0, &env).into()); + let mut constanted = false; + + while let Some(agr) = aggregate.last() { + if let NodeEnum::Constant(_) = agr.as_ref() { + constanted = true; + constant = + Add::new_rc(constant, aggregate.pop().unwrap()); + } else { + break; + } + } + + let mut out = aggregate.pop().unwrap(); + + while let Some(a) = aggregate.pop() { + out = Add::new_rc(out, a); + } + + if constanted { + Ok(Add::new_rc(constant.evaluate(env)?, out)) + } else { + Ok(out) + } + } + _ => Ok(Rc::new(Add::new(evaluated_left, evaluated_right).into())), } } fn as_string(&self, env: Option<&Environment>) -> String { - let left_string = if self.left.precedence() <= self.precedence() { + let left_string = if let NodeEnum::Add(_) = self.left.as_ref() { + self.left.as_string(env) + } else if self.left.precedence() <= self.precedence() { format!("({})", self.left.as_string(env)) } else { self.left.as_string(env) }; - let right_string = if self.right.precedence() <= self.precedence() { + let right_string = if let NodeEnum::Add(_) = self.right.as_ref() { + self.right.as_string(env) + } else if self.right.precedence() <= self.precedence() { format!("({})", self.right.as_string(env)) } else { self.right.as_string(env) diff --git a/src/lib/node/assign.rs b/src/lib/node/assign.rs index b739827..f7608a8 100644 --- a/src/lib/node/assign.rs +++ b/src/lib/node/assign.rs @@ -1,15 +1,16 @@ use std::rc::Rc; -use super::{Environment, Node, NodeEnum, Precedence, empty::Empty}; +use super::{Environment, Error, Node, NodeEnum, Precedence, empty::Empty}; #[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct Assign { left: Rc, right: Rc, + shadow: bool, } impl Node for Assign { - fn evaluate(&self, env: &mut Environment) -> Result, String> { + fn evaluate(&self, env: &mut Environment) -> Result, Error> { match self.left.as_ref() { NodeEnum::Symbol(symbol) => { let name = env @@ -18,9 +19,10 @@ impl Node for Assign { .clone(); let right = self.right.evaluate(env)?; - env.insert(symbol.get_value(), right.clone()); + env.insert(symbol.get_value(), right.clone(), !self.shadow); Ok(Empty::new(format!( - "{name} := {}", + "{name} {} {}", + if self.shadow { ":=" } else { "<-" }, right.as_string(Some(env)) ))) } @@ -30,7 +32,7 @@ impl Node for Assign { let values = set.get_values(); if values.len() == 0 { - return Err(format!("Cannot assign to an empty Set")); + return Err(Error::EmptySetAssignment); } let mut symbols = Vec::with_capacity(values.len()); @@ -38,7 +40,10 @@ impl Node for Assign { if let NodeEnum::Symbol(symbol) = node.as_ref() { symbols.push(symbol); } else { - return Err(format!("Cannot assign to a {}", node.type_str())); + return Err(Error::IllegalAssignment(format!( + "Cannot assign to a {}", + node.type_str() + ))); } } @@ -48,19 +53,19 @@ impl Node for Assign { if let NodeEnum::Set(set) = right.as_ref() { let values = set.get_values(); if values.len() != symbols.len() { - return Err(format!( - "Expected rhs with {} elements but got {}", + return Err(Error::SetAssignmentCountMismatch( symbols.len(), - values.len() + values.len(), )); } - for (symbol,value) in symbols.iter().zip(values.into_iter()) { - env.insert(symbol.get_value(), value.clone()); + for (symbol, value) in symbols.iter().zip(values.into_iter()) { + env.insert(symbol.get_value(), value.clone(), !self.shadow); } Ok(Empty::new(format!( - "[{}] := {}", + "[{}] {} {}", + if self.shadow { ":=" } else { "<-" }, symbols .iter() .map(|x| env.id_to_str(&x.get_value()).unwrap().clone()) @@ -69,18 +74,25 @@ impl Node for Assign { right.as_string(Some(env)) ))) } else { - return Err(format!("Cannot unpack {} for assignment", right.type_str())); + return Err(Error::IllegalRhsAssignment(format!( + "Cannot unpack {} for assignment", + right.type_str() + ))); } } - _ => Err(format!("Cannot assign to a {}", self.left.type_str())), + _ => Err(Error::IllegalAssignment(format!( + "Cannot assign to a {}", + self.left.type_str() + ))), } } fn as_string(&self, env: Option<&Environment>) -> String { format!( - "{} := {}", + "{} {} {}", self.left.as_string(env), + if self.shadow { ":=" } else { "<-" }, self.right.as_string(env) ) } @@ -91,8 +103,12 @@ impl Node for Assign { } impl Assign { - pub fn new(left: Rc, right: Rc) -> Self { - Self { left, right } + pub fn new(left: Rc, right: Rc, shadow: bool) -> Self { + Self { + left, + right, + shadow, + } } pub fn get_left(&self) -> Rc { diff --git a/src/lib/node/call.rs b/src/lib/node/call.rs index ec7657f..e56fb0d 100644 --- a/src/lib/node/call.rs +++ b/src/lib/node/call.rs @@ -5,7 +5,7 @@ use crate::{ node::{closure::Closure, function::FunctionType}, }; -use super::{Node, NodeEnum, Precedence}; +use super::{Error, Node, NodeEnum, Precedence}; #[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct Call { @@ -14,7 +14,7 @@ pub struct Call { } impl Node for Call { - fn evaluate(&self, env: &mut Environment) -> Result, String> { + fn evaluate(&self, env: &mut Environment) -> Result, Error> { if env.closures_enabled() { return Ok(Rc::new(self.clone().into())); } @@ -33,7 +33,7 @@ impl Node for Call { NodeEnum::Function(func) => func, NodeEnum::Closure(closure) => { for (k, v) in closure.get_captured_variables() { - env.insert(*k, v.clone()); + env.insert(*k, v.clone(), false); } closure.get_function() } @@ -56,15 +56,11 @@ impl Node for Call { FunctionType::Native(_name, native_function) => native_function(&arguments, env), FunctionType::UserFunction(body, fargs) => { if fargs.len() != arguments.len() { - return Err(format!( - "Error calling function. Expected {} arguments, but got {}", - fargs.len(), - arguments.len() - )); + return Err(Error::ArgumentCount(fargs.len(), arguments.len())); } // Define variables fargs.iter().zip(&arguments).for_each(|(symbol, value)| { - env.insert(symbol.get_value(), value.clone()); + env.insert(symbol.get_value(), value.clone(), false); }); let ev = body.evaluate(env); diff --git a/src/lib/node/closure.rs b/src/lib/node/closure.rs index 89d745e..d9bb0e4 100644 --- a/src/lib/node/closure.rs +++ b/src/lib/node/closure.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use crate::environment::{Environment, EnvironmentInternalSymbolKey}; -use super::{Node, NodeEnum, Precedence, function::Function}; +use super::{function::Function, Error, Node, NodeEnum, Precedence}; #[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct Closure { @@ -11,7 +11,7 @@ pub struct Closure { } impl Node for Closure { - fn evaluate(&self, _env: &mut Environment) -> Result, String> { + fn evaluate(&self, _env: &mut Environment) -> Result, Error> { Ok(Rc::new(self.clone().into())) } diff --git a/src/lib/node/comparison.rs b/src/lib/node/comparison.rs index 6464999..b2057cd 100644 --- a/src/lib/node/comparison.rs +++ b/src/lib/node/comparison.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use crate::environment::Environment; -use super::{Node, NodeEnum, Precedence, if_else::Bool}; +use super::{if_else::Bool, Error, Node, NodeEnum, Precedence}; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct Greater { @@ -29,7 +29,7 @@ pub struct LessEquals { } impl Node for Greater { - fn evaluate(&self, env: &mut Environment) -> Result, String> { + fn evaluate(&self, env: &mut Environment) -> Result, Error> { let mut expr = self.left.evaluate(env)?; for e in &self.right { let e = e.evaluate(env)?; @@ -66,7 +66,7 @@ impl Node for Greater { } impl Node for Less { - fn evaluate(&self, env: &mut Environment) -> Result, String> { + fn evaluate(&self, env: &mut Environment) -> Result, Error> { let mut expr = self.left.evaluate(env)?; for e in &self.right { let e = e.evaluate(env)?; @@ -103,14 +103,14 @@ impl Node for Less { } impl Node for GreaterEquals { - fn evaluate(&self, env: &mut Environment) -> Result, String> { + fn evaluate(&self, env: &mut Environment) -> Result, Error> { let mut expr = self.left.evaluate(env)?; for e in &self.right { let e = e.evaluate(env)?; let evaluation = expr.partial_cmp(&e); match evaluation { Some(cmp) - if cmp != std::cmp::Ordering::Equal || cmp != std::cmp::Ordering::Greater => + if cmp != std::cmp::Ordering::Equal && cmp != std::cmp::Ordering::Greater => { return Ok(Rc::new(Bool::False.into())); } @@ -142,14 +142,14 @@ impl Node for GreaterEquals { } impl Node for LessEquals { - fn evaluate(&self, env: &mut Environment) -> Result, String> { + fn evaluate(&self, env: &mut Environment) -> Result, Error> { let mut expr = self.left.evaluate(env)?; for e in &self.right { let e = e.evaluate(env)?; let evaluation = expr.partial_cmp(&e); match evaluation { Some(cmp) - if cmp != std::cmp::Ordering::Equal || cmp != std::cmp::Ordering::Less => + if cmp != std::cmp::Ordering::Equal && cmp != std::cmp::Ordering::Less => { return Ok(Rc::new(Bool::False.into())); } @@ -185,7 +185,7 @@ impl Greater { Rc::new(Self { left, right }.into()) } - fn abort(&self, env: &mut Environment) -> Result, String> { + fn abort(&self, env: &mut Environment) -> Result, Error> { let mut arguments = vec![]; for value in self.right.iter() { arguments.push(value.evaluate(env)?); @@ -199,7 +199,7 @@ impl Less { Rc::new(Self { left, right }.into()) } - fn abort(&self, env: &mut Environment) -> Result, String> { + fn abort(&self, env: &mut Environment) -> Result, Error> { let mut arguments = vec![]; for value in self.right.iter() { arguments.push(value.evaluate(env)?); @@ -213,7 +213,7 @@ impl LessEquals { Rc::new(Self { left, right }.into()) } - fn abort(&self, env: &mut Environment) -> Result, String> { + fn abort(&self, env: &mut Environment) -> Result, Error> { let mut arguments = vec![]; for value in self.right.iter() { arguments.push(value.evaluate(env)?); @@ -227,7 +227,7 @@ impl GreaterEquals { Rc::new(Self { left, right }.into()) } - fn abort(&self, env: &mut Environment) -> Result, String> { + fn abort(&self, env: &mut Environment) -> Result, Error> { let mut arguments = vec![]; for value in self.right.iter() { arguments.push(value.evaluate(env)?); diff --git a/src/lib/node/constant.rs b/src/lib/node/constant.rs index c9a79ed..0a451a2 100644 --- a/src/lib/node/constant.rs +++ b/src/lib/node/constant.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use rug::Float; -use super::{Environment, Node, Precedence}; +use super::{Environment, Error, Node, Precedence}; pub type ConstantValue = Float; @@ -18,7 +18,7 @@ impl From for Constant { } impl Node for Constant { - fn evaluate(&self, _: &mut super::Environment) -> Result, String> { + fn evaluate(&self, _: &mut super::Environment) -> Result, Error> { Ok(Rc::new(self.clone().into())) } diff --git a/src/lib/node/divide.rs b/src/lib/node/divide.rs index a8a3f0a..9a5bc46 100644 --- a/src/lib/node/divide.rs +++ b/src/lib/node/divide.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use rug::Float; -use super::{Node, NodeEnum, Precedence, constant::Constant, set::Set}; +use super::{constant::Constant, set::Set, Error, Node, NodeEnum, Precedence}; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct Divide { @@ -11,14 +11,14 @@ pub struct Divide { } impl Node for Divide { - fn evaluate(&self, env: &mut super::Environment) -> Result, String> { + fn evaluate(&self, env: &mut super::Environment) -> Result, Error> { let evaluated_left = self.left.evaluate(env)?; let evaluated_right = self.right.evaluate(env)?; match (evaluated_left.as_ref(), evaluated_right.as_ref()) { // Error if dividing by zero (_, NodeEnum::Constant(zero)) if zero.get_value() == &0.0 => { - Err("Division by Zero".into()) + Err(Error::DivideByZero) } (_, _) if evaluated_left == evaluated_right => { diff --git a/src/lib/node/empty.rs b/src/lib/node/empty.rs index 880abfb..8373f37 100644 --- a/src/lib/node/empty.rs +++ b/src/lib/node/empty.rs @@ -1,6 +1,6 @@ use std::{rc::Rc, sync::LazyLock}; -use super::{Environment, Node, NodeEnum, Precedence}; +use super::{Environment, Error, Node, NodeEnum, Precedence}; #[derive(Debug, Clone, PartialOrd)] pub struct Empty { @@ -15,7 +15,7 @@ impl PartialEq for Empty { impl Eq for Empty {} impl Node for Empty { - fn evaluate(&self, _: &mut Environment) -> Result, String> { + fn evaluate(&self, _: &mut Environment) -> Result, Error> { Ok(Empty::EMPTY.clone()) } diff --git a/src/lib/node/equals.rs b/src/lib/node/equals.rs index f228689..361af3d 100644 --- a/src/lib/node/equals.rs +++ b/src/lib/node/equals.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use super::{Environment, Node, NodeEnum, Precedence, if_else::Bool}; +use super::{if_else::Bool, Environment, Error, Node, NodeEnum, Precedence}; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct Equals { @@ -9,7 +9,7 @@ pub struct Equals { } impl Node for Equals { - fn evaluate(&self, env: &mut Environment) -> Result, String> { + fn evaluate(&self, env: &mut Environment) -> Result, Error> { let left = self.left.evaluate(env)?; for expr in &self.right { diff --git a/src/lib/node/exponent.rs b/src/lib/node/exponent.rs index 254bf03..663887b 100644 --- a/src/lib/node/exponent.rs +++ b/src/lib/node/exponent.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use rug::{Float, ops::Pow}; -use super::{Environment, Node, NodeEnum, Precedence, constant::Constant}; +use super::{constant::Constant, Environment, Error, Node, NodeEnum, Precedence}; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct Exponent { @@ -11,7 +11,7 @@ pub struct Exponent { } impl Node for Exponent { - fn evaluate(&self, env: &mut super::Environment) -> Result, String> { + fn evaluate(&self, env: &mut super::Environment) -> Result, Error> { let evaluated_left = self.left.evaluate(env)?; let evaluated_right = self.right.evaluate(env)?; diff --git a/src/lib/node/function.rs b/src/lib/node/function.rs index 8ae2998..6867728 100644 --- a/src/lib/node/function.rs +++ b/src/lib/node/function.rs @@ -2,10 +2,10 @@ use std::rc::Rc; use crate::environment::Environment; -use super::{symbol::Symbol, Node, NodeEnum, Precedence}; +use super::{symbol::Symbol, Error, Node, NodeEnum, Precedence}; pub type NativeFunctionType = - fn(&Vec>, env: &mut Environment) -> Result, String>; + fn(&Vec>, env: &mut Environment) -> Result, Error>; #[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum FunctionType { @@ -19,7 +19,7 @@ pub struct Function { } impl Node for Function { - fn evaluate(&self, _env: &mut Environment) -> Result, String> { + fn evaluate(&self, _env: &mut Environment) -> Result, Error> { Ok(Rc::new(self.clone().into())) // Ok(Rc::new( diff --git a/src/lib/node/if_else.rs b/src/lib/node/if_else.rs index 0d689c6..e18a1ef 100644 --- a/src/lib/node/if_else.rs +++ b/src/lib/node/if_else.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use crate::node::empty::Empty; -use super::{Environment, Node, NodeEnum, Precedence}; +use super::{Environment, Error, Node, NodeEnum, Precedence}; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub enum Bool { @@ -10,8 +10,14 @@ pub enum Bool { False, } +impl From for Bool { + fn from(value: bool) -> Self { + if value { Self::True } else { Self::False } + } +} + impl Node for Bool { - fn evaluate(&self, _: &mut Environment) -> Result, String> { + fn evaluate(&self, _: &mut Environment) -> Result, Error> { Ok(Rc::new(self.clone().into())) } @@ -43,22 +49,22 @@ pub struct IfElse { } impl Node for IfElse { - fn evaluate(&self, env: &mut Environment) -> Result, String> { + fn evaluate(&self, env: &mut Environment) -> Result, Error> { let condition_evaluated = self.condition.evaluate(env)?; let condition = if let NodeEnum::Bool(bool) = condition_evaluated.as_ref() { bool } else { - return Err(format!( + return Err(Error::MismatchedType(format!( "Cannot evaluate {} to a bool", condition_evaluated.as_string(Some(env)) - )); + ))); }; fn evaluate_block( block: &Vec>, env: &mut Environment, - ) -> Result, String> { + ) -> Result, Error> { env.push_stack()?; let ret = if let Some((last, to_evaluate)) = block.split_last() { for expr in to_evaluate { @@ -89,8 +95,8 @@ impl Node for IfElse { self.true_branch .iter() .map(|x| x.as_string(env)) - .reduce(|a, b| a + " " + &b) - .unwrap() + .reduce(|a, b| a + "; " + &b) + .unwrap_or("".to_owned()) .as_str(), match &self.else_branch { ElseBranchEnum::ElseIf(if_else) => if_else.as_string(env), @@ -100,8 +106,8 @@ impl Node for IfElse { .iter() .map(|x| x.as_string(env)) .reduce(|a, b| a + "\n" + &b) - .unwrap() - + "end", + .unwrap_or("".to_owned()) + + " end", ElseBranchEnum::None => " end".to_owned(), } .as_str() diff --git a/src/lib/node/loop.rs b/src/lib/node/loop.rs new file mode 100644 index 0000000..be33271 --- /dev/null +++ b/src/lib/node/loop.rs @@ -0,0 +1,78 @@ +use std::rc::Rc; + +use crate::environment::Environment; + +use super::{Error, Node, NodeEnum, Precedence, empty::Empty}; + +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub struct Break; + +impl Node for Break { + fn evaluate(&self,_: &mut Environment) -> Result,Error> { + return Err(Error::Break); + } + + fn as_string(&self,_:Option<&Environment>) -> String { + "break".to_owned() + } + + fn precedence(&self) -> Precedence { + Precedence::Primary + } +} + +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub struct Loop { + body: Vec>, +} + +impl Node for Loop { + fn evaluate(&self, env: &mut Environment) -> Result, Error> { + let mut break_loop = false; + while !break_loop { + env.push_stack()?; + + for statement in self.body.iter() { + let evaluated = statement.evaluate(env); + + match evaluated { + Err(Error::Break) => { + break_loop = true; + break; + } + Err(e) => { + env.pop_stack()?; + return Err(e); + } + _ => {} + } + } + + env.pop_stack()?; + } + + Ok(Empty::new("")) + } + + fn as_string(&self, env: Option<&Environment>) -> String { + format!( + "loop {} end", + self.body + .iter() + .map(|x| x.as_string(env)) + .reduce(|a, b| a + "; " + &b) + .unwrap() + .as_str() + ) + } + + fn precedence(&self) -> Precedence { + Precedence::Primary + } +} + +impl Loop { + pub fn new(body: Vec>) -> Rc { + Rc::new(Self { body }.into()) + } +} diff --git a/src/lib/node/mod.rs b/src/lib/node/mod.rs index cf41b6f..41371d5 100644 --- a/src/lib/node/mod.rs +++ b/src/lib/node/mod.rs @@ -3,6 +3,7 @@ use std::{fmt::Display, rc::Rc}; use add::Add; use assign::Assign; use call::Call; +use closure::Closure; use comparison::{Greater, GreaterEquals, Less, LessEquals}; use constant::Constant; use divide::Divide; @@ -12,18 +13,20 @@ use equals::Equals; use exponent::Exponent; use function::Function; use if_else::{Bool, IfElse}; +use r#loop::{Break, Loop}; use multiply::Multiply; +use range::Range; use set::Set; +use string_node::StringNode; use subtract::Subtract; use symbol::Symbol; -use string_node::StringNode; -use closure::Closure; use crate::environment::Environment; pub mod add; pub mod assign; pub mod call; +pub mod closure; pub mod comparison; pub mod constant; pub mod divide; @@ -32,13 +35,14 @@ pub mod equals; pub mod exponent; pub mod function; pub mod if_else; +pub mod r#loop; +pub mod range; pub mod multiply; pub mod node_ref; pub mod set; +pub mod string_node; pub mod subtract; pub mod symbol; -pub mod string_node; -pub mod closure; #[enum_dispatch] #[enum_dispatch(Debug, Clone, PartialEq, PartialOrd, ToString)] @@ -58,13 +62,17 @@ pub enum NodeEnum { Assign, Empty, Function, - Closure, // IMPLEMENT THIS SO CURRYING WORKS + Closure, Call, + Loop, + Break, + Bool, IfElse, Set, + Range, Equals, Greater, @@ -93,9 +101,59 @@ pub enum Precedence { Primary, } +pub enum Error { + Break, + StackOverflow, + StackUnderflow, + /// Expected, Got + ArgumentCount(usize, usize), + MismatchedType(String), + + ConversionError(String), + + IllegalAssignment(String), + IllegalRhsAssignment(String), + EmptySetAssignment, + /// Expected, Got + SetAssignmentCountMismatch(usize, usize), + + DivideByZero, + /// Lenght, Index + IndexOutOfBounds(usize, usize), + + Other(String), +} + +impl ToString for Error { + fn to_string(&self) -> String { + match self { + Error::Break => "Invalid break statement".to_owned(), + Error::StackOverflow => "Stack Overflow".to_owned(), + Error::StackUnderflow => "Stack Underflow".to_owned(), + Error::ArgumentCount(exp, got) => { + format!("Expected {} arguments, but got {}", exp, got) + } + Error::EmptySetAssignment => "Cannot assign to an empty Set".to_owned(), + Error::SetAssignmentCountMismatch(exp, got) => { + format!("Cannot define {} symbols from a Set of {} values", exp, got) + } + Error::DivideByZero => "Division by Zero!".to_owned(), + Error::IndexOutOfBounds(exp, got) => { + format!("Index {} out of bounds for Set of size {}", got, exp) + } + + Error::MismatchedType(s) + | Error::ConversionError(s) + | Error::IllegalAssignment(s) + | Error::IllegalRhsAssignment(s) + | Error::Other(s) => s.to_owned(), + } + } +} + #[enum_dispatch(NodeEnum)] pub trait Node { - fn evaluate(&self, _: &mut Environment) -> Result, String>; + fn evaluate(&self, _: &mut Environment) -> Result, Error>; fn as_string(&self, _: Option<&Environment>) -> String; fn precedence(&self) -> Precedence; } @@ -153,6 +211,7 @@ impl PartialOrd for NodeEnum { less_equals1.partial_cmp(less_equals2) } + // (a,b) => a.partial_cmp(b) _ => None, } } @@ -165,7 +224,7 @@ impl NodeEnum { NodeEnum::StringNode(_) => "String", NodeEnum::Add(_) => "Add", NodeEnum::Subtract(_) => "Subtract", - NodeEnum::Multiply(_) => "Multiply", + NodeEnum::Multiply(_) => "Multiply", NodeEnum::Divide(_) => "Divide", NodeEnum::Exponent(_) => "Exponent", NodeEnum::Symbol(_) => "Symbol", @@ -181,7 +240,11 @@ impl NodeEnum { NodeEnum::Greater(_) => "Greater", NodeEnum::GreaterEquals(_) => "Greater Equals", NodeEnum::Less(_) => "Less", - NodeEnum::LessEquals(_) => "Less Equals" - }.to_owned() + NodeEnum::LessEquals(_) => "Less Equals", + NodeEnum::Loop(_) => "Loop", + NodeEnum::Break(_) => "Break", + NodeEnum::Range(_) => "Range", + } + .to_owned() } } diff --git a/src/lib/node/multiply.rs b/src/lib/node/multiply.rs index b9b42f1..2143b98 100644 --- a/src/lib/node/multiply.rs +++ b/src/lib/node/multiply.rs @@ -1,12 +1,11 @@ -use std::rc::Rc; +use std::{cmp::Ordering, ops::Mul, rc::Rc}; use rug::Float; use crate::environment::Environment; use super::{ - Node, NodeEnum, Precedence, add::Add, constant::Constant, divide::Divide, set::Set, - subtract::Subtract, + add::Add, constant::Constant, divide::Divide, set::Set, subtract::Subtract, Error, Node, NodeEnum, Precedence }; #[derive(Clone, Debug, PartialEq, PartialOrd)] @@ -16,7 +15,7 @@ pub struct Multiply { } impl Node for Multiply { - fn evaluate(&self, env: &mut super::Environment) -> Result, String> { + fn evaluate(&self, env: &mut super::Environment) -> Result, Error> { let evaluated_left = self.left.evaluate(env)?; let evaluated_right = self.right.evaluate(env)?; @@ -29,51 +28,38 @@ impl Node for Multiply { } // Identity rule - (NodeEnum::Constant(one), _) if one.get_value() == &0.0 => Ok(evaluated_right), - (_, NodeEnum::Constant(one)) if one.get_value() == &0.0 => Ok(evaluated_left), + (x, NodeEnum::Constant(one)) | (NodeEnum::Constant(one), x) + if one.get_value() == &1.0 => + { + Ok(Rc::new(x.clone())) + } // Multiply into parenthesis (add) - (_, NodeEnum::Add(add)) => Ok(Add::new_rc( - Self::new_rc(evaluated_left.clone(), add.get_left()).evaluate(env)?, - Self::new_rc(evaluated_left, add.get_right()).evaluate(env)?, - )), - (NodeEnum::Add(add), _) => Ok(Add::new_rc( - Self::new_rc(evaluated_right.clone(), add.get_left()).evaluate(env)?, - Self::new_rc(evaluated_right, add.get_right()).evaluate(env)?, + (NodeEnum::Add(add), x) | (x, NodeEnum::Add(add)) => Ok(Add::new_rc( + Multiply::new_rc(Rc::new(x.clone()), add.get_left()).evaluate(env)?, + Multiply::new_rc(Rc::new(x.clone()), add.get_right()).evaluate(env)?, )), // Multiply into parenthesis (sub) - (_, NodeEnum::Subtract(sub)) => Ok(Subtract::new_rc( - Self::new_rc(evaluated_left.clone(), sub.get_left()).evaluate(env)?, - Self::new_rc(evaluated_left, sub.get_right()).evaluate(env)?, - )), - (NodeEnum::Subtract(sub), _) => Ok(Subtract::new_rc( - Self::new_rc(evaluated_right.clone(), sub.get_left()).evaluate(env)?, - Self::new_rc(evaluated_right, sub.get_right()).evaluate(env)?, + (NodeEnum::Subtract(sub), x) | (x, NodeEnum::Subtract(sub)) => Ok(Subtract::new_rc( + Multiply::new_rc(Rc::new(x.clone()), sub.get_left()).evaluate(env)?, + Multiply::new_rc(Rc::new(x.clone()), sub.get_right()).evaluate(env)?, )), // Multiply fraction - (NodeEnum::Divide(div), _) => Ok(Divide::new_rc( - Self::new_rc(evaluated_right, div.get_left()).evaluate(env)?, - div.get_right(), - )), - - (_, NodeEnum::Divide(div)) => Ok(Divide::new_rc( - Self::new_rc(evaluated_left, div.get_left()).evaluate(env)?, + (x, NodeEnum::Divide(div)) | (NodeEnum::Divide(div), x) => Ok(Divide::new_rc( + Self::new_rc(Rc::new(x.clone()), div.get_left()).evaluate(env)?, div.get_right(), )), // 0.5*n -> n/2 - (NodeEnum::Constant(c), _) if c.get_value() == &0.5 => Divide::new( - evaluated_right, - Rc::new(Constant::new_from_float(2, &env).into()), - ) - .evaluate(env), - (_, NodeEnum::Constant(c)) if c.get_value() == &0.5 => Divide::new( - evaluated_left, - Rc::new(Constant::new_from_float(2, &env).into()), - ) - .evaluate(env), + (x, NodeEnum::Constant(c)) | (NodeEnum::Constant(c), x) if c.get_value() == &0.5 => { + Divide::new( + Rc::new(x.clone()), + Rc::new(Constant::new_from_float(2, &env).into()), + ) + .evaluate(env) + } // Constant * Constant = Constant (NodeEnum::Constant(a), NodeEnum::Constant(b)) => { @@ -83,18 +69,11 @@ impl Node for Multiply { ))))) } - // Move constant infront of symbols - (NodeEnum::Symbol(_), NodeEnum::Constant(_)) - | (NodeEnum::Exponent(_), NodeEnum::Constant(_)) => Ok(Rc::new( - Multiply::new(evaluated_right, evaluated_left).into(), - )), - - // Multiply a set with a constant - (NodeEnum::Set(s), NodeEnum::Constant(c)) - | (NodeEnum::Constant(c), NodeEnum::Set(s)) => { + // Multiply into sets + (NodeEnum::Set(s), c) | (c, NodeEnum::Set(s)) => { let old_values = s.get_values(); let mut values = Vec::with_capacity(old_values.len()); - let c: Rc = Rc::new(c.clone().into()); + let c: Rc = Rc::new(c.clone()); for value in old_values.iter() { let new_value = Multiply::new(c.clone(), value.clone()); @@ -104,28 +83,76 @@ impl Node for Multiply { Ok(Set::new(values)) } - // Multiply a set with a constant - (NodeEnum::Set(s), NodeEnum::Symbol(c)) | (NodeEnum::Symbol(c), NodeEnum::Set(s)) => { - let old_values = s.get_values(); - let mut values = Vec::with_capacity(old_values.len()); - let c: Rc = Rc::new(c.clone().into()); - - for value in old_values.iter() { - let new_value = Multiply::new(c.clone(), value.clone()); - values.push(new_value.evaluate(env)?); + (NodeEnum::Symbol(s1), NodeEnum::Symbol(s2)) => { + if s1 > s2 { + Ok(Self::new_rc(evaluated_left, evaluated_right)) + } else { + Ok(Self::new_rc(evaluated_right, evaluated_left)) } - - Ok(Set::new(values)) } - // (NodeEnum::Constant(c), NodeEnum::Multiply(m)) - // | (NodeEnum::Multiply(m), NodeEnum::Constant(c)) => { - // Self::collapse_nested_multiply(c, m, env)?.evaluate(env) - // } + // At least one of the nodes is a nested multiply + (NodeEnum::Multiply(m), o) | (o, NodeEnum::Multiply(m)) => { + // Aggregate into a single vector for easy sorting + let mut aggregate = vec![]; + let mut queue = vec![]; + queue.push(Rc::new(o.clone())); + let mut c: Rc = Rc::new(m.clone().into()); - // (NodeEnum::Multiply(m1), NodeEnum::Multiply(m2)) => { - // Self::move_constants_to_left(evaluated_left.clone(), evaluated_right.clone()) - // } + loop { + match c.as_ref() { + NodeEnum::Multiply(m) => { + queue.push(m.get_left()); + queue.push(m.get_right()); + } + _ => { + aggregate.push(c.clone()); + } + } + + if !queue.is_empty() { + c = queue.pop().unwrap(); + } else { + break; + } + } + + aggregate.sort_by(|a, b| match (a.as_ref(), b.as_ref()) { + (_, NodeEnum::Constant(_)) => Ordering::Less, + (NodeEnum::Constant(_), _) => Ordering::Greater, + (NodeEnum::Symbol(s1), NodeEnum::Symbol(s2)) => s1.cmp(s2), + _ => a.partial_cmp(b).unwrap_or(Ordering::Equal) + }); + + let mut constant = Rc::new(Constant::new_from_float(1.0, &env).into()); + let mut constanted = false; + + while let Some(agr) = aggregate.last() { + if let NodeEnum::Constant(_) = agr.as_ref() { + constanted = true; + constant = + Multiply::new_rc(constant, aggregate.pop().unwrap()).evaluate(env)?; + } else { + break; + } + } + + let mut out = aggregate.pop().unwrap(); + + while let Some(a) = aggregate.pop() { + out = Multiply::new_rc(out, a); + } + + if constanted { + Ok(Multiply::new_rc(constant, out)) + } else { + Ok(out) + } + } + + (_, NodeEnum::Constant(_)) => { + Ok(Multiply::new_rc(evaluated_right, evaluated_left)) + } // Default to returning with left and right evaluated _ => Ok(Rc::new( @@ -135,12 +162,16 @@ impl Node for Multiply { } fn as_string(&self, env: Option<&super::Environment>) -> String { - let left_string = if self.left.precedence() <= self.precedence() { + let left_string = if let NodeEnum::Multiply(_) = self.left.as_ref() { + self.left.as_string(env) + } else if self.left.precedence() <= self.precedence() { format!("({})", self.left.as_string(env)) } else { self.left.as_string(env) }; - let right_string = if self.right.precedence() <= self.precedence() { + let right_string = if let NodeEnum::Multiply(_) = self.right.as_ref() { + self.right.as_string(env) + } else if self.right.precedence() <= self.precedence() { format!("({})", self.right.as_string(env)) } else { self.right.as_string(env) @@ -154,76 +185,14 @@ impl Node for Multiply { } impl Multiply { - fn collapse_nested_multiply( - constant: &Constant, - multiply: &Multiply, - env: &mut Environment, - ) -> Result, String> { - match (multiply.left.as_ref(), multiply.right.as_ref()) { - (NodeEnum::Constant(c2), _) => { - let new_const = Constant::new(Float::with_val_64( - env.get_float_precision(), - constant.get_value() * c2.get_value(), - )) - .into(); - Ok(Rc::new( - Multiply::new(Rc::new(new_const), multiply.right.clone()).into(), - )) + pub fn symbols_match(&self, other: &Multiply) -> bool { + if let NodeEnum::Constant(_) = self.left.as_ref() { + if let NodeEnum::Constant(_) = other.left.as_ref() { + return self.right.eq(&other.right); } - - (_, NodeEnum::Constant(c2)) => { - let new_const = Constant::new(Float::with_val_64( - env.get_float_precision(), - constant.get_value() * c2.get_value(), - )) - .into(); - Ok(Rc::new( - Multiply::new(Rc::new(new_const), multiply.right.clone()).into(), - )) - } - - (_, NodeEnum::Multiply(inner_mul)) => { - let expr = Self::collapse_nested_multiply(constant, inner_mul, env)?; - Ok(Rc::new(Multiply::new(expr, multiply.left.clone()).into())) - } - - (NodeEnum::Multiply(inner_mul), _) => { - let expr = Self::collapse_nested_multiply(constant, inner_mul, env)?; - Ok(Rc::new(Multiply::new(expr, multiply.right.clone()).into())) - } - - _ => Ok(Rc::new( - Multiply::new( - Rc::new(constant.clone().into()), - Rc::new(multiply.clone().into()), - ) - .into(), - )), } - } - #[allow(dead_code)] - fn move_constants_to_left( - left: Rc, - right: Rc, - ) -> Result, String> { - match (left.as_ref(), right.as_ref()) { - // Default cases that returns - (NodeEnum::Constant(_), NodeEnum::Multiply(_)) - | (NodeEnum::Symbol(_), NodeEnum::Symbol(_)) => Ok(Self::new_rc(left, right)), - - (NodeEnum::Multiply(m), NodeEnum::Constant(_c)) => Ok(Self::new_rc( - right.clone(), - Self::move_constants_to_left(m.left.clone(), m.right.clone())?, - )), - - (NodeEnum::Multiply(m1), NodeEnum::Multiply(m2)) => Ok(Self::new_rc( - Self::move_constants_to_left(m1.left.clone(), m1.right.clone())?, - Self::move_constants_to_left(m2.left.clone(), m2.right.clone())?, - )), - - _ => Ok(Self::new_rc(left, right)), - } + self.eq(other) } } diff --git a/src/lib/node/node_ref.rs b/src/lib/node/node_ref.rs index 2ab92e1..50113d4 100644 --- a/src/lib/node/node_ref.rs +++ b/src/lib/node/node_ref.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use super::{Environment, Node, NodeEnum, Precedence}; +use super::{Environment, Error, Node, NodeEnum, Precedence}; #[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct NodeRef { @@ -8,7 +8,7 @@ pub struct NodeRef { } impl Node for NodeRef { - fn evaluate(&self, env: &mut Environment) -> Result, String> { + fn evaluate(&self, env: &mut Environment) -> Result, Error> { self.value.evaluate(env) } diff --git a/src/lib/node/range.rs b/src/lib/node/range.rs new file mode 100644 index 0000000..79a4f11 --- /dev/null +++ b/src/lib/node/range.rs @@ -0,0 +1,43 @@ +use std::rc::Rc; + +use crate::environment::Environment; + +use super::{Error, Node, NodeEnum, Precedence, constant::ConstantValue}; + +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub struct Range { + pub from: ConstantValue, + pub to: ConstantValue, +} + +impl Node for Range { + fn evaluate(&self, _: &mut Environment) -> Result, Error> { + Ok(Rc::new(self.clone().into())) + } + + fn as_string(&self, _: Option<&Environment>) -> String { + format!( + "{}..{}", + self.from + .to_string_radix(10, Some(8)) + .trim_end_matches('0') + .trim_end_matches('.') + .to_string(), + self.to + .to_string_radix(10, Some(8)) + .trim_end_matches('0') + .trim_end_matches('.') + .to_string() + ) + } + + fn precedence(&self) -> Precedence { + Precedence::Primary + } +} + +impl Range { + pub fn new(from: ConstantValue, to: ConstantValue) -> Rc { + Rc::new(Self { from, to }.into()) + } +} diff --git a/src/lib/node/set.rs b/src/lib/node/set.rs index 6ba1fee..74652aa 100644 --- a/src/lib/node/set.rs +++ b/src/lib/node/set.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use super::{Environment, Node, NodeEnum, Precedence}; +use super::{Environment, Error, Node, NodeEnum, Precedence, constant::Constant}; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct Set { @@ -8,9 +8,22 @@ pub struct Set { } impl Node for Set { - fn evaluate(&self, env: &mut super::Environment) -> Result, String> { + fn evaluate(&self, env: &mut super::Environment) -> Result, Error> { let mut values = Vec::with_capacity(self.values.len()); + if self.values.len() == 1 { + let evaluated = self.values.first().unwrap().evaluate(env)?; + + if let NodeEnum::Range(r) = evaluated.as_ref() { + let mut i = r.from.clone(); + while i < r.to { + values.push(Rc::new(Constant::new(i.clone()).into())); + i += 1; + } + return Ok(Self::new(values)); + } + } + for value in self.values.iter() { values.push(value.evaluate(env)?); } diff --git a/src/lib/node/string_node.rs b/src/lib/node/string_node.rs index 9311f6c..00efc35 100644 --- a/src/lib/node/string_node.rs +++ b/src/lib/node/string_node.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use super::{Environment, Node, NodeEnum, Precedence}; +use super::{Environment, Error, Node, NodeEnum, Precedence}; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct StringNode { @@ -14,7 +14,7 @@ impl From for StringNode { } impl Node for StringNode { - fn evaluate(&self, _: &mut super::Environment) -> Result, String> { + fn evaluate(&self, _: &mut super::Environment) -> Result, Error> { Ok(Rc::new(self.clone().into())) } diff --git a/src/lib/node/subtract.rs b/src/lib/node/subtract.rs index d6bdbc1..1aeac62 100644 --- a/src/lib/node/subtract.rs +++ b/src/lib/node/subtract.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use rug::Float; -use super::{Environment, Node, NodeEnum, Precedence, constant::Constant, multiply::Multiply}; +use super::{constant::Constant, multiply::Multiply, Environment, Error, Node, NodeEnum, Precedence}; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct Subtract { @@ -11,7 +11,7 @@ pub struct Subtract { } impl Node for Subtract { - fn evaluate(&self, env: &mut super::Environment) -> Result, String> { + fn evaluate(&self, env: &mut super::Environment) -> Result, Error> { let evaluated_left = self.left.evaluate(env)?; let evaluated_right = self.right.evaluate(env)?; @@ -36,51 +36,60 @@ impl Node for Subtract { Subtract::new(evaluated_right, evaluated_left).into(), )), + // (NodeEnum::Multiply(m1), NodeEnum::Multiply(m2)) => { + // match ( + // (m1.get_left().as_ref(), m1.get_right().as_ref()), + // (m2.get_left().as_ref(), m2.get_right().as_ref()), + // ) { + // ((NodeEnum::Symbol(s1), o1), (NodeEnum::Symbol(s2), o2)) + // | ((o1, NodeEnum::Symbol(s1)), (NodeEnum::Symbol(s2), o2)) + // | ((NodeEnum::Symbol(s1), o1), (o2, NodeEnum::Symbol(s2))) + // | ((o1, NodeEnum::Symbol(s1)), (o2, NodeEnum::Symbol(s2))) => { + // if s1 == s2 { + // Multiply::new_rc( + // Subtract::new(Rc::new(o1.clone()), Rc::new(o2.clone())) + // .evaluate(env)?, + // Rc::new(s1.clone().into()), + // ) + // .evaluate(env) + // } else { + // Ok(Rc::new( + // Subtract::new(evaluated_left, evaluated_right).into(), + // )) + // } + // } + // + // ((NodeEnum::Exponent(e1), o1), (NodeEnum::Exponent(e2), o2)) + // | ((o1, NodeEnum::Exponent(e1)), (NodeEnum::Exponent(e2), o2)) + // | ((NodeEnum::Exponent(e1), o1), (o2, NodeEnum::Exponent(e2))) + // | ((o1, NodeEnum::Exponent(e1)), (o2, NodeEnum::Exponent(e2))) => { + // if e1 == e2 { + // Multiply::new_rc( + // Subtract::new(Rc::new(o1.clone()), Rc::new(o2.clone())) + // .evaluate(env)?, + // Rc::new(e1.clone().into()), + // ) + // .evaluate(env) + // } else { + // Ok(Rc::new( + // Subtract::new(evaluated_left, evaluated_right).into(), + // )) + // } + // } + // + // _ => Ok(Rc::new( + // Subtract::new(evaluated_left, evaluated_right).into(), + // )), + // } + // } (NodeEnum::Multiply(m1), NodeEnum::Multiply(m2)) => { - match ( - (m1.get_left().as_ref(), m1.get_right().as_ref()), - (m2.get_left().as_ref(), m2.get_right().as_ref()), - ) { - ((NodeEnum::Symbol(s1), o1), (NodeEnum::Symbol(s2), o2)) - | ((o1, NodeEnum::Symbol(s1)), (NodeEnum::Symbol(s2), o2)) - | ((NodeEnum::Symbol(s1), o1), (o2, NodeEnum::Symbol(s2))) - | ((o1, NodeEnum::Symbol(s1)), (o2, NodeEnum::Symbol(s2))) => { - if s1 == s2 { - Multiply::new_rc( - Subtract::new(Rc::new(o1.clone()), Rc::new(o2.clone())) - .evaluate(env)?, - Rc::new(s1.clone().into()), - ) - .evaluate(env) - } else { - Ok(Rc::new( - Subtract::new(evaluated_left, evaluated_right).into(), - )) - } - } - - ((NodeEnum::Exponent(e1), o1), (NodeEnum::Exponent(e2), o2)) - | ((o1, NodeEnum::Exponent(e1)), (NodeEnum::Exponent(e2), o2)) - | ((NodeEnum::Exponent(e1), o1), (o2, NodeEnum::Exponent(e2))) - | ((o1, NodeEnum::Exponent(e1)), (o2, NodeEnum::Exponent(e2))) => { - if e1 == e2 { - Multiply::new_rc( - Subtract::new(Rc::new(o1.clone()), Rc::new(o2.clone())) - .evaluate(env)?, - Rc::new(e1.clone().into()), - ) - .evaluate(env) - } else { - Ok(Rc::new( - Subtract::new(evaluated_left, evaluated_right).into(), - )) - } - } - - _ => Ok(Rc::new( - Subtract::new(evaluated_left, evaluated_right).into(), - )), + if m1.symbols_match(m2) { + return Ok(Multiply::new_rc( + Subtract::new_rc(m1.get_left(), m2.get_left()).evaluate(env)?, + m1.get_right(), + )); } + Ok(Subtract::new_rc(evaluated_left, evaluated_right)) } _ => { diff --git a/src/lib/node/symbol.rs b/src/lib/node/symbol.rs index de62a47..8c3375f 100644 --- a/src/lib/node/symbol.rs +++ b/src/lib/node/symbol.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use super::{Environment, Node, NodeEnum, Precedence}; +use super::{Environment, Error, Node, NodeEnum, Precedence}; use crate::environment::EnvironmentInternalSymbolKey; #[derive(Debug, Clone, PartialEq)] @@ -9,7 +9,7 @@ pub struct Symbol { } impl Node for Symbol { - fn evaluate(&self, env: &mut Environment) -> Result, String> { + fn evaluate(&self, env: &mut Environment) -> Result, Error> { if let Some(value) = env.get(&self.value) { // TODO: Detect cyclic references and make sure they get // deleted at some point @@ -60,7 +60,15 @@ impl Symbol { } impl PartialOrd for Symbol { - fn partial_cmp(&self, _other: &Self) -> Option { - None + fn partial_cmp(&self, other: &Self) -> Option { + other.value.partial_cmp(&self.value) + } +} + +impl Eq for Symbol {} + +impl Ord for Symbol { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + other.value.cmp(&self.value) } } diff --git a/src/lib/parser/mod.rs b/src/lib/parser/mod.rs index b9158e9..caec7c5 100644 --- a/src/lib/parser/mod.rs +++ b/src/lib/parser/mod.rs @@ -21,7 +21,9 @@ use crate::{ exponent::Exponent, function::{Function, FunctionType}, if_else::{Bool, ElseBranchEnum, IfElse}, + r#loop::{Break, Loop}, multiply::Multiply, + range::Range, set::Set, string_node::StringNode, subtract::Subtract, @@ -39,6 +41,8 @@ pub enum TokenType { Identifier(String), String(String), + DoubleDot, + Plus, Minus, Star, @@ -53,6 +57,7 @@ pub enum TokenType { ColonEquals, LeftArrow, + RightArrow, RParen, LParen, @@ -66,6 +71,10 @@ pub enum TokenType { Else, End, + Loop, + Block, + Break, + True, False, @@ -78,6 +87,8 @@ impl TokenType { TokenType::Number(n) => n.to_string().len(), TokenType::Identifier(s) | TokenType::String(s) => s.len(), + TokenType::DoubleDot => 2, + TokenType::Plus => 1, TokenType::Minus => 1, TokenType::Star => 1, @@ -92,6 +103,7 @@ impl TokenType { TokenType::ColonEquals => 2, TokenType::LeftArrow => 2, + TokenType::RightArrow => 2, TokenType::RParen => 1, TokenType::LParen => 1, @@ -105,6 +117,10 @@ impl TokenType { TokenType::Else => 4, TokenType::End => 3, + TokenType::Loop => 4, + TokenType::Block => 5, + TokenType::Break => 5, + TokenType::True => 4, TokenType::False => 5, @@ -149,19 +165,38 @@ impl<'a> Lexer<'a> { i += 1; } } - // Numbers with decimal points + // Double period .. + '.' if self.source.peek() == Some(&'.') => { + self.source.next(); + i += 1; + tokens.push(Token(i, TokenType::DoubleDot)); + } + // Numbers '0'..='9' | '.' => { - let mut digit = String::from(c); + let mut digits = String::from(c); let mut has_decimal = c == '.'; + let mut push = true; loop { let d = self.source.peek(); match d { Some('0'..='9') => { - digit.push(*d.unwrap()); - self.source.next(); + digits.push(self.source.next().unwrap()); i += 1; } Some('.') => { + let current = self.source.next(); + + if self.source.peek() == Some(&'.') { + // useless clone, TODO: Report this + tokens.push(Token(i, TokenType::Number(digits.clone()))); + push = false; + + self.source.next(); + i += 2; + tokens.push(Token(i, TokenType::DoubleDot)); + break; + } + if has_decimal { return Err(LexerError::UnexpectedChar( i + 1, @@ -169,8 +204,7 @@ impl<'a> Lexer<'a> { )); } - digit.push(*d.unwrap()); - self.source.next(); + digits.push(current.unwrap()); i += 1; has_decimal = true; } @@ -179,16 +213,10 @@ impl<'a> Lexer<'a> { } } } - // if let Some(v) = { - // v - // } else { - // return Err(LexerError::NumberParse( - // i, - // format!("Failed to convert {digit} to a number"), - // )); - // }; - tokens.push(Token(i, TokenType::Number(digit))); + if push { + tokens.push(Token(i, TokenType::Number(digits))); + } } '"' => { @@ -221,6 +249,11 @@ impl<'a> Lexer<'a> { i += 1; tokens.push(Token(i, TokenType::LeftArrow)); } + '<' if self.source.peek() == Some(&'-') => { + self.source.next(); + i += 1; + tokens.push(Token(i, TokenType::RightArrow)); + } '<' if self.source.peek() == Some(&'=') => { self.source.next(); @@ -261,10 +294,14 @@ impl<'a> Lexer<'a> { tokens.push(self.lex_identifier(&mut i, c)?); } + '\n' => { + continue; + } + _ => { return Err(LexerError::UnexpectedChar( i, - format!("Unexpected char {}", c), + format!("Unexpected char {c} ({c:?})"), )); } } @@ -295,6 +332,10 @@ impl<'a> Lexer<'a> { "else" => TokenType::Else, "end" => TokenType::End, + "loop" => TokenType::Loop, + "block" => TokenType::Block, + "break" => TokenType::Break, + "true" => TokenType::True, "false" => TokenType::False, @@ -411,7 +452,9 @@ impl<'a> Parser<'a> { let expr = self.equality()?; if self.match_type(TokenType::ColonEquals) { - return Ok(Rc::new(Assign::new(expr, self.equality()?).into())); + return Ok(Rc::new(Assign::new(expr, self.equality()?, true).into())); + } else if self.match_type(TokenType::RightArrow) { + return Ok(Rc::new(Assign::new(expr, self.equality()?, false).into())); } Ok(expr) @@ -472,28 +515,25 @@ impl<'a> Parser<'a> { } fn term(&mut self) -> Result, ParserError> { - let expr = self.factor()?; - if self.match_type(TokenType::Plus) { - Ok(Rc::new(Add::new(expr, self.comparison()?).into())) - } else if let Some(Token(_, TokenType::Minus)) = self.tokens.peek() { - self.consume(); - Ok(Rc::new(Subtract::new(expr, self.comparison()?).into())) - } else { - Ok(expr) + let mut expr = self.factor()?; + loop { + if self.match_type(TokenType::Plus) { + let right = self.factor()?; + expr = Add::new_rc(expr, right); + continue; + } else if self.match_type(TokenType::Minus) { + let right = self.factor()?; + expr = Subtract::new_rc(expr, right); + continue; + } + break; } + + Ok(expr) } fn factor(&mut self) -> Result, ParserError> { let mut expr = self.unary()?; - // if let Some(Token(_, TokenType::Star)) = self.tokens.peek() { - // self.consume(); - // Ok(Rc::new(Multiply::new(expr, self.comparison()?).into())) - // } else if let Some(Token(_, TokenType::Slash)) = self.tokens.peek() { - // self.consume(); - // Ok(Rc::new(Divide::new(expr, self.comparison()?).into())) - // } else { - // Ok(expr) - // } loop { if self.match_type(TokenType::Star) { @@ -508,6 +548,23 @@ impl<'a> Parser<'a> { break; } + // // Implicit multiplication + // if !self.is_at_end() { + // match self.tokens.peek().unwrap() { + // Token(_, TokenType::Identifier(_)) | Token(_, TokenType::Number(_)) => { + // return Ok(Multiply::new_rc(expr, self.equality()?)); + // } + // Token(_, TokenType::LParen) => { + // if let NodeEnum::Symbol(_) = expr.as_ref() { + // return Ok(expr); + // } + // + // return Ok(Multiply::new_rc(expr, self.equality()?)); + // } + // _ => {} + // } + // } + Ok(expr) } @@ -603,6 +660,7 @@ impl<'a> Parser<'a> { Assign::new( expr, Function::new(FunctionType::UserFunction(body, arguments)), + true, ) .into(), )); @@ -700,6 +758,7 @@ impl<'a> Parser<'a> { )); } expressions.push(self.expression()?); + let _ = self.match_type(TokenType::Terminator); } ElseBranchEnum::Block(expressions) @@ -712,6 +771,19 @@ impl<'a> Parser<'a> { return Ok(IfElse::new(condition, expressions, else_branch)); } + self.r#loop() + } + + fn r#loop(&mut self) -> Result, ParserError> { + if self.match_type(TokenType::Loop) { + let mut body = vec![]; + while !self.match_type(TokenType::End) { + body.push(self.expression()?); + let _ = self.match_type(TokenType::Terminator); + } + return Ok(Loop::new(body)); + } + self.set() } @@ -754,7 +826,39 @@ impl<'a> Parser<'a> { return Ok(Set::new(values)); } - self.primary() + self.range() + } + + fn range(&mut self) -> Result, ParserError> { + let exp = self.primary()?; + + if self.match_type(TokenType::DoubleDot) { + let to = self.primary()?; + + let from = match exp.as_ref() { + NodeEnum::Constant(c) => c.get_value().clone(), + _ => { + return Err(ParserError::UnexpectedNode( + self.previous.as_ref().unwrap().0, + "Expected a Constant here".to_owned(), + )); + } + }; + + let to = match to.as_ref() { + NodeEnum::Constant(c) => c.get_value().clone(), + _ => { + return Err(ParserError::UnexpectedNode( + self.previous.as_ref().unwrap().0, + "Expected a Constant here".to_owned(), + )); + } + }; + + return Ok(Range::new(from, to)); + } + + Ok(exp) } fn primary(&mut self) -> Result, ParserError> { @@ -784,6 +888,8 @@ impl<'a> Parser<'a> { TokenType::True => Ok(Rc::new(Bool::True.into())), TokenType::False => Ok(Rc::new(Bool::False.into())), + TokenType::Break => Ok(Rc::new(Break.into())), + TokenType::String(s) => Ok(StringNode::new(s)), TokenType::LParen => { @@ -814,23 +920,6 @@ impl<'a> Parser<'a> { )), }; - // Implicit multiplication - if !self.is_at_end() { - match self.tokens.peek().unwrap() { - Token(_, TokenType::Identifier(_)) | Token(_, TokenType::Number(_)) => { - return Ok(Multiply::new_rc(expr?, self.primary()?)); - } - Token(_, TokenType::LParen) if expr.is_ok() => { - if let NodeEnum::Symbol(_) = expr.as_ref().unwrap().as_ref() { - return expr; - } - - return Ok(Multiply::new_rc(expr?, self.primary()?)); - } - _ => {} - } - } - expr } } diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..3c614e0 --- /dev/null +++ b/test.txt @@ -0,0 +1,33 @@ +zip(x,y) := + if (length(x) = length(y)) = false then + break + else + out := []; + i := 0; + loop + if i = length(x) then + break + end + out <- out + [[get(x,i), get(y,i)]]; + i <- i+1 + end; + + out + end; + +fact'(a,x) := + block + loop + if x < 1 then + break + end + + a <- a*x + x <- x-1 + end; + a + end; + +fact(x) := fact'(1,x); + +print(fact(5))