Optimize layout.

This commit is contained in:
Stephen Chung 2021-03-10 22:12:48 +08:00
parent 874b3fc843
commit 728ed81173
5 changed files with 226 additions and 159 deletions

View File

@ -46,7 +46,7 @@ pub enum FnAccess {
#[derive(Debug, Clone)]
pub struct ScriptFnDef {
/// Function body.
pub body: Stmt,
pub body: StmtBlock,
/// Encapsulated running environment, if any.
pub lib: Option<Shared<Module>>,
/// Encapsulated imported modules.
@ -692,7 +692,7 @@ impl AST {
.chain({
#[cfg(not(feature = "no_function"))]
{
self.iter_fn_def().map(|f| &f.body)
self.iter_fn_def().flat_map(|f| f.body.statements.iter())
}
#[cfg(feature = "no_function")]
{
@ -862,8 +862,8 @@ pub enum Stmt {
Switch(
Expr,
Box<(
HashableHashMap<u64, Stmt, StraightHasherBuilder>,
Option<Stmt>,
HashableHashMap<u64, StmtBlock, StraightHasherBuilder>,
StmtBlock,
)>,
Position,
),
@ -914,6 +914,7 @@ impl Default for Stmt {
}
impl From<Stmt> for StmtBlock {
#[inline(always)]
fn from(stmt: Stmt) -> Self {
match stmt {
Stmt::Block(block, pos) => Self {
@ -924,7 +925,11 @@ impl From<Stmt> for StmtBlock {
statements: Default::default(),
pos,
},
_ => panic!("cannot convert {:?} into a StmtBlock", stmt),
_ => {
let pos = stmt.position();
let statements = vec![stmt].into();
Self { statements, pos }
}
}
}
}
@ -1043,8 +1048,11 @@ impl Stmt {
}
Self::Switch(expr, x, _) => {
expr.is_pure()
&& x.0.values().all(Stmt::is_pure)
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
&& x.0
.values()
.flat_map(|block| block.statements.iter())
.all(Stmt::is_pure)
&& x.1.statements.iter().all(Stmt::is_pure)
}
Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
condition.is_pure() && block.statements.iter().all(Stmt::is_pure)
@ -1083,10 +1091,10 @@ impl Stmt {
}
Self::Switch(e, x, _) => {
e.walk(path, on_node);
x.0.values().for_each(|s| s.walk(path, on_node));
if let Some(ref s) = x.1 {
s.walk(path, on_node);
}
x.0.values()
.flat_map(|block| block.statements.iter())
.for_each(|s| s.walk(path, on_node));
x.1.statements.iter().for_each(|s| s.walk(path, on_node));
}
Self::While(e, s, _) | Self::Do(s, e, _, _) => {
e.walk(path, on_node);
@ -1384,10 +1392,10 @@ pub enum Expr {
Unit(Position),
/// Variable access - (optional index, optional (hash, modules), variable name)
Variable(Box<(Option<NonZeroUsize>, Option<(u64, NamespaceRef)>, Ident)>),
/// Property access - (getter, hash, setter, hash, prop)
Property(Box<(ImmutableString, u64, ImmutableString, u64, Ident)>),
/// Property access - ((getter, hash), (setter, hash), prop)
Property(Box<((ImmutableString, u64), (ImmutableString, u64), Ident)>),
/// { [statement][Stmt] ... }
Stmt(Box<StaticVec<Stmt>>, Position),
Stmt(Box<StmtBlock>),
/// func `(` expr `,` ... `)`
FnCall(Box<FnCallExpr>, Position),
/// lhs `.` rhs
@ -1478,8 +1486,8 @@ impl Expr {
Self::FnPointer(_, pos) => *pos,
Self::Array(_, pos) => *pos,
Self::Map(_, pos) => *pos,
Self::Property(x) => (x.4).pos,
Self::Stmt(_, pos) => *pos,
Self::Property(x) => (x.2).pos,
Self::Stmt(x) => x.pos,
Self::Variable(x) => (x.2).pos,
Self::FnCall(_, pos) => *pos,
@ -1508,8 +1516,8 @@ impl Expr {
Self::Array(_, pos) => *pos = new_pos,
Self::Map(_, pos) => *pos = new_pos,
Self::Variable(x) => (x.2).pos = new_pos,
Self::Property(x) => (x.4).pos = new_pos,
Self::Stmt(_, pos) => *pos = new_pos,
Self::Property(x) => (x.2).pos = new_pos,
Self::Stmt(x) => x.pos = new_pos,
Self::FnCall(_, pos) => *pos = new_pos,
Self::And(_, pos) | Self::Or(_, pos) => *pos = new_pos,
Self::Unit(pos) => *pos = new_pos,
@ -1533,7 +1541,7 @@ impl Expr {
x.lhs.is_pure() && x.rhs.is_pure()
}
Self::Stmt(x, _) => x.iter().all(Stmt::is_pure),
Self::Stmt(x) => x.statements.iter().all(Stmt::is_pure),
Self::Variable(_) => true,
@ -1596,7 +1604,7 @@ impl Expr {
Self::StringConstant(_, _)
| Self::FnCall(_, _)
| Self::Stmt(_, _)
| Self::Stmt(_)
| Self::Dot(_, _)
| Self::Index(_, _)
| Self::Array(_, _)
@ -1632,7 +1640,7 @@ impl Expr {
on_node(path);
match self {
Self::Stmt(x, _) => x.iter().for_each(|s| s.walk(path, on_node)),
Self::Stmt(x) => x.statements.iter().for_each(|s| s.walk(path, on_node)),
Self::Array(x, _) => x.iter().for_each(|e| e.walk(path, on_node)),
Self::Map(x, _) => x.iter().for_each(|(_, e)| e.walk(path, on_node)),
Self::Index(x, _) | Self::Dot(x, _) | Expr::And(x, _) | Expr::Or(x, _) => {

View File

@ -1181,7 +1181,7 @@ impl Engine {
}
// {xxx:map}.id op= ???
Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => {
let Ident { name, pos, .. } = &x.4;
let Ident { name, pos, .. } = &x.2;
let index = name.clone().into();
let val = self.get_indexed_mut(
mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
@ -1194,7 +1194,7 @@ impl Engine {
}
// {xxx:map}.id
Expr::Property(x) if target_val.is::<Map>() => {
let Ident { name, pos, .. } = &x.4;
let Ident { name, pos, .. } = &x.2;
let index = name.clone().into();
let val = self.get_indexed_mut(
mods, state, lib, target_val, index, *pos, false, is_ref, false, level,
@ -1204,7 +1204,7 @@ impl Engine {
}
// xxx.id = ???
Expr::Property(x) if new_val.is_some() => {
let (_, _, setter, hash_set, Ident { pos, .. }) = x.as_ref();
let (_, (setter, hash_set), Ident { pos, .. }) = x.as_ref();
let hash = FnHash::from_native(*hash_set);
let mut new_val = new_val;
let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0];
@ -1216,7 +1216,7 @@ impl Engine {
}
// xxx.id
Expr::Property(x) => {
let (getter, hash_get, _, _, Ident { pos, .. }) = x.as_ref();
let ((getter, hash_get), _, Ident { pos, .. }) = x.as_ref();
let hash = FnHash::from_native(*hash_get);
let mut args = [target_val];
self.exec_fn_call(
@ -1229,7 +1229,7 @@ impl Engine {
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target_val.is::<Map>() => {
let mut val = match &x.lhs {
Expr::Property(p) => {
let Ident { name, pos, .. } = &p.4;
let Ident { name, pos, .. } = &p.2;
let index = name.clone().into();
self.get_indexed_mut(
mods, state, lib, target_val, index, *pos, false, is_ref, true,
@ -1264,7 +1264,7 @@ impl Engine {
match &x.lhs {
// xxx.prop[expr] | xxx.prop.expr
Expr::Property(p) => {
let (getter, hash_get, setter, hash_set, Ident { pos, .. }) =
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
p.as_ref();
let hash_get = FnHash::from_native(*hash_get);
let hash_set = FnHash::from_native(*hash_set);
@ -1456,7 +1456,7 @@ impl Engine {
}
Expr::Property(x) if parent_chain_type == ChainType::Dot => {
idx_values.push(ChainArgument::Property(x.4.pos))
idx_values.push(ChainArgument::Property(x.2.pos))
}
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
@ -1466,7 +1466,7 @@ impl Engine {
// Evaluate in left-to-right order
let lhs_val = match lhs {
Expr::Property(x) if parent_chain_type == ChainType::Dot => {
ChainArgument::Property(x.4.pos)
ChainArgument::Property(x.2.pos)
}
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
Expr::FnCall(x, _)
@ -1655,16 +1655,11 @@ impl Engine {
.map(|(val, _)| val.take_or_clone()),
// Statement block
Expr::Stmt(x, _) => self.eval_stmt_block(
scope,
mods,
state,
lib,
this_ptr,
x.as_ref().as_ref(),
true,
level,
),
Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT),
Expr::Stmt(x) => {
let statements = &x.statements;
self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, true, level)
}
// lhs[idx_expr]
#[cfg(not(feature = "no_index"))]
@ -1804,6 +1799,10 @@ impl Engine {
restore_prev_state: bool,
level: usize,
) -> RhaiResult {
if statements.is_empty() {
return Ok(Dynamic::UNIT);
}
let mut _extra_fn_resolution_cache = false;
let prev_always_search = state.always_search;
let prev_scope_len = scope.len();
@ -2025,6 +2024,7 @@ impl Engine {
}
// Block scope
Stmt::Block(statements, _) if statements.is_empty() => Ok(Dynamic::UNIT),
Stmt::Block(statements, _) => {
self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, true, level)
}
@ -2046,15 +2046,21 @@ impl Engine {
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))
.and_then(|guard_val| {
if guard_val {
self.eval_stmt_block(
scope, mods, state, lib, this_ptr, if_stmt, true, level,
)
} else if !else_stmt.is_empty() {
self.eval_stmt_block(
scope, mods, state, lib, this_ptr, else_stmt, true, level,
)
if !if_stmt.is_empty() {
self.eval_stmt_block(
scope, mods, state, lib, this_ptr, if_stmt, true, level,
)
} else {
Ok(Dynamic::UNIT)
}
} else {
Ok(Dynamic::UNIT)
if !else_stmt.is_empty() {
self.eval_stmt_block(
scope, mods, state, lib, this_ptr, else_stmt, true, level,
)
} else {
Ok(Dynamic::UNIT)
}
}
})
}
@ -2070,21 +2076,29 @@ impl Engine {
value.hash(hasher);
let hash = hasher.finish();
table
.get(&hash)
.map(|stmt| self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level))
table.get(&hash).map(|StmtBlock { statements, .. }| {
if !statements.is_empty() {
self.eval_stmt_block(
scope, mods, state, lib, this_ptr, statements, true, level,
)
} else {
Ok(Dynamic::UNIT)
}
})
} else {
// Non-hashable values never match any specific clause
None
}
.unwrap_or_else(|| {
// Default match clause
def_stmt.as_ref().map_or_else(
|| Ok(Dynamic::UNIT),
|def_stmt| {
self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level)
},
)
let def_stmt = &def_stmt.statements;
if !def_stmt.is_empty() {
self.eval_stmt_block(
scope, mods, state, lib, this_ptr, def_stmt, true, level,
)
} else {
Ok(Dynamic::UNIT)
}
})
}
@ -2102,20 +2116,22 @@ impl Engine {
true
};
if condition {
match self
.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level)
{
Ok(_) => (),
Err(err) => match *err {
EvalAltResult::LoopBreak(false, _) => (),
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
_ => return Err(err),
},
}
} else {
if !condition {
return Ok(Dynamic::UNIT);
}
if body.is_empty() {
continue;
}
match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level)
{
Ok(_) => (),
Err(err) => match *err {
EvalAltResult::LoopBreak(false, _) => (),
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
_ => return Err(err),
},
}
}
}
@ -2124,14 +2140,17 @@ impl Engine {
let body = &body.statements;
loop {
match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level)
{
Ok(_) => (),
Err(err) => match *err {
EvalAltResult::LoopBreak(false, _) => continue,
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
_ => return Err(err),
},
if !body.is_empty() {
match self
.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level)
{
Ok(_) => (),
Err(err) => match *err {
EvalAltResult::LoopBreak(false, _) => continue,
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
_ => return Err(err),
},
}
}
if self
@ -2203,6 +2222,10 @@ impl Engine {
self.inc_operations(state, *pos)?;
if statements.is_empty() {
continue;
}
match self.eval_stmt_block(
scope, mods, state, lib, this_ptr, statements, true, level,
) {

View File

@ -493,6 +493,10 @@ impl Engine {
self.inc_operations(state, pos)?;
if fn_def.body.is_empty() {
return Ok(Dynamic::UNIT);
}
// Check for stack overflow
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "unchecked"))]
@ -539,10 +543,10 @@ impl Engine {
}
// Evaluate the function
let stmt = &fn_def.body;
let body = &fn_def.body.statements;
let result = self
.eval_stmt(scope, mods, state, unified_lib, this_ptr, stmt, level)
.eval_stmt_block(scope, mods, state, unified_lib, this_ptr, body, true, level)
.or_else(|err| match *err {
// Convert return statement to return value
EvalAltResult::Return(x, _) => Ok(x),
@ -722,6 +726,10 @@ impl Engine {
let func = func.get_fn_def();
if func.body.is_empty() {
return Ok((Dynamic::UNIT, false));
}
let scope: &mut Scope = &mut Default::default();
// Move captured variables into scope
@ -1391,22 +1399,27 @@ impl Engine {
match func {
#[cfg(not(feature = "no_function"))]
Some(f) if f.is_script() => {
let args = args.as_mut();
let new_scope = &mut Default::default();
let fn_def = f.get_fn_def().clone();
let fn_def = f.get_fn_def();
let mut source = module.id_raw().cloned();
mem::swap(&mut state.source, &mut source);
if fn_def.body.is_empty() {
Ok(Dynamic::UNIT)
} else {
let args = args.as_mut();
let new_scope = &mut Default::default();
let level = level + 1;
let mut source = module.id_raw().cloned();
mem::swap(&mut state.source, &mut source);
let result = self.call_script_fn(
new_scope, mods, state, lib, &mut None, &fn_def, args, pos, level,
);
let level = level + 1;
state.source = source;
let result = self.call_script_fn(
new_scope, mods, state, lib, &mut None, fn_def, args, pos, level,
);
result
state.source = source;
result
}
}
Some(f) if f.is_plugin_fn() => f

View File

@ -1,6 +1,6 @@
//! Module implementing the [`AST`] optimizer.
use crate::ast::{Expr, Ident, Stmt};
use crate::ast::{Expr, Ident, Stmt, StmtBlock};
use crate::dynamic::AccessMode;
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
use crate::fn_builtin::get_builtin_binary_op_fn;
@ -362,29 +362,54 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
let table = &mut x.0;
if let Some(stmt) = table.get_mut(&hash) {
optimize_stmt(stmt, state, true);
*expr = Expr::Stmt(Box::new(vec![mem::take(stmt)].into()), *pos);
} else if let Some(def_stmt) = x.1.as_mut() {
optimize_stmt(def_stmt, state, true);
*expr = Expr::Stmt(Box::new(vec![mem::take(def_stmt)].into()), *pos);
let (statements, new_pos) = if let Some(block) = table.get_mut(&hash) {
(
optimize_stmt_block(
mem::take(&mut block.statements).into_vec(),
block.pos,
state,
true,
)
.into(),
block.pos,
)
} else {
*expr = Expr::Unit(*pos);
}
(
optimize_stmt_block(
mem::take(&mut x.1.statements).into_vec(),
x.1.pos,
state,
true,
)
.into(),
if x.1.pos.is_none() { *pos } else { x.1.pos },
)
};
*expr = Expr::Stmt(Box::new(StmtBlock {
statements,
pos: new_pos,
}));
}
// switch
Stmt::Switch(expr, x, _) => {
optimize_expr(expr, state);
x.0.values_mut()
.for_each(|stmt| optimize_stmt(stmt, state, preserve_result));
if let Some(def_stmt) = x.1.as_mut() {
optimize_stmt(def_stmt, state, preserve_result);
match def_stmt {
Stmt::Noop(_) | Stmt::Expr(Expr::Unit(_)) => x.1 = None,
_ => (),
}
}
x.0.values_mut().for_each(|block| {
block.statements = optimize_stmt_block(
mem::take(&mut block.statements).into_vec(),
block.pos,
state,
preserve_result,
)
.into()
});
x.1.statements = optimize_stmt_block(
mem::take(&mut x.1.statements).into_vec(),
x.1.pos,
state,
preserve_result,
)
.into()
}
// while false { block } -> Noop
@ -512,14 +537,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
.into();
}
// {}
Stmt::Expr(Expr::Stmt(x, pos)) if x.is_empty() => {
Stmt::Expr(Expr::Stmt(x)) if x.statements.is_empty() => {
state.set_dirty();
*stmt = Stmt::Noop(*pos);
*stmt = Stmt::Noop(x.pos);
}
// {...};
Stmt::Expr(Expr::Stmt(x, pos)) => {
Stmt::Expr(Expr::Stmt(x)) => {
state.set_dirty();
*stmt = Stmt::Block(mem::take(x).into_vec(), *pos);
*stmt = Stmt::Block(mem::take(&mut x.statements).into_vec(), x.pos);
}
// expr;
Stmt::Expr(expr) => optimize_expr(expr, state),
@ -542,18 +567,15 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
match expr {
// {}
Expr::Stmt(x, pos) if x.is_empty() => { state.set_dirty(); *expr = Expr::Unit(*pos) }
Expr::Stmt(x) if x.statements.is_empty() => { state.set_dirty(); *expr = Expr::Unit(x.pos) }
// { stmt; ... } - do not count promotion as dirty because it gets turned back into an array
Expr::Stmt(x, pos) => {
let statements = optimize_stmt_block(mem::take(x).into_vec(), *pos, state, true);
*expr = Expr::Stmt(Box::new(statements.into()), *pos);
}
Expr::Stmt(x) => x.statements = optimize_stmt_block(mem::take(&mut x.statements).into_vec(), x.pos, state, true).into(),
// lhs.rhs
#[cfg(not(feature = "no_object"))]
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
// map.string
(Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => {
let prop = &p.4.name;
let prop = &p.2.name;
// Map literal where everything is pure - promote the indexed item.
// All other items can be thrown away.
state.set_dirty();
@ -902,30 +924,34 @@ pub fn optimize_into_ast(
_functions
.into_iter()
.map(|mut fn_def| {
let pos = fn_def.body.position();
let pos = fn_def.body.pos;
// Optimize the function body
let mut body = optimize_top_level(
vec![fn_def.body],
fn_def.body.statements.into_vec(),
engine,
&Scope::new(),
&[&lib2],
level,
);
// {} -> Noop
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
match &mut body[..] {
// { return val; } -> val
Stmt::Return(crate::ast::ReturnType::Return, Some(expr), _) => {
Stmt::Expr(expr)
[Stmt::Return(crate::ast::ReturnType::Return, Some(expr), _)] => {
body[0] = Stmt::Expr(mem::take(expr))
}
// { return; } -> ()
Stmt::Return(crate::ast::ReturnType::Return, None, pos) => {
Stmt::Expr(Expr::Unit(pos))
[Stmt::Return(crate::ast::ReturnType::Return, None, _)] => {
body.clear();
}
// All others
stmt => stmt,
_ => (),
}
fn_def.body = StmtBlock {
statements: body.into(),
pos,
};
fn_def
})
.for_each(|fn_def| {

View File

@ -243,7 +243,11 @@ impl Expr {
let setter = state.get_interned_string(crate::engine::make_setter(&ident.name));
let hash_set = calc_fn_hash(empty(), &setter, 2);
Self::Property(Box::new((getter, hash_get, setter, hash_set, ident.into())))
Self::Property(Box::new((
(getter, hash_get),
(setter, hash_set),
ident.into(),
)))
}
_ => self,
}
@ -809,7 +813,7 @@ fn parse_switch(
}
}
let mut table = HashMap::new();
let mut table = HashMap::<u64, StmtBlock>::new();
let mut def_stmt = None;
loop {
@ -869,10 +873,10 @@ fn parse_switch(
let need_comma = !stmt.is_self_terminated();
def_stmt = if let Some(hash) = hash {
table.insert(hash, stmt);
table.insert(hash, stmt.into());
None
} else {
Some(stmt)
Some(stmt.into())
};
match input.peek().unwrap() {
@ -903,7 +907,10 @@ fn parse_switch(
Ok(Stmt::Switch(
item,
Box::new((final_table.into(), def_stmt)),
Box::new((
final_table.into(),
def_stmt.unwrap_or_else(|| Stmt::Noop(Position::NONE).into()),
)),
settings.pos,
))
}
@ -954,7 +961,7 @@ fn parse_primary(
// { - block statement as expression
Token::LeftBrace if settings.allow_stmt_expr => {
match parse_block(input, state, lib, settings.level_up())? {
Stmt::Block(statements, pos) => Expr::Stmt(Box::new(statements.into()), pos),
block @ Stmt::Block(_, _) => Expr::Stmt(Box::new(block.into())),
stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt),
}
}
@ -962,15 +969,14 @@ fn parse_primary(
Token::LeftParen => parse_paren_expr(input, state, lib, settings.level_up())?,
// If statement is allowed to act as expressions
Token::If if settings.allow_if_expr => Expr::Stmt(
Box::new(vec![parse_if(input, state, lib, settings.level_up())?].into()),
settings.pos,
),
Token::If if settings.allow_if_expr => Expr::Stmt(Box::new(
parse_if(input, state, lib, settings.level_up())?.into(),
)),
// Switch statement is allowed to act as expressions
Token::Switch if settings.allow_switch_expr => Expr::Stmt(
Box::new(vec![parse_switch(input, state, lib, settings.level_up())?].into()),
settings.pos,
),
Token::Switch if settings.allow_switch_expr => Expr::Stmt(Box::new(
parse_switch(input, state, lib, settings.level_up())?.into(),
)),
// | ...
#[cfg(not(feature = "no_function"))]
Token::Pipe | Token::Or if settings.allow_anonymous_fn => {
@ -1506,7 +1512,7 @@ fn make_dot_expr(
let setter = state.get_interned_string(crate::engine::make_setter(&ident.name));
let hash_set = calc_fn_hash(empty(), &setter, 2);
let rhs = Expr::Property(Box::new((getter, hash_get, setter, hash_set, ident)));
let rhs = Expr::Property(Box::new(((getter, hash_get), (setter, hash_set), ident)));
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
}
@ -1861,8 +1867,8 @@ fn parse_custom_syntax(
tokens.push(keyword);
}
MARKER_BLOCK => match parse_block(input, state, lib, settings)? {
Stmt::Block(statements, pos) => {
keywords.push(Expr::Stmt(Box::new(statements.into()), pos));
block @ Stmt::Block(_, _) => {
keywords.push(Expr::Stmt(Box::new(block.into())));
let keyword = state.get_interned_string(MARKER_BLOCK);
segments.push(keyword.clone());
tokens.push(keyword);
@ -2006,19 +2012,9 @@ fn parse_if(
Stmt::Noop(Position::NONE)
};
let else_body = match else_body {
Stmt::If(_, _, pos) => {
let mut statements: StaticVec<_> = Default::default();
statements.push(else_body);
StmtBlock { statements, pos }
}
Stmt::Block(_, _) | Stmt::Noop(_) => else_body.into(),
_ => unreachable!("should either be if or a block, not {:?}", else_body),
};
Ok(Stmt::If(
guard,
Box::new((if_body.into(), else_body)),
Box::new((if_body.into(), else_body.into())),
settings.pos,
))
}
@ -2741,7 +2737,8 @@ fn parse_fn(
parse_block(input, state, lib, settings.level_up())?
}
(_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
};
}
.into();
let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect();
@ -2803,7 +2800,7 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Po
let mut statements: StaticVec<_> = Default::default();
statements.extend(externals.into_iter().map(Stmt::Share));
statements.push(Stmt::Expr(expr));
Expr::Stmt(Box::new(statements), pos)
Expr::Stmt(Box::new(StmtBlock { statements, pos }))
}
/// Parse an anonymous function definition.
@ -2905,7 +2902,7 @@ fn parse_anon_fn(
params,
#[cfg(not(feature = "no_closure"))]
externals: Default::default(),
body,
body: body.into(),
lib: None,
#[cfg(not(feature = "no_module"))]
mods: Default::default(),