diff --git a/grimoire/src/lib.rs b/grimoire/src/lib.rs index 9d2490435c120be2aa6a13805cce8dd29fe2d67f..4a319192df5b2808e4c143023a965ee54df721a0 100644 --- a/grimoire/src/lib.rs +++ b/grimoire/src/lib.rs @@ -7,10 +7,22 @@ pub mod parser; pub mod spell; pub mod prelude { + // Types prefixed by crate name. pub use crate::{ - ast::expr::{Const as GrimoireConstant, Expression as GrimoireExpression}, + ast::{ + Const as GrimoireConstant, Expression as GrimoireExpression, + VarOrConst as GrimoireVarOrConst, + }, error::Error as GrimoireError, state::State as GrimoireState, types::Type as GrimoireType, }; + + // Spell structs/traits re-exports. + pub use crate::spell::{BuildableSpell, Spell, SpellArguments, SpellFactory}; + + // Standard spells re-rexports. + pub mod spell { + pub use crate::spell::std::*; + } } diff --git a/grimoire/src/spell/arguments.rs b/grimoire/src/spell/arguments.rs index 35ccba3780ed33fa17853e2d0c7a2e6cd370be8e..76e9b9855c53e0bd0b324904288600f0eaaf81ff 100644 --- a/grimoire/src/spell/arguments.rs +++ b/grimoire/src/spell/arguments.rs @@ -1,36 +1,118 @@ -use crate::{ - ast::expr::{Const, Expression}, - error::Error, - state::State, - types::Type, -}; +use crate::{ast::Const, error::Error, types::Type}; use std::collections::HashMap; /// The arguments that may be passed to a spell for its instanciation. #[derive(Debug, Default)] -pub struct SpellArguments(HashMap<String, Expression>); +pub struct SpellArguments { + named: HashMap<String, Const>, + trailing: Vec<Const>, +} impl SpellArguments { /// Set/Override the value in the [Argument] list. - pub fn set(&mut self, name: impl Into<String>, value: Expression) { - _ = self.0.insert(name.into(), value); + pub fn insert(&mut self, name: impl Into<String>, value: Const) { + _ = self.named.insert(name.into(), value); + } + + /// Push an unamed argument to the argument list. + pub fn push(&mut self, value: Const) { + self.trailing.push(value); + } + + /// Removes a named argument of a [SpellArguments], and try to cast it into the asked type. On + /// success returns [Option<Argument>::Some], otherwise [None]. + pub fn remove_as<S>(&mut self, name: S, ty: Type) -> Result<Const, Error> + where + S: AsRef<str>, + { + self.remove(name.as_ref()) + .ok_or_else(|| Error::Undefined(name.as_ref().to_string()))? + .cast_into(ty) } - /// Pops the value of an [Argument], and try to cast it into the asked type. On success returns + /// Removes a named argument of a [SpellArguments]. On success returns /// [Option<Argument>::Some], otherwise [None]. - pub fn pop<S>(&mut self, state: &State, name: S, ty: Type) -> Result<Const, Error> + pub fn remove<S>(&mut self, name: S) -> Option<Const> where S: AsRef<str>, { - self.0 - .remove(name.as_ref()) - .ok_or_else(|| Error::Undefined(name.as_ref().to_string())) - .map(|expr| state.evaluate_as(expr, ty))? + self.named.remove(name.as_ref()) + } + + /// Pops an unamed argument from the argument list. + pub fn pop_as(&mut self, ty: Type) -> Result<Const, Error> { + self.pop() + .ok_or(Error::Empty("unamed argument list"))? + .cast_into(ty) + } + + /// Pops an unamed argument from the argument list. + pub fn pop(&mut self) -> Option<Const> { + self.trailing.pop() + } + + /// Iterate over the name of the named arguments. + pub fn iter_named_keys(&self) -> impl Iterator<Item = &str> { + self.named.keys().map(String::as_str) + } + + /// Iterate over named arguments. + pub fn iter_named(&self) -> impl Iterator<Item = (&str, &Const)> { + self.named + .iter() + .map(|(key, constant)| (key.as_str(), constant)) + } + + /// Iterate over unnamed arguments. + pub fn iter_unamed(&self) -> impl Iterator<Item = &Const> { + self.trailing.iter() + } + + /// Tells wether we have named arguments or not. + pub fn has_named(&self) -> bool { + self.named.is_empty() + } + + /// Tells wether we have unamed arguments or not. + pub fn has_unamed(&self) -> bool { + self.trailing.is_empty() + } + + /// Tells wether the argument list is empty or not. Note that at the end of the + /// [crate::spell::Spell] creation, the argument list must be empty! + pub fn is_empty(&self) -> bool { + !self.has_named() && !self.has_unamed() } } -impl FromIterator<(String, Expression)> for SpellArguments { - fn from_iter<T: IntoIterator<Item = (String, Expression)>>(iter: T) -> Self { - Self(iter.into_iter().collect()) +/// Iterate over all the arguments. You are guarentied to have first the named arguments - in an +/// unspecified order - then the unamed arguments in order. +pub struct IntoIter { + named: <HashMap<String, Const> as IntoIterator>::IntoIter, + trailing: <Vec<Const> as IntoIterator>::IntoIter, +} + +impl IntoIterator for SpellArguments { + type Item = <Self::IntoIter as Iterator>::Item; + type IntoIter = IntoIter; + + /// Iterate over all the arguments. You are guarentied to have first the named arguments - in + /// an unspecified order - then the unamed arguments in order. + fn into_iter(self) -> Self::IntoIter { + IntoIter { + named: self.named.into_iter(), + trailing: self.trailing.into_iter(), + } + } +} + +impl Iterator for IntoIter { + type Item = (Option<String>, Const); + + fn next(&mut self) -> Option<Self::Item> { + self.named + .next() + .map(|(key, constant)| (Some(key), constant)) + .or_else(|| self.trailing.next().map(|constant| (None, constant))) } } diff --git a/grimoire/src/spell/traits.rs b/grimoire/src/spell/traits.rs index f9fa195ecb7c158925bc20845f86931f89422754..aa919904a2765f6e112195235424fbd5fdadb9e4 100644 --- a/grimoire/src/spell/traits.rs +++ b/grimoire/src/spell/traits.rs @@ -1,12 +1,9 @@ use crate::{error::Error, spell::SpellArguments, state::State}; -/// A spell, can be casted or reverted if we need to. +/// A spell, can be casted on a state to modify it. pub trait Spell { /// Cast the spell on a [State]. Returns a new [State]. - fn cast(&mut self, state: State) -> Result<State, Error>; - - /// UnCast the spell from a [State]. Returns the previous [State]. - fn uncast(&mut self, state: State) -> Result<State, Error>; + fn cast(&self, state: State) -> Result<State, Error>; } /// A [Spell] that we can create. Is used to be able to register it into a @@ -33,7 +30,7 @@ where /// Create a spell from a set of arguments. May fail. fn create(arguments: SpellArguments) -> Result<Box<dyn Spell>, Error> { Self::try_from(arguments) - .map(Box::new) - .map(|boxed| boxed as Box<dyn Spell>) + .map(|this| Box::new(this) as Box<dyn Spell>) + .map_err(|err| Error::BuildSpell(Self::name(), Box::new(err))) } }