Skip to content
Extraits de code Groupes Projets
Non vérifiée Valider 0910c21f rédigé par Kubat's avatar Kubat
Parcourir les fichiers

[VN] Add a way to use vn types in scripts and spells

parent cbb6e603
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
...@@ -9,13 +9,13 @@ pub enum Error { ...@@ -9,13 +9,13 @@ pub enum Error {
RedefinedSpell(&'static str, &'static str), RedefinedSpell(&'static str, &'static str),
#[error("already defined spell argument '{0}'")] #[error("already defined spell argument '{0}'")]
RedefinedSpellArgument(String), RedefinedSpellArgument(std::borrow::Cow<'static, str>),
#[error("unexpect: {0}")] #[error("unexpect: {0}")]
Unexpected(&'static str), Unexpected(std::borrow::Cow<'static, str>),
#[error("undefined spell '{0}'")] #[error("undefined spell '{0}'")]
Undefined(String), Undefined(std::borrow::Cow<'static, str>),
#[error("can't cast '{0}' into {1:?}")] #[error("can't cast '{0}' into {1:?}")]
CantCast(crate::ast::Const, crate::types::Type), CantCast(crate::ast::Const, crate::types::Type),
...@@ -30,7 +30,7 @@ pub enum Error { ...@@ -30,7 +30,7 @@ pub enum Error {
ParseBool(#[from] ParseBoolError), ParseBool(#[from] ParseBoolError),
#[error("empty {0}")] #[error("empty {0}")]
Empty(&'static str), Empty(std::borrow::Cow<'static, str>),
#[error("error in spell '{0}' instantiation: {1}")] #[error("error in spell '{0}' instantiation: {1}")]
BuildSpell(&'static str, Box<Error>), BuildSpell(&'static str, Box<Error>),
......
mod error; mod error;
mod state; mod state;
mod types; mod types;
mod vn;
pub mod ast; pub mod ast;
pub mod parser; pub mod parser;
...@@ -18,11 +19,19 @@ pub mod prelude { ...@@ -18,11 +19,19 @@ pub mod prelude {
types::Type as GrimoireType, types::Type as GrimoireType,
}; };
// Spell structs/traits re-exports. // The VN structs.
pub use crate::spell::{BuildableSpell, Spell, SpellArguments, SpellFactory}; pub mod vn {
pub use crate::vn::ConstOrVnType;
}
// Standard spells re-rexports. // Spell structs + standard spells re-rexports.
pub mod spell { pub mod spell {
pub use crate::spell::std::*; pub use crate::spell::{SpellArguments as Arguments, SpellFactory as Factory, std::*};
} }
// Traits.
pub use crate::{
spell::{BuildableSpell as GrimoireBuildableSpell, Spell as GrimoireSpell},
vn::{Actor as GrimoireActor, Printer as GrimoirePrinter},
};
} }
...@@ -43,13 +43,13 @@ impl SpellArguments { ...@@ -43,13 +43,13 @@ impl SpellArguments {
names: [&str; N], names: [&str; N],
) -> Result<[Expression; N], Error> { ) -> Result<[Expression; N], Error> {
if let Some(missing) = names.iter().find(|&&name| !self.named.contains_key(name)) { if let Some(missing) = names.iter().find(|&&name| !self.named.contains_key(name)) {
Err(Error::Undefined(format!( Err(Error::Undefined(Into::into(format!(
"mendatory spell argument '{missing}'" "mendatory spell argument '{missing}'"
))) ))))
} else if self.named.len() > N { } else if self.named.len() > N {
Err(Error::Unexpected( Err(Error::Unexpected(Into::into(
"too many named arguments in in spell arguments", "too many named arguments in in spell arguments",
)) )))
} else { } else {
Ok(names.map(|name| self.remove(name).unwrap())) Ok(names.map(|name| self.remove(name).unwrap()))
} }
......
...@@ -45,6 +45,6 @@ impl SpellFactory { ...@@ -45,6 +45,6 @@ impl SpellFactory {
self.dispatch self.dispatch
.get(name.as_ref()) .get(name.as_ref())
.copied() .copied()
.ok_or(Error::Undefined(name.as_ref().to_string())) .ok_or(Error::Undefined(name.as_ref().to_string().into()))
} }
} }
...@@ -7,10 +7,14 @@ pub struct Log { ...@@ -7,10 +7,14 @@ pub struct Log {
messages: Vec<GrimoireExpression>, messages: Vec<GrimoireExpression>,
} }
impl Spell for Log { impl GrimoireSpell for Log {
fn cast(&self, state: GrimoireState) -> Result<GrimoireState, GrimoireError> { fn cast(&self, state: GrimoireState) -> Result<GrimoireState, GrimoireError> {
if let Some(pred) = &self.if_predicate { if let Some(pred) = &self.if_predicate {
if !(state.evaluate_as(pred, GrimoireType::Boolean)?).unwrap_boolean() { if !state
.evaluate_as(pred, GrimoireType::Boolean)?
.try_into_const()?
.unwrap_boolean()
{
return Ok(state); return Ok(state);
} }
} }
...@@ -22,11 +26,15 @@ impl Spell for Log { ...@@ -22,11 +26,15 @@ impl Spell for Log {
let level = state let level = state
.evaluate_as(&self.level, GrimoireType::String)? .evaluate_as(&self.level, GrimoireType::String)?
.try_into_const()?
.to_string(); .to_string();
let logger = |ident: bool, line: &GrimoireExpression| -> Result<(), GrimoireError> { let logger = |ident: bool, line: &GrimoireExpression| -> Result<(), GrimoireError> {
let ident = ident.then_some("\t").unwrap_or_default(); let ident = ident.then_some("\t").unwrap_or_default();
let line = state.evaluate_as(line, GrimoireType::String)?.to_string(); let line = state
.evaluate_as(line, GrimoireType::String)?
.try_into_const()?
.to_string();
match level.as_str() { match level.as_str() {
"error" => log::error!(target: "grimoire", "{ident}{line}"), "error" => log::error!(target: "grimoire", "{ident}{line}"),
...@@ -45,10 +53,10 @@ impl Spell for Log { ...@@ -45,10 +53,10 @@ impl Spell for Log {
} }
} }
impl TryFrom<SpellArguments> for Log { impl TryFrom<spell::Arguments> for Log {
type Error = GrimoireError; type Error = GrimoireError;
fn try_from(mut args: SpellArguments) -> Result<Self, Self::Error> { fn try_from(mut args: spell::Arguments) -> Result<Self, Self::Error> {
let if_predicate = args.remove("if"); let if_predicate = args.remove("if");
let [level] = args.remove_exact(["level"])?; let [level] = args.remove_exact(["level"])?;
let messages = args let messages = args
...@@ -64,7 +72,7 @@ impl TryFrom<SpellArguments> for Log { ...@@ -64,7 +72,7 @@ impl TryFrom<SpellArguments> for Log {
} }
} }
impl BuildableSpell for Log { impl GrimoireBuildableSpell for Log {
fn name() -> &'static str { fn name() -> &'static str {
"log" "log"
} }
......
...@@ -2,24 +2,26 @@ use crate::{ ...@@ -2,24 +2,26 @@ use crate::{
ast::{Const, Expression, VarOrConst, operator}, ast::{Const, Expression, VarOrConst, operator},
error::Error, error::Error,
types::Type, types::Type,
vn::{Actor, ConstOrVnType, Printer},
};
use std::{
collections::{HashMap, HashSet},
rc::Rc,
}; };
use std::collections::{HashMap, HashSet};
#[derive(Debug, Default)] #[derive(Default)]
pub struct State { pub struct State {
variables: HashMap<String, Const>, variables: HashMap<String, Const>,
identifiers: HashSet<String>, identifiers: HashSet<String>,
actors: HashMap<String, Rc<dyn Actor>>,
// To transform into HashMap... printers: HashMap<String, Rc<dyn Printer>>,
actors: HashSet<String>,
printers: HashSet<String>,
} }
impl State { impl State {
pub fn resolve(&self, var: impl AsRef<str>) -> Result<&Const, Error> { pub fn resolve_variable(&self, var: impl AsRef<str>) -> Result<&Const, Error> {
self.variables self.variables
.get(var.as_ref()) .get(var.as_ref())
.ok_or_else(|| Error::Undefined(var.as_ref().to_string())) .ok_or_else(|| Error::Undefined(var.as_ref().to_string().into()))
} }
fn evaluate_numeric_binop( fn evaluate_numeric_binop(
...@@ -28,8 +30,8 @@ impl State { ...@@ -28,8 +30,8 @@ impl State {
op: operator::Numeric, op: operator::Numeric,
right: impl AsRef<Expression>, right: impl AsRef<Expression>,
) -> Result<Const, Error> { ) -> Result<Const, Error> {
let left = self.evaluate_as(left, Type::Number)?; let left = self.evaluate_as(left, Type::Number)?.try_into_const()?;
let right = self.evaluate_as(right, Type::Number)?; let right = self.evaluate_as(right, Type::Number)?.try_into_const()?;
match matches!(left, Const::Flt(_)) || matches!(right, Const::Flt(_)) { match matches!(left, Const::Flt(_)) || matches!(right, Const::Flt(_)) {
// Use float operations // Use float operations
...@@ -87,8 +89,14 @@ impl State { ...@@ -87,8 +89,14 @@ impl State {
right: impl AsRef<Expression>, right: impl AsRef<Expression>,
) -> Result<Const, Error> { ) -> Result<Const, Error> {
// Here we evaluate eagerly to catch errors! // Here we evaluate eagerly to catch errors!
let left = self.evaluate_as(left, Type::Boolean)?.unwrap_boolean(); let left = self
let right = self.evaluate_as(right, Type::Boolean)?.unwrap_boolean(); .evaluate_as(left, Type::Boolean)?
.try_into_const()?
.unwrap_boolean();
let right = self
.evaluate_as(right, Type::Boolean)?
.try_into_const()?
.unwrap_boolean();
Ok(Const::Bool(match op { Ok(Const::Bool(match op {
operator::Logic::Or => left || right, operator::Logic::Or => left || right,
operator::Logic::And => left && right, operator::Logic::And => left && right,
...@@ -102,8 +110,9 @@ impl State { ...@@ -102,8 +110,9 @@ impl State {
op: operator::Equal, op: operator::Equal,
right: impl AsRef<Expression>, right: impl AsRef<Expression>,
) -> Result<Const, Error> { ) -> Result<Const, Error> {
Ok(Const::Bool( let left = self.evaluate(left)?.try_into_const()?;
match (self.evaluate(left)?, self.evaluate(right)?) { let right = self.evaluate(right)?.try_into_const()?;
Ok(Const::Bool(match (left, right) {
// Easy! // Easy!
(Const::Int(x), Const::Int(y)) => op.apply(x == y), (Const::Int(x), Const::Int(y)) => op.apply(x == y),
(Const::Flt(x), Const::Flt(y)) => op.apply(x == y), (Const::Flt(x), Const::Flt(y)) => op.apply(x == y),
...@@ -129,34 +138,42 @@ impl State { ...@@ -129,34 +138,42 @@ impl State {
// Can be inter-casted, they are not equal! // Can be inter-casted, they are not equal!
_ => op.check_not_equals(), _ => op.check_not_equals(),
}, }))
))
} }
pub fn evaluate(&self, expr: impl AsRef<Expression>) -> Result<Const, Error> { pub fn evaluate(&self, expr: impl AsRef<Expression>) -> Result<ConstOrVnType, Error> {
match expr.as_ref() { match expr.as_ref() {
// Leaf, simple. // Leaf, simple.
Expression::Leaf(_, VarOrConst::Const(constant)) => Ok(constant.clone()), Expression::Leaf(_, VarOrConst::Const(constant)) => Ok(constant.clone().into()),
Expression::Leaf(_, VarOrConst::Var(var)) => self.resolve(var).cloned(), Expression::Leaf(_, VarOrConst::Var(var)) => {
self.resolve_variable(var).cloned().map(Into::into)
}
// Unary, not complicated. // Unary, not complicated.
Expression::Unary(_, op, inner) => match op { Expression::Unary(_, op, inner) => match op {
operator::Unary::Not => Ok(Const::Bool( operator::Unary::Not => Ok(Const::Bool(
!self.evaluate_as(inner, Type::Boolean)?.unwrap_boolean(), !self
.evaluate_as(inner, Type::Boolean)?
.try_into_const()?
.unwrap_boolean(),
)), )),
operator::Unary::Neg => match self.evaluate_as(inner, Type::Number)? { operator::Unary::Neg => {
match self.evaluate_as(inner, Type::Number)?.try_into_const()? {
Const::Int(x) => Ok(Const::Int(-x)), Const::Int(x) => Ok(Const::Int(-x)),
Const::Flt(x) => Ok(Const::Flt(-x)), Const::Flt(x) => Ok(Const::Flt(-x)),
_ => unreachable!(), _ => unreachable!(),
}, }
}, }
}
.map(Into::into),
// Binary, just long to write... // Binary, just long to write...
Expression::Binary(_, left, op, right) => match op { Expression::Binary(_, left, op, right) => match op {
operator::Binary::Numeric(op) => self.evaluate_numeric_binop(left, *op, right), operator::Binary::Numeric(op) => self.evaluate_numeric_binop(left, *op, right),
operator::Binary::Logic(op) => self.evaluate_logic_binop(left, *op, right), operator::Binary::Logic(op) => self.evaluate_logic_binop(left, *op, right),
operator::Binary::Equal(op) => self.evaluate_equal_binop(left, *op, right), operator::Binary::Equal(op) => self.evaluate_equal_binop(left, *op, right),
}, }
.map(Into::into),
} }
} }
...@@ -164,33 +181,56 @@ impl State { ...@@ -164,33 +181,56 @@ impl State {
let x = ident.into(); let x = ident.into();
match self.identifiers.contains(&x) { match self.identifiers.contains(&x) {
true => Ok(Const::Ident(x)), true => Ok(Const::Ident(x)),
false => Err(Error::Undefined(x)), false => Err(Error::Undefined(x.into())),
} }
} }
fn find_actor(&self, actor: impl Into<String>) -> Result<Const, Error> { fn find_actor(&self, actor: impl Into<String>) -> Result<Rc<dyn Actor>, Error> {
let x = actor.into(); let actor = actor.into();
match self.actors.contains(&x) { self.actors
true => Ok(Const::Ident(x)), .get(&actor)
false => Err(Error::Undefined(x)), .cloned()
} .ok_or(Error::Undefined(actor.into()))
} }
fn find_printer(&self, printer: impl Into<String>) -> Result<Const, Error> { fn find_printer(&self, printer: impl Into<String>) -> Result<Rc<dyn Printer>, Error> {
let x = printer.into(); let printer = printer.into();
match self.printers.contains(&x) { self.printers
true => Ok(Const::Ident(x)), .get(&printer)
false => Err(Error::Undefined(x)), .cloned()
} .ok_or(Error::Undefined(printer.into()))
} }
pub fn evaluate_as(&self, expr: impl AsRef<Expression>, ty: Type) -> Result<Const, Error> { pub fn evaluate_as(
let constant = self.evaluate(expr)?; &self,
expr: impl AsRef<Expression>,
ty: Type,
) -> Result<ConstOrVnType, Error> {
let res = self.evaluate(expr)?;
match ty { match ty {
Type::Identifier => self.check_identifier(constant.unwrap_identifier()), Type::Identifier => self
Type::Actor => self.find_actor(constant.unwrap_identifier()), .check_identifier(res.try_into_const()?.unwrap_identifier())
Type::Printer => self.find_printer(constant.unwrap_identifier()), .map(ConstOrVnType::Const),
ty => constant.cast_into(ty),
Type::Actor => match res.is_actor() {
true => Ok(res),
false => self
.find_actor(res.try_into_const()?.unwrap_identifier())
.map(ConstOrVnType::Actor),
},
Type::Printer => match res.is_printer() {
true => Ok(res),
false => self
.find_printer(res.try_into_const()?.unwrap_identifier())
.map(ConstOrVnType::Printer),
},
ty => res
.try_into_const()?
.cast_into(ty)
.map(ConstOrVnType::Const),
} }
} }
} }
pub trait Actor {
fn name(&self) -> &'static str;
}
mod actor;
mod printer;
use crate::{ast::Const, error::Error};
use std::{fmt, rc::Rc};
pub use self::{actor::Actor, printer::Printer};
#[derive(Clone)]
pub enum ConstOrVnType {
Const(Const),
Actor(Rc<dyn Actor>),
Printer(Rc<dyn Printer>),
}
impl fmt::Debug for ConstOrVnType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Const(arg0) => f.debug_tuple("Const").field(arg0).finish(),
Self::Actor(arg0) => f.debug_tuple("Actor").field(&arg0.name()).finish(),
Self::Printer(arg0) => f.debug_tuple("Printer").field(&arg0.name()).finish(),
}
}
}
impl ConstOrVnType {
pub fn is_actor(&self) -> bool {
matches!(self, ConstOrVnType::Actor(_))
}
pub fn is_printer(&self) -> bool {
matches!(self, ConstOrVnType::Printer(_))
}
pub fn try_into_const(self) -> Result<Const, Error> {
use ConstOrVnType::*;
match self {
Const(constant) => Ok(constant),
Actor(vn) => Err(Error::Unexpected(Into::into(format!(
"can't cast actor '{name}' into constant",
name = vn.name()
)))),
Printer(vn) => Err(Error::Unexpected(Into::into(format!(
"can't cast printer '{name}' into constant",
name = vn.name()
)))),
}
}
}
impl From<Const> for ConstOrVnType {
fn from(value: Const) -> Self {
Self::Const(value)
}
}
impl From<Rc<dyn Actor>> for ConstOrVnType {
fn from(value: Rc<dyn Actor>) -> Self {
Self::Actor(value)
}
}
impl From<Rc<dyn Printer>> for ConstOrVnType {
fn from(value: Rc<dyn Printer>) -> Self {
Self::Printer(value)
}
}
pub trait Printer {
fn name(&self) -> &'static str;
}
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Veuillez vous inscrire ou vous pour commenter