mod binding_flow;
mod binding_tree;
mod branch_pretty;
mod cleanup;
mod expr_analysis;
mod field_access_sugar;
mod function_sugar;
mod global_decl_pretty;
mod inline_exprs;
mod installer_iife;
mod local_coalesce;
mod loop_header_merge;
mod luajit_goto_safety;
mod materialize_temps;
mod short_circuit_pretty;
mod statement_merge;
mod traverse;
mod visit;
mod walk;
use super::common::{AstModule, AstTargetDialect};
use crate::readability::ReadabilityOptions;
use crate::timing::TimingCollector;
#[derive(Clone, Copy)]
pub(super) struct ReadabilityContext {
pub target: AstTargetDialect,
pub options: ReadabilityOptions,
}
#[derive(Clone, Copy)]
struct ReadabilityPass {
name: &'static str,
apply: fn(&mut AstModule, ReadabilityContext) -> bool,
}
#[derive(Clone, Copy)]
struct ReadabilityStage {
name: &'static str,
passes: &'static [ReadabilityPass],
cleanup_after_passes: bool,
}
const CLEANUP_PASS: ReadabilityPass = ReadabilityPass {
name: "cleanup",
apply: cleanup::apply,
};
const fn stage(name: &'static str, passes: &'static [ReadabilityPass]) -> ReadabilityStage {
ReadabilityStage {
name,
passes,
cleanup_after_passes: false,
}
}
const fn stage_with_cleanup(
name: &'static str,
passes: &'static [ReadabilityPass],
) -> ReadabilityStage {
ReadabilityStage {
name,
passes,
cleanup_after_passes: true,
}
}
const STRUCTURAL_CLEANUP_STAGE: ReadabilityStage = stage("structural-cleanup", &[CLEANUP_PASS]);
const EXPR_INLINE_STAGE: ReadabilityStage = stage_with_cleanup(
"expr-inline",
&[
ReadabilityPass {
name: "inline-exprs",
apply: inline_exprs::apply,
},
ReadabilityPass {
name: "field-access-sugar-post-inline",
apply: field_access_sugar::apply,
},
],
);
const ACCESS_SUGAR_STAGE: ReadabilityStage = stage(
"access-sugar",
&[ReadabilityPass {
name: "field-access-sugar",
apply: field_access_sugar::apply,
}],
);
const STATEMENT_MERGE_STAGE: ReadabilityStage = stage_with_cleanup(
"statement-merge",
&[ReadabilityPass {
name: "statement-merge",
apply: statement_merge::apply,
}],
);
const LOCAL_COALESCE_STAGE: ReadabilityStage = stage_with_cleanup(
"local-coalesce",
&[ReadabilityPass {
name: "local-coalesce",
apply: local_coalesce::apply,
}],
);
const LOOP_HEADER_MERGE_STAGE: ReadabilityStage = stage_with_cleanup(
"loop-header-merge",
&[ReadabilityPass {
name: "loop-header-merge",
apply: loop_header_merge::apply,
}],
);
const CONTROL_FLOW_PRETTY_STAGE: ReadabilityStage = stage(
"control-flow-pretty",
&[ReadabilityPass {
name: "branch-pretty",
apply: branch_pretty::apply,
}],
);
const POST_CONTROL_FLOW_STATEMENT_MERGE_STAGE: ReadabilityStage = stage_with_cleanup(
"post-control-flow-statement-merge",
&[ReadabilityPass {
name: "statement-merge-post-control-flow",
apply: statement_merge::apply,
}],
);
const POST_CONTROL_FLOW_LOCAL_COALESCE_STAGE: ReadabilityStage = stage_with_cleanup(
"post-control-flow-local-coalesce",
&[ReadabilityPass {
name: "local-coalesce-post-control-flow",
apply: local_coalesce::apply,
}],
);
const SHORT_CIRCUIT_PRETTY_STAGE: ReadabilityStage = stage(
"short-circuit-pretty",
&[ReadabilityPass {
name: "short-circuit-pretty",
apply: short_circuit_pretty::apply,
}],
);
const FUNCTION_SUGAR_STAGE: ReadabilityStage = stage_with_cleanup(
"function-sugar",
&[
ReadabilityPass {
name: "installer-iife",
apply: installer_iife::apply,
},
ReadabilityPass {
name: "function-sugar",
apply: function_sugar::apply,
},
],
);
const GLOBAL_DECL_PRETTY_STAGE: ReadabilityStage = stage_with_cleanup(
"global-decl-pretty",
&[ReadabilityPass {
name: "global-decl-pretty",
apply: global_decl_pretty::apply,
}],
);
const LUAJIT_GOTO_SAFETY_STAGE: ReadabilityStage = stage(
"luajit-goto-safety",
&[ReadabilityPass {
name: "luajit-goto-safety",
apply: luajit_goto_safety::apply,
}],
);
const TEMP_MATERIALIZE_STAGE: ReadabilityStage = stage(
"temp-materialize",
&[ReadabilityPass {
name: "materialize-temps",
apply: materialize_temps::apply,
}],
);
const READABILITY_STAGES: &[ReadabilityStage] = &[
STRUCTURAL_CLEANUP_STAGE,
LOCAL_COALESCE_STAGE,
STATEMENT_MERGE_STAGE,
LOOP_HEADER_MERGE_STAGE,
ACCESS_SUGAR_STAGE,
CONTROL_FLOW_PRETTY_STAGE,
POST_CONTROL_FLOW_STATEMENT_MERGE_STAGE,
POST_CONTROL_FLOW_LOCAL_COALESCE_STAGE,
EXPR_INLINE_STAGE,
SHORT_CIRCUIT_PRETTY_STAGE,
TEMP_MATERIALIZE_STAGE,
FUNCTION_SUGAR_STAGE,
GLOBAL_DECL_PRETTY_STAGE,
LUAJIT_GOTO_SAFETY_STAGE,
];
const MAX_STAGE_ROUNDS: usize = 64;
pub fn make_readable(module: &AstModule, target: AstTargetDialect) -> AstModule {
make_readable_with_options(module, target, ReadabilityOptions::default())
}
pub fn make_readable_with_options(
module: &AstModule,
target: AstTargetDialect,
options: ReadabilityOptions,
) -> AstModule {
let timings = TimingCollector::disabled();
make_readable_with_options_and_timing(module, target, options, &timings)
}
pub(crate) fn make_readable_with_options_and_timing(
module: &AstModule,
target: AstTargetDialect,
options: ReadabilityOptions,
timings: &TimingCollector,
) -> AstModule {
let mut module = module.clone();
let context = ReadabilityContext { target, options };
for stage in READABILITY_STAGES {
timings.record(stage.name, || {
let mut rounds = 0;
loop {
let changed = timings.record("fixed-point-round", || {
let mut changed = false;
for pass in stage.passes {
changed |= timings.record(pass.name, || (pass.apply)(&mut module, context));
}
if stage.cleanup_after_passes {
changed |= timings.record(CLEANUP_PASS.name, || {
(CLEANUP_PASS.apply)(&mut module, context)
});
}
changed
});
if !changed {
break;
}
rounds += 1;
assert!(
rounds < MAX_STAGE_ROUNDS,
"AST readability stage did not converge within {MAX_STAGE_ROUNDS} rounds"
);
}
});
}
module
}