did some grinding off camera

This commit is contained in:
Snorre 2025-03-20 12:27:27 +01:00
parent cd758a7fce
commit f6909e5e23
26 changed files with 1070 additions and 421 deletions

206
README.md Normal file
View file

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

View file

@ -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));
}
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<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
fn print(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
for expr in args {
println!("\r{}", expr.as_string(Some(env)));
}
@ -208,17 +211,17 @@ fn print(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>
Ok(Empty::new(""))
}
fn unset(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
fn unset(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>
fn get_float_precision(
args: &Vec<Rc<NodeEnum>>,
env: &mut Environment,
) -> Result<Rc<NodeEnum>, String> {
) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>,
env: &mut Environment,
) -> Result<Rc<NodeEnum>, String> {
) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
fn map(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>,
Ok(Set::new(out))
}
fn get(args: &Vec<Rc<NodeEnum>>, _env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
fn get(args: &Vec<Rc<NodeEnum>>, _env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
fn length(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum
))
}
fn benchmark(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
fn benchmark(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeE
Ok(Set::new(vec![time_str, time_const, result]))
}
fn head(args: &Vec<Rc<NodeEnum>>, _env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
fn head(args: &Vec<Rc<NodeEnum>>, _env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>, _env: &mut Environment) -> Result<Rc<NodeEnum>
Ok(Set::new(vec![head, Set::new(tail)]))
}
fn diff(args: &Vec<Rc<NodeEnum>>, _env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
fn mult_equal(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>, _env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>, _env: &mut Environment) -> Result<Rc<NodeEnum>
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,31 +457,7 @@ fn diff_internally(
todo!()
}
fn main() -> Result<(), io::Error> {
let mut input = Input::new();
let mut env = Environment::new();
env.set_float_precision(128);
env.define_native_function("print", print);
env.define_native_function("unset", unset);
env.define_native_function("get_float_precision", get_float_precision);
env.define_native_function("set_float_precision", set_float_precision);
env.define_native_function("map", map);
env.define_native_function("head", head);
env.define_native_function("get", get);
env.define_native_function("length", length);
env.define_native_function("benchmark", benchmark);
env.define(
"pi",
Rc::new(Constant::new_from_float(std::f64::consts::PI, &env).into()),
);
while let Some(source) = input.get()? {
input.disable_raw()?;
fn evaluate(source: &String, mut env: &mut Environment, verbose: bool) {
let mut lexer = Lexer::new(&source);
let tokens_result = lexer.lex();
@ -461,7 +465,7 @@ fn main() -> Result<(), io::Error> {
match tokens_result.err().unwrap() {
LexerError::UnexpectedChar(i, exp) => print_err(i, 1, exp),
}
continue;
return;
}
let tokens = tokens_result.unwrap();
@ -478,7 +482,7 @@ fn main() -> Result<(), io::Error> {
print_err(i, 1, exp)
}
}
continue;
return;
}
};
@ -487,10 +491,54 @@ fn main() -> Result<(), io::Error> {
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),
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);
env.define_native_function("print", print);
env.define_native_function("unset", unset);
env.define_native_function("get_float_precision", get_float_precision);
env.define_native_function("set_float_precision", set_float_precision);
env.define_native_function("map", map);
env.define_native_function("head", head);
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<String> = 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()?;
evaluate(&source, &mut env, true);
input.enable_raw()?;

View file

@ -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<String>, value: Rc<NodeEnum>) {
@ -174,8 +179,13 @@ impl Environment {
self.stack.get(start..len).unwrap()
}
pub fn insert(&mut self, key: EnvironmentInternalSymbolKey, value: Rc<NodeEnum>) {
if self.stack_depth != 0 {
pub fn insert(
&mut self,
key: EnvironmentInternalSymbolKey,
value: Rc<NodeEnum>,
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) {

View file

@ -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<Rc<NodeEnum>, String> {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<NodeEnum> = 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)

View file

@ -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<NodeEnum>,
right: Rc<NodeEnum>,
shadow: bool,
}
impl Node for Assign {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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());
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<NodeEnum>, right: Rc<NodeEnum>) -> Self {
Self { left, right }
pub fn new(left: Rc<NodeEnum>, right: Rc<NodeEnum>, shadow: bool) -> Self {
Self {
left,
right,
shadow,
}
}
pub fn get_left(&self) -> Rc<NodeEnum> {

View file

@ -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<Rc<NodeEnum>, String> {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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);

View file

@ -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<Rc<NodeEnum>, String> {
fn evaluate(&self, _env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
Ok(Rc::new(self.clone().into()))
}

View file

@ -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<Rc<NodeEnum>, String> {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>, String> {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>, String> {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>, String> {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>, String> {
fn abort(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>, String> {
fn abort(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>, String> {
fn abort(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>, String> {
fn abort(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
let mut arguments = vec![];
for value in self.right.iter() {
arguments.push(value.evaluate(env)?);

View file

@ -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<ConstantValue> for Constant {
}
impl Node for Constant {
fn evaluate(&self, _: &mut super::Environment) -> Result<Rc<super::NodeEnum>, String> {
fn evaluate(&self, _: &mut super::Environment) -> Result<Rc<super::NodeEnum>, Error> {
Ok(Rc::new(self.clone().into()))
}

View file

@ -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<Rc<super::NodeEnum>, String> {
fn evaluate(&self, env: &mut super::Environment) -> Result<Rc<super::NodeEnum>, 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 => {

View file

@ -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<Rc<NodeEnum>, String> {
fn evaluate(&self, _: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
Ok(Empty::EMPTY.clone())
}

View file

@ -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<Rc<NodeEnum>, String> {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
let left = self.left.evaluate(env)?;
for expr in &self.right {

View file

@ -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<Rc<super::NodeEnum>, String> {
fn evaluate(&self, env: &mut super::Environment) -> Result<Rc<super::NodeEnum>, Error> {
let evaluated_left = self.left.evaluate(env)?;
let evaluated_right = self.right.evaluate(env)?;

View file

@ -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<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String>;
fn(&Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>, String> {
fn evaluate(&self, _env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
Ok(Rc::new(self.clone().into()))
// Ok(Rc::new(

View file

@ -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<bool> for Bool {
fn from(value: bool) -> Self {
if value { Self::True } else { Self::False }
}
}
impl Node for Bool {
fn evaluate(&self, _: &mut Environment) -> Result<Rc<NodeEnum>, String> {
fn evaluate(&self, _: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>, String> {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>,
env: &mut Environment,
) -> Result<Rc<NodeEnum>, String> {
) -> Result<Rc<NodeEnum>, 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,7 +106,7 @@ impl Node for IfElse {
.iter()
.map(|x| x.as_string(env))
.reduce(|a, b| a + "\n" + &b)
.unwrap()
.unwrap_or("".to_owned())
+ " end",
ElseBranchEnum::None => " end".to_owned(),
}

78
src/lib/node/loop.rs Normal file
View file

@ -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<Rc<NodeEnum>,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<Rc<NodeEnum>>,
}
impl Node for Loop {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<NodeEnum>>) -> Rc<NodeEnum> {
Rc::new(Self { body }.into())
}
}

View file

@ -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<Rc<NodeEnum>, String>;
fn evaluate(&self, _: &mut Environment) -> Result<Rc<NodeEnum>, 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,
}
}
@ -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()
}
}

View file

@ -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<Rc<super::NodeEnum>, String> {
fn evaluate(&self, env: &mut super::Environment) -> Result<Rc<super::NodeEnum>, 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,
(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),
(_, NodeEnum::Constant(c)) if c.get_value() == &0.5 => Divide::new(
evaluated_left,
Rc::new(Constant::new_from_float(2, &env).into()),
)
.evaluate(env),
.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<NodeEnum> = Rc::new(c.clone().into());
let c: Rc<NodeEnum> = 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<NodeEnum> = 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))
// 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<NodeEnum> = Rc::new(m.clone().into());
loop {
match c.as_ref() {
NodeEnum::Multiply(m) => {
queue.push(m.get_left());
queue.push(m.get_right());
}
_ => {
aggregate.push(c.clone());
}
}
// (NodeEnum::Constant(c), NodeEnum::Multiply(m))
// | (NodeEnum::Multiply(m), NodeEnum::Constant(c)) => {
// Self::collapse_nested_multiply(c, m, env)?.evaluate(env)
// }
if !queue.is_empty() {
c = queue.pop().unwrap();
} else {
break;
}
}
// (NodeEnum::Multiply(m1), NodeEnum::Multiply(m2)) => {
// Self::move_constants_to_left(evaluated_left.clone(), evaluated_right.clone())
// }
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<Rc<NodeEnum>, 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(),
))
}
(_, 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(),
)),
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);
}
}
#[allow(dead_code)]
fn move_constants_to_left(
left: Rc<NodeEnum>,
right: Rc<NodeEnum>,
) -> Result<Rc<NodeEnum>, 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)
}
}

View file

@ -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<Rc<NodeEnum>, String> {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
self.value.evaluate(env)
}

43
src/lib/node/range.rs Normal file
View file

@ -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<Rc<NodeEnum>, 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<NodeEnum> {
Rc::new(Self { from, to }.into())
}
}

View file

@ -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<Rc<NodeEnum>, String> {
fn evaluate(&self, env: &mut super::Environment) -> Result<Rc<NodeEnum>, 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)?);
}

View file

@ -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<String> for StringNode {
}
impl Node for StringNode {
fn evaluate(&self, _: &mut super::Environment) -> Result<Rc<super::NodeEnum>, String> {
fn evaluate(&self, _: &mut super::Environment) -> Result<Rc<super::NodeEnum>, Error> {
Ok(Rc::new(self.clone().into()))
}

View file

@ -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<Rc<super::NodeEnum>, String> {
fn evaluate(&self, env: &mut super::Environment) -> Result<Rc<super::NodeEnum>, 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))
}
_ => {

View file

@ -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<Rc<NodeEnum>, String> {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<std::cmp::Ordering> {
None
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
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)
}
}

View file

@ -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<Rc<NodeEnum>, ParserError> {
let expr = self.factor()?;
let mut expr = self.factor()?;
loop {
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 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<Rc<NodeEnum>, 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<Rc<NodeEnum>, 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<Rc<NodeEnum>, 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<Rc<NodeEnum>, 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
}
}

33
test.txt Normal file
View file

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