diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index c09c3ff5..1754ee56 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -144,10 +144,17 @@ impl fmt::Debug for StmtBlock { } } -impl From for Stmt { - #[inline(always)] - fn from(block: StmtBlock) -> Self { - Self::Block(block.0.into_boxed_slice(), block.1) +impl From for StmtBlock { + #[inline] + fn from(stmt: Stmt) -> Self { + match stmt { + Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos), + Stmt::Noop(pos) => Self(StaticVec::new_const(), pos), + _ => { + let pos = stmt.position(); + Self(vec![stmt].into(), pos) + } + } } } @@ -176,7 +183,13 @@ pub enum Stmt { Noop(Position), /// `if` expr `{` stmt `}` `else` `{` stmt `}` If(Expr, Box<(StmtBlock, StmtBlock)>, Position), - /// `switch` expr `if` condition `{` literal or _ `=>` stmt `,` ... `}` + /// `switch` expr `{` literal or range or _ `if` condition `=>` stmt `,` ... `}` + /// + /// ### Data Structure + /// + /// 0) Hash table for (condition, block) + /// 1) Default block + /// 2) List of ranges: (start, end, inclusive, condition, statement) Switch( Expr, Box<( @@ -262,17 +275,10 @@ impl Default for Stmt { } } -impl From for StmtBlock { - #[inline] - fn from(stmt: Stmt) -> Self { - match stmt { - Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos), - Stmt::Noop(pos) => Self(StaticVec::new_const(), pos), - _ => { - let pos = stmt.position(); - Self(vec![stmt].into(), pos) - } - } +impl From for Stmt { + #[inline(always)] + fn from(block: StmtBlock) -> Self { + Self::Block(block.0.into_boxed_slice(), block.1) } } diff --git a/src/parser.rs b/src/parser.rs index 2d0601ca..538e990e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -15,7 +15,7 @@ use crate::tokenizer::{ }; use crate::types::dynamic::AccessMode; use crate::{ - calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Engine, ExclusiveRange, + calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Dynamic, Engine, ExclusiveRange, Identifier, ImmutableString, InclusiveRange, LexError, ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST, INT, }; @@ -1095,7 +1095,29 @@ fn parse_switch( def_stmt = match (hash, range) { (None, Some(range)) => { - ranges.push((range.0, range.1, range.2, condition, stmt.into())); + let is_empty = if range.2 { + (range.0..=range.1).is_empty() + } else { + (range.0..range.1).is_empty() + }; + + if !is_empty { + match (range.1.checked_sub(range.0), range.2) { + // Unroll single range + (Some(1), false) | (Some(0), true) => { + let value = Dynamic::from_int(range.0); + let hasher = &mut get_hasher(); + value.hash(hasher); + let hash = hasher.finish(); + + if !table.contains_key(&hash) { + table.insert(hash, (condition.clone(), stmt.into()).into()); + } + } + // Other range + _ => ranges.push((range.0, range.1, range.2, condition, stmt.into())), + } + } None } (Some(hash), None) => { @@ -1103,9 +1125,7 @@ fn parse_switch( None } (None, None) => Some(stmt.into()), - (Some(_), Some(_)) => { - unreachable!("cannot have both a hash and a range in a `switch` statement") - } + _ => unreachable!("both hash and range in `switch` statement case"), }; match input.peek().expect(NEVER_ENDS) {