use chumsky::{input::Stream, prelude::*};

const DEBUG_COMPILE: bool = true;
const DEBUG_RUN: bool = true;

mod memory_sandbox;

mod spans;
use crate::spans::Spanned;

mod lexer;
use crate::lexer::lexer;

mod parser;
use crate::parser::{parser, Ast};

mod validator;

mod compiler;
use crate::compiler::Compiler;

mod value;

mod vm;
use vm::Vm;

pub fn run(src: &'static str) {
    let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
    if !lex_errs.is_empty() {
        println!("{:?}", lex_errs);
        return;
    }

    let tokens = tokens.unwrap();

    let (parse_result, parse_errors) = parser()
        .parse(Stream::from_iter(tokens).map((0..src.len()).into(), |(t, s)| (t, s)))
        .into_output_errors();
    if !parse_errors.is_empty() {
        println!("{:?}", parse_errors);
        return;
    }

    // ::sigh:: The AST should be 'static
    // This simplifies lifetimes, and
    // in any event, the AST should live forever
    let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap()));

    let mut compiler = Compiler::new(parsed, "test", src);
    compiler.compile();
    if DEBUG_COMPILE {
        compiler.disassemble();
        println!("\n\n")
    }

    if DEBUG_RUN {
        println!("=== vm run: test ===");
    }

    let mut vm = Vm::new(&compiler.chunk);
    let result = vm.interpret();
    let output = match result {
        Ok(val) => val.show(&compiler.chunk),
        Err(panic) => format!("{:?}", panic),
    };
    println!("{output}");
}

pub fn main() {
    let src = "
let foo = 42
match foo with {
    :foo -> {
        1
        2
        x
    }
    2 -> :two
    42 -> :everything
    _ -> :something_else
}
    ";
    run(src);
}