From 335c3abc67c5da89f1c85f09f7090d49859559e0 Mon Sep 17 00:00:00 2001 From: Kubat <maelle.martin@proton.me> Date: Thu, 1 May 2025 13:09:01 +0200 Subject: [PATCH] [AST] Update AST and begin runtime implementation - Separate the Const part of the Var in AST - Begin to implement the SpellArgument getter (add methods to State) - Named exports of some top-level structs/enums --- grimoire/src/ast/expr.rs | 45 ++++++++++++++++--- grimoire/src/error.rs | 23 ++++++++-- grimoire/src/lib.rs | 15 ++++++- grimoire/src/parser/expr.rs | 14 +++--- grimoire/src/parser/utils.rs | 2 +- grimoire/src/spell/arguments.rs | 50 ++++++++------------- grimoire/src/spell/factory.rs | 12 ++--- grimoire/src/spell/traits.rs | 10 ++--- grimoire/src/state.rs | 77 ++++++++++++++++++++++++++++++++- grimoire/src/types.rs | 12 +++++ 10 files changed, 198 insertions(+), 62 deletions(-) create mode 100644 grimoire/src/types.rs diff --git a/grimoire/src/ast/expr.rs b/grimoire/src/ast/expr.rs index bfd3b87..ff04c04 100644 --- a/grimoire/src/ast/expr.rs +++ b/grimoire/src/ast/expr.rs @@ -1,7 +1,8 @@ +use derive_more::Display; use crate::ast::AstSpan; -#[derive(Debug, Clone, PartialEq)] -pub enum VarOrConstant { +#[derive(Debug, Clone, PartialEq, Display)] +pub enum Const { /// Integer, like 1, 42, -1, etc Int(i32), @@ -9,11 +10,11 @@ pub enum VarOrConstant { Flt(f32), /// A string, can be multiline or not: "something", '''Some other things''' + /// + /// TODO: Esapce in display! + #[display("'''{_0}'''")] Str(String), - /// A variable, just an identifier preceded by `$`, like: `$CynthiaLike` - Var(String), - /// A boolean value, `true` or `false` Bool(bool), @@ -21,11 +22,43 @@ pub enum VarOrConstant { Ident(String), } +#[derive(Debug, Clone, PartialEq, Display)] +pub enum VarOrConst { + /// A [Const]. + Const(Const), + + /// A variable, just an identifier preceded by `$`, like: `$CynthiaLike` + #[display("${_0}")] + Var(String), +} + +impl VarOrConst { + pub fn integer(x: i32) -> Self { + Self::Const(Const::Int(x)) + } + + pub fn float(x: f32) -> Self { + Self::Const(Const::Flt(x)) + } + + pub fn boolean(x: bool) -> Self { + Self::Const(Const::Bool(x)) + } + + pub fn identifier(x: impl Into<String>) -> Self { + Self::Const(Const::Ident(x.into())) + } + + pub fn string(x: impl Into<String>) -> Self { + Self::Const(Const::Str(x.into())) + } +} + #[derive(Debug)] pub enum Expression { Binary(AstSpan, Box<Expression>, BinOp, Box<Expression>), Unary(AstSpan, UnOp, Box<Expression>), - Leaf(AstSpan, VarOrConstant), + Leaf(AstSpan, VarOrConst), } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/grimoire/src/error.rs b/grimoire/src/error.rs index dbe8c26..a9dbb09 100644 --- a/grimoire/src/error.rs +++ b/grimoire/src/error.rs @@ -1,8 +1,25 @@ +use std::{ + num::{ParseFloatError, ParseIntError}, + str::ParseBoolError, +}; + #[derive(Debug, thiserror::Error)] -pub enum SpellError { - #[error("Already defined spell '{0}': {1}")] +pub enum Error { + #[error("already defined spell '{0}': {1}")] Redefined(&'static str, &'static str), - #[error("Undefined spell '{0}'")] + #[error("undefined spell '{0}'")] Undefined(String), + + #[error("can't cast '{0}' into {1:?}")] + CantCast(crate::ast::expr::Const, crate::types::Type), + + #[error("{0}")] + ParseFloat(#[from] ParseFloatError), + + #[error("{0}")] + ParseInt(#[from] ParseIntError), + + #[error("{0}")] + ParseBool(#[from] ParseBoolError), } diff --git a/grimoire/src/lib.rs b/grimoire/src/lib.rs index c3d4d7b..9d24904 100644 --- a/grimoire/src/lib.rs +++ b/grimoire/src/lib.rs @@ -1,5 +1,16 @@ +mod error; +mod state; +mod types; + pub mod ast; -pub mod error; pub mod parser; pub mod spell; -pub mod state; + +pub mod prelude { + pub use crate::{ + ast::expr::{Const as GrimoireConstant, Expression as GrimoireExpression}, + error::Error as GrimoireError, + state::State as GrimoireState, + types::Type as GrimoireType, + }; +} diff --git a/grimoire/src/parser/expr.rs b/grimoire/src/parser/expr.rs index 33f4c0b..340f2be 100644 --- a/grimoire/src/parser/expr.rs +++ b/grimoire/src/parser/expr.rs @@ -16,13 +16,13 @@ fn var_or_constant(s: Span) -> ParserResult<Expression> { // Note: the order is important! utils::map_with_locaiton( alt(( - map(utils::number, VarOrConstant::Int), - map(utils::float, VarOrConstant::Flt), - map(utils::string, VarOrConstant::Str), - value(VarOrConstant::Bool(true), utils::keyword(keywords::TRUE)), - value(VarOrConstant::Bool(false), utils::keyword(keywords::FALSE)), - map(preceded(char('$'), utils::identifier), VarOrConstant::Var), - map(utils::identifier, VarOrConstant::Ident), + map(utils::integer, VarOrConst::integer), + map(utils::float, VarOrConst::float), + map(utils::string, VarOrConst::string), + value(VarOrConst::boolean(true), utils::keyword(keywords::TRUE)), + value(VarOrConst::boolean(false), utils::keyword(keywords::FALSE)), + map(preceded(char('$'), utils::identifier), VarOrConst::Var), + map(utils::identifier, VarOrConst::identifier), )), Expression::Leaf, ) diff --git a/grimoire/src/parser/utils.rs b/grimoire/src/parser/utils.rs index 5b870a8..5ebaa20 100644 --- a/grimoire/src/parser/utils.rs +++ b/grimoire/src/parser/utils.rs @@ -76,7 +76,7 @@ pub fn identifier(s: Span) -> ParserResult<String> { .parse(s) } -pub fn number<'a>(s: Span<'a>) -> ParserResult<'a, i32> { +pub fn integer<'a>(s: Span<'a>) -> ParserResult<'a, i32> { let s = multispace0(s)?.0; digit1 .map_res(|s: Span<'a>| str::parse::<i32>(s.into())) diff --git a/grimoire/src/spell/arguments.rs b/grimoire/src/spell/arguments.rs index bf62381..35ccba3 100644 --- a/grimoire/src/spell/arguments.rs +++ b/grimoire/src/spell/arguments.rs @@ -1,48 +1,36 @@ -use crate::ast::expr::Expression; +use crate::{ + ast::expr::{Const, Expression}, + error::Error, + state::State, + types::Type, +}; use std::collections::HashMap; -/// Allowed types for arguments. -#[derive(Debug)] -pub enum ArgumentType { - Boolean, - Integer, - Identifier, - Actor, - Printer, -} - -/// The argument. -#[derive(Debug)] -pub enum Argument { - Expression(Expression), - Identifier(String), -} - -impl Argument { - pub fn into_a(&self, _into_type: ArgumentType) -> Option<Self> { - todo!() - } -} - /// The arguments that may be passed to a spell for its instanciation. #[derive(Debug, Default)] -pub struct SpellArguments(HashMap<String, Argument>); +pub struct SpellArguments(HashMap<String, Expression>); impl SpellArguments { /// Set/Override the value in the [Argument] list. - pub fn set(&mut self, name: String, value: Argument) { - _ = self.0.insert(name, value); + pub fn set(&mut self, name: impl Into<String>, value: Expression) { + _ = self.0.insert(name.into(), value); } /// Pops the value of an [Argument], and try to cast it into the asked type. On success returns /// [Option<Argument>::Some], otherwise [None]. - pub fn pop(&mut self, name: impl AsRef<str>, into_type: ArgumentType) -> Option<Argument> { - self.0.remove(name.as_ref())?.into_a(into_type) + pub fn pop<S>(&mut self, state: &State, name: S, ty: Type) -> Result<Const, Error> + 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))? } } -impl FromIterator<(String, Argument)> for SpellArguments { - fn from_iter<T: IntoIterator<Item = (String, Argument)>>(iter: T) -> Self { +impl FromIterator<(String, Expression)> for SpellArguments { + fn from_iter<T: IntoIterator<Item = (String, Expression)>>(iter: T) -> Self { Self(iter.into_iter().collect()) } } diff --git a/grimoire/src/spell/factory.rs b/grimoire/src/spell/factory.rs index 8c9ec57..84c1281 100644 --- a/grimoire/src/spell/factory.rs +++ b/grimoire/src/spell/factory.rs @@ -1,11 +1,11 @@ use crate::{ - error::SpellError, + error::Error, spell::{BuildableSpell, Spell, SpellArguments}, }; use std::collections::HashMap; /// The builder function, call it with some arguments to try to build the spell -pub type SpellBuilderFunction = fn(SpellArguments) -> Result<Box<dyn Spell>, SpellError>; +pub type SpellBuilderFunction = fn(SpellArguments) -> Result<Box<dyn Spell>, Error>; /// The spell factory, you have to register the spell before trying to create them. #[derive(Debug, Default)] @@ -21,9 +21,9 @@ impl SpellFactory { } /// Register a spell into the factory. In case of re-registering returns an [Err]. - pub fn register<Sp: BuildableSpell + 'static>(&mut self) -> Result<(), SpellError> { + pub fn register<Sp: BuildableSpell + 'static>(&mut self) -> Result<(), Error> { Sp::name_list().try_for_each(|name| match self.dispatch.entry(name) { - std::collections::hash_map::Entry::Occupied(_) => Err(SpellError::Redefined( + std::collections::hash_map::Entry::Occupied(_) => Err(Error::Redefined( Sp::name(), "name or alias was already registered", )), @@ -35,10 +35,10 @@ impl SpellFactory { } /// Get the builder of a [Spell] by its name. In case of undefined [Spell], returns an [Err]. - pub fn get_builder(&self, name: impl AsRef<str>) -> Result<SpellBuilderFunction, SpellError> { + pub fn get_builder(&self, name: impl AsRef<str>) -> Result<SpellBuilderFunction, Error> { self.dispatch .get(name.as_ref()) .copied() - .ok_or(SpellError::Undefined(name.as_ref().to_string())) + .ok_or(Error::Undefined(name.as_ref().to_string())) } } diff --git a/grimoire/src/spell/traits.rs b/grimoire/src/spell/traits.rs index c180e62..f9fa195 100644 --- a/grimoire/src/spell/traits.rs +++ b/grimoire/src/spell/traits.rs @@ -1,19 +1,19 @@ -use crate::{error::SpellError, spell::SpellArguments, state::State}; +use crate::{error::Error, spell::SpellArguments, state::State}; /// A spell, can be casted or reverted if we need to. pub trait Spell { /// Cast the spell on a [State]. Returns a new [State]. - fn cast(&mut self, state: State) -> Result<State, SpellError>; + 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, SpellError>; + fn uncast(&mut self, state: State) -> Result<State, Error>; } /// A [Spell] that we can create. Is used to be able to register it into a /// [crate::spell::SpellFactory]. pub trait BuildableSpell where - Self: Spell + TryFrom<SpellArguments, Error = SpellError> + 'static, + Self: Spell + TryFrom<SpellArguments, Error = Error> + 'static, { /// The name of the spell. fn name() -> &'static str; @@ -31,7 +31,7 @@ where } /// Create a spell from a set of arguments. May fail. - fn create(arguments: SpellArguments) -> Result<Box<dyn Spell>, SpellError> { + fn create(arguments: SpellArguments) -> Result<Box<dyn Spell>, Error> { Self::try_from(arguments) .map(Box::new) .map(|boxed| boxed as Box<dyn Spell>) diff --git a/grimoire/src/state.rs b/grimoire/src/state.rs index 02d38c7..3fa2f57 100644 --- a/grimoire/src/state.rs +++ b/grimoire/src/state.rs @@ -1 +1,76 @@ -pub struct State {} +use crate::{ + ast::expr::{Const, Expression}, + error::Error, + types::Type, +}; +use std::collections::{HashMap, HashSet}; + +#[derive(Debug, Default)] +pub struct State { + variables: HashMap<String, Const>, + identifiers: HashSet<String>, + + // To transform into HashMap... + actors: HashSet<String>, + printers: HashSet<String>, +} + +impl State { + pub fn resolve(&self, var: impl AsRef<str>) -> Result<&Const, Error> { + self.variables + .get(var.as_ref()) + .ok_or_else(|| Error::Undefined(var.as_ref().to_string())) + } + + pub fn evaluate(&self, expr: Expression) -> Result<Const, Error> { + todo!() + } + + pub fn evaluate_as(&self, expr: Expression, ty: Type) -> Result<Const, Error> { + match self.evaluate(expr)? { + Const::Int(x) => match ty { + Type::Float => Ok(Const::Flt(x as f32)), + Type::String => Ok(Const::Str(x.to_string())), + Type::Boolean => Ok(Const::Bool(x != 0)), + Type::Integer => Ok(Const::Int(x)), + ty => Err(Error::CantCast(Const::Int(x), ty)), + }, + Const::Flt(x) => match ty { + Type::Float => Ok(Const::Flt(x)), + Type::String => Ok(Const::Str(x.to_string())), + Type::Boolean => Ok(Const::Bool(x.abs() <= f32::EPSILON)), + Type::Integer => Ok(Const::Int(x as i32)), + ty => Err(Error::CantCast(Const::Flt(x), ty)), + }, + Const::Bool(x) => match ty { + Type::Float => Ok(Const::Flt(if x { 0. } else { 1. })), + Type::String => Ok(Const::Str(x.to_string())), + Type::Boolean => Ok(Const::Bool(x)), + Type::Integer => Ok(Const::Int(x as i32)), + ty => Err(Error::CantCast(Const::Bool(x), ty)), + }, + Const::Str(x) => match ty { + Type::String => Ok(Const::Str(x)), + Type::Float => str::parse::<f32>(&x).map(Const::Flt).map_err(Into::into), + Type::Integer => str::parse::<i32>(&x).map(Const::Int).map_err(Into::into), + Type::Boolean => str::parse::<bool>(&x).map(Const::Bool).map_err(Into::into), + Type::Identifier | Type::Actor | Type::Printer => unreachable!(), + }, + Const::Ident(x) => match ty { + Type::Identifier => match self.identifiers.contains(&x) { + true => Ok(Const::Ident(x)), + false => Err(Error::Undefined(x)), + }, + Type::Actor => match self.actors.contains(&x) { + true => Ok(Const::Ident(x)), // TODO: Return the Actor object? + false => Err(Error::Undefined(x)), + }, + Type::Printer => match self.printers.contains(&x) { + true => Ok(Const::Ident(x)), // TODO: Return the Printer object? + false => Err(Error::Undefined(x)), + }, + ty => Err(Error::CantCast(Const::Ident(x), ty)), + }, + } + } +} diff --git a/grimoire/src/types.rs b/grimoire/src/types.rs new file mode 100644 index 0000000..da91ab8 --- /dev/null +++ b/grimoire/src/types.rs @@ -0,0 +1,12 @@ +/// Allowed types for arguments. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Type { + Float, + String, + Integer, + Boolean, + + Identifier, + Actor, + Printer, +} -- GitLab