trion 0.7.2

Trion is an assembler designed to be used with the Raspberry Pico (RP2040) microcontroller.
Documentation
use core::fmt;
use core::ops::BitOr;
use std::error::Error;

use crate::asm::arcob::Arcob;
use crate::asm::Context;
use crate::asm::simplify::{OverflowError, SimplifyError, simplify_raw};
use crate::asm::constant::{Lookup, Realm};
use crate::text::parse::{Argument, ArgumentType};
use crate::text::token::Number;

pub fn evaluate<'l>(arg: &mut Argument<'l>, ctx: &Context<'_>) -> Result<Evaluation<'l>, EvalError>
{
	match arg
	{
		Argument::Constant(..) => Ok(Evaluation::Complete{changed: false}),
		Argument::Identifier(name) =>
		{
			if !ctx.get_instruction_set().is_register(name)
			{
				let realm = if ctx.has_curr_file() {Realm::Local} else {Realm::Global};
				return match ctx.get_constant(name, realm)
				{
					Lookup::NotFound =>
					{
						name.make_arced();
						return Err(EvalError::NoSuchVariable{name: Arcob::Arced(name.to_arc()), realm: Realm::Local});
					},
					Lookup::Deferred => return Ok(Evaluation::Deferred{changed: false, cause: name.clone()}),
					Lookup::Found(val) =>
					{
						*arg = Argument::Constant(Number::Integer(val));
						Ok(Evaluation::Complete{changed: true})
					},
				};
			}
			else {Ok(Evaluation::Complete{changed: false})}
		},
		Argument::String(..) => Ok(Evaluation::Complete{changed: false}),
		Argument::Sequence(args) | Argument::Function{args, ..} =>
		{
			args.iter_mut().try_fold(Evaluation::Complete{changed: false}, |c, a|
			{
				match evaluate(a, ctx)
				{
					Ok(r) => Ok(c | r),
					Err(e) => Err(e),
				}
			})
		},
		_ =>
		{
			let eval = match arg
			{
				Argument::Add{lhs, rhs} | Argument::Subtract{lhs, rhs} | Argument::Multiply{lhs, rhs} | Argument::Divide{lhs, rhs}
					| Argument::Modulo{lhs, rhs} | Argument::BitAnd{lhs, rhs} | Argument::BitOr{lhs, rhs} | Argument::BitXor{lhs, rhs}
					| Argument::LeftShift{lhs, rhs} | Argument::RightShift{lhs, rhs} =>
				{
					evaluate(lhs, ctx)? | evaluate(rhs, ctx)?
				},
				Argument::Negate(inner) | Argument::Not(inner) | Argument::Address(inner) => evaluate(inner, ctx)?,
				_ => unreachable!(),
			};
			Ok(eval | Evaluation::Complete{changed: simplify_raw(arg)?})
		},
	}
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Evaluation<'c>
{
	Complete{changed: bool},
	Deferred{changed: bool, cause: Arcob<'c, str>},
}

impl<'c> Evaluation<'c>
{
	pub fn is_changed(&self) -> bool
	{
		match *self
		{
			Self::Complete{changed} => changed,
			Self::Deferred{changed, ..} => changed,
		}
	}
}

impl<'c> BitOr for Evaluation<'c>
{
	type Output = Self;
	
	fn bitor(self, rhs: Self) -> Self::Output
	{
		let (ch_lhs, dc_lhs) = match self
		{
			Self::Complete{changed} => (changed, None),
			Self::Deferred{changed, cause} => (changed, Some(cause)),
		};
		let (ch_rhs, dc_rhs) = match rhs
		{
			Self::Complete{changed} => (changed, None),
			Self::Deferred{changed, cause} => (changed, Some(cause)),
		};
		match dc_lhs.or(dc_rhs)
		{
			None => Self::Complete{changed: ch_lhs | ch_rhs},
			Some(cause) => Self::Deferred{changed: ch_lhs | ch_rhs, cause},
		}
	}
}

#[derive(Clone, Debug)]
pub enum EvalError
{
	NoSuchVariable{name: Arcob<'static, str>, realm: Realm},
	BadType{kind: ArgumentType, op: ArgumentType},
	Overflow(OverflowError),
}

impl From<SimplifyError> for EvalError
{
	fn from(value: SimplifyError) -> Self
	{
		match value
		{
			SimplifyError::BadType{kind, op} => Self::BadType{kind, op},
			SimplifyError::Overflow(e) => Self::Overflow(e),
		}
	}
}

impl fmt::Display for EvalError
{
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
	{
		match self
		{
			Self::NoSuchVariable{ref name, realm} => write!(f, "no such {realm} constant {:?}", name.as_ref()),
			Self::BadType{kind, op} => write!(f, "{op} not supported for {kind}"),
			Self::Overflow(..) => f.write_str("arithmetic overflow"),
		}
	}
}

impl Error for EvalError
{
	fn source(&self) -> Option<&(dyn Error + 'static)>
	{
		match self
		{
			Self::Overflow(e) => Some(e),
			_ => None,
		}
	}
}