From f641186e01fd382d60f5759b51405a0293cdc40b Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Sun, 21 Nov 2021 00:33:13 -0800 Subject: [PATCH] Compress the Constant enum from 136 to 32 bytes --- .../dmm-tools/src/render_passes/structures.rs | 4 +- crates/dreamchecker/src/lib.rs | 10 ++-- crates/dreammaker/src/builtins.rs | 12 +++-- crates/dreammaker/src/constants.rs | 52 +++++++++---------- crates/dreammaker/src/objtree.rs | 16 +++--- crates/dreammaker/tests/format_tests.rs | 4 +- 6 files changed, 51 insertions(+), 47 deletions(-) diff --git a/crates/dmm-tools/src/render_passes/structures.rs b/crates/dmm-tools/src/render_passes/structures.rs index 0c71a68e..b7cf08e3 100644 --- a/crates/dmm-tools/src/render_passes/structures.rs +++ b/crates/dmm-tools/src/render_passes/structures.rs @@ -13,14 +13,14 @@ impl RenderPass for Spawners { } match atom.get_var("spawn_list", objtree) { &Constant::List(ref elements) => { - for &(ref key, _) in elements { + for &(ref key, _) in elements.iter() { // TODO: use a more civilized lookup method let type_key; let reference = match key { &Constant::String(ref s) => s, &Constant::Prefab(ref fab) => { type_key = dm::ast::FormatTreePath(&fab.path).to_string(); - &type_key + type_key.as_str() }, _ => continue, }; diff --git a/crates/dreamchecker/src/lib.rs b/crates/dreamchecker/src/lib.rs index 506a1ce1..80c2d9b5 100644 --- a/crates/dreamchecker/src/lib.rs +++ b/crates/dreamchecker/src/lib.rs @@ -1677,8 +1677,8 @@ impl<'o, 's> AnalyzeProc<'o, 's> { Term::Null => Analysis::null(), Term::Int(number) => Analysis::from_value(self.objtree, Constant::from(*number), type_hint), Term::Float(number) => Analysis::from_value(self.objtree, Constant::from(*number), type_hint), - Term::String(text) => Analysis::from_value(self.objtree, Constant::String(text.to_owned()), type_hint), - Term::Resource(text) => Analysis::from_value(self.objtree, Constant::Resource(text.to_owned()), type_hint), + Term::String(text) => Analysis::from_value(self.objtree, Constant::String(text.as_str().into()), type_hint), + Term::Resource(text) => Analysis::from_value(self.objtree, Constant::Resource(text.as_str().into()), type_hint), Term::As(_) => assumption_set![Assumption::IsNum(true)].into(), Term::Ident(unscoped_name) => { @@ -1707,7 +1707,7 @@ impl<'o, 's> AnalyzeProc<'o, 's> { Analysis { static_ty: StaticType::None, aset: assumption_set![Assumption::IsPath(true, nav.ty())].into(), - value: Some(Constant::Prefab(pop)), + value: Some(Constant::Prefab(Box::new(pop))), fix_hint: None, is_impure: None, } @@ -2247,7 +2247,7 @@ impl<'o, 's> AnalyzeProc<'o, 's> { .register(self.context); return Analysis::empty() }); - guard!(let Some(arglist) = VALID_FILTER_TYPES.get(typevalue.as_str()) else { + guard!(let Some(arglist) = VALID_FILTER_TYPES.get(&typevalue) else { error(location, format!("filter() called with invalid type keyword parameter value '{}'", typevalue)) .register(self.context); return Analysis::empty() @@ -2259,7 +2259,7 @@ impl<'o, 's> AnalyzeProc<'o, 's> { .register(self.context); } } - if let Some((flagfieldname, exclusive, can_be_zero, valid_flags)) = VALID_FILTER_FLAGS.get(typevalue.as_str()) { + if let Some((flagfieldname, exclusive, can_be_zero, valid_flags)) = VALID_FILTER_FLAGS.get(&typevalue) { if let Some(flagsvalue) = param_expr_map.get(flagfieldname) { self.check_filter_flag(flagsvalue, *can_be_zero, location, typevalue, valid_flags, flagfieldname, *exclusive); } diff --git a/crates/dreammaker/src/builtins.rs b/crates/dreammaker/src/builtins.rs index 97dbe05d..c17c164d 100644 --- a/crates/dreammaker/src/builtins.rs +++ b/crates/dreammaker/src/builtins.rs @@ -200,12 +200,16 @@ pub fn default_defines(defines: &mut DefineMap) { /// Register BYOND builtins into the specified object tree. pub fn register_builtins(tree: &mut ObjectTreeBuilder) { + fn path(path: &'static [&'static str]) -> Constant { + Constant::Prefab(Box::new(super::constants::Pop { + path: path.iter().copied().map(String::from).collect::>(), + vars: Default::default(), + })) + } + macro_rules! path { ($(/$elem:ident)*) => { - Constant::Prefab(super::constants::Pop { - path: vec![$(stringify!($elem).into()),*].into_boxed_slice(), - vars: Default::default(), - }) + path(&[$(stringify!($elem),)*]) } } macro_rules! int { diff --git a/crates/dreammaker/src/constants.rs b/crates/dreammaker/src/constants.rs index 63c212f2..bd9959c9 100644 --- a/crates/dreammaker/src/constants.rs +++ b/crates/dreammaker/src/constants.rs @@ -56,20 +56,20 @@ pub enum Constant { /// A `new` call. New { /// The type to be instantiated. - type_: Option, + type_: Option>, /// The list of arugments to pass to the `New()` proc. - args: Option)>>, + args: Option)]>>, }, /// A `list` literal. Elements have optional associations. - List(Vec<(Constant, Option)>), + List(Box<[(Constant, Option)]>), /// A call to a constant type constructor. - Call(ConstFn, Vec<(Constant, Option)>), + Call(ConstFn, Box<[(Constant, Option)]>), /// A prefab literal. - Prefab(Pop), + Prefab(Box), /// A string literal. - String(String), + String(Box), /// A resource literal. - Resource(String), + Resource(Box), /// A floating-point (or integer) literal, following BYOND's rules. Float(f32), } @@ -144,15 +144,13 @@ impl Constant { // ------------------------------------------------------------------------ // Constructors - pub const EMPTY_STRING: &'static Constant = &Constant::String(String::new()); - pub fn null() -> &'static Constant { static NULL: Constant = Constant::Null(None); &NULL } #[inline] - pub fn string>(s: S) -> Constant { + pub fn string>>(s: S) -> Constant { Constant::String(s.into()) } @@ -212,7 +210,7 @@ impl Constant { pub fn eq_string(&self, string: &str) -> bool { match *self { - Constant::String(ref s) => s == string, + Constant::String(ref s) => &**s == string, _ => false, } } @@ -220,7 +218,7 @@ impl Constant { pub fn eq_resource(&self, resource: &str) -> bool { match self { Constant::String(ref s) | - Constant::Resource(ref s) => s == resource, + Constant::Resource(ref s) => &**s == resource, _ => false, } } @@ -230,7 +228,7 @@ impl Constant { pub fn contains_key(&self, key: &Constant) -> bool { if let Constant::List(ref elements) = *self { - for &(ref k, _) in elements { + for &(ref k, _) in elements.iter() { if key == k { return true; } @@ -243,7 +241,7 @@ impl Constant { match (self, key) { // Narrowing conversion is intentional. (&Constant::List(ref elements), &Constant::Float(i)) => return elements.get(i as usize).map(|&(ref k, _)| k), - (&Constant::List(ref elements), key) => for &(ref k, ref v) in elements { + (&Constant::List(ref elements), key) => for &(ref k, ref v) in elements.iter() { if key == k { return v.as_ref(); } @@ -292,7 +290,7 @@ impl PartialEq for Constant { fn eq(&self, other: &str) -> bool { match self { Constant::String(ref s) | - Constant::Resource(ref s) => s == other, + Constant::Resource(ref s) => &**s == other, _ => false, } } @@ -585,7 +583,7 @@ impl<'a> ConstantFolder<'a> { /// list of expressions, keyword arguments disallowed #[allow(dead_code)] fn expr_vec(&mut self, v: Vec) -> Result, DMError> { - let mut out = Vec::new(); + let mut out = Vec::with_capacity(v.len()); for each in v { out.push(self.expr(each, None)?); } @@ -593,8 +591,8 @@ impl<'a> ConstantFolder<'a> { } /// arguments or keyword arguments - fn arguments(&mut self, v: Box<[Expression]>) -> Result)>, DMError> { - let mut out = Vec::new(); + fn arguments(&mut self, v: Box<[Expression]>) -> Result)]>, DMError> { + let mut out = Vec::with_capacity(v.len()); for each in Vec::from(v).into_iter() { out.push(match each { // handle associations @@ -604,7 +602,7 @@ impl<'a> ConstantFolder<'a> { rhs, } => { let key = match Term::from(*lhs) { - Term::Ident(ident) => Constant::String(ident), + Term::Ident(ident) => Constant::String(ident.into()), other => self.term(other, None)?, }; (key, Some(self.expr(*rhs, None)?)) @@ -612,7 +610,7 @@ impl<'a> ConstantFolder<'a> { key => (self.expr(key, None)?, None), }); } - Ok(out) + Ok(out.into()) } fn follow(&mut self, term: Constant, follow: Follow) -> Result { @@ -696,7 +694,7 @@ impl<'a> ConstantFolder<'a> { integer!(RShift >>); match (op, lhs, rhs) { - (BinaryOp::Add, String(lhs), String(rhs)) => Ok(String(lhs + &rhs)), + (BinaryOp::Add, String(lhs), String(rhs)) => Ok(String((std::string::String::from(lhs) + &rhs).into())), (BinaryOp::Eq, lhs, rhs) => Ok(Constant::from(lhs == rhs)), (BinaryOp::NotEq, lhs, rhs) => Ok(Constant::from(lhs != rhs)), (BinaryOp::And, lhs, rhs) => Ok(if lhs.to_bool() { rhs } else { lhs }), @@ -709,7 +707,7 @@ impl<'a> ConstantFolder<'a> { Ok(match term { Term::Null => Constant::Null(type_hint.cloned()), Term::NewPrefab { prefab, args } => Constant::New { - type_: Some(self.prefab(*prefab)?), + type_: Some(Box::new(self.prefab(*prefab)?)), args: match args { Some(args) => Some(self.arguments(args)?), None => None, @@ -737,7 +735,7 @@ impl<'a> ConstantFolder<'a> { "cos" => self.trig_op(args, f32::cos)?, "arcsin" => self.trig_op(args, f32::asin)?, "arccos" => self.trig_op(args, f32::acos)?, - "rgb" => Constant::String(self.rgb(args)?), + "rgb" => Constant::String(self.rgb(args)?.into()), "defined" if self.defines.is_some() => { let defines = self.defines.unwrap(); // annoying, but keeps the match clean if args.len() != 1 { @@ -751,10 +749,10 @@ impl<'a> ConstantFolder<'a> { // other functions are no-goes _ => return Err(self.error(format!("non-constant function call: {}", ident))), }, - Term::Prefab(prefab) => Constant::Prefab(self.prefab(*prefab)?), + Term::Prefab(prefab) => Constant::Prefab(Box::new(self.prefab(*prefab)?)), Term::Ident(ident) => self.ident(ident, false)?, - Term::String(v) => Constant::String(v), - Term::Resource(v) => Constant::Resource(v), + Term::String(v) => Constant::String(v.into()), + Term::Resource(v) => Constant::Resource(v.into()), Term::Int(v) => Constant::Float(v as f32), Term::Float(v) => Constant::from(v), Term::Expr(expr) => self.expr(*expr, type_hint)?, @@ -865,7 +863,7 @@ impl<'a> ConstantFolder<'a> { let mut color_args = ColorArgs::default(); // Get the value of the `space` kwarg if present, or collect which kwargs are set to automatically determine the color space. - for (value, potential_kwarg_value) in &arguments { + for (value, potential_kwarg_value) in arguments.iter() { // Check for kwargs if we're in the right form if let Some(kwarg_value) = potential_kwarg_value { if let Some(kwarg) = value.as_str() { diff --git a/crates/dreammaker/src/objtree.rs b/crates/dreammaker/src/objtree.rs index 3297f3d6..2c52f125 100644 --- a/crates/dreammaker/src/objtree.rs +++ b/crates/dreammaker/src/objtree.rs @@ -7,7 +7,7 @@ use indexmap::IndexMap; use ahash::RandomState; use super::ast::{Expression, VarType, VarTypeBuilder, VarSuffix, PathOp, Parameter, Block, ProcDeclKind, Ident}; -use super::constants::{Constant, Pop}; +use super::constants::Constant; use super::docs::DocCollection; use super::{DMError, Location, Context, Severity}; @@ -730,9 +730,9 @@ impl ObjectTree { } pub fn type_by_constant(&self, constant: &Constant) -> Option { - match *constant { - Constant::String(ref string_path) => self.find(string_path), - Constant::Prefab(Pop { ref path, .. }) => self.type_by_path(path.iter()), + match constant { + Constant::String(string_path) => self.find(string_path), + Constant::Prefab(pop) => self.type_by_path(pop.path.iter()), _ => None, } } @@ -827,6 +827,7 @@ impl ObjectTreeBuilder { } else { let constant_buf; let mut parent_type_buf; + let empty_string; let parent_type = if path == "/atom" { "/datum" } else if path == "/turf" || path == "/area" { @@ -855,7 +856,8 @@ impl ObjectTreeBuilder { Err(e) => Err(e), } } else if path == "/client" { - Ok(Constant::EMPTY_STRING) + empty_string = Constant::String("".into()); + Ok(&empty_string) } else { // A weird situation which should not happen. Err(DMError::new(location, format!("missing {}/parent_type", path))) @@ -865,9 +867,9 @@ impl ObjectTreeBuilder { Ok(Constant::String(s)) => { parent_type = s; } - Ok(Constant::Prefab(Pop { ref path, ref vars })) if vars.is_empty() => { + Ok(Constant::Prefab(ref pop)) if pop.vars.is_empty() => { parent_type_buf = String::new(); - for piece in path.iter() { + for piece in pop.path.iter() { parent_type_buf.push('/'); parent_type_buf.push_str(&piece); } diff --git a/crates/dreammaker/tests/format_tests.rs b/crates/dreammaker/tests/format_tests.rs index 10e246e7..8fcf4b9c 100644 --- a/crates/dreammaker/tests/format_tests.rs +++ b/crates/dreammaker/tests/format_tests.rs @@ -43,9 +43,9 @@ fn lists() { assert_eq!(List(vec![ (Constant::string("KNOCKDOWN"), Some(Float(0.))), (Constant::string("THROW"), Some(Float(0.))), - ]).to_string(), r#"list("KNOCKDOWN" = 0, "THROW" = 0)"#); + ].into()).to_string(), r#"list("KNOCKDOWN" = 0, "THROW" = 0)"#); assert_eq!(List(vec![ (Constant::string("neutral"), None), (Constant::string("Syndicate"), None), - ]).to_string(), r#"list("neutral","Syndicate")"#); + ].into()).to_string(), r#"list("neutral","Syndicate")"#); }