sphinx-lang 0.8.6

An intepreter for a dynamic language implemented in Rust
Documentation
use std::path::Path;

use sphinx;
use sphinx::builtins;
use sphinx::source::ModuleSource;
use sphinx::codegen::{Program, CompiledProgram};
use sphinx::runtime::{Module, VirtualMachine};
use sphinx::runtime::errors::{ExecResult, ErrorKind};


fn build_program(source: &ModuleSource) -> Option<CompiledProgram> {
    match sphinx::build_module(source) {
        Err(errors) => {
            sphinx::print_build_errors(&errors, source);
            None
        },
        
        Ok(program) => Some(program)
    }
}

fn run_test_script(path: &Path) -> ExecResult<()> {
    let source = ModuleSource::File(path.into());
    let build = build_program(&source).expect("build failed");
    
    let program = Program::load(build.program);
    
    let main_env = builtins::create_prelude();
    let main_module = Module::with_env(Some(source), program.data, main_env);
    
    let vm = VirtualMachine::new(main_module, &program.main);
    vm.run()?;
    
    Ok(())
}

macro_rules! test_script {
    ( $name:tt, $path:expr ) => {
        #[test]
        fn $name() {
            if let Err(error) = run_test_script(Path::new($path)) {
                panic!("{}{}", error.traceback(), error);
            }
        }
    };
    ( $name:tt, $path:expr, error: $error:pat ) => {
        #[test]
        fn $name() {
            let error = run_test_script(Path::new($path)).unwrap_err();
            assert!(matches!(error.kind(), $error));
        }
    };
}



test_script!(empty_file, "tests/empty_file.sph");
test_script!(precedence, "tests/precedence.sph");

mod if_tests {
    use super::*;
    
    test_script!(elif, "tests/if/elif.sph");
    test_script!(else_, "tests/if/else.sph");
    test_script!(if_, "tests/if/if.sph");
    test_script!(truth, "tests/if/truth.sph");
}

mod loop_tests {
    use super::*;
    
    test_script!(loop_, "tests/loop/loop.sph");
    test_script!(continue_, "tests/loop/continue.sph");
}

mod tuple_tests {
    use super::*;
    
    test_script!(assignment, "tests/tuple/assignment.sph");
    test_script!(comparison, "tests/tuple/comparison.sph");
}

mod while_tests {
    use super::*;
    
    test_script!(while_, "tests/while/while.sph");
    test_script!(continue_, "tests/while/continue.sph");
}

mod for_tests {
    use super::*;
    
    test_script!(for_, "tests/for/for.sph");
    test_script!(continue_, "tests/for/continue.sph");
}


mod iterator_tests {
    use super::*;
    
    test_script!(zip_unzip, "tests/iterators/zip_unzip.sph");
}

mod variable_tests {
    use super::*;
    
    test_script!(early_bound, "tests/variable/early_bound.sph");
    test_script!(in_middle_of_block, "tests/variable/in_middle_of_block.sph");
    test_script!(in_nested_block, "tests/variable/in_nested_block.sph");
    test_script!(redeclare_global, "tests/variable/redeclare_global.sph");
    test_script!(assign_to_outer_block, "tests/variable/assign_to_outer_block.sph");
}

mod function_tests {
    use super::*;
    
    test_script!(empty_body, "tests/function/empty_body.sph");
    test_script!(local_recursion, "tests/function/local_recursion.sph");
    test_script!(nested_call_with_arguments, "tests/function/nested_call_with_arguments.sph");
    test_script!(inner_block, "tests/function/inner_block.sph");
    test_script!(missing_arguments, "tests/function/missing_arguments.sph", error: ErrorKind::MissingArguments {..});
    test_script!(argument_unpack, "tests/function/argument_unpack.sph");
}

mod closure_tests {
    use super::*;
    
    test_script!(open_closure_in_function, "tests/closure/open_closure_in_function.sph");
    test_script!(assign_to_upvalue, "tests/closure/assign_to_upvalue.sph");
    test_script!(nested_closure, "tests/closure/nested_closure.sph");
}