thingymagick
This commit is contained in:
parent
84a4689a76
commit
5ab9cc93ac
|
@ -2,11 +2,11 @@
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1736012469,
|
"lastModified": 1739020877,
|
||||||
"narHash": "sha256-/qlNWm/IEVVH7GfgAIyP6EsVZI6zjAx1cV5zNyrs+rI=",
|
"narHash": "sha256-mIvECo/NNdJJ/bXjNqIh8yeoSjVLAuDuTUzAo7dzs8Y=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d",
|
"rev": "a79cfe0ebd24952b580b1cf08cd906354996d547",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
6
src/environment.rs
Normal file
6
src/environment.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
use std::{collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
|
use crate::node::NodeEnum;
|
||||||
|
|
||||||
|
pub type EnvironmentInternalSymbolKey = u16;
|
||||||
|
pub type Environment = HashMap<EnvironmentInternalSymbolKey, Rc<NodeEnum>>;
|
20
src/main.rs
20
src/main.rs
|
@ -1,18 +1,18 @@
|
||||||
use node::{add::Add, constant::Constant, subtract::Subtract, Environment, Node, NodeEnum};
|
use std::io::{self, stdout, BufRead, Write};
|
||||||
|
|
||||||
mod node;
|
mod node;
|
||||||
|
mod environment;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut env = Environment::new();
|
loop {
|
||||||
|
print!("> ");
|
||||||
let a = Constant::new(69.0).into();
|
let _ = stdout().flush();
|
||||||
let b = Constant::new(420.0).into();
|
|
||||||
let c = Constant::new(360.0).into();
|
let mut line = String::new();
|
||||||
let d: NodeEnum = Subtract::new(Add::new(a,b).into(), c).into();
|
let stdin = io::stdin();
|
||||||
|
stdin.lock().read_line(&mut line).unwrap();
|
||||||
println!("d_0: {}", d);
|
}
|
||||||
println!("d_1: {}", d.evaluate(&mut env).unwrap());
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,36 @@
|
||||||
use super::{Node, NodeEnum, Precedence, constant::Constant};
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
use super::{constant::Constant, Environment, Node, NodeEnum, Precedence};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
pub struct Add {
|
pub struct Add {
|
||||||
left: Box<NodeEnum>,
|
left: Rc<NodeEnum>,
|
||||||
right: Box<NodeEnum>,
|
right: Rc<NodeEnum>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for Add {
|
impl Node for Add {
|
||||||
fn evaluate(&self, env: &mut super::Environment) -> Result<super::NodeEnum, String> {
|
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
let evaluated_left = self.left.evaluate(env)?;
|
let evaluated_left = self.left.evaluate(env)?;
|
||||||
let evaluated_right = self.right.evaluate(env)?;
|
let evaluated_right = self.right.evaluate(env)?;
|
||||||
|
|
||||||
match (evaluated_left, evaluated_right) {
|
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
|
||||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
||||||
Ok(Constant::new(a.get_value() + b.get_value()).into())
|
Ok(Rc::new(Constant::new(a.get_value() + b.get_value()).into()))
|
||||||
}
|
}
|
||||||
_ => Err(format!("Invalid Add operation: {:?}", self)),
|
_ => Err(format!("Invalid Add operation: {:?}", self)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_string(&self) -> String {
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
let left_string = if self.left.precedence() <= self.precedence() {
|
let left_string = if self.left.precedence() <= self.precedence() {
|
||||||
format!("({})", self.left.to_string())
|
format!("({})", self.left.as_string(env))
|
||||||
} else {
|
} else {
|
||||||
self.left.to_string()
|
self.left.as_string(env)
|
||||||
};
|
};
|
||||||
let right_string = if self.right.precedence() <= self.precedence() {
|
let right_string = if self.right.precedence() <= self.precedence() {
|
||||||
format!("({})", self.right.to_string())
|
format!("({})", self.right.as_string(env))
|
||||||
} else {
|
} else {
|
||||||
self.right.to_string()
|
self.right.as_string(env)
|
||||||
};
|
};
|
||||||
format!("{}+{}", left_string, right_string)
|
format!("{}+{}", left_string, right_string)
|
||||||
}
|
}
|
||||||
|
@ -41,8 +43,8 @@ impl Node for Add {
|
||||||
impl Add {
|
impl Add {
|
||||||
pub fn new(left: NodeEnum, right: NodeEnum) -> Self {
|
pub fn new(left: NodeEnum, right: NodeEnum) -> Self {
|
||||||
Self {
|
Self {
|
||||||
left: Box::new(left),
|
left: Rc::new(left),
|
||||||
right: Box::new(right),
|
right: Rc::new(right),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
28
src/node/assign.rs
Normal file
28
src/node/assign.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use super::{empty::Empty, symbol::Symbol, Environment, Node, NodeEnum, Precedence};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub struct Assign {
|
||||||
|
left: Symbol,
|
||||||
|
right: Rc<NodeEnum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Assign {
|
||||||
|
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
env.insert(self.left.get_value(), self.right.clone());
|
||||||
|
Ok(Empty::EMPTY.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
|
format!(
|
||||||
|
"{} := {}",
|
||||||
|
self.left.as_string(env),
|
||||||
|
self.right.as_string(env)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
Precedence::Assign
|
||||||
|
}
|
||||||
|
}
|
60
src/node/call.rs
Normal file
60
src/node/call.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::environment::Environment;
|
||||||
|
|
||||||
|
use super::{Node, NodeEnum, Precedence, symbol::Symbol};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub struct Call {
|
||||||
|
left: Rc<NodeEnum>,
|
||||||
|
right: Vec<Rc<NodeEnum>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Call {
|
||||||
|
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
todo!();
|
||||||
|
let function = match self.left.as_ref() {
|
||||||
|
NodeEnum::Symbol(symbol) => match symbol.evaluate(env)?.as_ref() {
|
||||||
|
_ => {
|
||||||
|
return Err(format!(
|
||||||
|
"Cannot call {} as a function",
|
||||||
|
symbol.as_string(Some(env))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeEnum::Function(function) => function,
|
||||||
|
},
|
||||||
|
NodeEnum::Function(function) => function,
|
||||||
|
NodeEnum::Call(call) => match call.evaluate(env)?.as_ref() {
|
||||||
|
NodeEnum::Function(function) => function,
|
||||||
|
|
||||||
|
// FIXME: This might fail for long chains of calls
|
||||||
|
_ => {
|
||||||
|
return Err(format!(
|
||||||
|
"Cannot call {} as a function",
|
||||||
|
self.left.as_string(None)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
return Err(format!(
|
||||||
|
"Cannot call {} as a function",
|
||||||
|
self.left.as_string(None)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// match function {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, _: Option<&Environment>) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
Precedence::Call
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,18 @@
|
||||||
use std::fmt::Display;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::{Node, NodeEnum, Precedence};
|
use super::{Environment, Node, Precedence};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
pub struct Constant {
|
pub struct Constant {
|
||||||
value: f64,
|
value: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for Constant {
|
impl Node for Constant {
|
||||||
fn evaluate(&self, _: &mut super::Environment) -> Result<super::NodeEnum, String> {
|
fn evaluate(&self, _: &mut super::Environment) -> Result<Rc<super::NodeEnum>, String> {
|
||||||
Ok(self.clone().into())
|
Ok(Rc::new(self.clone().into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_string(&self) -> String {
|
fn as_string(&self, _env: Option<&Environment>) -> String {
|
||||||
self.value.to_string()
|
self.value.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
50
src/node/divide.rs
Normal file
50
src/node/divide.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use super::{Node, NodeEnum, Precedence, constant::Constant};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
pub struct Divide {
|
||||||
|
left: Rc<NodeEnum>,
|
||||||
|
right: Rc<NodeEnum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Divide {
|
||||||
|
fn evaluate(&self, env: &mut super::Environment) -> Result<Rc<super::NodeEnum>, String> {
|
||||||
|
let evaluated_left = self.left.evaluate(env)?;
|
||||||
|
let evaluated_right = self.right.evaluate(env)?;
|
||||||
|
|
||||||
|
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
|
||||||
|
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
||||||
|
Ok(Rc::new(Constant::new(a.get_value() / b.get_value()).into()))
|
||||||
|
}
|
||||||
|
_ => Err(format!("Invalid Divide operation: {:?}", self)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, env: Option<&super::Environment>) -> String {
|
||||||
|
let left_string = 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() {
|
||||||
|
format!("({})", self.right.as_string(env))
|
||||||
|
} else {
|
||||||
|
self.right.as_string(env)
|
||||||
|
};
|
||||||
|
format!("{}/{}", left_string, right_string)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
Precedence::Factor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Divide {
|
||||||
|
pub fn new(left: NodeEnum, right: NodeEnum) -> Self {
|
||||||
|
Self {
|
||||||
|
left: Rc::new(left),
|
||||||
|
right: Rc::new(right),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
src/node/empty.rs
Normal file
23
src/node/empty.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
use std::{rc::Rc, sync::LazyLock};
|
||||||
|
|
||||||
|
use super::{Environment, Node, NodeEnum, Precedence};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub struct Empty;
|
||||||
|
impl Node for Empty {
|
||||||
|
fn evaluate(&self, _: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
Ok(Empty::EMPTY.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, _: Option<&Environment>) -> String {
|
||||||
|
String::from("{{#VOID}}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
Precedence::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Empty {
|
||||||
|
pub const EMPTY: LazyLock<Rc<NodeEnum>> = LazyLock::new(|| Rc::new(Empty{}.into()));
|
||||||
|
}
|
53
src/node/function.rs
Normal file
53
src/node/function.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::environment::Environment;
|
||||||
|
|
||||||
|
use super::{Node, NodeEnum, Precedence, symbol::Symbol};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub enum FunctionType {
|
||||||
|
Native(
|
||||||
|
&'static str,
|
||||||
|
fn(Vec<Rc<NodeEnum>>) -> Result<Rc<NodeEnum>, String>,
|
||||||
|
),
|
||||||
|
UserFunction(Rc<NodeEnum>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub struct Function {
|
||||||
|
function: FunctionType,
|
||||||
|
// TODO: Finish reasoning about whether or not this is
|
||||||
|
// the right way to implement functions
|
||||||
|
arguments: Vec<Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Function {
|
||||||
|
fn evaluate(&self, _: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
Ok(Rc::new(self.clone().into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
|
let args = self
|
||||||
|
.arguments
|
||||||
|
.iter()
|
||||||
|
.map(|x| Node::as_string(x, None))
|
||||||
|
.reduce(|acc, e| format!("{acc}, {e}"))
|
||||||
|
.unwrap_or("()".to_owned());
|
||||||
|
|
||||||
|
match &self.function {
|
||||||
|
FunctionType::Native(name, _) => name.to_owned().to_owned(),
|
||||||
|
FunctionType::UserFunction(body) => format!("({args} -> {})", body.as_string(env)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
Precedence::Call
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Function {
|
||||||
|
// TODO: Implement new.
|
||||||
|
// pub fn new(t: FunctionType, ) -> Self {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
}
|
|
@ -1,41 +1,71 @@
|
||||||
use std::{cmp::Ordering, collections::HashMap, fmt::Display};
|
use std::{collections::HashMap, fmt::Display, rc::Rc};
|
||||||
|
|
||||||
use add::Add;
|
use add::Add;
|
||||||
|
use assign::Assign;
|
||||||
|
use call::Call;
|
||||||
use constant::Constant;
|
use constant::Constant;
|
||||||
|
use divide::Divide;
|
||||||
|
use empty::Empty;
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
use function::Function;
|
||||||
|
use multiply::Multiply;
|
||||||
|
use node_ref::NodeRef;
|
||||||
use subtract::Subtract;
|
use subtract::Subtract;
|
||||||
|
use symbol::Symbol;
|
||||||
|
|
||||||
|
use crate::environment::Environment;
|
||||||
|
|
||||||
pub mod constant;
|
|
||||||
pub mod add;
|
pub mod add;
|
||||||
|
pub mod constant;
|
||||||
|
pub mod divide;
|
||||||
|
pub mod multiply;
|
||||||
pub mod subtract;
|
pub mod subtract;
|
||||||
|
pub mod symbol;
|
||||||
|
mod node_ref;
|
||||||
|
mod assign;
|
||||||
|
mod empty;
|
||||||
|
mod function;
|
||||||
|
mod call;
|
||||||
|
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
#[enum_dispatch(Debug, Clone, PartialEq, PartialOrd, ToString)]
|
#[enum_dispatch(Debug, Clone, PartialEq, PartialOrd, ToString)]
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||||
pub enum NodeEnum {
|
pub enum NodeEnum {
|
||||||
Constant(Constant),
|
Constant,
|
||||||
Add(Add),
|
Add,
|
||||||
Subtract(Subtract)
|
Subtract,
|
||||||
|
Multiply,
|
||||||
|
Divide,
|
||||||
|
Symbol,
|
||||||
|
// NodeRef, // DEPRECATED, use Symbol
|
||||||
|
Assign,
|
||||||
|
Empty,
|
||||||
|
Function,
|
||||||
|
Call
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum Precedence {
|
pub enum Precedence {
|
||||||
|
Empty,
|
||||||
|
Assign,
|
||||||
|
Compare,
|
||||||
|
Equality,
|
||||||
Term,
|
Term,
|
||||||
Factor,
|
Factor,
|
||||||
|
Unary,
|
||||||
|
Call,
|
||||||
Primary,
|
Primary,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Environment = HashMap<u16, NodeEnum>;
|
|
||||||
|
|
||||||
#[enum_dispatch(NodeEnum)]
|
#[enum_dispatch(NodeEnum)]
|
||||||
pub trait Node {
|
pub trait Node {
|
||||||
fn evaluate(&self, _: &mut Environment) -> Result<NodeEnum, String>;
|
fn evaluate(&self, _: &mut Environment) -> Result<Rc<NodeEnum>, String>;
|
||||||
fn to_string(&self) -> String;
|
fn as_string(&self, _: Option<&Environment>) -> String;
|
||||||
fn precedence(&self) -> Precedence;
|
fn precedence(&self) -> Precedence;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for NodeEnum {
|
impl Display for NodeEnum {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", Node::to_string(self))
|
write!(f, "{}", Node::as_string(self, None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
50
src/node/multiply.rs
Normal file
50
src/node/multiply.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use super::{Node, NodeEnum, Precedence, constant::Constant};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
pub struct Multiply {
|
||||||
|
left: Rc<NodeEnum>,
|
||||||
|
right: Rc<NodeEnum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Multiply {
|
||||||
|
fn evaluate(&self, env: &mut super::Environment) -> Result<Rc<super::NodeEnum>, String> {
|
||||||
|
let evaluated_left = self.left.evaluate(env)?;
|
||||||
|
let evaluated_right = self.right.evaluate(env)?;
|
||||||
|
|
||||||
|
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
|
||||||
|
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
||||||
|
Ok(Rc::new(Constant::new(a.get_value() * b.get_value()).into()))
|
||||||
|
}
|
||||||
|
_ => Err(format!("Invalid Multiply operation: {:?}", self)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, env: Option<&super::Environment>) -> String {
|
||||||
|
let left_string = 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() {
|
||||||
|
format!("({})", self.right.as_string(env))
|
||||||
|
} else {
|
||||||
|
self.right.as_string(env)
|
||||||
|
};
|
||||||
|
format!("{}*{}", left_string, right_string)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
Precedence::Factor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Multiply {
|
||||||
|
pub fn new(left: NodeEnum, right: NodeEnum) -> Self {
|
||||||
|
Self {
|
||||||
|
left: Rc::new(left),
|
||||||
|
right: Rc::new(right),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/node/node_ref.rs
Normal file
30
src/node/node_ref.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use super::{Environment, Node, NodeEnum, Precedence};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub struct NodeRef {
|
||||||
|
value: Rc<NodeEnum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for NodeRef {
|
||||||
|
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
self.value.evaluate(env)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
|
self.value.as_string(env)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
self.value.precedence()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeRef {
|
||||||
|
pub fn new(value: &Rc<NodeEnum>) -> Self {
|
||||||
|
Self {
|
||||||
|
value: value.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,34 +1,36 @@
|
||||||
use super::{Node, NodeEnum, Precedence, constant::Constant};
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
use super::{constant::Constant, Environment, Node, NodeEnum, Precedence};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
pub struct Subtract {
|
pub struct Subtract {
|
||||||
left: Box<NodeEnum>,
|
left: Rc<NodeEnum>,
|
||||||
right: Box<NodeEnum>,
|
right: Rc<NodeEnum>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for Subtract {
|
impl Node for Subtract {
|
||||||
fn evaluate(&self, env: &mut super::Environment) -> Result<super::NodeEnum, String> {
|
fn evaluate(&self, env: &mut super::Environment) -> Result<Rc<super::NodeEnum>, String> {
|
||||||
let evaluated_left = self.left.evaluate(env)?;
|
let evaluated_left = self.left.evaluate(env)?;
|
||||||
let evaluated_right = self.right.evaluate(env)?;
|
let evaluated_right = self.right.evaluate(env)?;
|
||||||
|
|
||||||
match (evaluated_left, evaluated_right) {
|
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
|
||||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
||||||
Ok(Constant::new(a.get_value() - b.get_value()).into())
|
Ok(Rc::new(Constant::new(a.get_value() - b.get_value()).into()))
|
||||||
}
|
}
|
||||||
_ => Err(format!("Invalid Add operation: {:?}", self)),
|
_ => Err(format!("Invalid Add operation: {:?}", self)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_string(&self) -> String {
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
let left_string = if self.left.precedence() <= self.precedence() {
|
let left_string = if self.left.precedence() <= self.precedence() {
|
||||||
format!("({})", self.left.to_string())
|
format!("({})", self.left.as_string(env))
|
||||||
} else {
|
} else {
|
||||||
self.left.to_string()
|
self.left.as_string(env)
|
||||||
};
|
};
|
||||||
let right_string = if self.right.precedence() <= self.precedence() {
|
let right_string = if self.right.precedence() <= self.precedence() {
|
||||||
format!("({})", self.right.to_string())
|
format!("({})", self.right.as_string(env))
|
||||||
} else {
|
} else {
|
||||||
self.right.to_string()
|
self.right.as_string(env)
|
||||||
};
|
};
|
||||||
format!("{}-{}", left_string, right_string)
|
format!("{}-{}", left_string, right_string)
|
||||||
}
|
}
|
||||||
|
@ -41,8 +43,8 @@ impl Node for Subtract {
|
||||||
impl Subtract {
|
impl Subtract {
|
||||||
pub fn new(left: NodeEnum, right: NodeEnum) -> Self {
|
pub fn new(left: NodeEnum, right: NodeEnum) -> Self {
|
||||||
Self {
|
Self {
|
||||||
left: Box::new(left),
|
left: Rc::new(left),
|
||||||
right: Box::new(right),
|
right: Rc::new(right),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
86
src/node/symbol.rs
Normal file
86
src/node/symbol.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
rc::Rc,
|
||||||
|
sync::{LazyLock, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{Environment, Node, NodeEnum, Precedence, node_ref::NodeRef};
|
||||||
|
use crate::environment::EnvironmentInternalSymbolKey;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub struct Symbol {
|
||||||
|
value: EnvironmentInternalSymbolKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Symbol {
|
||||||
|
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
if let Some(value) = env.get(&self.value) {
|
||||||
|
// TODO: Detect cyclic references and make sure they get
|
||||||
|
// deleted at some point
|
||||||
|
Ok(value.clone())
|
||||||
|
} else {
|
||||||
|
Ok(Rc::new(self.clone().into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
|
if let Some(env) = env {
|
||||||
|
if let Some(value) = env.get(&self.value) {
|
||||||
|
value.as_string(Some(env))
|
||||||
|
} else {
|
||||||
|
Symbol::ID_TO_STR_MAP
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get(&self.value)
|
||||||
|
.unwrap_or(&format!("{{#SYMBOL {}}}", self.value))
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Symbol::ID_TO_STR_MAP
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get(&self.value)
|
||||||
|
.unwrap_or(&format!("{{#SYMBOL {}}}", self.value))
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
Precedence::Primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Symbol {
|
||||||
|
const STR_TO_ID_MAP: LazyLock<Mutex<HashMap<String, EnvironmentInternalSymbolKey>>> =
|
||||||
|
LazyLock::new(|| HashMap::new().into());
|
||||||
|
const ID_TO_STR_MAP: LazyLock<Mutex<HashMap<EnvironmentInternalSymbolKey, String>>> =
|
||||||
|
LazyLock::new(|| HashMap::new().into());
|
||||||
|
|
||||||
|
pub fn new(value: EnvironmentInternalSymbolKey) -> Self {
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_str(str: String) -> Self {
|
||||||
|
if let Some(value) = Symbol::STR_TO_ID_MAP.lock().unwrap().get(str.as_str()) {
|
||||||
|
Self::new(value.clone())
|
||||||
|
} else {
|
||||||
|
let id = Symbol::STR_TO_ID_MAP
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
Symbol::STR_TO_ID_MAP
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(str.clone(), id);
|
||||||
|
Symbol::ID_TO_STR_MAP.lock().unwrap().insert(id, str);
|
||||||
|
|
||||||
|
Self::new(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> EnvironmentInternalSymbolKey {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,10 @@
|
||||||
mod arithmetic {
|
mod arithmetic {
|
||||||
use crate::node::{
|
use std::rc::Rc;
|
||||||
Environment, Node, NodeEnum, add::Add, constant::Constant, subtract::Subtract,
|
|
||||||
};
|
use crate::{environment, node::{
|
||||||
|
add::Add, constant::Constant, divide::Divide, multiply::Multiply, subtract::Subtract, Node, NodeEnum
|
||||||
|
}};
|
||||||
|
use environment::Environment;
|
||||||
|
|
||||||
use test_case::test_case;
|
use test_case::test_case;
|
||||||
|
|
||||||
|
@ -17,7 +20,7 @@ mod arithmetic {
|
||||||
let b = Constant::new(b).into();
|
let b = Constant::new(b).into();
|
||||||
let d: NodeEnum = Add::new(a, b).into();
|
let d: NodeEnum = Add::new(a, b).into();
|
||||||
|
|
||||||
let value = d.evaluate(&mut env).unwrap();
|
let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
value == Constant::new(e).into(),
|
value == Constant::new(e).into(),
|
||||||
|
@ -40,7 +43,7 @@ mod arithmetic {
|
||||||
let c = Constant::new(bb).into();
|
let c = Constant::new(bb).into();
|
||||||
let d: NodeEnum = Subtract::new(Add::new(a, b).into(), c).into();
|
let d: NodeEnum = Subtract::new(Add::new(a, b).into(), c).into();
|
||||||
|
|
||||||
let value = d.evaluate(&mut env).unwrap();
|
let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
value == Constant::new(e).into(),
|
value == Constant::new(e).into(),
|
||||||
|
@ -55,28 +58,72 @@ mod arithmetic {
|
||||||
#[test_case(5.0, -10.0, -50.0 ; "when right is negative")]
|
#[test_case(5.0, -10.0, -50.0 ; "when right is negative")]
|
||||||
#[test_case(-5.0, -10.0, 50.0 ; "when both are negative")]
|
#[test_case(-5.0, -10.0, 50.0 ; "when both are negative")]
|
||||||
#[test_case(2734589235234.23, 0.0, 0.0 ; "when 0 is involved")]
|
#[test_case(2734589235234.23, 0.0, 0.0 ; "when 0 is involved")]
|
||||||
fn multiplication(a: f64, b: f64, e: f64) {}
|
fn multiplication(a: f64, b: f64, e: f64) {
|
||||||
|
let mut env = Environment::new();
|
||||||
|
|
||||||
|
let a = Constant::new(a).into();
|
||||||
|
let b = Constant::new(b).into();
|
||||||
|
let d: NodeEnum = Multiply::new(a, b).into();
|
||||||
|
|
||||||
|
let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
value == Constant::new(e).into(),
|
||||||
|
"Expected {} got {}",
|
||||||
|
e,
|
||||||
|
value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test_case(5.0, 10.0, 0.5 ; "when both are positive")]
|
#[test_case(5.0, 10.0, 0.5 ; "when both are positive")]
|
||||||
#[test_case(-5.0, 10.0, -0.5 ; "when left is negative")]
|
#[test_case(-5.0, 10.0, -0.5 ; "when left is negative")]
|
||||||
#[test_case(5.0, -10.0, -0.5 ; "when right is negative")]
|
#[test_case(5.0, -10.0, -0.5 ; "when right is negative")]
|
||||||
#[test_case(-5.0, -10.0, 0.5 ; "when both are negative")]
|
#[test_case(-5.0, -10.0, 0.5 ; "when both are negative")]
|
||||||
fn division(a: f64, b: f64, e: f64) {}
|
fn division(a: f64, b: f64, e: f64) {
|
||||||
|
let mut env = Environment::new();
|
||||||
|
|
||||||
|
let a = Constant::new(a).into();
|
||||||
|
let b = Constant::new(b).into();
|
||||||
|
let d: NodeEnum = Divide::new(a, b).into();
|
||||||
|
|
||||||
|
let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
value == Constant::new(e).into(),
|
||||||
|
"Expected {} got {}",
|
||||||
|
e,
|
||||||
|
value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod functions {
|
||||||
|
#[test]
|
||||||
|
fn stringification() {
|
||||||
|
// let f = Function
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod expected_errors {
|
mod expected_errors {
|
||||||
use test_case::test_case;
|
use test_case::test_case;
|
||||||
|
|
||||||
#[test_case(5.0, 0.0 ; "divide by zero")]
|
#[test_case(5.0, 0.0 ; "divide by zero")]
|
||||||
fn division(a: f64, b: f64) {}
|
fn division(a: f64, b: f64) {
|
||||||
|
let _ = a+b;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod misc {
|
mod misc {
|
||||||
use crate::node::{
|
use test_case::test_case;
|
||||||
Environment, Node, NodeEnum, add::Add, constant::Constant, subtract::Subtract,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test_case(30, '+', 60, '-', 20, "(30+60)-20", "add and subtract")]
|
#[test_case(30, '+', 60, '-', 20, "(30+60)-20" ; "add and subtract")]
|
||||||
fn convert_to_string(a: into<f64>, op1: char, b: Into<f64>, op2: char, c: Into<f64>, e: String) {
|
fn convert_to_string(
|
||||||
|
a: impl Into<f64>,
|
||||||
|
op1: char,
|
||||||
|
b: impl Into<f64>,
|
||||||
|
op2: char,
|
||||||
|
c: impl Into<f64>,
|
||||||
|
e: &'static str,
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue