Rewrite combo chains in parser.
This commit is contained in:
parent
a0869004a3
commit
e28bdd5b17
@ -857,63 +857,6 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optimize the structure of a chained expression.
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
|
||||||
fn optimize_chain_structure(expr: &mut Expr, state: &mut OptimizerState) {
|
|
||||||
let (mut x, options, pos, mut lhs_x, mut lhs_options, lhs_pos, sub, root): (
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
fn(_, _, _) -> Expr,
|
|
||||||
fn(_, _, _) -> Expr,
|
|
||||||
) = match expr.take() {
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(mut x, opt, pos) => match x.lhs.take() {
|
|
||||||
Expr::Index(x2, opt2, pos2) => (x, opt, pos, x2, opt2, pos2, Expr::Index, Expr::Index),
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Expr::Dot(x2, opt2, pos2) => (x, opt, pos, x2, opt2, pos2, Expr::Index, Expr::Dot),
|
|
||||||
_ => return,
|
|
||||||
},
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Expr::Dot(mut x, opt, pos) => match x.lhs.take() {
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(x2, opt2, pos2) => (x, opt, pos, x2, opt2, pos2, Expr::Dot, Expr::Index),
|
|
||||||
Expr::Dot(x2, opt2, pos2) => (x, opt, pos, x2, opt2, pos2, Expr::Dot, Expr::Index),
|
|
||||||
_ => return,
|
|
||||||
},
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Find the end of the chain
|
|
||||||
let mut last_node = lhs_x.as_mut();
|
|
||||||
let mut last_options = &mut lhs_options;
|
|
||||||
|
|
||||||
while !last_options.contains(ASTFlags::BREAK) {
|
|
||||||
match last_node.rhs {
|
|
||||||
Expr::Index(ref mut x, ref mut options2, ..) => {
|
|
||||||
last_node = x.as_mut();
|
|
||||||
last_options = options2;
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Expr::Dot(ref mut x, ref mut options2, ..) => {
|
|
||||||
last_node = x.as_mut();
|
|
||||||
last_options = options2;
|
|
||||||
}
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
last_options.remove(ASTFlags::BREAK);
|
|
||||||
|
|
||||||
x.lhs = last_node.rhs.take();
|
|
||||||
last_node.rhs = sub(x, options, pos);
|
|
||||||
*expr = root(lhs_x, lhs_options, lhs_pos);
|
|
||||||
state.set_dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Optimize an [expression][Expr].
|
/// Optimize an [expression][Expr].
|
||||||
fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||||
// These keywords are handled specially
|
// These keywords are handled specially
|
||||||
@ -979,11 +922,6 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
*expr = Expr::from_dynamic(Dynamic::FALSE, *pos);
|
*expr = Expr::from_dynamic(Dynamic::FALSE, *pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
// (lhs_lhs[lhs_rhs])[rhs]
|
|
||||||
(Expr::Index(..), ..) => optimize_chain_structure(expr, state),
|
|
||||||
// (lhs_lhs[lhs_rhs]).rhs
|
|
||||||
(Expr::Dot(..), ..) => optimize_chain_structure(expr, state),
|
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
(lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, true); }
|
(lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, true); }
|
||||||
}
|
}
|
||||||
@ -1054,11 +992,6 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
}
|
}
|
||||||
// var[rhs]
|
// var[rhs]
|
||||||
(Expr::Variable(..), rhs) => optimize_expr(rhs, state, true),
|
(Expr::Variable(..), rhs) => optimize_expr(rhs, state, true),
|
||||||
// (lhs_lhs[lhs_rhs])[rhs]
|
|
||||||
(Expr::Index(..), ..) => optimize_chain_structure(expr, state),
|
|
||||||
// (lhs_lhs[lhs_rhs]).rhs
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
(Expr::Dot(..), ..) => optimize_chain_structure(expr, state),
|
|
||||||
// lhs[rhs]
|
// lhs[rhs]
|
||||||
(lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, true); }
|
(lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, true); }
|
||||||
},
|
},
|
||||||
|
@ -539,6 +539,82 @@ fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Optimize the structure of a chained expression where the root expression is another chained expression.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the expression is not a combo chain.
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
fn optimize_combo_chain(expr: &mut Expr) {
|
||||||
|
let (mut x, x_options, x_pos, mut root, mut root_options, root_pos, make_sub, make_root): (
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
fn(_, _, _) -> Expr,
|
||||||
|
fn(_, _, _) -> Expr,
|
||||||
|
) = match expr.take() {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Expr::Index(mut x, opt, pos) => match x.lhs.take() {
|
||||||
|
Expr::Index(x2, opt2, pos2) => (x, opt, pos, x2, opt2, pos2, Expr::Index, Expr::Index),
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Expr::Dot(x2, opt2, pos2) => (x, opt, pos, x2, opt2, pos2, Expr::Index, Expr::Dot),
|
||||||
|
_ => panic!("combo chain expected"),
|
||||||
|
},
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Expr::Dot(mut x, opt, pos) => match x.lhs.take() {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Expr::Index(x2, opt2, pos2) => (x, opt, pos, x2, opt2, pos2, Expr::Dot, Expr::Index),
|
||||||
|
Expr::Dot(x2, opt2, pos2) => (x, opt, pos, x2, opt2, pos2, Expr::Dot, Expr::Index),
|
||||||
|
_ => panic!("combo chain expected"),
|
||||||
|
},
|
||||||
|
_ => panic!("combo chain expected"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rewrite the chains like this:
|
||||||
|
//
|
||||||
|
// Source: ( x[y].prop_a )[z].prop_b
|
||||||
|
// ^ ^
|
||||||
|
// parentheses that generated the combo chain
|
||||||
|
//
|
||||||
|
// From: Index( Index( x, Dot(y, prop_a) ), Dot(z, prop_b) )
|
||||||
|
// ^ ^ ^
|
||||||
|
// x root tail
|
||||||
|
//
|
||||||
|
// To: Index( x, Dot(y, Index(prop_a, Dot(z, prop_b) ) ) )
|
||||||
|
//
|
||||||
|
// Equivalent to: x[y].prop_a[z].prop_b
|
||||||
|
|
||||||
|
// Find the end of the root chain.
|
||||||
|
let mut tail = root.as_mut();
|
||||||
|
let mut tail_options = &mut root_options;
|
||||||
|
|
||||||
|
while !tail_options.contains(ASTFlags::BREAK) {
|
||||||
|
match tail.rhs {
|
||||||
|
Expr::Index(ref mut x, ref mut options2, ..) => {
|
||||||
|
tail = x.as_mut();
|
||||||
|
tail_options = options2;
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Expr::Dot(ref mut x, ref mut options2, ..) => {
|
||||||
|
tail = x.as_mut();
|
||||||
|
tail_options = options2;
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we attach the outer chain to the root chain, we no longer terminate at the end of the
|
||||||
|
// root chain, so remove the ASTFlags::BREAK flag.
|
||||||
|
tail_options.remove(ASTFlags::BREAK);
|
||||||
|
|
||||||
|
x.lhs = tail.rhs.take(); // remove tail and insert it into head of outer chain
|
||||||
|
tail.rhs = make_sub(x, x_options, x_pos); // attach outer chain to tail
|
||||||
|
*expr = make_root(root, root_options, root_pos);
|
||||||
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Parse a function call.
|
/// Parse a function call.
|
||||||
fn parse_fn_call(
|
fn parse_fn_call(
|
||||||
@ -1857,6 +1933,13 @@ impl Engine {
|
|||||||
parent_options = ASTFlags::empty();
|
parent_options = ASTFlags::empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optimize chain where the root expression is another chain
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
if matches!(lhs, Expr::Index(ref x, ..) | Expr::Dot(ref x, ..) if matches!(x.lhs, Expr::Index(..) | Expr::Dot(..)))
|
||||||
|
{
|
||||||
|
optimize_combo_chain(&mut lhs)
|
||||||
|
}
|
||||||
|
|
||||||
// Cache the hash key for namespace-qualified variables
|
// Cache the hash key for namespace-qualified variables
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let namespaced_variable = match lhs {
|
let namespaced_variable = match lhs {
|
||||||
|
Loading…
Reference in New Issue
Block a user