mod check_goto;
mod comment;
mod exprs;
mod stats;
use emmylua_parser::{LuaAst, LuaAstNode, LuaBlock, LuaChunk, LuaExpr};
use crate::{
FlowAntecedent, FlowId, FlowNodeKind,
compilation::analyzer::flow::{
bind_analyze::{
comment::bind_comment,
exprs::bind_expr,
stats::{
bind_assign_stat, bind_break_stat, bind_call_expr_stat, bind_do_stat,
bind_for_range_stat, bind_for_stat, bind_func_stat, bind_goto_stat, bind_if_stat,
bind_label_stat, bind_local_func_stat, bind_local_stat, bind_repeat_stat,
bind_return_stat, bind_while_stat,
},
},
binder::FlowBinder,
},
};
pub use check_goto::check_goto_label;
pub fn bind_analyze(binder: &mut FlowBinder, chunk: LuaChunk) -> Option<()> {
let block = chunk.get_block()?;
let start = binder.start;
bind_block(binder, block, start);
Some(())
}
fn bind_block(binder: &mut FlowBinder, block: LuaBlock, current: FlowId) -> FlowId {
let mut return_flow_id = current;
let mut can_change_flow = true;
for node in block.children::<LuaAst>() {
let node_flow_id = bind_node(binder, node, return_flow_id);
if can_change_flow {
return_flow_id = node_flow_id;
}
if let Some(flow_node) = binder.get_flow(return_flow_id) {
match &flow_node.kind {
FlowNodeKind::Return | FlowNodeKind::Break => {
return_flow_id = binder.unreachable;
can_change_flow = false;
}
_ => {}
}
}
}
return_flow_id
}
fn bind_each_child(binder: &mut FlowBinder, ast_node: LuaAst, mut current: FlowId) -> FlowId {
for node in ast_node.children::<LuaAst>() {
current = bind_node(binder, node, current);
}
current
}
fn bind_node(binder: &mut FlowBinder, node: LuaAst, current: FlowId) -> FlowId {
match node {
LuaAst::LuaBlock(block) => bind_block(binder, block, current),
LuaAst::LuaAssignStat(assign_stat) => bind_assign_stat(binder, assign_stat, current),
LuaAst::LuaLocalStat(local_stat) => bind_local_stat(binder, local_stat, current),
LuaAst::LuaCallExprStat(call_expr_stat) => {
bind_call_expr_stat(binder, call_expr_stat, current)
}
LuaAst::LuaLabelStat(label_stat) => bind_label_stat(binder, label_stat, current),
LuaAst::LuaBreakStat(break_stat) => bind_break_stat(binder, break_stat, current),
LuaAst::LuaGotoStat(goto_stat) => bind_goto_stat(binder, goto_stat, current),
LuaAst::LuaReturnStat(return_stat) => bind_return_stat(binder, return_stat, current),
LuaAst::LuaDoStat(do_stat) => bind_do_stat(binder, do_stat, current),
LuaAst::LuaWhileStat(while_stat) => bind_while_stat(binder, while_stat, current),
LuaAst::LuaRepeatStat(repeat_stat) => bind_repeat_stat(binder, repeat_stat, current),
LuaAst::LuaIfStat(if_stat) => bind_if_stat(binder, if_stat, current),
LuaAst::LuaForStat(for_stat) => bind_for_stat(binder, for_stat, current),
LuaAst::LuaForRangeStat(for_range_stat) => {
bind_for_range_stat(binder, for_range_stat, current)
}
LuaAst::LuaFuncStat(func_stat) => bind_func_stat(binder, func_stat, current),
LuaAst::LuaLocalFuncStat(local_func_stat) => {
bind_local_func_stat(binder, local_func_stat, current)
}
LuaAst::LuaNameExpr(_)
| LuaAst::LuaIndexExpr(_)
| LuaAst::LuaTableExpr(_)
| LuaAst::LuaBinaryExpr(_)
| LuaAst::LuaUnaryExpr(_)
| LuaAst::LuaParenExpr(_)
| LuaAst::LuaCallExpr(_)
| LuaAst::LuaLiteralExpr(_)
| LuaAst::LuaClosureExpr(_) => bind_expr(
binder,
LuaExpr::cast(node.syntax().clone()).expect("cast always succeedss"),
current,
),
LuaAst::LuaComment(comment) => bind_comment(binder, comment, current),
LuaAst::LuaTableField(_)
| LuaAst::LuaParamList(_)
| LuaAst::LuaParamName(_)
| LuaAst::LuaCallArgList(_)
| LuaAst::LuaLocalName(_) => bind_each_child(binder, node, current),
_ => current,
}
}
fn finish_flow_label(binder: &mut FlowBinder, label: FlowId, default: FlowId) -> FlowId {
if let Some(flow_node) = binder.get_flow(label) {
if let Some(antecedent) = &flow_node.antecedent {
if let FlowAntecedent::Single(existing_id) = antecedent {
return *existing_id;
}
} else {
return default;
}
} else {
return binder.unreachable;
}
label
}