bean-script 0.2.12

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

use crate::{
	data::Data,
	error::{BeanResult, Error, ErrorSource},
	parser::{Node, PosNode},
	scope::{block_scope::BlockScope, function::Function, ScopeRef},
};

pub fn evaluate_verbose(
	pos_node: &PosNode,
	scope_ref: ScopeRef,
	return_scope: bool,
	access_scope_ref: Option<ScopeRef>,
) -> Result<Data, Error> {
	match &pos_node.node {
		Node::FnCall {
			name,
			parameters,
			body_fn,
		} => {
			let scope = RefCell::borrow(&scope_ref);
			let function = scope.get_function(&name).ok_or_else(|| {
				Error::new(
					&format!("Unknown value or function {}.", name),
					ErrorSource::Line(pos_node.ln),
				)
			})?;
			drop(scope);

			let mut args: Vec<Data> = Vec::new();
			for n in parameters {
				args.push(evaluate(
					n,
					Rc::clone(access_scope_ref.as_ref().unwrap_or(&scope_ref)),
				)?);
			}

			let return_value = function
				.call_from(
					args,
					if let Some(body) = body_fn {
						Some(Function::Custom {
							body: Rc::new(*body.clone()),
							scope_ref: Rc::clone(
								access_scope_ref.as_ref().unwrap_or(&scope_ref),
							),
						})
					} else {
						None
					},
					Rc::clone(&scope_ref),
					access_scope_ref,
				)
				.trace(ErrorSource::Line(pos_node.ln));

			return return_value;
		}
		Node::Scope { body } => {
			let scope = BlockScope::new(Some(Rc::clone(&scope_ref)));
			let scope_ref = Rc::new(RefCell::new(scope));

			for n in body {
				evaluate(n, Rc::clone(&scope_ref) as ScopeRef)?;
				if RefCell::borrow(&scope_ref).did_break() {
					break;
				}
			}

			let scope: &RefCell<BlockScope> = scope_ref.borrow();
			let return_value = scope.borrow().return_value.clone();
			return if return_scope {
				Ok(Data::Scope(scope_ref))
			} else {
				Ok(return_value)
			};
		}
		Node::ParameterBlock { body } => {
			let mut return_value: Data = Data::None;
			for n in body {
				return_value = evaluate(n, Rc::clone(&scope_ref))?;
			}

			return Ok(return_value);
		}
		Node::Program { body } => {
			for n in body {
				evaluate(n, Rc::clone(&scope_ref))?;
			}
			return Ok(Data::None);
		}
		Node::FnAccess { target, call } => {
			let target = evaluate(target, Rc::clone(&scope_ref))?;

			if let Data::Scope(target_scope) = target {
				evaluate_verbose(
					&call,
					Rc::clone(&target_scope),
					false,
					Some(Rc::clone(access_scope_ref.as_ref().unwrap_or(&scope_ref))),
				)
			} else {
				return Err(Error::new(
					&format!(
						"Expected scope for dot operator, but got {}.",
						target.get_type().to_string()
					),
					ErrorSource::Line(pos_node.ln),
				));
			}
		}
		Node::Boolean(v) => Ok(Data::Boolean(*v)),
		Node::Number(v) => Ok(Data::Number(*v)),
		Node::String(v) => Ok(Data::String(v.clone())),
		Node::Name(name) => Ok(Data::Name {
			scope: Rc::clone(&scope_ref),
			name: name.clone(),
		}),
		Node::None => Ok(Data::None),
	}
}

pub fn evaluate(node: &PosNode, scope_ref: ScopeRef) -> Result<Data, Error> {
	evaluate_verbose(node, scope_ref, false, None)
}