Make default switch case Optional.

This commit is contained in:
Stephen Chung 2022-07-18 22:30:09 +08:00
parent 7dca916c45
commit f2724444bb
4 changed files with 81 additions and 56 deletions

View File

@ -266,10 +266,10 @@ pub struct SwitchCasesCollection {
pub case_blocks: StaticVec<ConditionalStmtBlock>, pub case_blocks: StaticVec<ConditionalStmtBlock>,
/// Dictionary mapping value hashes to [`ConditionalStmtBlock`]'s. /// Dictionary mapping value hashes to [`ConditionalStmtBlock`]'s.
pub cases: BTreeMap<u64, CaseBlocksList>, pub cases: BTreeMap<u64, CaseBlocksList>,
/// Statements block for the default case (there can be no condition for the default case).
pub def_case: usize,
/// List of range cases. /// List of range cases.
pub ranges: StaticVec<RangeCase>, pub ranges: StaticVec<RangeCase>,
/// Statements block for the default case (there can be no condition for the default case).
pub def_case: Option<usize>,
} }
/// _(internals)_ A `try-catch` block. /// _(internals)_ A `try-catch` block.
@ -765,7 +765,8 @@ impl Stmt {
let block = &sw.case_blocks[r.index()]; let block = &sw.case_blocks[r.index()];
block.condition.is_pure() && block.statements.iter().all(Stmt::is_pure) block.condition.is_pure() && block.statements.iter().all(Stmt::is_pure)
}) })
&& sw.case_blocks[sw.def_case] && sw.def_case.is_some()
&& sw.case_blocks[sw.def_case.unwrap()]
.statements .statements
.iter() .iter()
.all(Stmt::is_pure) .all(Stmt::is_pure)
@ -935,12 +936,14 @@ impl Stmt {
} }
} }
} }
for s in &sw.case_blocks[sw.def_case].statements { if let Some(index) = sw.def_case {
for s in &sw.case_blocks[index].statements {
if !s.walk(path, on_node) { if !s.walk(path, on_node) {
return false; return false;
} }
} }
} }
}
Self::While(x, ..) | Self::Do(x, ..) => { Self::While(x, ..) | Self::Do(x, ..) => {
if !x.0.walk(path, on_node) { if !x.0.walk(path, on_node) {
return false; return false;

View File

@ -498,7 +498,8 @@ impl Engine {
} }
} else if let Ok(None) = stmt_block_result { } else if let Ok(None) = stmt_block_result {
// Default match clause // Default match clause
let def_case = &case_blocks[*def_case].statements; if let Some(index) = def_case {
let def_case = &case_blocks[*index].statements;
if !def_case.is_empty() { if !def_case.is_empty() {
self.eval_stmt_block( self.eval_stmt_block(
@ -507,6 +508,9 @@ impl Engine {
} else { } else {
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
} else {
Ok(Dynamic::UNIT)
}
} else { } else {
stmt_block_result.map(|_| Dynamic::UNIT) stmt_block_result.map(|_| Dynamic::UNIT)
} }

View File

@ -559,16 +559,22 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def } // switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
optimize_expr(condition, state, false); optimize_expr(condition, state, false);
let def_case = &mut case_blocks[*def_case].statements; let else_stmt = if let Some(index) = def_case {
let def_case = &mut case_blocks[*index].statements;
let def_span = def_case.span_or_else(*pos, Position::NONE); let def_span = def_case.span_or_else(*pos, Position::NONE);
let def_case: StmtBlockContainer = mem::take(def_case); let def_case: StmtBlockContainer = mem::take(def_case);
let def_stmt = let def_stmt =
optimize_stmt_block(def_case, state, true, true, false); optimize_stmt_block(def_case, state, true, true, false);
StmtBlock::new_with_span(def_stmt, def_span)
} else {
StmtBlock::NONE
};
*stmt = Stmt::If( *stmt = Stmt::If(
( (
mem::take(condition), mem::take(condition),
mem::take(&mut b.statements), mem::take(&mut b.statements),
StmtBlock::new_with_span(def_stmt, def_span), else_stmt,
) )
.into(), .into(),
match_expr.start_position(), match_expr.start_position(),
@ -630,21 +636,21 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def } // switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def }
optimize_expr(&mut condition, state, false); optimize_expr(&mut condition, state, false);
let def_case = &mut case_blocks[*def_case].statements; let else_stmt = if let Some(index) = def_case {
let def_case = &mut case_blocks[*index].statements;
let def_span = def_case.span_or_else(*pos, Position::NONE); let def_span = def_case.span_or_else(*pos, Position::NONE);
let def_case: StmtBlockContainer = mem::take(def_case); let def_case: StmtBlockContainer = mem::take(def_case);
let def_stmt = let def_stmt =
optimize_stmt_block(def_case, state, true, true, false); optimize_stmt_block(def_case, state, true, true, false);
StmtBlock::new_with_span(def_stmt, def_span)
} else {
StmtBlock::NONE
};
let statements = mem::take(&mut case_blocks[r.index()].statements); let statements = mem::take(&mut case_blocks[r.index()].statements);
*stmt = Stmt::If( *stmt = Stmt::If(
( (condition, statements, else_stmt).into(),
condition,
statements,
StmtBlock::new_with_span(def_stmt, def_span),
)
.into(),
match_expr.start_position(), match_expr.start_position(),
); );
} }
@ -690,11 +696,16 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// Promote the default case // Promote the default case
state.set_dirty(); state.set_dirty();
let def_case = &mut case_blocks[*def_case].statements;
if let Some(index) = def_case {
let def_case = &mut case_blocks[*index].statements;
let def_span = def_case.span_or_else(*pos, Position::NONE); let def_span = def_case.span_or_else(*pos, Position::NONE);
let def_case: StmtBlockContainer = mem::take(def_case); let def_case: StmtBlockContainer = mem::take(def_case);
let def_stmt = optimize_stmt_block(def_case, state, true, true, false); let def_stmt = optimize_stmt_block(def_case, state, true, true, false);
*stmt = (def_stmt, def_span).into(); *stmt = (def_stmt, def_span).into();
} else {
*stmt = StmtBlock::empty(*pos).into();
}
} }
// switch // switch
Stmt::Switch(x, ..) => { Stmt::Switch(x, ..) => {
@ -735,11 +746,13 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
} }
// Remove false cases // Remove false cases
let cases_len = cases.len();
cases.retain(|_, list| { cases.retain(|_, list| {
// Remove all entries that have false conditions // Remove all entries that have false conditions
list.retain(|index| match case_blocks[*index].condition { list.retain(|index| match case_blocks[*index].condition {
Expr::BoolConstant(false, ..) => false, Expr::BoolConstant(false, ..) => {
state.set_dirty();
false
}
_ => true, _ => true,
}); });
// Remove all entries after a `true` condition // Remove all entries after a `true` condition
@ -750,14 +763,21 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
_ => false, _ => false,
}) })
{ {
if n + 1 < list.len() {
state.set_dirty();
list.truncate(n + 1); list.truncate(n + 1);
} }
// Remove if no entry left
!list.is_empty()
});
if cases.len() != cases_len {
state.set_dirty();
} }
// Remove if no entry left
match list.is_empty() {
true => {
state.set_dirty();
false
}
false => true,
}
});
// Remove false ranges // Remove false ranges
ranges.retain(|r| match case_blocks[r.index()].condition { ranges.retain(|r| match case_blocks[r.index()].condition {
Expr::BoolConstant(false, ..) => { Expr::BoolConstant(false, ..) => {
@ -767,13 +787,16 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
_ => true, _ => true,
}); });
let def_stmt_block = &mut case_blocks[*def_case].statements; if let Some(index) = def_case {
let def_stmt_block = &mut case_blocks[*index].statements;
let def_block = mem::take(&mut **def_stmt_block); let def_block = mem::take(&mut **def_stmt_block);
**def_stmt_block = optimize_stmt_block(def_block, state, preserve_result, true, false); **def_stmt_block =
optimize_stmt_block(def_block, state, preserve_result, true, false);
}
// Remove unused block statements // Remove unused block statements
for index in 0..case_blocks.len() { for index in 0..case_blocks.len() {
if *def_case == index if *def_case == Some(index)
|| cases.values().flat_map(|c| c.iter()).any(|&n| n == index) || cases.values().flat_map(|c| c.iter()).any(|&n| n == index)
|| ranges.iter().any(|r| r.index() == index) || ranges.iter().any(|r| r.index() == index)
{ {

View File

@ -1058,8 +1058,8 @@ impl Engine {
let mut case_blocks = StaticVec::<ConditionalStmtBlock>::new(); let mut case_blocks = StaticVec::<ConditionalStmtBlock>::new();
let mut cases = BTreeMap::<u64, CaseBlocksList>::new(); let mut cases = BTreeMap::<u64, CaseBlocksList>::new();
let mut ranges = StaticVec::<RangeCase>::new(); let mut ranges = StaticVec::<RangeCase>::new();
let mut def_stmt_pos = Position::NONE; let mut def_case = None;
let mut def_stmt_index = None; let mut def_case_pos = Position::NONE;
loop { loop {
const MISSING_RBRACE: &str = "to end this switch block"; const MISSING_RBRACE: &str = "to end this switch block";
@ -1075,8 +1075,8 @@ impl Engine {
.into_err(*pos), .into_err(*pos),
) )
} }
(Token::Underscore, pos) if def_stmt_index.is_none() => { (Token::Underscore, pos) if def_case.is_none() => {
def_stmt_pos = *pos; def_case_pos = *pos;
eat_token(input, Token::Underscore); eat_token(input, Token::Underscore);
let (if_clause, if_pos) = match_token(input, Token::If); let (if_clause, if_pos) = match_token(input, Token::If);
@ -1087,8 +1087,8 @@ impl Engine {
(Default::default(), Expr::BoolConstant(true, Position::NONE)) (Default::default(), Expr::BoolConstant(true, Position::NONE))
} }
_ if def_stmt_index.is_some() => { _ if def_case.is_some() => {
return Err(PERR::WrongSwitchDefaultCase.into_err(def_stmt_pos)) return Err(PERR::WrongSwitchDefaultCase.into_err(def_case_pos))
} }
_ => { _ => {
@ -1197,7 +1197,7 @@ impl Engine {
.or_insert_with(|| [index].into()); .or_insert_with(|| [index].into());
} }
} else { } else {
def_stmt_index = Some(index); def_case = Some(index);
} }
match input.peek().expect(NEVER_ENDS) { match input.peek().expect(NEVER_ENDS) {
@ -1223,11 +1223,6 @@ impl Engine {
} }
} }
let def_case = def_stmt_index.unwrap_or_else(|| {
case_blocks.push(Default::default());
case_blocks.len() - 1
});
let cases = SwitchCasesCollection { let cases = SwitchCasesCollection {
case_blocks, case_blocks,
cases, cases,