leptonvm 0.1.0

A VM to play nucleus on
Documentation
use leptonvm::cpu::*;
use leptonvm::parser::*;


use peg::{error::ParseError, str::LineCol};
use anyhow::Result;

use clap::Parser;

use std::path::PathBuf;
use std::fs;

const ITERATIONS: u32 = 4096*8;

#[derive(Parser, Debug)]
#[clap(author = "Ellie")]
struct Args {
    #[clap(long, short)]
    verbose: bool,

    #[clap(long, short)]
    playback: bool,
    
    #[clap(parse(from_os_str))]
    files: Vec<PathBuf>,

    #[clap(long, short)]
    iterations: Option<u32>
}

fn format_msg(e: ParseError::<LineCol>, input: &str, filename: &str) -> anyhow::Error {
    let line_with_error = input.lines().nth(e.location.line - 1).unwrap();
    let expected: Vec<&str> = e.expected.tokens().collect();
    let msg = if expected.len() == 1 {
        format!("Expected {}", expected[0])
    } else {
        format!("Expected one of {}", expected.join(", "))
    };
    let final_msg = format!("{}, found \"{}\"", msg, line_with_error.chars().nth(e.location.column -1).unwrap());
    let mut output = String::new();
    output.push_str(&format!("Error: {}:\n", final_msg));
    output.push_str(&format!("--> {}:{}:{}\n", filename, e.location.line, e.location.column));
    output.push_str(&format!("{} | {}\n", e.location.line, line_with_error));
    output.push_str(&format!("{}^ {}", " ".repeat(e.location.column + (e.location.line as f32).log10() as usize + 3), msg));
    anyhow::Error::msg(output)
}

fn main() -> Result<()> {
    pretty_env_logger::init();
    let args = Args::parse();
    
    if args.files.len() < 2 {
        return Err(anyhow::Error::msg::<&'static str>("USAGE: lepton [OPTIONS] QUARK1 QUARK2 [QUARKn...]".into()));
    }

    let mut programs: Vec<Program> = vec![];
    for source in args.files {
        let code = fs::read_to_string(&source)?;
        match pcode::program(&code) {
            Ok(program) => programs.push(program),
            Err(e) => {
                println!("{}", format_msg(e, &code, &source.to_string_lossy()));
                std::process::exit(2);
            }
        };
    }

    let mut comp = Cpu::new(programs);
    for _ in 0..args.iterations.unwrap_or(ITERATIONS) {
        if comp.halted { break }
        if args.playback {
            println!("{:?}", serde_json::to_string(&comp).unwrap());
        }
        comp = comp.do_cycle();
    }
    if !comp.halted {
        println!("Execution timed out; there is no winner");
        println!("You could try to increase the number of iterations using `-i` (ran for {} iterations)", ITERATIONS);
    }
    Ok(())
}