use std::collections::HashMap;
use std::fs::{self, write};
use std::path::{Path, PathBuf};
use blah::compiler::CodeGen;
use blah::interpreter::Interpreter;
use blah::package::{self, Manifest};
use blah::shared::Operation;
use clap::{CommandFactory, Parser, Subcommand};
use inkwell::context::Context;
#[derive(Parser, Debug)]
#[command(version, about = "Barb: Unified Toolchain for Brainfuck.")]
struct Cli {
#[arg(short, long, global = true)]
interpret: bool,
#[command(subcommand)]
command: Option<Commands>,
file: Option<PathBuf>,
}
#[derive(Subcommand, Debug)]
enum Commands {
#[command(about = "Run a brainfuck file.")]
Run { file: PathBuf },
#[command(about = "Build native binary.")]
Build {
file: PathBuf,
#[arg(short, long, default_value = "build/")]
output_dir: PathBuf,
},
#[command(about = "Create a new project.")]
New { name: String },
#[command(about = "Install packages from bark.toml.")]
Install,
}
fn main() {
let cli = Cli::parse();
match &cli.command {
Some(Commands::Run { file }) => {
let operations = parse(file);
run(operations, cli.interpret);
}
Some(Commands::Build { file, output_dir }) => {
let operations = parse(file);
let context = Context::create();
let codegen = CodeGen::new(&context);
codegen.compile(&operations);
codegen.build(output_dir, file);
}
Some(Commands::New { name }) => {
let dir = Path::new(name);
fs::create_dir_all(dir).unwrap();
let manifest = Manifest {
name: name.clone(),
features: HashMap::new(),
dependencies: HashMap::new(),
};
write(dir.join("bark.toml"), toml::to_string(&manifest).unwrap()).unwrap();
let hello = ">++++++++[<+++++++++>-]<.>++++[<+++++++>-]<+.+++++++..+++.>>++++++[<+++++++>-]<++.------------.>++++++[<+++++++++>-]<+.<.+++.------.--------.>>>++++[<++++++++>-]<+.";
write(dir.join("main.bf"), hello).unwrap();
println!("Created project: {name}");
}
Some(Commands::Install) => {
let root = std::env::current_dir().unwrap();
match package::Resolver::new(&root) {
Some(ref r) => {
let _ = r;
println!("Dependencies resolved");
}
None => eprintln!("error: no bark.toml found in the current directory"),
}
}
None => {
let file = cli.file.as_ref().unwrap_or_else(|| {
Cli::command()
.error(
clap::error::ErrorKind::MissingRequiredArgument,
"requires a brainfuck file",
)
.exit();
});
let operations = parse(file);
run(operations, cli.interpret);
}
}
}
fn parse(path: &Path) -> Vec<Operation> {
package::parse_source(path)
}
fn run(operations: Vec<Operation>, interpret: bool) {
if interpret {
let mut interpreter = Interpreter {
memory: [0; 30000],
data_ptr: 0,
instr_ptr: 0,
program: operations,
};
interpreter.run();
} else {
let context = Context::create();
let codegen = CodeGen::new(&context);
codegen.compile(&operations);
codegen.jit_run();
}
}