Handle constants propagation.

This commit is contained in:
Stephen Chung 2021-05-16 21:21:13 +08:00
parent cce2e02428
commit d1fc362eec
2 changed files with 58 additions and 55 deletions

View File

@ -4,7 +4,6 @@ use crate::ast::{Expr, OpAssignment, Stmt};
use crate::dynamic::AccessMode; use crate::dynamic::AccessMode;
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF}; use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF};
use crate::fn_builtin::get_builtin_binary_op_fn; use crate::fn_builtin::get_builtin_binary_op_fn;
use crate::parser::map_dynamic_to_expr;
use crate::token::Token; use crate::token::Token;
use crate::utils::get_hasher; use crate::utils::get_hasher;
use crate::{ use crate::{
@ -59,7 +58,7 @@ struct State<'a> {
/// Has the [`AST`] been changed during this pass? /// Has the [`AST`] been changed during this pass?
changed: bool, changed: bool,
/// Collection of constants to use for eager function evaluations. /// Collection of constants to use for eager function evaluations.
variables: Vec<(String, AccessMode, Expr)>, variables: Vec<(String, AccessMode, Option<Dynamic>)>,
/// Activate constants propagation? /// Activate constants propagation?
propagate_constants: bool, propagate_constants: bool,
/// An [`Engine`] instance for eager function evaluation. /// An [`Engine`] instance for eager function evaluation.
@ -109,21 +108,21 @@ impl<'a> State<'a> {
} }
/// Add a new constant to the list. /// Add a new constant to the list.
#[inline(always)] #[inline(always)]
pub fn push_var(&mut self, name: &str, access: AccessMode, value: Expr) { pub fn push_var(&mut self, name: &str, access: AccessMode, value: Option<Dynamic>) {
self.variables.push((name.into(), access, value)) self.variables.push((name.into(), access, value))
} }
/// Look up a constant from the list. /// Look up a constant from the list.
#[inline] #[inline]
pub fn find_constant(&self, name: &str) -> Option<&Expr> { pub fn find_constant(&self, name: &str) -> Option<&Dynamic> {
if !self.propagate_constants { if !self.propagate_constants {
return None; return None;
} }
self.variables.iter().rev().find_map(|(n, access, expr)| { self.variables.iter().rev().find_map(|(n, access, value)| {
if n == name { if n == name {
match access { match access {
AccessMode::ReadWrite => None, AccessMode::ReadWrite => None,
AccessMode::ReadOnly => Some(expr), AccessMode::ReadOnly => value.as_ref(),
} }
} else { } else {
None None
@ -217,13 +216,17 @@ fn optimize_stmt_block(
optimize_expr(value_expr, state); optimize_expr(value_expr, state);
if value_expr.is_constant() { if value_expr.is_constant() {
state.push_var(&x.name, AccessMode::ReadOnly, value_expr.clone()); state.push_var(
&x.name,
AccessMode::ReadOnly,
value_expr.get_constant_value(),
);
} }
} }
// Add variables into the state // Add variables into the state
Stmt::Let(value_expr, x, _, _) => { Stmt::Let(value_expr, x, _, _) => {
optimize_expr(value_expr, state); optimize_expr(value_expr, state);
state.push_var(&x.name, AccessMode::ReadWrite, Expr::Unit(x.pos)); state.push_var(&x.name, AccessMode::ReadWrite, None);
} }
// Optimize the statement // Optimize the statement
_ => optimize_stmt(stmt, state, preserve_result), _ => optimize_stmt(stmt, state, preserve_result),
@ -925,15 +928,16 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
// Search for overloaded operators (can override built-in). // Search for overloaded operators (can override built-in).
if !has_native_fn(state, x.hashes.native_hash(), arg_types.as_ref()) { if !has_native_fn(state, x.hashes.native_hash(), arg_types.as_ref()) {
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1]) if let Some(mut result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
.and_then(|f| { .and_then(|f| {
let ctx = (state.engine, x.name.as_ref(), state.lib).into(); let ctx = (state.engine, x.name.as_ref(), state.lib).into();
let (first, second) = arg_values.split_first_mut().unwrap(); let (first, second) = arg_values.split_first_mut().unwrap();
(f)(ctx, &mut [ first, &mut second[0] ]).ok() (f)(ctx, &mut [ first, &mut second[0] ]).ok()
}) })
.and_then(|result| map_dynamic_to_expr(result, *pos)) .map(Expr::from)
{ {
state.set_dirty(); state.set_dirty();
result.set_position(*pos);
*expr = result; *expr = result;
return; return;
} }
@ -977,18 +981,19 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
"" ""
}; };
if let Some(result) = call_fn_with_constant_arguments(&state, x.name.as_ref(), &mut arg_values) if let Some(mut result) = call_fn_with_constant_arguments(&state, x.name.as_ref(), &mut arg_values)
.or_else(|| { .or_else(|| {
if !arg_for_type_of.is_empty() { if !arg_for_type_of.is_empty() {
// Handle `type_of()` // Handle `type_of()`
Some(arg_for_type_of.to_string().into()) Some(arg_for_type_of.to_string().into())
} else { } else {
None None
} }
}) })
.and_then(|result| map_dynamic_to_expr(result, *pos)) .map(Expr::from)
{ {
state.set_dirty(); state.set_dirty();
result.set_position(*pos);
*expr = result; *expr = result;
return; return;
} }
@ -1014,12 +1019,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
// constant-name // constant-name
Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => { Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => {
state.set_dirty();
// Replace constant with value // Replace constant with value
let mut result = state.find_constant(&x.2).unwrap().clone(); let pos = *pos;
result.set_position(*pos); *expr = Expr::from(state.find_constant(&x.2).unwrap().clone());
*expr = result; expr.set_position(pos);
state.set_dirty();
} }
// Custom syntax // Custom syntax
@ -1055,13 +1059,9 @@ fn optimize_top_level(
// Add constants and variables from the scope // Add constants and variables from the scope
scope.iter().for_each(|(name, constant, value)| { scope.iter().for_each(|(name, constant, value)| {
if !constant { if !constant {
state.push_var(name, AccessMode::ReadWrite, Expr::Unit(Position::NONE)); state.push_var(name, AccessMode::ReadWrite, None);
} else { } else {
state.push_var( state.push_var(name, AccessMode::ReadOnly, Some(value));
name,
AccessMode::ReadOnly,
Expr::DynamicConstant(Box::new(value), Position::NONE),
);
} }
}); });

View File

@ -3121,29 +3121,32 @@ impl Engine {
} }
} }
/// Map a `Dynamic` value to an expression. impl From<Dynamic> for Expr {
/// fn from(value: Dynamic) -> Self {
/// Returns Some(expression) if conversion is successful. Otherwise None. match value.0 {
pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> { #[cfg(not(feature = "no_float"))]
match value.0 { Union::Float(value, _, _) => Self::FloatConstant(value, Position::NONE),
#[cfg(not(feature = "no_float"))]
Union::Float(value, _, _) => Some(Expr::FloatConstant(value, pos)),
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
Union::Decimal(value, _, _) => Some(Expr::DynamicConstant(Box::new((*value).into()), pos)), Union::Decimal(value, _, _) => {
Self::DynamicConstant(Box::new((*value).into()), Position::NONE)
}
Union::Unit(_, _, _) => Some(Expr::Unit(pos)), Union::Unit(_, _, _) => Self::Unit(Position::NONE),
Union::Int(value, _, _) => Some(Expr::IntegerConstant(value, pos)), Union::Int(value, _, _) => Self::IntegerConstant(value, Position::NONE),
Union::Char(value, _, _) => Some(Expr::CharConstant(value, pos)), Union::Char(value, _, _) => Self::CharConstant(value, Position::NONE),
Union::Str(value, _, _) => Some(Expr::StringConstant(value, pos)), Union::Str(value, _, _) => Self::StringConstant(value, Position::NONE),
Union::Bool(value, _, _) => Some(Expr::BoolConstant(value, pos)), Union::Bool(value, _, _) => Self::BoolConstant(value, Position::NONE),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(array, _, _) => Some(Expr::DynamicConstant(Box::new((*array).into()), pos)), Union::Array(array, _, _) => {
Self::DynamicConstant(Box::new((*array).into()), Position::NONE)
}
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(map, _, _) => Some(Expr::DynamicConstant(Box::new((*map).into()), pos)), Union::Map(map, _, _) => Self::DynamicConstant(Box::new((*map).into()), Position::NONE),
_ => None, _ => Self::DynamicConstant(Box::new(value.into()), Position::NONE),
}
} }
} }