Create non-iter operations and implement basics for Turing completeness

This commit is contained in:
NGnius (Graham) 2022-02-19 20:17:31 -05:00
parent 72b63b6feb
commit 3a2c2629ca
34 changed files with 2510 additions and 22 deletions

View file

@ -159,10 +159,33 @@ pub(crate) fn standard_vocab(vocabulary: &mut MpsLanguageDictionary) {
.add(crate::lang::vocabulary::filters::field_re_filter()) .add(crate::lang::vocabulary::filters::field_re_filter())
// sorters // sorters
.add(crate::lang::vocabulary::sorters::empty_sort()) .add(crate::lang::vocabulary::sorters::empty_sort())
.add(crate::lang::vocabulary::sorters::shuffle_sort()) // accepts valid field ~(shuffle) .add(crate::lang::vocabulary::sorters::shuffle_sort()) // accepts ~(shuffle)
.add(crate::lang::vocabulary::sorters::field_sort()) .add(crate::lang::vocabulary::sorters::field_sort()) // accepts any ~(something)
.add(crate::lang::vocabulary::sorters::bliss_sort()) .add(crate::lang::vocabulary::sorters::bliss_sort())
.add(crate::lang::vocabulary::sorters::bliss_next_sort()) .add(crate::lang::vocabulary::sorters::bliss_next_sort())
// iter blocks
.add(
crate::lang::MpsItemBlockFactory::new()
.add(crate::lang::vocabulary::item_ops::ConstantItemOpFactory)
.add(crate::lang::vocabulary::item_ops::VariableAssignItemOpFactory)
.add(crate::lang::vocabulary::item_ops::FieldAssignItemOpFactory)
.add(crate::lang::vocabulary::item_ops::VariableDeclareItemOpFactory)
.add(crate::lang::vocabulary::item_ops::InterpolateStringItemOpFactory)
.add(crate::lang::vocabulary::item_ops::BranchItemOpFactory)
//.add(crate::lang::vocabulary::item_ops::IterItemOpFactory)
.add(crate::lang::vocabulary::item_ops::ConstructorItemOpFactory)
.add(crate::lang::vocabulary::item_ops::EmptyItemOpFactory)
.add(crate::lang::vocabulary::item_ops::RemoveItemOpFactory)
.add(crate::lang::vocabulary::item_ops::VariableRetrieveItemOpFactory)
.add(crate::lang::vocabulary::item_ops::NegateItemOpFactory)
.add(crate::lang::vocabulary::item_ops::NotItemOpFactory)
.add(crate::lang::vocabulary::item_ops::CompareItemOpFactory)
.add(crate::lang::vocabulary::item_ops::AddItemOpFactory)
.add(crate::lang::vocabulary::item_ops::SubtractItemOpFactory)
.add(crate::lang::vocabulary::item_ops::OrItemOpFactory)
.add(crate::lang::vocabulary::item_ops::AndItemOpFactory)
.add(crate::lang::vocabulary::item_ops::BracketsItemOpFactory)
)
// functions and misc // functions and misc
// functions don't enforce bracket coherence // functions don't enforce bracket coherence
// -- function().() is valid despite the ).( in between brackets // -- function().() is valid despite the ).( in between brackets

View file

@ -32,6 +32,14 @@ impl MpsItem {
pub fn remove_field(&mut self, name: &str) -> Option<MpsTypePrimitive> { pub fn remove_field(&mut self, name: &str) -> Option<MpsTypePrimitive> {
self.fields.remove(name) self.fields.remove(name)
} }
pub fn iter(&self) -> impl Iterator<Item=&String> {
self.fields.keys()
}
pub fn len(&self) -> usize {
self.fields.len()
}
} }
impl Display for MpsItem { impl Display for MpsItem {

View file

@ -0,0 +1,353 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use std::iter::Iterator;
use std::marker::PhantomData;
use crate::lang::utility::{assert_token_raw, assert_token_raw_back};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{BoxedMpsOpFactory, MpsIteratorItem, MpsOp, PseudoOp};
use crate::lang::{RuntimeError, RuntimeMsg, RuntimeOp, SyntaxError};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
//use crate::MpsItem;
const ITEM_VARIABLE_NAME: &str = "item";
pub trait MpsItemOp: Debug + Display {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg>;
}
pub trait MpsItemOpFactory<T: Deref<Target = dyn MpsItemOp> + 'static> {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool;
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<T, SyntaxError>;
}
pub struct MpsItemOpBoxer<X: MpsItemOpFactory<Y> + 'static, Y: Deref<Target = dyn MpsItemOp> + MpsItemOp + 'static> {
idc: PhantomData<Y>,
factory: X,
}
impl<X: MpsItemOpFactory<Y> + 'static, Y: Deref<Target = dyn MpsItemOp> + MpsItemOp + 'static> MpsItemOpFactory<Box<dyn MpsItemOp>>
for MpsItemOpBoxer<X, Y>
{
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
self.factory.is_item_op(tokens)
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<Box<dyn MpsItemOp>, SyntaxError> {
Ok(Box::new(self.factory.build_item_op(tokens, factory, dict)?))
}
}
#[derive(Debug)]
pub struct MpsItemBlockStatement {
statements: Vec<Box<dyn MpsItemOp>>,
iterable: PseudoOp,
// state
last_item: Option<PseudoOp>,
}
impl Display for MpsItemBlockStatement {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{}.{{", self.iterable)?;
if self.statements.len() > 0 {
write!(f, "\n")?;
}
for statement in self.statements.iter() {
write!(f, "{}\n", statement)?;
}
write!(f, "}}")
}
}
impl std::clone::Clone for MpsItemBlockStatement {
fn clone(&self) -> Self {
Self {
statements: Vec::with_capacity(0),
iterable: self.iterable.clone(),
last_item: None,
}
}
}
impl MpsOp for MpsItemBlockStatement {
fn enter(&mut self, ctx: MpsContext) {
self.iterable.try_real().unwrap().enter(ctx)
}
fn escape(&mut self) -> MpsContext {
self.iterable.try_real().unwrap().escape()
}
fn is_resetable(&self) -> bool {
if let Ok(iter) = self.iterable.try_real_ref() {
iter.is_resetable()
} else {
false
}
}
fn reset(&mut self) -> Result<(), RuntimeError> {
self.iterable.try_real()?.reset()
}
}
impl Iterator for MpsItemBlockStatement {
type Item = MpsIteratorItem;
fn next(&mut self) -> Option<Self::Item> {
let real_op = match self.iterable.try_real() {
Ok(op) => op,
Err(e) => return Some(Err(e)),
};
if let Some(last_item) = self.last_item.as_mut() {
let real_last = match last_item.try_real() {
Ok(op) => op,
Err(e) => return Some(Err(e)),
};
real_last.enter(real_op.escape());
let next_item = real_last.next();
real_op.enter(real_last.escape());
if let Some(item) = next_item {
return Some(item);
} else {
self.last_item = None;
}
}
while let Some(item) = real_op.next() {
if let Err(e) = item {
return Some(Err(e));
}
let item = item.unwrap();
let mut ctx = real_op.escape();
let old_var = replace_item_var(&mut ctx, MpsType::Item(item));
for op in self.statements.iter_mut() {
match op.execute(&mut ctx) {
Ok(_) => {},
Err(e) => {
#[allow(unused_must_use)]
{restore_item_var(&mut ctx, old_var);}
real_op.enter(ctx);
return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self)))));
}
}
}
let item = match restore_item_var(&mut ctx, old_var) {
Ok(item) => item,
Err(e) => {
real_op.enter(ctx);
return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self)))));
}
};
real_op.enter(ctx);
match item {
Some(MpsType::Item(item)) => {
return Some(Ok(item))
},
Some(MpsType::Op(mut op)) => {
op.enter(real_op.escape());
if let Some(item) = op.next() {
real_op.enter(op.escape());
self.last_item = Some(op.into());
return Some(item);
} else {
real_op.enter(op.escape());
}
},
Some(x) => return Some(Err(RuntimeError {
line: 0,
op: PseudoOp::from_printable(self),
msg: format!("Expected `item` like MpsType::Item(MpsItem[...]), got {}", x),
})),
None => {}
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iterable
.try_real_ref()
.map(|x| x.size_hint())
.unwrap_or((0, None))
}
}
pub struct MpsItemBlockFactory {
vocabulary: Vec<Box<dyn MpsItemOpFactory<Box<dyn MpsItemOp>>>>,
}
impl MpsItemBlockFactory {
pub fn add<T: MpsItemOpFactory<Y> + 'static, Y: Deref<Target=dyn MpsItemOp> + MpsItemOp + 'static>(
mut self,
factory: T
) -> Self {
self.vocabulary.push(Box::new(MpsItemOpBoxer {
factory: factory,
idc: PhantomData,
}));
self
}
pub fn try_build_item_statement(
&self,
tokens: &mut VecDeque<MpsToken>,
dict: &MpsLanguageDictionary,
) -> Result<Box<dyn MpsItemOp>, SyntaxError> {
for factory in &self.vocabulary {
if factory.is_item_op(tokens) {
return factory.build_item_op(tokens, self, dict);
}
}
Err(match tokens.pop_front() {
Some(x) => SyntaxError {
line: 0,
token: MpsToken::Name("{any item op}".into()),
got: Some(x),
},
None => SyntaxError {
line: 0,
token: MpsToken::Name("{item op}".into()),
got: None,
},
})
}
pub fn new() -> Self {
Self {
vocabulary: Vec::new(),
}
}
}
impl BoxedMpsOpFactory for MpsItemBlockFactory {
fn is_op_boxed(&self, tokens: &VecDeque<MpsToken>) -> bool {
tokens[tokens.len()-1].is_close_curly()
}
fn build_op_boxed(
&self,
tokens: &mut VecDeque<MpsToken>,
dict: &MpsLanguageDictionary,
) -> Result<Box<dyn MpsOp>, SyntaxError> {
let open_curly_pos = if let Some(pos) = find_last_open_curly(tokens) {
Ok(pos)
} else {
Err(SyntaxError {
line: 0,
token: MpsToken::OpenCurly,
got: None,
})
}?;
let block_tokens = tokens.split_off(open_curly_pos - 1); // . always before {
let inner_op = dict.try_build_statement(tokens)?;
tokens.extend(block_tokens);
assert_token_raw(MpsToken::Dot, tokens)?;
assert_token_raw(MpsToken::OpenCurly, tokens)?;
assert_token_raw_back(MpsToken::CloseCurly, tokens)?;
let mut item_ops = Vec::with_capacity(tokens.len() / 8);
while !tokens.is_empty() {
if let Some(next_comma) = find_next_comma(tokens) {
let end_tokens = tokens.split_off(next_comma);
item_ops.push(self.try_build_item_statement(tokens, dict)?);
tokens.extend(end_tokens);
assert_token_raw(MpsToken::Comma, tokens)?;
} else {
item_ops.push(self.try_build_item_statement(tokens, dict)?);
}
}
Ok(Box::new(MpsItemBlockStatement {
statements: item_ops,
iterable: inner_op.into(),
last_item: None,
}))
}
}
fn replace_item_var(ctx: &mut MpsContext, item: MpsType) -> Option<MpsType> {
// remove existing item variable if exists
let old_var = if ctx.variables.exists(ITEM_VARIABLE_NAME) {
ctx.variables.remove(ITEM_VARIABLE_NAME).ok()
} else {
None
};
ctx.variables.declare(ITEM_VARIABLE_NAME, item).unwrap();
old_var
}
fn restore_item_var(ctx: &mut MpsContext, old_var: Option<MpsType>) -> Result<Option<MpsType>, RuntimeMsg> {
let new_var;
if ctx.variables.exists(ITEM_VARIABLE_NAME) {
new_var = Some(ctx.variables.remove(ITEM_VARIABLE_NAME)?);
} else {
new_var = None;
}
if let Some(old_var) = old_var {
ctx.variables.declare(ITEM_VARIABLE_NAME, old_var)?;
}
Ok(new_var)
}
fn find_last_open_curly(tokens: &VecDeque<MpsToken>) -> Option<usize> {
let mut bracket_depth = 0;
let mut curly_found = false;
for i in (0..tokens.len()).rev() {
let token = &tokens[i];
match token {
MpsToken::OpenCurly => {
if bracket_depth != 0 {
bracket_depth -= 1;
}
},
MpsToken::CloseCurly => {
bracket_depth += 1;
},
MpsToken::Dot => {
if bracket_depth == 0 && curly_found {
return Some(i+1);
}
}
_ => {},
}
if token.is_open_curly() {
curly_found = true;
} else {
curly_found = false;
}
}
None
}
fn find_next_comma(tokens: &VecDeque<MpsToken>) -> Option<usize> {
let mut bracket_depth = 0;
let mut curly_depth = 0;
for i in 0..tokens.len() {
let token = &tokens[i];
if token.is_comma() && bracket_depth == 0 && curly_depth == 0 {
return Some(i);
} else if token.is_open_bracket() {
bracket_depth += 1;
} else if token.is_close_bracket() && bracket_depth != 0 {
bracket_depth -= 1;
} else if token.is_open_curly() {
curly_depth += 1;
} else if token.is_close_curly() && curly_depth != 0 {
curly_depth -= 1;
}
}
None
}

View file

@ -4,6 +4,7 @@ mod error;
mod filter; mod filter;
mod filter_replace; mod filter_replace;
mod function; mod function;
mod iter_block;
mod lookup; mod lookup;
mod operation; mod operation;
mod pseudo_op; mod pseudo_op;
@ -21,6 +22,7 @@ pub use filter::{
}; };
pub use filter_replace::MpsFilterReplaceStatement; pub use filter_replace::MpsFilterReplaceStatement;
pub use function::{MpsFunctionFactory, MpsFunctionStatementFactory}; pub use function::{MpsFunctionFactory, MpsFunctionStatementFactory};
pub use iter_block::{MpsItemBlockFactory, MpsItemOp, MpsItemOpFactory};
pub use lookup::Lookup; pub use lookup::Lookup;
pub use operation::{BoxedMpsOpFactory, MpsIteratorItem, MpsOp, MpsOpFactory, SimpleMpsOpFactory}; pub use operation::{BoxedMpsOpFactory, MpsIteratorItem, MpsOp, MpsOpFactory, SimpleMpsOpFactory};
pub use pseudo_op::PseudoOp; pub use pseudo_op::PseudoOp;

View file

@ -10,6 +10,7 @@ pub enum MpsTypePrimitive {
UInt(u64), UInt(u64),
Float(f64), Float(f64),
Bool(bool), Bool(bool),
Empty,
} }
impl MpsTypePrimitive { impl MpsTypePrimitive {
@ -39,6 +40,7 @@ impl MpsTypePrimitive {
Self::Int(x) => format!("{}", x), Self::Int(x) => format!("{}", x),
Self::Float(x) => format!("{}", x), Self::Float(x) => format!("{}", x),
Self::Bool(x) => format!("{}", x), Self::Bool(x) => format!("{}", x),
Self::Empty => "".to_owned(),
} }
} }
@ -75,6 +77,66 @@ impl MpsTypePrimitive {
Self::String(s) Self::String(s)
} }
} }
// math operations
#[inline]
pub fn try_add(&self, other: &Self) -> Result<Self, String> {
match self {
Self::String(s) => match other {
Self::String(other_s) => Ok(Self::String(s.to_owned() + other_s)),
other => Err(format!("Cannot add {} and {}: incompatible types", self, other)),
},
Self::Int(i) => match other {
Self::Int(other_i) => Ok(Self::Int(i + other_i)),
Self::UInt(u) => Ok(Self::Int(i + *u as i64)),
Self::Float(f) => Ok(Self::Float(*i as f64 + f)),
other => Err(format!("Cannot add {} and {}: incompatible types", self, other)),
},
Self::UInt(u) => match other {
Self::UInt(other_u) => Ok(Self::UInt(u + other_u)),
Self::Int(i) => Ok(Self::UInt(u + *i as u64)),
Self::Float(f) => Ok(Self::Float(*u as f64 + f)),
other => Err(format!("Cannot add {} and {}: incompatible types", self, other)),
},
Self::Float(f) => match other {
Self::Float(other_f) => Ok(Self::Float(f + other_f)),
Self::Int(i) => Ok(Self::Float(f + *i as f64)),
Self::UInt(u) => Ok(Self::Float(f + *u as f64)),
other => Err(format!("Cannot add {} and {}: incompatible types", self, other)),
},
Self::Bool(_) => Err(format!("Cannot add {} and {}: incompatible types", self, other)),
Self::Empty => Err(format!("Cannot add {} and {}: incompatible types", self, other)),
}
}
#[inline]
pub fn try_subtract(&self, other: &Self) -> Result<Self, String> {
match other {
Self::UInt(other_u) => match self {
Self::UInt(u) => Ok(Self::UInt(u - other_u)),
_ => self.try_add(&Self::Int(-(*other_u as i64))),
},
other => self.try_add(&other.try_negate()?),
}
}
#[inline]
pub fn try_negate(&self) -> Result<Self, String> {
match self {
Self::Int(i) => Ok(Self::Int(-*i)),
Self::Float(f) => Ok(Self::Float(-*f)),
_ => Err(format!("Cannot negate {}: incompatible type", self)),
}
}
#[inline]
pub fn try_not(&self) -> Result<Self, String> {
match self {
Self::Bool(b) => Ok(Self::Bool(!*b)),
_ => Err(format!("Cannot apply logical NOT to {}: incompatible type", self)),
}
}
} }
impl Display for MpsTypePrimitive { impl Display for MpsTypePrimitive {
@ -85,6 +147,7 @@ impl Display for MpsTypePrimitive {
Self::UInt(u) => write!(f, "UInt[{}]", *u), Self::UInt(u) => write!(f, "UInt[{}]", *u),
Self::Float(f_) => write!(f, "Float[{}]", *f_), Self::Float(f_) => write!(f, "Float[{}]", *f_),
Self::Bool(b) => write!(f, "Bool[{}]", *b), Self::Bool(b) => write!(f, "Bool[{}]", *b),
Self::Empty => write!(f, "Empty[]"),
} }
} }
} }
@ -140,6 +203,10 @@ impl PartialOrd for MpsTypePrimitive {
} }
_ => None, _ => None,
}, },
Self::Empty => match other {
Self::Empty => Some(std::cmp::Ordering::Equal),
_ => None,
}
} }
} }
} }
@ -155,6 +222,7 @@ impl std::hash::Hash for MpsTypePrimitive {
Self::UInt(u) => u.hash(state), Self::UInt(u) => u.hash(state),
Self::Float(f_) => (*f_ as u64).hash(state), Self::Float(f_) => (*f_ as u64).hash(state),
Self::Bool(b) => b.hash(state), Self::Bool(b) => b.hash(state),
Self::Empty => {},
} }
} }
} }

View file

@ -1,7 +1,7 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter}; use std::fmt::{Debug, Display, Error, Formatter};
use super::utility::assert_comparison_operator; use super::utility::{assert_comparison_operator, comparison_op};
use crate::lang::utility::{assert_token, assert_type, check_is_type}; use crate::lang::utility::{assert_token, assert_type, check_is_type};
use crate::lang::MpsLanguageDictionary; use crate::lang::MpsLanguageDictionary;
use crate::lang::MpsTypePrimitive; use crate::lang::MpsTypePrimitive;
@ -34,19 +34,6 @@ pub enum FieldFilterErrorHandling {
Include, // return Ok(true) when error encountered Include, // return Ok(true) when error encountered
} }
#[inline(always)]
fn comparison_op(c: &[i8; 2]) -> &str {
match c {
[-1, -1] => "<",
[0, 0] => "==",
[1, 1] => ">",
[0, -1] => "<=",
[0, 1] => ">=",
[-1, 1] => "!=",
_ => "??",
}
}
impl Display for FieldFilter { impl Display for FieldFilter {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
let comp_op = comparison_op(&self.comparison); let comp_op = comparison_op(&self.comparison);

View file

@ -55,3 +55,16 @@ pub fn assert_comparison_operator(tokens: &mut VecDeque<MpsToken>) -> Result<[i8
}), }),
} }
} }
#[inline(always)]
pub fn comparison_op(c: &[i8; 2]) -> &str {
match c {
[-1, -1] => "<",
[0, 0] => "==",
[1, 1] => ">",
[0, -1] => "<=",
[0, 1] => ">=",
[-1, 1] => "!=",
_ => "??",
}
}

View file

@ -0,0 +1,90 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::assert_token_raw;
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct AddItemOp {
lhs: Box<dyn MpsItemOp>,
rhs: Box<dyn MpsItemOp>,
}
impl Deref for AddItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for AddItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{} + {}", self.lhs, self.rhs)
}
}
impl MpsItemOp for AddItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
let lhs = self.lhs.execute(context)?;
if let MpsType::Primitive(lhs) = &lhs {
let rhs = self.rhs.execute(context)?;
if let MpsType::Primitive(rhs) = &rhs {
Ok(MpsType::Primitive(lhs.try_add(rhs).map_err(|e| RuntimeMsg(e))?))
} else {
Err(RuntimeMsg(format!("Cannot add right-hand side `{}` ({}): not primitive type", self.rhs, rhs)))
}
} else {
Err(RuntimeMsg(format!("Cannot add left-hand side `{}` ({}): not primitive type", self.lhs, lhs)))
}
}
}
pub struct AddItemOpFactory;
impl MpsItemOpFactory<AddItemOp> for AddItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
if let Some(plus_location) = first_plus(tokens) {
plus_location != 0
} else {
false
}
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<AddItemOp, SyntaxError> {
let plus_location = first_plus(tokens).unwrap();
let mut end_tokens = tokens.split_off(plus_location);
let lhs_op = factory.try_build_item_statement(tokens, dict)?;
assert_token_raw(MpsToken::Plus, &mut end_tokens)?;
let rhs_op = factory.try_build_item_statement(&mut end_tokens, dict)?;
Ok(AddItemOp {
lhs: lhs_op,
rhs: rhs_op,
})
}
}
fn first_plus(tokens: &VecDeque<MpsToken>) -> Option<usize> {
let mut bracket_depth = 0;
for i in 0..tokens.len() {
let token = &tokens[i];
if token.is_plus() && bracket_depth == 0 {
return Some(i);
} else if token.is_open_bracket() {
bracket_depth += 1;
} else if token.is_close_bracket() && bracket_depth != 0 {
bracket_depth -= 1;
}
}
None
}

View file

@ -0,0 +1,36 @@
use std::convert::AsRef;
use std::collections::VecDeque;
use crate::lang::utility::{assert_token_raw, assert_token_raw_back};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
pub struct BracketsItemOpFactory;
impl MpsItemOpFactory<Box<dyn MpsItemOp>> for BracketsItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
tokens.len() >= 2 && tokens[0].is_open_bracket() && tokens[tokens.len()-1].is_close_bracket()
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<Box<dyn MpsItemOp>, SyntaxError> {
assert_token_raw(MpsToken::OpenBracket, tokens)?;
assert_token_raw_back(MpsToken::CloseBracket, tokens)?;
factory.try_build_item_statement(tokens, dict)
}
}
impl MpsItemOp for Box<dyn MpsItemOp> {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
// while this sort of looks like it's (infinitely) recursive, it's actually not
self.as_ref().execute(context)
}
}

View file

@ -0,0 +1,237 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::{assert_token_raw, check_name, assert_name};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory, MpsTypePrimitive};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct BranchItemOp {
condition: Box<dyn MpsItemOp>,
inner_ifs: Vec<Box<dyn MpsItemOp>>,
inner_elses: Vec<Box<dyn MpsItemOp>>,
}
impl Deref for BranchItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for BranchItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "if {} {{", self.condition)?;
if self.inner_ifs.len() > 1 {
write!(f, "\n")?;
}
for i in 0..self.inner_ifs.len() {
write!(f, "{}", self.inner_ifs[i])?;
if i != self.inner_ifs.len() - 1 {
write!(f, ",\n")?;
}
}
if self.inner_ifs.len() > 1 {
write!(f, "\n")?;
}
write!(f, "}} else {{")?;
if self.inner_elses.len() > 1 {
write!(f, "\n")?;
}
for i in 0..self.inner_elses.len() {
write!(f, "{}", self.inner_elses[i])?;
if i != self.inner_elses.len() - 1 {
write!(f, ",\n")?;
}
}
if self.inner_elses.len() > 1 {
write!(f, "\n")?;
}
write!(f, "}}")
}
}
impl MpsItemOp for BranchItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
let condition_val = self.condition.execute(context)?;
if let MpsType::Primitive(MpsTypePrimitive::Bool(condition)) = condition_val {
let mut last_result = None;
if condition {
for op in self.inner_ifs.iter() {
last_result = Some(op.execute(context)?);
}
} else {
for op in self.inner_elses.iter() {
last_result = Some(op.execute(context)?);
}
}
if let Some(result) = last_result {
Ok(result)
} else {
Ok(MpsType::empty())
}
} else {
Err(RuntimeMsg(format!("Cannot use {} ({}) as if branch condition (should be Bool)", self.condition, condition_val)))
}
}
}
pub struct BranchItemOpFactory;
impl MpsItemOpFactory<BranchItemOp> for BranchItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
!tokens.is_empty() && check_name("if", &tokens[0])
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<BranchItemOp, SyntaxError> {
assert_name("if", tokens)?;
// if condition
let condition_op;
if let Some(curly_pos) = next_curly_open_bracket(tokens) {
let end_tokens = tokens.split_off(curly_pos);
condition_op = factory.try_build_item_statement(tokens, dict)?;
tokens.extend(end_tokens);
} else {
return Err(SyntaxError {
line: 0,
token: MpsToken::OpenCurly,
got: tokens.pop_front(),
});
}
// if block
assert_token_raw(MpsToken::OpenCurly, tokens)?;
let next_close_curly;
if let Some(curly_pos) = next_curly_close_bracket(tokens) {
next_close_curly = curly_pos;
} else {
return Err(SyntaxError {
line: 0,
token: MpsToken::CloseCurly,
got: tokens.pop_back(),
});
}
let end_tokens = tokens.split_off(next_close_curly);
let mut inner_if_ops = Vec::new();
while !tokens.is_empty() {
if let Some(next_comma) = find_next_comma(tokens) {
let end_tokens = tokens.split_off(next_comma);
inner_if_ops.push(factory.try_build_item_statement(tokens, dict)?);
tokens.extend(end_tokens);
assert_token_raw(MpsToken::Comma, tokens)?;
} else {
inner_if_ops.push(factory.try_build_item_statement(tokens, dict)?);
}
}
tokens.extend(end_tokens);
assert_token_raw(MpsToken::CloseCurly, tokens)?;
if tokens.is_empty() {
// else block is omitted
Ok(BranchItemOp {
condition: condition_op,
inner_ifs: inner_if_ops,
inner_elses: Vec::with_capacity(0),
})
} else {
// else block
assert_name("else", tokens)?;
assert_token_raw(MpsToken::OpenCurly, tokens)?;
let next_close_curly;
if let Some(curly_pos) = next_curly_close_bracket(tokens) {
next_close_curly = curly_pos;
} else {
return Err(SyntaxError {
line: 0,
token: MpsToken::CloseCurly,
got: tokens.pop_back(),
});
}
let end_tokens = tokens.split_off(next_close_curly);
let mut inner_else_ops = Vec::new();
while !tokens.is_empty() {
if let Some(next_comma) = find_next_comma(tokens) {
let end_tokens = tokens.split_off(next_comma);
inner_else_ops.push(factory.try_build_item_statement(tokens, dict)?);
tokens.extend(end_tokens);
assert_token_raw(MpsToken::Comma, tokens)?;
} else {
inner_else_ops.push(factory.try_build_item_statement(tokens, dict)?);
}
}
tokens.extend(end_tokens);
assert_token_raw(MpsToken::CloseCurly, tokens)?;
Ok(BranchItemOp {
condition: condition_op,
inner_ifs: inner_if_ops,
inner_elses: inner_else_ops,
})
}
}
}
fn next_curly_open_bracket(tokens: &VecDeque<MpsToken>) -> Option<usize> {
let mut bracket_depth = 0;
for i in 0..tokens.len() {
match &tokens[i] {
MpsToken::OpenBracket => bracket_depth += 1,
MpsToken::CloseBracket => if bracket_depth != 0 {
bracket_depth -= 1;
},
MpsToken::OpenCurly => if bracket_depth == 0 {
return Some(i);
},
_ => {},
}
}
None
}
fn next_curly_close_bracket(tokens: &VecDeque<MpsToken>) -> Option<usize> {
let mut bracket_depth = 0;
let mut curly_depth = 0;
for i in 0..tokens.len() {
match &tokens[i] {
MpsToken::OpenBracket => bracket_depth += 1,
MpsToken::CloseBracket => if bracket_depth != 0 {
bracket_depth -= 1;
},
MpsToken::OpenCurly => curly_depth += 1,
MpsToken::CloseCurly => if bracket_depth == 0 && curly_depth == 0 {
return Some(i);
} else if curly_depth != 0 {
curly_depth -= 1;
},
_ => {},
}
}
None
}
fn find_next_comma(tokens: &VecDeque<MpsToken>) -> Option<usize> {
let mut bracket_depth = 0;
let mut curly_depth = 0;
for i in 0..tokens.len() {
let token = &tokens[i];
if token.is_comma() && bracket_depth == 0 && curly_depth == 0 {
return Some(i);
} else if token.is_open_bracket() {
bracket_depth += 1;
} else if token.is_close_bracket() && bracket_depth != 0 {
bracket_depth -= 1;
} else if token.is_open_curly() {
curly_depth += 1;
} else if token.is_close_curly() && curly_depth != 0 {
curly_depth -= 1;
}
}
None
}

View file

@ -0,0 +1,107 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::{MpsLanguageDictionary, MpsTypePrimitive};
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory};
use crate::lang::vocabulary::filters::utility::{assert_comparison_operator, comparison_op};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct CompareItemOp {
comparison: [i8; 2],
lhs: Box<dyn MpsItemOp>,
rhs: Box<dyn MpsItemOp>,
}
impl Deref for CompareItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for CompareItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{} {} {}", self.lhs, comparison_op(&self.comparison), self.rhs)
}
}
impl MpsItemOp for CompareItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
let lhs_val = self.lhs.execute(context)?;
let rhs_val = self.rhs.execute(context)?;
if let MpsType::Primitive(lhs) = lhs_val {
if let MpsType::Primitive(rhs) = rhs_val {
let compare = lhs.compare(&rhs).map_err(|e| RuntimeMsg(e))?;
let mut is_match = false;
for comparator in self.comparison {
if comparator == compare {
is_match = true;
break;
}
}
Ok(MpsType::Primitive(MpsTypePrimitive::Bool(is_match)))
} else {
Err(RuntimeMsg(format!("Cannot compare non-primitive right-hand side {} ({})", self.rhs, rhs_val)))
}
} else {
Err(RuntimeMsg(format!("Cannot compare non-primitive left-hand side {} ({})", self.lhs, lhs_val)))
}
}
}
pub struct CompareItemOpFactory;
impl MpsItemOpFactory<CompareItemOp> for CompareItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
find_first_comparison(tokens).is_some()
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<CompareItemOp, SyntaxError> {
let comparison_loc = find_first_comparison(tokens).unwrap();
let end_tokens = tokens.split_off(comparison_loc);
let lhs_op = factory.try_build_item_statement(tokens, dict)?;
tokens.extend(end_tokens);
let comparison_arr = assert_comparison_operator(tokens)?;
let rhs_op = factory.try_build_item_statement(tokens, dict)?;
Ok(CompareItemOp {
comparison: comparison_arr,
lhs: lhs_op,
rhs: rhs_op,
})
}
}
fn find_first_comparison(tokens: &VecDeque<MpsToken>) -> Option<usize> {
let mut curly_depth = 0;
let mut bracket_depth = 0;
for i in 0..tokens.len() {
match &tokens[i] {
MpsToken::OpenCurly => curly_depth += 1,
MpsToken::CloseCurly => if curly_depth != 0 {
curly_depth -= 1;
},
MpsToken::OpenBracket => bracket_depth += 1,
MpsToken::CloseBracket => if bracket_depth != 0 {
curly_depth -= 1;
},
MpsToken::OpenAngleBracket | MpsToken::CloseAngleBracket => if curly_depth == 0 && bracket_depth == 0 {
return Some(i);
},
MpsToken::Equals | MpsToken::Exclamation => if curly_depth == 0 && bracket_depth == 0 && i+1 != tokens.len() && tokens[i+1].is_equals() {
return Some(i);
}
_ => {}
}
}
None
}

View file

@ -0,0 +1,56 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::{check_is_type, assert_type};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory, MpsTypePrimitive};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct ConstantItemOp {
value: MpsTypePrimitive,
}
impl Deref for ConstantItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for ConstantItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{}", self.value)
}
}
impl MpsItemOp for ConstantItemOp {
fn execute(&self, _context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
Ok(MpsType::Primitive(self.value.clone()))
}
}
pub struct ConstantItemOpFactory;
impl MpsItemOpFactory<ConstantItemOp> for ConstantItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
tokens.len() == 1
&& check_is_type(&tokens[0])
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
_factory: &MpsItemBlockFactory,
_dict: &MpsLanguageDictionary,
) -> Result<ConstantItemOp, SyntaxError> {
let const_value = assert_type(tokens)?;
Ok(ConstantItemOp {
value: const_value,
})
}
}

View file

@ -0,0 +1,131 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::{assert_token_raw, assert_token_raw_back, check_name, assert_name, assert_token};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
use crate::MpsItem;
#[derive(Debug)]
struct FieldAssignment {
name: String,
value: Box<dyn MpsItemOp>,
}
#[derive(Debug)]
pub struct ConstructorItemOp {
fields: Vec<FieldAssignment>,
}
impl Deref for ConstructorItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for ConstructorItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "Item(")?;
if self.fields.len() > 1 {
write!(f, "\n")?;
for i in 0..self.fields.len()-1 {
let field = &self.fields[i];
write!(f, "{}: {}, ", field.name, field.value)?;
}
let field = &self.fields[self.fields.len()-1];
write!(f, "{}: {}", field.name, field.value)?;
} else if !self.fields.is_empty() {
let field = &self.fields[0];
write!(f, "{}: {}", field.name, field.value)?;
}
write!(f, ")")
}
}
impl MpsItemOp for ConstructorItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
let mut result = MpsItem::new();
for field in &self.fields {
let value = field.value.execute(context)?;
if let MpsType::Primitive(value) = value {
result.set_field(&field.name, value);
} else {
return Err(RuntimeMsg(format!("Cannot assign non-primitive {} to Item field `{}`", value, &field.name)));
}
}
Ok(MpsType::Item(result))
}
}
pub struct ConstructorItemOpFactory;
impl MpsItemOpFactory<ConstructorItemOp> for ConstructorItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
tokens.len() > 2
&& check_name("Item", &tokens[0])
&& tokens[1].is_open_bracket()
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<ConstructorItemOp, SyntaxError> {
assert_name("Item", tokens)?;
assert_token_raw(MpsToken::OpenBracket, tokens)?;
assert_token_raw_back(MpsToken::CloseBracket, tokens)?;
let mut field_descriptors = Vec::new();
while !tokens.is_empty() {
let field_name = assert_token(|t| match t {
MpsToken::Name(n) => Some(n),
_ => None,
}, MpsToken::Name("field_name".into()), tokens)?;
assert_token_raw(MpsToken::Equals, tokens)?;
let field_val;
if let Some(comma_pos) = find_next_comma(tokens) {
let end_tokens = tokens.split_off(comma_pos);
field_val = factory.try_build_item_statement(tokens, dict)?;
tokens.extend(end_tokens);
assert_token_raw(MpsToken::Comma, tokens)?;
} else {
field_val = factory.try_build_item_statement(tokens, dict)?;
}
field_descriptors.push(
FieldAssignment {
name: field_name,
value: field_val,
}
);
}
Ok(ConstructorItemOp {
fields: field_descriptors,
})
}
}
fn find_next_comma(tokens: &VecDeque<MpsToken>) -> Option<usize> {
let mut bracket_depth = 0;
let mut curly_depth = 0;
for i in 0..tokens.len() {
let token = &tokens[i];
if token.is_comma() && bracket_depth == 0 && curly_depth == 0 {
return Some(i);
} else if token.is_open_bracket() {
bracket_depth += 1;
} else if token.is_close_bracket() && bracket_depth != 0 {
bracket_depth -= 1;
} else if token.is_open_curly() {
curly_depth += 1;
} else if token.is_close_curly() && curly_depth != 0 {
curly_depth -= 1;
}
}
None
}

View file

@ -0,0 +1,56 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::{assert_token_raw, check_name, assert_name};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct EmptyItemOp;
impl Deref for EmptyItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for EmptyItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "empty()")
}
}
impl MpsItemOp for EmptyItemOp {
fn execute(&self, _context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
Ok(MpsType::empty())
}
}
pub struct EmptyItemOpFactory;
impl MpsItemOpFactory<EmptyItemOp> for EmptyItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
tokens.len() == 3
&& check_name("empty", &tokens[0])
&& tokens[1].is_open_bracket()
&& tokens[2].is_close_bracket()
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
_factory: &MpsItemBlockFactory,
_dict: &MpsLanguageDictionary,
) -> Result<EmptyItemOp, SyntaxError> {
assert_name("empty", tokens)?;
assert_token_raw(MpsToken::OpenBracket, tokens)?;
assert_token_raw(MpsToken::CloseBracket, tokens)?;
Ok(EmptyItemOp)
}
}

View file

@ -0,0 +1,87 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::{assert_token_raw, assert_token};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct FieldAssignItemOp {
variable_name: String,
field_name: String,
inner: Box<dyn MpsItemOp>,
}
impl Deref for FieldAssignItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for FieldAssignItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{}.{} = {}", &self.variable_name, &self.field_name, &self.inner)
}
}
impl MpsItemOp for FieldAssignItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
let mps_type = self.inner.execute(context)?;
let var = context.variables.get_mut(&self.variable_name)?;
if let MpsType::Item(var) = var {
if let MpsType::Primitive(val) = mps_type {
var.set_field(&self.field_name, val);
Ok(MpsType::empty())
} else {
Err(RuntimeMsg(format!("Cannot assign non-primitive {} to variable field `{}.{}`", mps_type, &self.variable_name, &self.field_name)))
}
} else {
Err(RuntimeMsg(format!("Cannot access field `{}` on variable `{}` ({} is not Item)", &self.field_name, &self.variable_name, var)))
}
}
}
pub struct FieldAssignItemOpFactory;
impl MpsItemOpFactory<FieldAssignItemOp> for FieldAssignItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
(tokens.len() > 4 && tokens[0].is_name() && tokens[1].is_dot() && tokens[2].is_name() && tokens[3].is_equals())
||
(tokens.len() > 3 && tokens[0].is_dot() && tokens[1].is_name() && tokens[2].is_equals())
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<FieldAssignItemOp, SyntaxError> {
let var_name;
if tokens[0].is_dot() {
var_name = "item".to_string();
} else {
var_name = assert_token(|t| match t {
MpsToken::Name(s) => Some(s),
_ => None,
}, MpsToken::Name("variable_name".into()), tokens)?
}
assert_token_raw(MpsToken::Dot, tokens)?;
let f_name = assert_token(|t| match t {
MpsToken::Name(s) => Some(s),
_ => None,
}, MpsToken::Name("field_name".into()), tokens)?;
assert_token_raw(MpsToken::Equals, tokens)?;
let inner_op = factory.try_build_item_statement(tokens, dict)?;
Ok(FieldAssignItemOp {
variable_name: var_name,
field_name: f_name,
inner: inner_op,
})
}
}

View file

@ -0,0 +1,57 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::{check_name, assert_name};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory, MpsOp};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct IterItemOp {
inner: Box<dyn MpsOp>,
}
impl Deref for IterItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for IterItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "iter {}", self.inner)
}
}
impl MpsItemOp for IterItemOp {
fn execute(&self, _context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
Ok(MpsType::Op(self.inner.duplicate().into()))
}
}
pub struct IterItemOpFactory;
impl MpsItemOpFactory<IterItemOp> for IterItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
!tokens.is_empty()
&& check_name("iter", &tokens[0])
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
_factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<IterItemOp, SyntaxError> {
assert_name("iter", tokens)?;
let inner_op = dict.try_build_statement(tokens)?;
Ok(IterItemOp {
inner: inner_op,
})
}
}

View file

@ -0,0 +1,95 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::assert_token_raw;
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory, MpsTypePrimitive};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct AndItemOp {
lhs: Box<dyn MpsItemOp>,
rhs: Box<dyn MpsItemOp>,
}
impl Deref for AndItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for AndItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{} && {}", self.lhs, self.rhs)
}
}
impl MpsItemOp for AndItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
let lhs = self.lhs.execute(context)?;
if let MpsType::Primitive(MpsTypePrimitive::Bool(lhs)) = lhs {
if !lhs {
// short-circuit
return Ok(MpsType::Primitive(MpsTypePrimitive::Bool(false)));
}
let rhs = self.rhs.execute(context)?;
if let MpsType::Primitive(MpsTypePrimitive::Bool(rhs)) = rhs {
Ok(MpsType::Primitive(MpsTypePrimitive::Bool(rhs)))
} else {
Err(RuntimeMsg(format!("Cannot apply logical AND to right-hand side of `{}` ({}): not Bool type", self.rhs, rhs)))
}
} else {
Err(RuntimeMsg(format!("Cannot apply logical AND to left-hand side of `{}` ({}): not Bool type", self.lhs, lhs)))
}
}
}
pub struct AndItemOpFactory;
impl MpsItemOpFactory<AndItemOp> for AndItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
if let Some(and_location) = first_and(tokens) {
and_location != 0
} else {
false
}
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<AndItemOp, SyntaxError> {
let and_location = first_and(tokens).unwrap();
let mut end_tokens = tokens.split_off(and_location);
let lhs_op = factory.try_build_item_statement(tokens, dict)?;
assert_token_raw(MpsToken::Ampersand, &mut end_tokens)?;
assert_token_raw(MpsToken::Ampersand, &mut end_tokens)?;
let rhs_op = factory.try_build_item_statement(&mut end_tokens, dict)?;
Ok(AndItemOp {
lhs: lhs_op,
rhs: rhs_op,
})
}
}
fn first_and(tokens: &VecDeque<MpsToken>) -> Option<usize> {
let mut bracket_depth = 0;
for i in 0..tokens.len()-1 {
let token = &tokens[i];
if token.is_ampersand() && bracket_depth == 0 && tokens[i+1].is_ampersand() {
return Some(i);
} else if token.is_open_bracket() {
bracket_depth += 1;
} else if token.is_close_bracket() && bracket_depth != 0 {
bracket_depth -= 1;
}
}
None
}

View file

@ -0,0 +1,95 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::assert_token_raw;
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory, MpsTypePrimitive};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct OrItemOp {
lhs: Box<dyn MpsItemOp>,
rhs: Box<dyn MpsItemOp>,
}
impl Deref for OrItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for OrItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{} || {}", self.lhs, self.rhs)
}
}
impl MpsItemOp for OrItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
let lhs = self.lhs.execute(context)?;
if let MpsType::Primitive(MpsTypePrimitive::Bool(lhs)) = lhs {
if lhs {
// short-circuit
return Ok(MpsType::Primitive(MpsTypePrimitive::Bool(true)));
}
let rhs = self.rhs.execute(context)?;
if let MpsType::Primitive(MpsTypePrimitive::Bool(rhs)) = rhs {
Ok(MpsType::Primitive(MpsTypePrimitive::Bool(rhs)))
} else {
Err(RuntimeMsg(format!("Cannot apply logical OR to right-hand side of `{}` ({}): not Bool type", self.rhs, rhs)))
}
} else {
Err(RuntimeMsg(format!("Cannot apply logical OR to left-hand side of `{}` ({}): not Bool type", self.lhs, lhs)))
}
}
}
pub struct OrItemOpFactory;
impl MpsItemOpFactory<OrItemOp> for OrItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
if let Some(or_location) = first_or(tokens) {
or_location != 0
} else {
false
}
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<OrItemOp, SyntaxError> {
let or_location = first_or(tokens).unwrap();
let mut end_tokens = tokens.split_off(or_location);
let lhs_op = factory.try_build_item_statement(tokens, dict)?;
assert_token_raw(MpsToken::Pipe, &mut end_tokens)?;
assert_token_raw(MpsToken::Pipe, &mut end_tokens)?;
let rhs_op = factory.try_build_item_statement(&mut end_tokens, dict)?;
Ok(OrItemOp {
lhs: lhs_op,
rhs: rhs_op,
})
}
}
fn first_or(tokens: &VecDeque<MpsToken>) -> Option<usize> {
let mut bracket_depth = 0;
for i in 0..tokens.len()-1 {
let token = &tokens[i];
if token.is_pipe() && bracket_depth == 0 && tokens[i+1].is_pipe() {
return Some(i);
} else if token.is_open_bracket() {
bracket_depth += 1;
} else if token.is_close_bracket() && bracket_depth != 0 {
bracket_depth -= 1;
}
}
None
}

View file

@ -0,0 +1,39 @@
mod add;
mod brackets;
mod branch;
mod compare;
mod constant;
mod constructor;
mod empty;
mod field_assign;
//mod iter_op;
mod logical_and;
mod logical_or;
mod negate;
mod not;
mod remove_variable;
mod retrieve_variable;
mod string_interpolate;
mod subtract;
mod variable_assign;
mod variable_declare;
pub use add::{AddItemOpFactory, AddItemOp};
pub use brackets::BracketsItemOpFactory;
pub use branch::{BranchItemOpFactory, BranchItemOp};
pub use constant::{ConstantItemOpFactory, ConstantItemOp};
pub use compare::{CompareItemOpFactory, CompareItemOp};
pub use constructor::{ConstructorItemOpFactory, ConstructorItemOp};
pub use empty::{EmptyItemOpFactory, EmptyItemOp};
pub use field_assign::{FieldAssignItemOpFactory, FieldAssignItemOp};
//pub use iter_op::{IterItemOpFactory, IterItemOp};
pub use logical_and::{AndItemOpFactory, AndItemOp};
pub use logical_or::{OrItemOpFactory, OrItemOp};
pub use negate::{NegateItemOpFactory, NegateItemOp};
pub use not::{NotItemOpFactory, NotItemOp};
pub use remove_variable::{RemoveItemOpFactory, RemoveItemOp};
pub use retrieve_variable::{VariableRetrieveItemOpFactory, VariableRetrieveItemOp};
pub use string_interpolate::{InterpolateStringItemOpFactory, InterpolateStringItemOp};
pub use subtract::{SubtractItemOpFactory, SubtractItemOp};
pub use variable_assign::{VariableAssignItemOpFactory, VariableAssignItemOp};
pub use variable_declare::{VariableDeclareItemOpFactory, VariableDeclareItemOp};

View file

@ -0,0 +1,61 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::assert_token_raw;
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct NegateItemOp {
rhs: Box<dyn MpsItemOp>,
}
impl Deref for NegateItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for NegateItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "- {}", self.rhs)
}
}
impl MpsItemOp for NegateItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
let rhs = self.rhs.execute(context)?;
if let MpsType::Primitive(rhs) = &rhs {
Ok(MpsType::Primitive(rhs.try_negate().map_err(|e| RuntimeMsg(e))?))
} else {
Err(RuntimeMsg(format!("Cannot negate `{}` ({}): not primitive type", self.rhs, rhs)))
}
}
}
pub struct NegateItemOpFactory;
impl MpsItemOpFactory<NegateItemOp> for NegateItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
tokens.len() >= 2 && tokens[0].is_minus()
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<NegateItemOp, SyntaxError> {
assert_token_raw(MpsToken::Minus, tokens)?;
let rhs_op = factory.try_build_item_statement(tokens, dict)?;
Ok(NegateItemOp {
rhs: rhs_op,
})
}
}

View file

@ -0,0 +1,61 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::assert_token_raw;
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct NotItemOp {
rhs: Box<dyn MpsItemOp>,
}
impl Deref for NotItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for NotItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "! {}", self.rhs)
}
}
impl MpsItemOp for NotItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
let rhs = self.rhs.execute(context)?;
if let MpsType::Primitive(rhs) = &rhs {
Ok(MpsType::Primitive(rhs.try_not().map_err(|e| RuntimeMsg(e))?))
} else {
Err(RuntimeMsg(format!("Cannot apply logical NOT to `{}` ({}): not primitive type", self.rhs, rhs)))
}
}
}
pub struct NotItemOpFactory;
impl MpsItemOpFactory<NotItemOp> for NotItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
tokens.len() >= 2 && tokens[0].is_exclamation()
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<NotItemOp, SyntaxError> {
assert_token_raw(MpsToken::Exclamation, tokens)?;
let rhs_op = factory.try_build_item_statement(tokens, dict)?;
Ok(NotItemOp {
rhs: rhs_op,
})
}
}

View file

@ -0,0 +1,86 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::{assert_token, check_name, assert_name, assert_token_raw};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory};
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,
})
}
}

View file

@ -0,0 +1,93 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::{assert_token_raw, assert_token};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct VariableRetrieveItemOp {
variable_name: String,
field_name: Option<String>,
}
impl Deref for VariableRetrieveItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for VariableRetrieveItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
if let Some(field) = &self.field_name {
write!(f, "{}.{}", &self.variable_name, field)
} else {
write!(f, "{}", &self.variable_name)
}
}
}
impl MpsItemOp for VariableRetrieveItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
let var = context.variables.get(&self.variable_name)?;
if let Some(field_name) = &self.field_name {
if let MpsType::Item(item) = var {
Ok(match item.field(field_name) {
Some(val) => MpsType::Primitive(val.clone()),
None => MpsType::empty(),
})
} else {
Err(RuntimeMsg(format!("Cannot access field `{}` on variable `{}` ({} is not Item)", field_name, self.variable_name, var)))
}
} else {
match var {
MpsType::Op(op) => Err(RuntimeMsg(format!("Cannot clone op-type `{}` variable `{}`", op, self.variable_name))),
MpsType::Primitive(x) => Ok(MpsType::Primitive(x.clone())),
MpsType::Item(item) => Ok(MpsType::Item(item.clone()))
}
}
}
}
pub struct VariableRetrieveItemOpFactory;
impl MpsItemOpFactory<VariableRetrieveItemOp> for VariableRetrieveItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
(tokens.len() == 1 && tokens[0].is_name())
||
(tokens.len() == 3 && tokens[0].is_name() && tokens[1].is_dot() && tokens[2].is_name())
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
_factory: &MpsItemBlockFactory,
_dict: &MpsLanguageDictionary,
) -> Result<VariableRetrieveItemOp, SyntaxError> {
let var_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(VariableRetrieveItemOp {
variable_name: var_name,
field_name: field_opt,
})
}
}

View file

@ -0,0 +1,89 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::{assert_token_raw, assert_token};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory, MpsTypePrimitive};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct InterpolateStringItemOp {
format: String,
inner_op: Box<dyn MpsItemOp>,
}
impl Deref for InterpolateStringItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for InterpolateStringItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "~ `{}` {}", &self.format, self.inner_op)
}
}
impl MpsItemOp for InterpolateStringItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
let inner_val = self.inner_op.execute(context)?;
match inner_val {
MpsType::Primitive(val) => {
let result = self.format.replace("{}", &val.as_str());
Ok(MpsType::Primitive(MpsTypePrimitive::String(result)))
},
MpsType::Item(item) => {
let mut result;
if item.len() == 0 {
result = self.format.clone();
} else {
let mut iter = item.iter();
let field1 = iter.next().unwrap();
result = self.format.replace(&format!("{{{}}}", field1), &item.field(field1).unwrap().as_str());
for field in iter {
result = result.replace(&format!("{{{}}}", field), &item.field(field).unwrap().as_str());
}
}
Ok(MpsType::Primitive(MpsTypePrimitive::String(result)))
},
MpsType::Op(op) => {
let result = self.format.replace("{}", &format!("{}", op));
Ok(MpsType::Primitive(MpsTypePrimitive::String(result)))
},
//val => Err(RuntimeMsg(format!("Cannot insert {} ({}) into format string", self.inner_op, val)))
}
//Ok(MpsType::empty())
}
}
pub struct InterpolateStringItemOpFactory;
impl MpsItemOpFactory<InterpolateStringItemOp> for InterpolateStringItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
!tokens.is_empty()
&& tokens[0].is_tilde()
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<InterpolateStringItemOp, SyntaxError> {
assert_token_raw(MpsToken::Tilde, tokens)?;
let format_str = assert_token(|t| match t {
MpsToken::Literal(s) => Some(s),
_ => None,
}, MpsToken::Literal("format_string".into()), tokens)?;
let inner = factory.try_build_item_statement(tokens, dict)?;
Ok(InterpolateStringItemOp {
format: format_str,
inner_op: inner
})
}
}

View file

@ -0,0 +1,90 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::assert_token_raw;
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct SubtractItemOp {
lhs: Box<dyn MpsItemOp>,
rhs: Box<dyn MpsItemOp>,
}
impl Deref for SubtractItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for SubtractItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{} - {}", self.lhs, self.rhs)
}
}
impl MpsItemOp for SubtractItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
let lhs = self.lhs.execute(context)?;
if let MpsType::Primitive(lhs) = &lhs {
let rhs = self.rhs.execute(context)?;
if let MpsType::Primitive(rhs) = &rhs {
Ok(MpsType::Primitive(lhs.try_subtract(rhs).map_err(|e| RuntimeMsg(e))?))
} else {
Err(RuntimeMsg(format!("Cannot subtract right-hand side `{}` ({}): not primitive type", self.rhs, rhs)))
}
} else {
Err(RuntimeMsg(format!("Cannot subtract left-hand side `{}` ({}): not primitive type", self.lhs, lhs)))
}
}
}
pub struct SubtractItemOpFactory;
impl MpsItemOpFactory<SubtractItemOp> for SubtractItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
if let Some(minus_location) = first_minus(tokens) {
minus_location != 0
} else {
false
}
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<SubtractItemOp, SyntaxError> {
let minus_location = first_minus(tokens).unwrap();
let mut end_tokens = tokens.split_off(minus_location);
let lhs_op = factory.try_build_item_statement(tokens, dict)?;
assert_token_raw(MpsToken::Minus, &mut end_tokens)?;
let rhs_op = factory.try_build_item_statement(&mut end_tokens, dict)?;
Ok(SubtractItemOp {
lhs: lhs_op,
rhs: rhs_op,
})
}
}
fn first_minus(tokens: &VecDeque<MpsToken>) -> Option<usize> {
let mut bracket_depth = 0;
for i in 0..tokens.len() {
let token = &tokens[i];
if token.is_minus() && bracket_depth == 0 {
return Some(i);
} else if token.is_open_bracket() {
bracket_depth += 1;
} else if token.is_close_bracket() && bracket_depth != 0 {
bracket_depth -= 1;
}
}
None
}

View file

@ -0,0 +1,64 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::{assert_token_raw, assert_token};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct VariableAssignItemOp {
variable_name: String,
inner: Box<dyn MpsItemOp>,
}
impl Deref for VariableAssignItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for VariableAssignItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{} = {}", &self.variable_name, &self.inner)
}
}
impl MpsItemOp for VariableAssignItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
let mps_type = self.inner.execute(context)?;
context.variables.assign(&self.variable_name, mps_type)?;
Ok(MpsType::empty())
}
}
pub struct VariableAssignItemOpFactory;
impl MpsItemOpFactory<VariableAssignItemOp> for VariableAssignItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
tokens.len() > 2 && tokens[0].is_name() && tokens[1].is_equals()
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<VariableAssignItemOp, SyntaxError> {
let var_name = assert_token(|t| match t {
MpsToken::Name(s) => Some(s),
_ => None,
}, MpsToken::Name("variable_name".into()), tokens)?;
assert_token_raw(MpsToken::Equals, tokens)?;
let inner_op = factory.try_build_item_statement(tokens, dict)?;
Ok(VariableAssignItemOp {
variable_name: var_name,
inner: inner_op,
})
}
}

View file

@ -0,0 +1,83 @@
use core::ops::Deref;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::{assert_token_raw, assert_token};
use crate::lang::MpsLanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{MpsItemOp, MpsItemOpFactory, MpsItemBlockFactory};
use crate::processing::general::MpsType;
use crate::tokens::MpsToken;
use crate::MpsContext;
#[derive(Debug)]
pub struct VariableDeclareItemOp {
variable_name: String,
inner: Option<Box<dyn MpsItemOp>>,
}
impl Deref for VariableDeclareItemOp {
type Target = dyn MpsItemOp;
fn deref(&self) -> &Self::Target {
self
}
}
impl Display for VariableDeclareItemOp {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
if let Some(inner) = &self.inner {
write!(f, "let {} = {}", &self.variable_name, inner)
} else {
write!(f, "let {}", &self.variable_name)
}
}
}
impl MpsItemOp for VariableDeclareItemOp {
fn execute(&self, context: &mut MpsContext) -> Result<MpsType, RuntimeMsg> {
if let Some(inner) = &self.inner {
let mps_type = inner.execute(context)?;
if !context.variables.exists(&self.variable_name) {
context.variables.declare(&self.variable_name, mps_type)?;
}
} else {
if !context.variables.exists(&self.variable_name) {
context.variables.declare(&self.variable_name, MpsType::empty())?;
}
}
Ok(MpsType::empty())
}
}
pub struct VariableDeclareItemOpFactory;
impl MpsItemOpFactory<VariableDeclareItemOp> for VariableDeclareItemOpFactory {
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
tokens.len() > 2 && tokens[0].is_let() && tokens[1].is_name()
}
fn build_item_op(
&self,
tokens: &mut VecDeque<MpsToken>,
factory: &MpsItemBlockFactory,
dict: &MpsLanguageDictionary,
) -> Result<VariableDeclareItemOp, SyntaxError> {
assert_token_raw(MpsToken::Let, tokens)?;
//assert_name("let", tokens)?;
let var_name = assert_token(|t| match t {
MpsToken::Name(s) => Some(s),
_ => None,
}, MpsToken::Name("variable_name".into()), tokens)?;
let inner_op: Option<Box<dyn MpsItemOp>>;
if !tokens.is_empty() {
assert_token_raw(MpsToken::Equals, tokens)?;
inner_op = Some(factory.try_build_item_statement(tokens, dict)?);
} else {
inner_op = None;
}
Ok(VariableDeclareItemOp {
variable_name: var_name,
inner: inner_op,
})
}
}

View file

@ -23,4 +23,5 @@ pub use union::{union_function_factory, UnionStatementFactory};
pub use variable_assign::{AssignStatement, AssignStatementFactory}; pub use variable_assign::{AssignStatement, AssignStatementFactory};
pub mod filters; pub mod filters;
pub mod item_ops;
pub mod sorters; pub mod sorters;

View file

@ -20,6 +20,8 @@ pub trait MpsMusicAnalyzer: Debug {
fn prepare_item(&mut self, item: &MpsItem) -> Result<(), RuntimeMsg>; fn prepare_item(&mut self, item: &MpsItem) -> Result<(), RuntimeMsg>;
fn get_distance(&mut self, from: &MpsItem, to: &MpsItem) -> Result<f64, RuntimeMsg>; fn get_distance(&mut self, from: &MpsItem, to: &MpsItem) -> Result<f64, RuntimeMsg>;
fn clear_cache(&mut self) -> Result<(), RuntimeMsg>;
} }
#[cfg(feature = "bliss-audio")] #[cfg(feature = "bliss-audio")]
@ -108,6 +110,12 @@ impl MpsMusicAnalyzer for MpsDefaultAnalyzer {
} }
Err(RuntimeMsg("Channel closed without response: internal error".to_owned())) Err(RuntimeMsg("Channel closed without response: internal error".to_owned()))
} }
fn clear_cache(&mut self) -> Result<(), RuntimeMsg> {
self.requests.send(
RequestType::Clear {}
).map_err(|e| RuntimeMsg(format!("Channel send error: {}", e)))
}
} }
#[cfg(not(feature = "bliss-audio"))] #[cfg(not(feature = "bliss-audio"))]
@ -140,6 +148,7 @@ enum RequestType {
path: String, path: String,
ack: bool, ack: bool,
}, },
Clear {},
//End {} //End {}
} }
@ -383,6 +392,10 @@ impl CacheThread {
break 'outer; break 'outer;
} }
}, },
RequestType::Clear{} => {
self.distance_cache.clear();
self.song_cache.clear();
}
} }
} }
} }

View file

@ -5,11 +5,13 @@ use std::collections::HashMap;
use crate::lang::MpsOp; use crate::lang::MpsOp;
use crate::lang::MpsTypePrimitive; use crate::lang::MpsTypePrimitive;
use crate::lang::RuntimeMsg; use crate::lang::RuntimeMsg;
use crate::MpsItem;
#[derive(Debug)] #[derive(Debug)]
pub enum MpsType { pub enum MpsType {
Op(Box<dyn MpsOp>), Op(Box<dyn MpsOp>),
Primitive(MpsTypePrimitive), Primitive(MpsTypePrimitive),
Item(MpsItem),
} }
impl Display for MpsType { impl Display for MpsType {
@ -17,10 +19,18 @@ impl Display for MpsType {
match self { match self {
Self::Op(op) => write!(f, "Op({})", op), Self::Op(op) => write!(f, "Op({})", op),
Self::Primitive(p) => write!(f, "{}", p), Self::Primitive(p) => write!(f, "{}", p),
Self::Item(item) => write!(f, "{}", item),
} }
} }
} }
impl MpsType {
#[inline(always)]
pub const fn empty() -> Self {
MpsType::Primitive(MpsTypePrimitive::Empty)
}
}
pub trait MpsVariableStorer: Debug { pub trait MpsVariableStorer: Debug {
fn get(&self, name: &str) -> Result<&'_ MpsType, RuntimeMsg> { fn get(&self, name: &str) -> Result<&'_ MpsType, RuntimeMsg> {
match self.get_opt(name) { match self.get_opt(name) {

View file

@ -21,6 +21,10 @@ pub enum MpsToken {
Ampersand, Ampersand,
Colon, Colon,
Tilde, Tilde,
OpenCurly,
CloseCurly,
Plus,
Minus,
} }
impl MpsToken { impl MpsToken {
@ -42,6 +46,10 @@ impl MpsToken {
"&" => Ok(Self::Ampersand), "&" => Ok(Self::Ampersand),
":" => Ok(Self::Colon), ":" => Ok(Self::Colon),
"~" => Ok(Self::Tilde), "~" => Ok(Self::Tilde),
"{" => Ok(Self::OpenCurly),
"}" => Ok(Self::CloseCurly),
"+" => Ok(Self::Plus),
"-" => Ok(Self::Minus),
_ => { _ => {
// name validation // name validation
let mut ok = true; let mut ok = true;
@ -194,6 +202,34 @@ impl MpsToken {
_ => false, _ => false,
} }
} }
pub fn is_open_curly(&self) -> bool {
match self {
Self::OpenCurly => true,
_ => false,
}
}
pub fn is_close_curly(&self) -> bool {
match self {
Self::CloseCurly => true,
_ => false,
}
}
pub fn is_plus(&self) -> bool {
match self {
Self::Plus => true,
_ => false,
}
}
pub fn is_minus(&self) -> bool {
match self {
Self::Minus => true,
_ => false,
}
}
} }
impl Display for MpsToken { impl Display for MpsToken {
@ -218,6 +254,10 @@ impl Display for MpsToken {
Self::Ampersand => write!(f, "&"), Self::Ampersand => write!(f, "&"),
Self::Colon => write!(f, ":"), Self::Colon => write!(f, ":"),
Self::Tilde => write!(f, "~"), Self::Tilde => write!(f, "~"),
Self::OpenCurly => write!(f, "{{"),
Self::CloseCurly => write!(f, "}}"),
Self::Plus => write!(f, "+"),
Self::Minus => write!(f, "-"),
} }
} }
} }

View file

@ -261,7 +261,7 @@ impl ReaderStateMachine {
'\n' | '\r' | '\t' | ' ' => Self::EndToken {}, '\n' | '\r' | '\t' | ' ' => Self::EndToken {},
';' => Self::EndStatement {}, ';' => Self::EndStatement {},
'\0' => Self::EndOfFile {}, '\0' => Self::EndOfFile {},
'(' | ')' | ',' | '=' | '<' | '>' | '.' | '!' | '?' | '|' | ':' => { '(' | ')' | ',' | '=' | '<' | '>' | '.' | '!' | '?' | '|' | '&' | ':' | '{' | '}' | '+' | '-' | '~' => {
Self::SingleCharToken { out: input } Self::SingleCharToken { out: input }
} }
_ => Self::Regular { out: input }, _ => Self::Regular { out: input },

View file

@ -44,7 +44,11 @@ fn execute_single_line(
should_be_emtpy: bool, should_be_emtpy: bool,
should_complete: bool, should_complete: bool,
) -> Result<(), Box<dyn MpsLanguageError>> { ) -> Result<(), Box<dyn MpsLanguageError>> {
if line.contains('\n') {
println!("--- Executing MPS code ---\n{}\n--- Executing MPS code ---", line);
} else {
println!("--- Executing MPS code: '{}' ---", line); println!("--- Executing MPS code: '{}' ---", line);
}
let cursor = Cursor::new(line); let cursor = Cursor::new(line);
let tokenizer = MpsTokenizer::new(cursor); let tokenizer = MpsTokenizer::new(cursor);
@ -63,7 +67,7 @@ fn execute_single_line(
} }
} // no need to spam the rest of the songs } // no need to spam the rest of the songs
println!( println!(
"Got song `{}` (file: `{}`)", "Got song `{}` (filename: `{}`)",
item.field("title") item.field("title")
.expect("Expected field `title` to exist") .expect("Expected field `title` to exist")
.clone() .clone()
@ -463,3 +467,255 @@ fn execute_intersectionfn_line() -> Result<(), Box<dyn MpsLanguageError>> {
true, true,
) )
} }
#[test]
fn execute_declareitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{let x = empty()}",
false,
true,
)
}
#[test]
fn execute_removeitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{remove item.title, remove item}",
true,
true,
)
}
#[test]
fn execute_multiitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
let x = empty(),
remove item,
remove x
}",
true,
true,
)
}
#[test]
fn execute_fieldassignitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
item.potato = empty(),
.test = empty()
}",
false,
true,
)
}
#[test]
fn execute_constitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
`str const`,
1234,
false,
item.test_field = 1234,
let foo = false
}",
false,
true,
)
}
#[test]
fn execute_retrieveitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
item.path = item.filename,
item.not_a_field,
item.new_field = 42,
item.title = item.path,
}",
false,
true,
)
}
#[test]
fn execute_additemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
item.title = `TEST` + item.title,
item.test = 1234 + 94,
}",
false,
true,
)
}
#[test]
fn execute_subtractitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
item.test = 1234 - 94,
}",
false,
true,
)
}
#[test]
fn execute_negateitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
item.test = 1234,
item.test = -item.test,
item.test = -42,
}",
false,
true,
)
}
#[test]
fn execute_notitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
item.test = false,
item.test = !item.test,
item.test = !true,
}",
false,
true,
)
}
#[test]
fn execute_orlogicalitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
item.test = true || true,
item.test = !true || false,
}",
false,
true,
)
}
#[test]
fn execute_andlogicalitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
item.test = true && true,
item.test = !true && false,
}",
false,
true,
)
}
#[test]
fn execute_bracketsitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
item.test = true && true && (false || false),
item.test = (!true && false || (false || !false)),
}",
false,
true,
)
}
#[test]
fn execute_stringifyitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
item.filepath = ~`test out: {test}` item,
item.test = true && true && (false || false),
item.test = item.test || ((!true && false) || (false || !false)),
item.title = ~`test out: {test}` item
}",
false,
true,
)
}
#[test]
fn execute_branchitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
if false {
item.title = 42,
item.title = `THIS IS WRONG ` + item.title
} else {
item.title = `OK `+ item.title,
if true {item.filename = `RIGHT`},
if true {} else {item.filename = `WRONG`},
}
}",
false,
true,
)
}
#[test]
fn execute_compareitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
if 42 != 42 {
item.title = `THIS IS WRONG ` + item.title
} else {
item.title = `OK `+ item.title
}
}",
false,
true,
)
}
#[test]
fn execute_computeitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
let count = 1,
item.track = count,
item.title = ~`Song #{track}` item,
if count > 5 {
item.filename = `¯\\\\_()_/¯`
} else {
item.filename = `/shrug`,
},
count = count + 1,
}",
false,
true,
)
}
#[test]
fn execute_constructitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
let other_item = Item (),
let temp_item = Item (
filename= `???`,
title= `???`,
),
other_item = temp_item,
temp_item = item,
item = other_item,
}",
false,
true,
)
}
/*#[test]
fn execute_iteritemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
item = iter empty()
}",
false,
true,
)
}*/

View file

@ -179,16 +179,17 @@ impl SystemControlWrapper {
fn enqueued(item: MpsItem, dbus_ctrl: &Sender<DbusControl>) { fn enqueued(item: MpsItem, dbus_ctrl: &Sender<DbusControl>) {
//println!("Got enqueued item {}", &item.title); //println!("Got enqueued item {}", &item.title);
let file_uri = item.field("filename").and_then(|x| x.to_owned().to_str()).map(|x| format!("file://{}", x));
dbus_ctrl dbus_ctrl
.send(DbusControl::SetMetadata(Metadata { .send(DbusControl::SetMetadata(Metadata {
length: None, length: None,
art_url: None, art_url: None, //file_uri.clone() TODO do this without having to rip the art image from the file like Elisa
album: item.field("album").and_then(|x| x.to_owned().to_str()), album: item.field("album").and_then(|x| x.to_owned().to_str()),
album_artist: None, // TODO maybe? album_artist: None, // TODO maybe?
artist: item artist: item
.field("artist") .field("artist")
.and_then(|x| x.to_owned().to_str()) .and_then(|x| x.to_owned().to_str())
.map(|x| vec![x]), .map(|x| x.split(",").map(|s| s.trim().to_owned()).collect()),
composer: None, composer: None,
disc_number: None, disc_number: None,
genre: item genre: item
@ -200,7 +201,7 @@ impl SystemControlWrapper {
.field("track") .field("track")
.and_then(|x| x.to_owned().to_i64()) .and_then(|x| x.to_owned().to_i64())
.map(|track| track as i32), .map(|track| track as i32),
url: item.field("filename").and_then(|x| x.to_owned().to_str()), url: file_uri,
})) }))
.unwrap_or(()); .unwrap_or(());
} }