Create non-iter operations and implement basics for Turing completeness
This commit is contained in:
parent
72b63b6feb
commit
3a2c2629ca
34 changed files with 2510 additions and 22 deletions
|
@ -159,10 +159,33 @@ pub(crate) fn standard_vocab(vocabulary: &mut MpsLanguageDictionary) {
|
|||
.add(crate::lang::vocabulary::filters::field_re_filter())
|
||||
// sorters
|
||||
.add(crate::lang::vocabulary::sorters::empty_sort())
|
||||
.add(crate::lang::vocabulary::sorters::shuffle_sort()) // accepts valid field ~(shuffle)
|
||||
.add(crate::lang::vocabulary::sorters::field_sort())
|
||||
.add(crate::lang::vocabulary::sorters::shuffle_sort()) // accepts ~(shuffle)
|
||||
.add(crate::lang::vocabulary::sorters::field_sort()) // accepts any ~(something)
|
||||
.add(crate::lang::vocabulary::sorters::bliss_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 don't enforce bracket coherence
|
||||
// -- function().() is valid despite the ).( in between brackets
|
||||
|
|
|
@ -32,6 +32,14 @@ impl MpsItem {
|
|||
pub fn remove_field(&mut self, name: &str) -> Option<MpsTypePrimitive> {
|
||||
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 {
|
||||
|
|
353
mps-interpreter/src/lang/iter_block.rs
Normal file
353
mps-interpreter/src/lang/iter_block.rs
Normal 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
|
||||
}
|
|
@ -4,6 +4,7 @@ mod error;
|
|||
mod filter;
|
||||
mod filter_replace;
|
||||
mod function;
|
||||
mod iter_block;
|
||||
mod lookup;
|
||||
mod operation;
|
||||
mod pseudo_op;
|
||||
|
@ -21,6 +22,7 @@ pub use filter::{
|
|||
};
|
||||
pub use filter_replace::MpsFilterReplaceStatement;
|
||||
pub use function::{MpsFunctionFactory, MpsFunctionStatementFactory};
|
||||
pub use iter_block::{MpsItemBlockFactory, MpsItemOp, MpsItemOpFactory};
|
||||
pub use lookup::Lookup;
|
||||
pub use operation::{BoxedMpsOpFactory, MpsIteratorItem, MpsOp, MpsOpFactory, SimpleMpsOpFactory};
|
||||
pub use pseudo_op::PseudoOp;
|
||||
|
|
|
@ -10,6 +10,7 @@ pub enum MpsTypePrimitive {
|
|||
UInt(u64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl MpsTypePrimitive {
|
||||
|
@ -39,6 +40,7 @@ impl MpsTypePrimitive {
|
|||
Self::Int(x) => format!("{}", x),
|
||||
Self::Float(x) => format!("{}", x),
|
||||
Self::Bool(x) => format!("{}", x),
|
||||
Self::Empty => "".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,6 +77,66 @@ impl MpsTypePrimitive {
|
|||
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 {
|
||||
|
@ -85,6 +147,7 @@ impl Display for MpsTypePrimitive {
|
|||
Self::UInt(u) => write!(f, "UInt[{}]", *u),
|
||||
Self::Float(f_) => write!(f, "Float[{}]", *f_),
|
||||
Self::Bool(b) => write!(f, "Bool[{}]", *b),
|
||||
Self::Empty => write!(f, "Empty[]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,6 +203,10 @@ impl PartialOrd for MpsTypePrimitive {
|
|||
}
|
||||
_ => 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::Float(f_) => (*f_ as u64).hash(state),
|
||||
Self::Bool(b) => b.hash(state),
|
||||
Self::Empty => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::VecDeque;
|
||||
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::MpsLanguageDictionary;
|
||||
use crate::lang::MpsTypePrimitive;
|
||||
|
@ -34,19 +34,6 @@ pub enum FieldFilterErrorHandling {
|
|||
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 {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
let comp_op = comparison_op(&self.comparison);
|
||||
|
|
|
@ -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] => "!=",
|
||||
_ => "??",
|
||||
}
|
||||
}
|
||||
|
|
90
mps-interpreter/src/lang/vocabulary/item_ops/add.rs
Normal file
90
mps-interpreter/src/lang/vocabulary/item_ops/add.rs
Normal 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
|
||||
}
|
36
mps-interpreter/src/lang/vocabulary/item_ops/brackets.rs
Normal file
36
mps-interpreter/src/lang/vocabulary/item_ops/brackets.rs
Normal 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)
|
||||
}
|
||||
}
|
237
mps-interpreter/src/lang/vocabulary/item_ops/branch.rs
Normal file
237
mps-interpreter/src/lang/vocabulary/item_ops/branch.rs
Normal 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
|
||||
}
|
107
mps-interpreter/src/lang/vocabulary/item_ops/compare.rs
Normal file
107
mps-interpreter/src/lang/vocabulary/item_ops/compare.rs
Normal 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
|
||||
}
|
56
mps-interpreter/src/lang/vocabulary/item_ops/constant.rs
Normal file
56
mps-interpreter/src/lang/vocabulary/item_ops/constant.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
131
mps-interpreter/src/lang/vocabulary/item_ops/constructor.rs
Normal file
131
mps-interpreter/src/lang/vocabulary/item_ops/constructor.rs
Normal 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
|
||||
}
|
56
mps-interpreter/src/lang/vocabulary/item_ops/empty.rs
Normal file
56
mps-interpreter/src/lang/vocabulary/item_ops/empty.rs
Normal 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)
|
||||
}
|
||||
}
|
87
mps-interpreter/src/lang/vocabulary/item_ops/field_assign.rs
Normal file
87
mps-interpreter/src/lang/vocabulary/item_ops/field_assign.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
57
mps-interpreter/src/lang/vocabulary/item_ops/iter_op.rs
Normal file
57
mps-interpreter/src/lang/vocabulary/item_ops/iter_op.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
95
mps-interpreter/src/lang/vocabulary/item_ops/logical_and.rs
Normal file
95
mps-interpreter/src/lang/vocabulary/item_ops/logical_and.rs
Normal 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
|
||||
}
|
95
mps-interpreter/src/lang/vocabulary/item_ops/logical_or.rs
Normal file
95
mps-interpreter/src/lang/vocabulary/item_ops/logical_or.rs
Normal 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
|
||||
}
|
39
mps-interpreter/src/lang/vocabulary/item_ops/mod.rs
Normal file
39
mps-interpreter/src/lang/vocabulary/item_ops/mod.rs
Normal 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};
|
61
mps-interpreter/src/lang/vocabulary/item_ops/negate.rs
Normal file
61
mps-interpreter/src/lang/vocabulary/item_ops/negate.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
61
mps-interpreter/src/lang/vocabulary/item_ops/not.rs
Normal file
61
mps-interpreter/src/lang/vocabulary/item_ops/not.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
90
mps-interpreter/src/lang/vocabulary/item_ops/subtract.rs
Normal file
90
mps-interpreter/src/lang/vocabulary/item_ops/subtract.rs
Normal 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
|
||||
}
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -23,4 +23,5 @@ pub use union::{union_function_factory, UnionStatementFactory};
|
|||
pub use variable_assign::{AssignStatement, AssignStatementFactory};
|
||||
|
||||
pub mod filters;
|
||||
pub mod item_ops;
|
||||
pub mod sorters;
|
||||
|
|
|
@ -20,6 +20,8 @@ pub trait MpsMusicAnalyzer: Debug {
|
|||
fn prepare_item(&mut self, item: &MpsItem) -> Result<(), RuntimeMsg>;
|
||||
|
||||
fn get_distance(&mut self, from: &MpsItem, to: &MpsItem) -> Result<f64, RuntimeMsg>;
|
||||
|
||||
fn clear_cache(&mut self) -> Result<(), RuntimeMsg>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "bliss-audio")]
|
||||
|
@ -108,6 +110,12 @@ impl MpsMusicAnalyzer for MpsDefaultAnalyzer {
|
|||
}
|
||||
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"))]
|
||||
|
@ -140,6 +148,7 @@ enum RequestType {
|
|||
path: String,
|
||||
ack: bool,
|
||||
},
|
||||
Clear {},
|
||||
//End {}
|
||||
}
|
||||
|
||||
|
@ -383,6 +392,10 @@ impl CacheThread {
|
|||
break 'outer;
|
||||
}
|
||||
},
|
||||
RequestType::Clear{} => {
|
||||
self.distance_cache.clear();
|
||||
self.song_cache.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,13 @@ use std::collections::HashMap;
|
|||
use crate::lang::MpsOp;
|
||||
use crate::lang::MpsTypePrimitive;
|
||||
use crate::lang::RuntimeMsg;
|
||||
use crate::MpsItem;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MpsType {
|
||||
Op(Box<dyn MpsOp>),
|
||||
Primitive(MpsTypePrimitive),
|
||||
Item(MpsItem),
|
||||
}
|
||||
|
||||
impl Display for MpsType {
|
||||
|
@ -17,10 +19,18 @@ impl Display for MpsType {
|
|||
match self {
|
||||
Self::Op(op) => write!(f, "Op({})", op),
|
||||
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 {
|
||||
fn get(&self, name: &str) -> Result<&'_ MpsType, RuntimeMsg> {
|
||||
match self.get_opt(name) {
|
||||
|
|
|
@ -21,6 +21,10 @@ pub enum MpsToken {
|
|||
Ampersand,
|
||||
Colon,
|
||||
Tilde,
|
||||
OpenCurly,
|
||||
CloseCurly,
|
||||
Plus,
|
||||
Minus,
|
||||
}
|
||||
|
||||
impl MpsToken {
|
||||
|
@ -42,6 +46,10 @@ impl MpsToken {
|
|||
"&" => Ok(Self::Ampersand),
|
||||
":" => Ok(Self::Colon),
|
||||
"~" => Ok(Self::Tilde),
|
||||
"{" => Ok(Self::OpenCurly),
|
||||
"}" => Ok(Self::CloseCurly),
|
||||
"+" => Ok(Self::Plus),
|
||||
"-" => Ok(Self::Minus),
|
||||
_ => {
|
||||
// name validation
|
||||
let mut ok = true;
|
||||
|
@ -194,6 +202,34 @@ impl MpsToken {
|
|||
_ => 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 {
|
||||
|
@ -218,6 +254,10 @@ impl Display for MpsToken {
|
|||
Self::Ampersand => write!(f, "&"),
|
||||
Self::Colon => write!(f, ":"),
|
||||
Self::Tilde => write!(f, "~"),
|
||||
Self::OpenCurly => write!(f, "{{"),
|
||||
Self::CloseCurly => write!(f, "}}"),
|
||||
Self::Plus => write!(f, "+"),
|
||||
Self::Minus => write!(f, "-"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -261,7 +261,7 @@ impl ReaderStateMachine {
|
|||
'\n' | '\r' | '\t' | ' ' => Self::EndToken {},
|
||||
';' => Self::EndStatement {},
|
||||
'\0' => Self::EndOfFile {},
|
||||
'(' | ')' | ',' | '=' | '<' | '>' | '.' | '!' | '?' | '|' | ':' => {
|
||||
'(' | ')' | ',' | '=' | '<' | '>' | '.' | '!' | '?' | '|' | '&' | ':' | '{' | '}' | '+' | '-' | '~' => {
|
||||
Self::SingleCharToken { out: input }
|
||||
}
|
||||
_ => Self::Regular { out: input },
|
||||
|
|
|
@ -44,7 +44,11 @@ fn execute_single_line(
|
|||
should_be_emtpy: bool,
|
||||
should_complete: bool,
|
||||
) -> Result<(), Box<dyn MpsLanguageError>> {
|
||||
println!("--- Executing MPS code: '{}' ---", line);
|
||||
if line.contains('\n') {
|
||||
println!("--- Executing MPS code ---\n{}\n--- Executing MPS code ---", line);
|
||||
} else {
|
||||
println!("--- Executing MPS code: '{}' ---", line);
|
||||
}
|
||||
let cursor = Cursor::new(line);
|
||||
|
||||
let tokenizer = MpsTokenizer::new(cursor);
|
||||
|
@ -63,7 +67,7 @@ fn execute_single_line(
|
|||
}
|
||||
} // no need to spam the rest of the songs
|
||||
println!(
|
||||
"Got song `{}` (file: `{}`)",
|
||||
"Got song `{}` (filename: `{}`)",
|
||||
item.field("title")
|
||||
.expect("Expected field `title` to exist")
|
||||
.clone()
|
||||
|
@ -463,3 +467,255 @@ fn execute_intersectionfn_line() -> Result<(), Box<dyn MpsLanguageError>> {
|
|||
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,
|
||||
)
|
||||
}*/
|
||||
|
|
|
@ -179,16 +179,17 @@ impl SystemControlWrapper {
|
|||
|
||||
fn enqueued(item: MpsItem, dbus_ctrl: &Sender<DbusControl>) {
|
||||
//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
|
||||
.send(DbusControl::SetMetadata(Metadata {
|
||||
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_artist: None, // TODO maybe?
|
||||
artist: item
|
||||
.field("artist")
|
||||
.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,
|
||||
disc_number: None,
|
||||
genre: item
|
||||
|
@ -200,7 +201,7 @@ impl SystemControlWrapper {
|
|||
.field("track")
|
||||
.and_then(|x| x.to_owned().to_i64())
|
||||
.map(|track| track as i32),
|
||||
url: item.field("filename").and_then(|x| x.to_owned().to_str()),
|
||||
url: file_uri,
|
||||
}))
|
||||
.unwrap_or(());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue