muss/mps-interpreter/src/lang/vocabulary/item_ops/remove_variable.rs

97 lines
2.9 KiB
Rust

use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::{assert_name, assert_token, assert_token_raw, check_name};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{MpsItemBlockFactory, MpsItemOp, MpsItemOpFactory};
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct RemoveItemOp {
variable_name: String,
field_name: Option<String>,
}
impl Deref for RemoveItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for RemoveItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
if let Some(field_name) = &self.field_name {
write!(f, "remove {}.{}", self.variable_name, field_name)
} else {
write!(f, "remove {}", self.variable_name)
}
}
}
impl MpsItemOp for RemoveItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
if let Some(field_name) = &self.field_name {
let var = context.variables.get_mut(&self.variable_name)?;
if let MpsType::Item(item) = var {
item.remove_field(field_name);
Ok(MpsType::empty())
} else {
Err(RuntimeMsg(format!(
"Cannot access field `{}` on variable `{}` ({} is not Item)",
field_name, &self.variable_name, var
)))
}
} else {
context.variables.remove(&self.variable_name)?;
Ok(MpsType::empty())
}
}
}
pub struct RemoveItemOpFactory;
impl MpsItemOpFactory<RemoveItemOp> for RemoveItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
(tokens.len() == 2 || tokens.len() == 4) && check_name("remove", &tokens[0])
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
_factory: &MpsItemBlockFactory,
_dict: &MpsLanguageDictionary,
) -> Result<RemoveItemOp, SyntaxError> {
assert_name("remove", tokens)?;
let name = assert_token(
|t| match t {
MpsToken::Name(s) => Some(s),
_ => None,
},
MpsToken::Name("variable_name".into()),
tokens,
)?;
let field_opt;
if tokens.is_empty() {
field_opt = None;
} else {
assert_token_raw(MpsToken::Dot, tokens)?;
field_opt = Some(assert_token(
|t| match t {
MpsToken::Name(s) => Some(s),
_ => None,
},
MpsToken::Name("field_name".into()),
tokens,
)?);
}
Ok(RemoveItemOp {
variable_name: name,
field_name: field_opt,
})
}
}