hyeong 0.1.0-beta

Hyeo-ung Programming Language Compiler
Documentation
use std::io::{stderr, stdin, stdout, Write};

use clap::*;

use hyeong::code::State;
use hyeong::{build, code, debug, execute, interpreter, io, optimize};
use std::path::Path;
use std::process::Command;

fn main() {
    let matches = App::new("hyeong")
        .version("0.1.0-beta")
        .about("Hyeo-ung programming language tool")
        .subcommand(
            App::new("build")
                .about("Compiles hyeong code")
                .arg(
                    Arg::with_name("input")
                        .value_name("input_file")
                        .takes_value(true)
                        .required(true)
                        .help("input file to compile"),
                )
                .arg(
                    Arg::with_name("optimize")
                        .value_name("optimize")
                        .takes_value(true)
                        .short("O")
                        .long("optimize")
                        .help("optimize level (0: no optimize, 1: basic optimize, 2: hard optimize")
                        .default_value("2"),
                )
                .arg(
                    Arg::with_name("output")
                        .value_name("output")
                        .takes_value(true)
                        .short("o")
                        .long("output")
                        .help("binary output file (filename by default)"),
                )
                .arg(
                    Arg::with_name("warning")
                        .value_name("warning")
                        .takes_value(true)
                        .short("W")
                        .long("warn")
                        .help("warning level (0/none: no warning, 1/all: all warning")
                        .default_value("all"),
                ),
        )
        .subcommand(
            App::new("check")
                .about("Parse your code and check if you are right")
                .arg(
                    Arg::with_name("input")
                        .value_name("input_file")
                        .takes_value(true)
                        .required(true)
                        .help("input file to check"),
                ),
        )
        .subcommand(
            App::new("debug")
                .about("Debug your code command by command")
                .arg(
                    Arg::with_name("input")
                        .value_name("input_file")
                        .takes_value(true)
                        .required(true)
                        .help("input file to debug"),
                )
                .arg(
                    Arg::with_name("from")
                        .value_name("from")
                        .takes_value(true)
                        .short("f")
                        .long("from")
                        .help("place to start debugging from")
                        .default_value("0"),
                ),
        )
        .subcommand(
            App::new("run")
                .about("Run hyeong code directly")
                .arg(
                    Arg::with_name("input")
                        .value_name("input_file")
                        .takes_value(true)
                        .required(true)
                        .help("input file to run"),
                )
                .arg(
                    Arg::with_name("optimize")
                        .value_name("optimize")
                        .takes_value(true)
                        .short("O")
                        .long("optimize")
                        .help(
                            "optimize level (0: no optimize, 1: basic optimize, 2: hard optimize)",
                        )
                        .default_value("2"),
                )
                .arg(
                    Arg::with_name("warning")
                        .value_name("warning")
                        .takes_value(true)
                        .short("W")
                        .long("warn")
                        .help("warning level (0/none: no warning, 1/all: all warning")
                        .default_value("all"),
                ),
        )
        .get_matches();

    if let Some(ref matches) = matches.subcommand_matches("build") {
        let file = matches.value_of("input").unwrap();
        let un_opt_code = io::read_file(file);
        let level_str = matches.value_of("optimize").unwrap();
        let level = io::handle_error(level_str.parse::<usize>());
        let output_file = match matches.value_of("output") {
            Some(v) => v.to_string(),
            None => {
                let v = file.split(".").collect::<Vec<_>>();
                v[..v.len() - 1].join(".")
            }
        };

        let source = if level >= 1 {
            let (state, opt_code) = optimize::optimize(un_opt_code, level);
            io::print_log("compiling to rust");
            build::build_source(state, &opt_code, level)
        } else {
            let state = code::UnOptState::new();
            io::print_log("compiling to rust");
            build::build_source(state, &un_opt_code, 0)
        };
        if !Path::new(&*io::get_build_path()).exists() {
            io::print_log("making temporary crate");
            io::execute_command_stderr(
                &*format!(
                    "cargo new {} --color always --vcs none",
                    io::get_build_path()
                ),
                &*format!(
                    "cargo new {} --color always --vcs none",
                    io::get_build_path()
                ),
            );
        }
        io::save_to_file(&*(io::get_build_path() + "/src/main.rs"), source);
        io::print_log("compiling rust code");
        io::execute_command_stderr(
            &*format!(
                "cargo build --manifest-path={}\\Cargo.toml --release --color always",
                io::get_build_path()
            ),
            &*format!(
                "cargo build --manifest-path={}/Cargo.toml --release --color always",
                io::get_build_path()
            ),
        );
        io::print_log("moving binary to current directory");
        if cfg!(target_os = "windows") {
            io::handle_error(Command::new("cmd").arg("/C").arg(format!(
                "copy %USERPROFILE%\\.hyeong\\hyeong-build\\target\\release\\hyeong-build.exe {}.exe",
                output_file
            )).output())
        } else {
            io::handle_error(
                Command::new("bash")
                    .arg("-c")
                    .arg(format!(
                        "cp \"$HOME\"/.hyeong/hyeong-build/target/release/hyeong-build {}",
                        output_file
                    ))
                    .output(),
            )
        };
        io::print_log("done!");
    } else if let Some(ref matches) = matches.subcommand_matches("check") {
        let file = matches.value_of("input").unwrap();
        let code = io::read_file(file);
        for c in code.iter() {
            println!("{}:{}", file, c.to_string())
        }
    } else if let Some(ref matches) = matches.subcommand_matches("debug") {
        let file = matches.value_of("input").unwrap();
        let code = io::read_file(file);
        let from = io::handle_error(matches.value_of("from").unwrap().parse::<usize>());
        debug::run(code, from);
    } else if let Some(ref matches) = matches.subcommand_matches("run") {
        let file = matches.value_of("input").unwrap();
        let un_opt_code = io::read_file(file);
        let level_str = matches.value_of("optimize").unwrap();

        let level = io::handle_error(level_str.parse::<usize>());
        let mut stdout = stdout();
        let mut stderr = stderr();

        if level >= 1 {
            let (mut state, opt_code) = optimize::optimize(un_opt_code, level);
            io::print_log("running code");

            if !state.get_stack(1).is_empty() {
                for num in state.get_stack(1).iter() {
                    io::write(
                        &mut stdout,
                        &*format!("{}", num.floor().to_int() as u8 as char),
                    );
                }
                io::handle_error(stdout.flush());
                state.get_stack(1).clear();
            }

            if !state.get_stack(2).is_empty() {
                for num in state.get_stack(2).iter() {
                    io::write(
                        &mut stderr,
                        &*format!("{}", num.floor().to_int() as u8 as char),
                    );
                }
                io::handle_error(stderr.flush());
                state.get_stack(2).clear();
            }
            for code in opt_code {
                state = execute::execute(&mut stdin(), &mut stdout, &mut stderr, state, &code);
            }
        } else {
            let mut state = code::UnOptState::new();
            io::print_log("running code");
            for code in un_opt_code {
                state = execute::execute(&mut stdin(), &mut stdout, &mut stderr, state, &code);
            }
        }
    } else {
        interpreter::run();
    }
}