pub mod desugar_idiom;
pub mod fold;
pub mod passes;
pub mod visitor;
pub use desugar_idiom::IdiomDesugar;
pub use fold::Fold;
pub use passes::{ConstantFolding, DeadCodeElimination, TreeShaking, TreeShakingStats};
pub use visitor::{MutVisitor, Visitor};
use crate::ast::Declaration;
use std::fmt;
#[derive(Debug, Clone)]
pub struct PassError {
pub message: String,
pub pass_name: String,
pub location: Option<String>,
}
impl PassError {
pub fn new(pass_name: impl Into<String>, message: impl Into<String>) -> Self {
Self {
message: message.into(),
pass_name: pass_name.into(),
location: None,
}
}
pub fn with_location(mut self, location: impl Into<String>) -> Self {
self.location = Some(location.into());
self
}
}
impl fmt::Display for PassError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref loc) = self.location {
write!(f, "[{}] {} at {}", self.pass_name, self.message, loc)
} else {
write!(f, "[{}] {}", self.pass_name, self.message)
}
}
}
impl std::error::Error for PassError {}
pub type PassResult<T> = Result<T, PassError>;
pub trait Pass {
fn name(&self) -> &str;
fn should_run(&self, _decl: &Declaration) -> bool {
true
}
fn run(&mut self, decl: Declaration) -> PassResult<Declaration>;
}
pub struct PassPipeline {
passes: Vec<Box<dyn Pass>>,
}
impl PassPipeline {
pub fn new() -> Self {
Self { passes: Vec::new() }
}
pub fn add<P: Pass + 'static>(&mut self, pass: P) -> &mut Self {
self.passes.push(Box::new(pass));
self
}
pub fn run(&mut self, decl: Declaration) -> PassResult<Declaration> {
let mut current = decl;
for pass in &mut self.passes {
if pass.should_run(¤t) {
current = pass.run(current)?;
}
}
Ok(current)
}
pub fn run_all(&mut self, decls: Vec<Declaration>) -> PassResult<Vec<Declaration>> {
decls.into_iter().map(|d| self.run(d)).collect()
}
}
impl Default for PassPipeline {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct PassConfig {
pub debug: bool,
pub max_iterations: usize,
}
impl Default for PassConfig {
fn default() -> Self {
Self {
debug: false,
max_iterations: 100,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct PassStats {
pub nodes_visited: usize,
pub nodes_transformed: usize,
pub expressions_folded: usize,
}
impl PassStats {
pub fn new() -> Self {
Self::default()
}
pub fn merge(&mut self, other: &PassStats) {
self.nodes_visited += other.nodes_visited;
self.nodes_transformed += other.nodes_transformed;
self.expressions_folded += other.expressions_folded;
}
}
#[cfg(test)]
mod tests {
use super::*;
struct CountingPass {
count: usize,
}
impl Pass for CountingPass {
fn name(&self) -> &str {
"counting"
}
fn run(&mut self, decl: Declaration) -> PassResult<Declaration> {
self.count += 1;
Ok(decl)
}
}
#[test]
fn test_pipeline_runs_passes() {
use crate::ast::{Gen, Span, Visibility};
let gene = Gen {
visibility: Visibility::default(),
name: "test".to_string(),
extends: None,
statements: vec![],
exegesis: "Test gene".to_string(),
span: Span::new(0, 0, 1, 1),
};
let decl = Declaration::Gene(gene);
let mut pipeline = PassPipeline::new();
let pass1 = CountingPass { count: 0 };
let pass2 = CountingPass { count: 0 };
pipeline.add(pass1).add(pass2);
let result = pipeline.run(decl);
assert!(result.is_ok());
}
#[test]
fn test_pass_error_display() {
let err = PassError::new("test_pass", "something went wrong");
assert_eq!(format!("{}", err), "[test_pass] something went wrong");
let err_with_loc = err.with_location("line 42");
assert_eq!(
format!("{}", err_with_loc),
"[test_pass] something went wrong at line 42"
);
}
}