cranefack-cli 0.4.1

Commandline utility for the cranefack brainfuck compiler
use std::error::Error;
use std::ffi::OsStr;
use std::io::{stdin, stdout, Write};
use std::time::SystemTime;

use codespan_reporting::term::termcolor::{
    Color, ColorChoice, ColorSpec, StandardStream, WriteColor,
};
use cranefack::CompiledJitModule;
use cranefack::{
    analyze, optimize_with_config, parse, CraneFackError, Interpreter, OptimizeConfig, Warning,
};

use crate::utils::read_input;

pub fn run_file(
    opt_mode: OptimizeConfig,
    jit: bool,
    verbose: bool,
    path: &OsStr,
) -> Result<(), Box<dyn Error>> {
    let source = read_input(path)?;

    let mut ts = SystemTime::now();

    let mut program = match parse(&source) {
        Ok(program) => program,
        Err(err) => {
            return err.pretty_print(&source, Some(&path.to_string_lossy()));
        }
    };

    if verbose {
        let mut writer = StandardStream::stderr(ColorChoice::Auto);
        writer.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?;

        let (op_count, dloop_count, lloop_count, iloop_count, cloop_count, if_count) =
            program.get_statistics();

        writeln!(
            writer,
            "Parsed program with {} instructions ({},{},{},{}) loops and {} ifs in {}ms",
            op_count,
            dloop_count,
            lloop_count,
            iloop_count,
            cloop_count,
            if_count,
            ts.elapsed()?.as_micros() as f32 / 1000.0
        )?;
        writer.reset()?;
        ts = SystemTime::now();
    }

    if opt_mode.optimize() {
        let opt_loop_count = optimize_with_config(&mut program, &opt_mode);

        if verbose {
            let mut writer = StandardStream::stderr(ColorChoice::Auto);
            writer.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?;

            let (op_count, dloop_count, lloop_count, iloop_count, cloop_count, if_count) =
                program.get_statistics();

            writeln!(writer, "Optimized program with {} instructions ({},{},{},{}) loops and {} ifs in {}ms and {} iterations",
                     op_count,
                     dloop_count,
                     lloop_count,
                     iloop_count,
                     cloop_count,
                     if_count,
                     ts.elapsed()?.as_micros() as f32 / 1000.0,
                     opt_loop_count
            )?;
            writer.reset()?;
            ts = SystemTime::now();
        }
    }

    let warnings = analyze(&program);
    if !warnings.is_empty() {
        Warning::pretty_print(&warnings, &source, Some(&path.to_string_lossy()))?;
    }

    if jit {
        let module = match CompiledJitModule::new(&program, &opt_mode) {
            Ok(module) => module,
            Err(err) => {
                return err.pretty_print(&source, Some(&path.to_string_lossy()));
            }
        };

        if verbose {
            let mut writer = StandardStream::stderr(ColorChoice::Auto);
            writer.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?;
            writeln!(
                writer,
                "Compiled program in {}ms",
                ts.elapsed()?.as_micros() as f32 / 1000.0
            )?;
            writer.reset()?;
            ts = SystemTime::now();
        }

        module.execute(stdin(), stdout());
    } else {
        let mut interpreter = Interpreter::new(stdin(), stdout());

        if let Err(err) = interpreter.execute(&program) {
            return err.pretty_print(&source, Some(&path.to_string_lossy()));
        }
    }

    if verbose {
        let mut writer = StandardStream::stderr(ColorChoice::Auto);
        writer.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?;
        writeln!(
            writer,
            "Executed program in {}ms",
            ts.elapsed()?.as_micros() as f32 / 1000.0
        )?;
        writer.reset()?;
    }

    Ok(())
}