Add ranges.

This commit is contained in:
Stephen Chung
2021-12-15 12:06:17 +08:00
parent 7251f34bce
commit ef14079c61
35 changed files with 1206 additions and 269 deletions

View File

@@ -9,14 +9,10 @@ use crate::func::builtin::get_builtin_binary_op_fn;
use crate::func::hashing::get_hasher;
use crate::tokenizer::Token;
use crate::types::dynamic::AccessMode;
use crate::{
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Position, Scope,
StaticVec, AST,
};
use crate::{calc_fn_hash, Dynamic, Engine, FnPtr, Position, Scope, StaticVec, AST, INT};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
any::TypeId,
convert::TryFrom,
hash::{Hash, Hasher},
mem,
@@ -155,17 +151,6 @@ impl<'a> OptimizerState<'a> {
.ok()
.map(|(v, _)| v)
}
// Has a system function a Rust-native override?
pub fn has_native_fn_override(&self, hash_script: u64, arg_types: &[TypeId]) -> bool {
let hash_params = calc_fn_params_hash(arg_types.iter().cloned());
let hash = combine_hashes(hash_script, hash_params);
// First check the global namespace and packages, but skip modules that are standard because
// they should never conflict with system functions.
self.engine.global_modules.iter().filter(|m| !m.standard).any(|m| m.contains_fn(hash))
// Then check sub-modules
|| self.engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash))
}
}
/// Optimize a block of [statements][Stmt].
@@ -478,21 +463,21 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
value.hash(hasher);
let hash = hasher.finish();
state.set_dirty();
let table = &mut x.0;
// First check hashes
if let Some(block) = table.get_mut(&hash) {
if let Some(mut condition) = mem::take(&mut block.0) {
// switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
optimize_expr(&mut condition, state, false);
let def_block = mem::take(&mut *x.1);
let def_stmt = optimize_stmt_block(def_block, state, true, true, false);
let def_pos = if x.1.position().is_none() {
*pos
} else {
x.1.position()
};
let def_stmt =
optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false);
*stmt = Stmt::If(
condition,
@@ -505,46 +490,121 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
} else {
// Promote the matched case
let new_pos = block.1.position();
let statements = mem::take(&mut *block.1);
let statements = optimize_stmt_block(statements, state, true, true, false);
let statements =
optimize_stmt_block(mem::take(&mut *block.1), state, true, true, false);
*stmt = Stmt::Block(statements.into_boxed_slice(), new_pos);
}
} else {
// Promote the default case
let def_block = mem::take(&mut *x.1);
let def_stmt = optimize_stmt_block(def_block, state, true, true, false);
let def_pos = if x.1.position().is_none() {
*pos
} else {
x.1.position()
};
*stmt = Stmt::Block(def_stmt.into_boxed_slice(), def_pos);
state.set_dirty();
return;
}
// Then check ranges
let ranges = &mut x.2;
if value.is::<INT>() && !ranges.is_empty() {
let value = value.as_int().expect("`INT`");
// Only one range or all ranges without conditions
if ranges.len() == 1 || ranges.iter().all(|(_, _, _, c, _)| c.is_none()) {
for (_, _, _, condition, stmt_block) in
ranges
.iter_mut()
.filter(|&&mut (start, end, inclusive, _, _)| {
(!inclusive && (start..end).contains(&value))
|| (inclusive && (start..=end).contains(&value))
})
{
if let Some(mut condition) = mem::take(condition) {
// switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def }
optimize_expr(&mut condition, state, false);
let def_block = mem::take(&mut *x.1);
let def_stmt = optimize_stmt_block(def_block, state, true, true, false);
let def_pos = if x.1.position().is_none() {
*pos
} else {
x.1.position()
};
*stmt = Stmt::If(
condition,
Box::new((
mem::take(stmt_block),
Stmt::Block(def_stmt.into_boxed_slice(), def_pos).into(),
)),
match_expr.position(),
);
} else {
// Promote the matched case
let new_pos = stmt_block.position();
let statements = mem::take(&mut **stmt_block);
let statements =
optimize_stmt_block(statements, state, true, true, false);
*stmt = Stmt::Block(statements.into_boxed_slice(), new_pos);
}
state.set_dirty();
return;
}
} else {
// Multiple ranges - clear the table and just keep the right ranges
if !table.is_empty() {
state.set_dirty();
}
table.clear();
let old_ranges_len = ranges.len();
ranges.retain(|&mut (start, end, inclusive, _, _)| {
(!inclusive && (start..end).contains(&value))
|| (inclusive && (start..=end).contains(&value))
});
if ranges.len() != old_ranges_len {
state.set_dirty();
}
for (_, _, _, condition, stmt_block) in ranges.iter_mut() {
let statements = mem::take(&mut **stmt_block);
**stmt_block =
optimize_stmt_block(statements, state, preserve_result, true, false);
if let Some(mut c) = mem::take(condition) {
optimize_expr(&mut c, state, false);
match c {
Expr::Unit(_) | Expr::BoolConstant(true, _) => state.set_dirty(),
_ => *condition = Some(c),
}
}
}
return;
}
}
// Promote the default case
state.set_dirty();
let def_pos = if x.1.position().is_none() {
*pos
} else {
x.1.position()
};
let def_stmt = optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false);
*stmt = Stmt::Block(def_stmt.into_boxed_slice(), def_pos);
}
// switch
Stmt::Switch(match_expr, x, _) => {
optimize_expr(match_expr, state, false);
x.0.values_mut().for_each(|block| {
let condition = mem::take(&mut block.0).map_or_else(
|| Expr::Unit(Position::NONE),
|mut condition| {
optimize_expr(&mut condition, state, false);
condition
},
);
let statements = mem::take(block.1.deref_mut());
*block.1 = optimize_stmt_block(statements, state, preserve_result, true, false);
match condition {
Expr::Unit(_) | Expr::BoolConstant(true, _) => (),
_ => {
block.0 = Some(condition);
*block.1 = optimize_stmt_block(
mem::take(block.1.deref_mut()),
state,
preserve_result,
true,
false,
);
if let Some(mut condition) = mem::take(&mut block.0) {
optimize_expr(&mut condition, state, false);
match condition {
Expr::Unit(_) | Expr::BoolConstant(true, _) => state.set_dirty(),
_ => block.0 = Some(condition),
}
}
});
@@ -990,7 +1050,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
return;
}
// Overloaded operators can override built-in.
_ if x.args.len() == 2 && !state.has_native_fn_override(x.hashes.native, arg_types.as_ref()) => {
_ if x.args.len() == 2 && !state.engine.has_native_fn_override(x.hashes.native, arg_types.as_ref()) => {
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
.and_then(|f| {
#[cfg(not(feature = "no_function"))]