use anyhow::{Context, Result};
use clap::{Parser, ValueEnum};
use std::fs::File;
use std::io::{self, Read, Write};
use brainstem::{compile_brain_stem, run_program};
#[derive(ValueEnum, Debug, Clone)]
enum IntegerType {
U8,
I8,
U32,
I32,
}
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(short, long, default_value = "-")]
input: String,
#[arg(short, long, default_value = "")]
output: String,
#[arg(short, long)]
run: bool,
#[arg(short = 'I', long, default_value = "-")]
run_input: String,
#[arg(short = 'O', long, default_value = "-")]
run_output: String,
#[arg(short = 't', long, value_enum, default_value = "u32")]
int_type: IntegerType,
#[arg(short = 'M', long)]
max_steps: Option<usize>,
}
fn main() -> Result<()> {
let args = if std::env::args().len() <= 1 {
Args::parse_from(["rbfc", "--help"]);
return Ok(());
} else {
Args::parse()
};
let mut input_content = String::new();
if args.input == "-" {
io::stdin()
.read_to_string(&mut input_content)
.context("Failed to read from stdin")?;
} else {
let mut file = File::open(&args.input)
.with_context(|| format!("Failed to open input file: {}", args.input))?;
file.read_to_string(&mut input_content)
.with_context(|| format!("Failed to read from input file: {}", args.input))?;
}
let bf_code = compile_brain_stem(&input_content).context("Failed to compile bf script")?;
if args.output == "-" {
io::stdout()
.write_all(bf_code.as_bytes())
.context("Failed to write to stdout")?;
} else if !args.output.is_empty() {
let mut file = File::create(&args.output)
.with_context(|| format!("Failed to create output file: {}", args.output))?;
file.write_all(bf_code.as_bytes())
.with_context(|| format!("Failed to write to output file: {}", args.output))?;
}
if args.run {
let mut input: Box<dyn Read> =
if args.run_input == "-" {
Box::new(io::stdin())
} else {
Box::new(File::open(&args.run_input).with_context(|| {
format!("Failed to open run input file: {}", args.run_input)
})?)
};
let mut output: Box<dyn Write> = if args.run_output == "-" {
Box::new(io::stdout())
} else {
Box::new(File::create(&args.run_output).with_context(|| {
format!("Failed to create run output file: {}", args.run_output)
})?)
};
let _steps = match args.int_type {
IntegerType::U8 => {
run_program::<u8>(&bf_code, &mut input, &mut output, args.max_steps)?
}
IntegerType::I8 => {
run_program::<i8>(&bf_code, &mut input, &mut output, args.max_steps)?
}
IntegerType::U32 => {
run_program::<u32>(&bf_code, &mut input, &mut output, args.max_steps)?
}
IntegerType::I32 => {
run_program::<i32>(&bf_code, &mut input, &mut output, args.max_steps)?
}
};
}
Ok(())
}