use crate::arena::Arena;
use crate::ast::stmt::{Expr, Stmt};
use crate::intern::{Interner, Symbol};
pub fn fuse_place_splices<'a>(
stmts: Vec<Stmt<'a>>,
expr_arena: &'a Arena<Expr<'a>>,
stmt_arena: &'a Arena<Stmt<'a>>,
interner: &Interner,
) -> Vec<Stmt<'a>> {
stmts
.into_iter()
.map(|s| fuse_stmt(s, expr_arena, stmt_arena, interner))
.collect()
}
fn fuse_block<'a>(
block: &'a [Stmt<'a>],
expr_arena: &'a Arena<Expr<'a>>,
stmt_arena: &'a Arena<Stmt<'a>>,
interner: &Interner,
) -> &'a [Stmt<'a>] {
let fused: Vec<Stmt<'a>> = block
.iter()
.cloned()
.map(|s| fuse_stmt(s, expr_arena, stmt_arena, interner))
.collect();
stmt_arena.alloc_slice(fused)
}
fn fuse_stmt<'a>(
stmt: Stmt<'a>,
expr_arena: &'a Arena<Expr<'a>>,
stmt_arena: &'a Arena<Stmt<'a>>,
interner: &Interner,
) -> Stmt<'a> {
match stmt {
Stmt::Splice { body } => {
if let Some(direct) = try_fuse_place_splice(body, expr_arena, interner) {
return direct;
}
Stmt::Splice { body: fuse_block(body, expr_arena, stmt_arena, interner) }
}
Stmt::If { cond, then_block, else_block } => Stmt::If {
cond,
then_block: fuse_block(then_block, expr_arena, stmt_arena, interner),
else_block: else_block.map(|b| fuse_block(b, expr_arena, stmt_arena, interner)),
},
Stmt::While { cond, body, decreasing } => Stmt::While {
cond,
body: fuse_block(body, expr_arena, stmt_arena, interner),
decreasing,
},
Stmt::Repeat { pattern, iterable, body } => Stmt::Repeat {
pattern,
iterable,
body: fuse_block(body, expr_arena, stmt_arena, interner),
},
Stmt::FunctionDef { name, params, generics, body, return_type, is_native, native_path, is_exported, export_target, opt_flags } => Stmt::FunctionDef {
name,
params,
generics,
body: fuse_block(body, expr_arena, stmt_arena, interner),
return_type,
is_native,
native_path,
is_exported,
export_target,
opt_flags,
},
Stmt::Inspect { target, arms, has_otherwise } => Stmt::Inspect {
target,
arms: arms
.into_iter()
.map(|arm| crate::ast::stmt::MatchArm {
enum_name: arm.enum_name,
variant: arm.variant,
bindings: arm.bindings,
body: fuse_block(arm.body, expr_arena, stmt_arena, interner),
})
.collect(),
has_otherwise,
},
Stmt::Zone { name, capacity, source_file, body } => Stmt::Zone {
name,
capacity,
source_file,
body: fuse_block(body, expr_arena, stmt_arena, interner),
},
Stmt::Concurrent { tasks } => Stmt::Concurrent {
tasks: fuse_block(tasks, expr_arena, stmt_arena, interner),
},
Stmt::Parallel { tasks } => Stmt::Parallel {
tasks: fuse_block(tasks, expr_arena, stmt_arena, interner),
},
other => other,
}
}
fn is_place_temp(interner: &Interner, sym: Symbol, tag: &str) -> bool {
interner.resolve(sym).starts_with(tag)
}
fn try_fuse_place_splice<'a>(
body: &'a [Stmt<'a>],
expr_arena: &'a Arena<Expr<'a>>,
interner: &Interner,
) -> Option<Stmt<'a>> {
match body {
[Stmt::Let { var: ti, value: index_expr, .. },
Stmt::Let { var: tv, value: value_expr, .. },
Stmt::Let { var: tk, value: key_expr, .. },
Stmt::Let { var: tt, value: Expr::Index { collection: base, index: Expr::Identifier(read_k) }, .. },
Stmt::SetIndex {
collection: Expr::Identifier(w_t),
index: Expr::Identifier(w_i),
value: Expr::Identifier(w_v),
},
_write_back]
if is_place_temp(interner, *ti, "__place_i")
&& is_place_temp(interner, *tv, "__place_v")
&& is_place_temp(interner, *tk, "__place_k")
&& is_place_temp(interner, *tt, "__place_t")
&& read_k == tk
&& w_t == tt
&& w_i == ti
&& w_v == tv =>
{
let place = expr_arena.alloc(Expr::Index { collection: *base, index: *key_expr });
Some(Stmt::SetIndex { collection: place, index: *index_expr, value: *value_expr })
}
[Stmt::Let { var: tv, value: value_expr, .. },
Stmt::Let { var: tk, value: key_expr, .. },
Stmt::Let { var: tt, value: Expr::Index { collection: base, index: Expr::Identifier(read_k) }, .. },
Stmt::Push { value: Expr::Identifier(w_v), collection: Expr::Identifier(w_t) },
_write_back]
if !crate::semantics::collections::value_semantics_enabled()
&& is_place_temp(interner, *tv, "__place_v")
&& is_place_temp(interner, *tk, "__place_k")
&& is_place_temp(interner, *tt, "__place_t")
&& read_k == tk
&& w_v == tv
&& w_t == tt =>
{
let place = expr_arena.alloc(Expr::Index { collection: *base, index: *key_expr });
Some(Stmt::Push { value: *value_expr, collection: place })
}
_ => None,
}
}