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

View File

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