use std::path::Path; use std::rc::Rc; pub mod compiler; pub mod tokenizer; pub mod vm; pub mod typer; mod error; use error::Error; use tokenizer::TokenStream; pub fn run_file(path: &Path, print: bool) -> Result<(), Vec> { run(tokenizer::file_to_tokens(path), path, print) } pub fn run_string(s: &str, print: bool) -> Result<(), Vec> { run(tokenizer::string_to_tokens(s), Path::new("builtin"), print) } pub fn run(tokens: TokenStream, path: &Path, print: bool) -> Result<(), Vec> { match compiler::compile("main", path, tokens) { Ok(block) => vm::VM::new().print_blocks(print) .print_ops(print) .run(Rc::new(block)).or_else(|e| Err(vec![e])), Err(errors) => Err(errors), } } #[cfg(test)] mod tests { use super::{run_file, run_string}; use std::path::Path; #[macro_export] macro_rules! assert_errs { ($result:expr, [ $( $kind:pat ),* ]) => { println!("{} => {:?}", stringify!($result), $result); assert!(matches!( $result.unwrap_err().as_slice(), &[$($crate::error::Error { kind: $kind, file: _, line: _, message: _, }, )*] )) }; } #[macro_export] macro_rules! test_string { ($fn:ident, $prog:literal) => { #[test] fn $fn() { $crate::run_string($prog, true).unwrap(); } }; ($fn:ident, $prog:literal, $errs:tt) => { #[test] fn $fn() { $crate::assert_errs!($crate::run_string($prog, true), $errs); } } } #[macro_export] macro_rules! test_file { ($fn:ident, $path:literal) => { #[test] fn $fn() { let file = Path::new($path); run_file(&file, true).unwrap(); } }; } use crate::error::ErrorKind; #[test] fn unreachable_token() { assert_errs!(run_string("\n", true), [ErrorKind::Unreachable]); } test_file!(order_of_operations, "tests/order-of-operations.tdy"); test_file!(variables, "tests/variables.tdy"); test_file!(scoping, "tests/scoping.tdy"); test_file!(if_, "tests/if.tdy"); test_file!(for_, "tests/for.tdy"); test_file!(fun, "tests/fun.tdy"); }