bean-script 0.2.12

Simple scripting language for easy use in other projects.
Documentation
use std::{cell::RefCell, hash::Hash, rc::Rc};

use crate::{
	error::{Error, ErrorSource},
	pat_check,
	scope::ScopeRef,
};

pub enum DataType {
	Boolean,
	Number,
	String,
	Name,
	Scope,
	None,
	Or(Box<DataType>, Box<DataType>),
	Any,
}

impl DataType {
	pub fn from_string(string: &String) -> Result<DataType, Error> {
		if string == "boolean" {
			Ok(DataType::Boolean)
		} else if string == "number" {
			Ok(DataType::Number)
		} else if string == "string" {
			Ok(DataType::String)
		} else if string == "name" {
			Ok(DataType::Name)
		} else if string == "scope" {
			Ok(DataType::Scope)
		} else if string == "none" {
			Ok(DataType::None)
		} else if string == "any" {
			Ok(DataType::Any)
		} else {
			Err(Error::new(
				&format!("Invalid type string {}.", string),
				ErrorSource::Internal,
			))
		}
	}

	pub fn to_string(&self) -> String {
		match self {
			DataType::Boolean => String::from("boolean"),
			DataType::Number => String::from("number"),
			DataType::String => String::from("string"),
			DataType::Name => String::from("name"),
			DataType::Scope => String::from("scope"),
			DataType::None => String::from("none"),
			DataType::Any => String::from("any"),
			DataType::Or(a, b) => a.to_string() + " | " + &b.to_string(),
		}
	}

	pub fn matches(&self, data: &Data) -> bool {
		match self {
			DataType::Boolean => pat_check!(Data::Boolean(_) = data),
			DataType::Number => pat_check!(Data::Number(_) = data),
			DataType::String => pat_check!(Data::String(_) = data),
			DataType::Name => pat_check!(Data::Name { .. } = data),
			DataType::Scope => pat_check!(Data::Scope(_) = data),
			DataType::None => pat_check!(Data::None = data),
			DataType::Or(a, b) => a.matches(data) || b.matches(data),
			DataType::Any => true,
		}
	}
}

#[derive(Clone, Debug)]
pub enum Data {
	Boolean(bool),
	Number(f64),
	String(String),
	Name { scope: ScopeRef, name: String },
	Scope(ScopeRef),
	None,
}

impl Data {
	pub fn get_type(&self) -> DataType {
		match self {
			Data::Boolean(_) => DataType::Boolean,
			Data::Number(_) => DataType::Number,
			Data::String(_) => DataType::String,
			Data::Name { .. } => DataType::Name,
			Data::Scope(_) => DataType::Scope,
			Data::None => DataType::None,
		}
	}
}

impl PartialEq for Data {
	fn eq(&self, other: &Self) -> bool {
		match (self, other) {
			(Self::Boolean(l), Self::Boolean(r)) => l == r,
			(Self::Number(l), Self::Number(r)) => l == r,
			(Self::String(l), Self::String(r)) => l == r,
			(Self::None, Self::None) => true,
			(
				Self::Name { scope, name },
				Self::Name {
					scope: r_scope,
					name: r_name,
				},
			) => Rc::ptr_eq(scope, r_scope) && name == r_name,
			(Self::Scope(l), Self::Scope(r)) => Rc::ptr_eq(l, r),
			_ => false,
		}
	}
}

impl Eq for Data {}

impl Hash for Data {
	fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
		core::mem::discriminant(self).hash(state);
	}
}

impl Default for Data {
	fn default() -> Self {
		Data::None
	}
}

impl ToString for Data {
	fn to_string(&self) -> String {
		match self {
			Data::Boolean(v) => {
				if *v {
					String::from("true")
				} else {
					String::from("false")
				}
			}
			Data::Number(v) => v.to_string(),
			Data::String(s) => s.clone(),
			Data::Name { scope: _, name } => format!("<{}>", name),
			Data::Scope(scope) => RefCell::borrow(&scope).to_string(),
			Data::None => String::from("[none]"),
		}
	}
}