thingymagick
This commit is contained in:
parent
84a4689a76
commit
5ab9cc93ac
|
@ -2,11 +2,11 @@
|
|||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1736012469,
|
||||
"narHash": "sha256-/qlNWm/IEVVH7GfgAIyP6EsVZI6zjAx1cV5zNyrs+rI=",
|
||||
"lastModified": 1739020877,
|
||||
"narHash": "sha256-mIvECo/NNdJJ/bXjNqIh8yeoSjVLAuDuTUzAo7dzs8Y=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d",
|
||||
"rev": "a79cfe0ebd24952b580b1cf08cd906354996d547",
|
||||
"type": "github"
|
||||
},
|
||||
"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 environment;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
fn main() {
|
||||
let mut env = Environment::new();
|
||||
|
||||
let a = Constant::new(69.0).into();
|
||||
let b = Constant::new(420.0).into();
|
||||
let c = Constant::new(360.0).into();
|
||||
let d: NodeEnum = Subtract::new(Add::new(a,b).into(), c).into();
|
||||
|
||||
println!("d_0: {}", d);
|
||||
println!("d_1: {}", d.evaluate(&mut env).unwrap());
|
||||
loop {
|
||||
print!("> ");
|
||||
let _ = stdout().flush();
|
||||
|
||||
let mut line = String::new();
|
||||
let stdin = io::stdin();
|
||||
stdin.lock().read_line(&mut line).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 {
|
||||
left: Box<NodeEnum>,
|
||||
right: Box<NodeEnum>,
|
||||
left: Rc<NodeEnum>,
|
||||
right: Rc<NodeEnum>,
|
||||
}
|
||||
|
||||
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_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)) => {
|
||||
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)),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||
let left_string = if self.left.precedence() <= self.precedence() {
|
||||
format!("({})", self.left.to_string())
|
||||
format!("({})", self.left.as_string(env))
|
||||
} else {
|
||||
self.left.to_string()
|
||||
self.left.as_string(env)
|
||||
};
|
||||
let right_string = if self.right.precedence() <= self.precedence() {
|
||||
format!("({})", self.right.to_string())
|
||||
format!("({})", self.right.as_string(env))
|
||||
} else {
|
||||
self.right.to_string()
|
||||
self.right.as_string(env)
|
||||
};
|
||||
format!("{}+{}", left_string, right_string)
|
||||
}
|
||||
|
@ -41,8 +43,8 @@ impl Node for Add {
|
|||
impl Add {
|
||||
pub fn new(left: NodeEnum, right: NodeEnum) -> Self {
|
||||
Self {
|
||||
left: Box::new(left),
|
||||
right: Box::new(right),
|
||||
left: Rc::new(left),
|
||||
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 {
|
||||
value: f64,
|
||||
}
|
||||
|
||||
impl Node for Constant {
|
||||
fn evaluate(&self, _: &mut super::Environment) -> Result<super::NodeEnum, String> {
|
||||
Ok(self.clone().into())
|
||||
fn evaluate(&self, _: &mut super::Environment) -> Result<Rc<super::NodeEnum>, String> {
|
||||
Ok(Rc::new(self.clone().into()))
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
fn as_string(&self, _env: Option<&Environment>) -> 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 assign::Assign;
|
||||
use call::Call;
|
||||
use constant::Constant;
|
||||
use divide::Divide;
|
||||
use empty::Empty;
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use function::Function;
|
||||
use multiply::Multiply;
|
||||
use node_ref::NodeRef;
|
||||
use subtract::Subtract;
|
||||
use symbol::Symbol;
|
||||
|
||||
use crate::environment::Environment;
|
||||
|
||||
pub mod constant;
|
||||
pub mod add;
|
||||
pub mod constant;
|
||||
pub mod divide;
|
||||
pub mod multiply;
|
||||
pub mod subtract;
|
||||
pub mod symbol;
|
||||
mod node_ref;
|
||||
mod assign;
|
||||
mod empty;
|
||||
mod function;
|
||||
mod call;
|
||||
|
||||
#[enum_dispatch]
|
||||
#[enum_dispatch(Debug, Clone, PartialEq, PartialOrd, ToString)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub enum NodeEnum {
|
||||
Constant(Constant),
|
||||
Add(Add),
|
||||
Subtract(Subtract)
|
||||
Constant,
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
Symbol,
|
||||
// NodeRef, // DEPRECATED, use Symbol
|
||||
Assign,
|
||||
Empty,
|
||||
Function,
|
||||
Call
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Precedence {
|
||||
Empty,
|
||||
Assign,
|
||||
Compare,
|
||||
Equality,
|
||||
Term,
|
||||
Factor,
|
||||
Unary,
|
||||
Call,
|
||||
Primary,
|
||||
}
|
||||
|
||||
pub type Environment = HashMap<u16, NodeEnum>;
|
||||
|
||||
#[enum_dispatch(NodeEnum)]
|
||||
pub trait Node {
|
||||
fn evaluate(&self, _: &mut Environment) -> Result<NodeEnum, String>;
|
||||
fn to_string(&self) -> String;
|
||||
fn evaluate(&self, _: &mut Environment) -> Result<Rc<NodeEnum>, String>;
|
||||
fn as_string(&self, _: Option<&Environment>) -> String;
|
||||
fn precedence(&self) -> Precedence;
|
||||
}
|
||||
|
||||
impl Display for NodeEnum {
|
||||
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 {
|
||||
left: Box<NodeEnum>,
|
||||
right: Box<NodeEnum>,
|
||||
left: Rc<NodeEnum>,
|
||||
right: Rc<NodeEnum>,
|
||||
}
|
||||
|
||||
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_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)) => {
|
||||
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)),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||
let left_string = if self.left.precedence() <= self.precedence() {
|
||||
format!("({})", self.left.to_string())
|
||||
format!("({})", self.left.as_string(env))
|
||||
} else {
|
||||
self.left.to_string()
|
||||
self.left.as_string(env)
|
||||
};
|
||||
let right_string = if self.right.precedence() <= self.precedence() {
|
||||
format!("({})", self.right.to_string())
|
||||
format!("({})", self.right.as_string(env))
|
||||
} else {
|
||||
self.right.to_string()
|
||||
self.right.as_string(env)
|
||||
};
|
||||
format!("{}-{}", left_string, right_string)
|
||||
}
|
||||
|
@ -41,8 +43,8 @@ impl Node for Subtract {
|
|||
impl Subtract {
|
||||
pub fn new(left: NodeEnum, right: NodeEnum) -> Self {
|
||||
Self {
|
||||
left: Box::new(left),
|
||||
right: Box::new(right),
|
||||
left: Rc::new(left),
|
||||
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 {
|
||||
use crate::node::{
|
||||
Environment, Node, NodeEnum, add::Add, constant::Constant, subtract::Subtract,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::{environment, node::{
|
||||
add::Add, constant::Constant, divide::Divide, multiply::Multiply, subtract::Subtract, Node, NodeEnum
|
||||
}};
|
||||
use environment::Environment;
|
||||
|
||||
use test_case::test_case;
|
||||
|
||||
|
@ -17,7 +20,7 @@ mod arithmetic {
|
|||
let b = Constant::new(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!(
|
||||
value == Constant::new(e).into(),
|
||||
|
@ -40,7 +43,7 @@ mod arithmetic {
|
|||
let c = Constant::new(bb).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!(
|
||||
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 both are negative")]
|
||||
#[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 left 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")]
|
||||
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 {
|
||||
use test_case::test_case;
|
||||
|
||||
#[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 {
|
||||
use crate::node::{
|
||||
Environment, Node, NodeEnum, add::Add, constant::Constant, subtract::Subtract,
|
||||
};
|
||||
use test_case::test_case;
|
||||
|
||||
#[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) {
|
||||
#[test_case(30, '+', 60, '-', 20, "(30+60)-20" ; "add and subtract")]
|
||||
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