firework_lang 0.3.5

Firework Programming Language
Documentation
extern crate inkwell_llvm12 as inkwell;

use clap::{App, AppSettings, Arg, SubCommand};

use firework_lang::codegen::CodeGen;
use firework_lang::core::install_core;
use firework_lang::firework_project::FireworkProject;
use firework_lang::{todo_feature, unrecoverable_error};
use inkwell::context::Context;
use inkwell::OptimizationLevel;
use std::error::Error;
use strsim::damerau_levenshtein;

const SUBCOMMANDS: [&str; 6] = ["install", "new", "build", "dump_ir", "dump_ast", "repl"];

fn main() -> Result<(), Box<dyn Error>> {
    let clap_app = App::new("Firework")
        .settings(&[
            AppSettings::ColorAlways,
            AppSettings::AllowExternalSubcommands,
            AppSettings::ArgRequiredElseHelp,
        ])
        .version(env!("CARGO_PKG_VERSION"))
        .author(env!("CARGO_PKG_AUTHORS"))
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .subcommand(
            SubCommand::with_name("install")
                .help("Installs or updates the core library required of Firework"),
        )
        .subcommand(SubCommand::with_name("new").arg(Arg::with_name("project").takes_value(true)))
        .subcommand(SubCommand::with_name("run").help("Runs a firework project"))
        .subcommand(SubCommand::with_name("dump_ir").help("Dumps LLVM's output to ir.ll"))
        .subcommand(SubCommand::with_name("dump_asm").help("Dumps LLVM's assembly output to ir.ll"))
        .subcommand(SubCommand::with_name("repl").help("Runs the firework repl"));

    let matches = clap_app.get_matches();
    let context = Context::create();
    let module = context.create_module("main");
    let execution_engine = module
        .create_jit_execution_engine(OptimizationLevel::Aggressive)
        .unwrap();

    let codegen = CodeGen::new(&context, module, context.create_builder(), execution_engine);
    let project = FireworkProject::new(codegen);

    match matches.subcommand() {
        ("new", Some(matches)) => {
            if let Some(project_name) = matches.value_of("project") {
                project.new_project(project_name)
            } else {
                unrecoverable_error!("No project name supplied!")
            }
        }
        ("run", _) => project.run()?,
        ("dump_ir", _) => project.dump_ir()?,
        ("dump_asm", _) => project.dump_asm()?,
        ("repl", _) => todo_feature!("The REPL"),
        ("install", _) => install_core()?,
        (other, _) => {
            if !other.chars().map(|c| c.is_numeric()).all(|x| x) {
                let words: Vec<usize> = SUBCOMMANDS
                    .iter()
                    .map(|command| damerau_levenshtein(other, command))
                    .collect();

                let closest_word_index: usize = words
                    .iter()
                    .position(|a| a == words.iter().min().unwrap())
                    .unwrap();

                unrecoverable_error!(format!(
                    "Subcommand `{}` doesn't exist! Did you mean `{}`?",
                    other, SUBCOMMANDS[closest_word_index]
                ));
            } else {
                unrecoverable_error!(format!("Subcommand `{}` doesn't exist!", other));
            }
        }
    }
    Ok(())
}