[fine] Multi-module compilation
It's a little bit complicated, loading a module is a two-step dance but here's how it's done. Probably some surface-area refactoring needs to happen so that we do the right thing.
This commit is contained in:
parent
ab477cd783
commit
a3d4c24f11
8 changed files with 506 additions and 274 deletions
|
|
@ -1,7 +1,10 @@
|
|||
use fine::compiler::{compile, Function, Module};
|
||||
use fine::compile_program;
|
||||
use fine::compiler::{compile_module, CompiledModule, Function};
|
||||
use fine::program::{
|
||||
Module, ModuleLoadError, ModuleLoader, ModuleSource, Program, StandardModuleLoader,
|
||||
};
|
||||
use fine::semantics::{Error, Type};
|
||||
use fine::vm::{eval_export_fn, Context};
|
||||
use fine::{ModuleLoadError, ModuleLoader, ModuleSource, Runtime, StandardModuleLoader};
|
||||
use fine::vm::{eval_export_fn, Context, VMError};
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::fmt::Write as _;
|
||||
|
|
@ -168,11 +171,11 @@ impl ModuleLoader for TestLoader {
|
|||
}
|
||||
}
|
||||
|
||||
fn test_runtime(_source_path: &str, source: Rc<str>) -> Runtime {
|
||||
Runtime::new(TestLoader::new(_source_path.into(), source))
|
||||
fn test_runtime(_source_path: &str, source: Rc<str>) -> Program {
|
||||
Program::new(TestLoader::new(_source_path.into(), source))
|
||||
}
|
||||
|
||||
fn assert_type_at(module: Rc<fine::Module>, pos: usize, expected: &str, _source_path: &str) {
|
||||
fn assert_type_at(module: Rc<Module>, pos: usize, expected: &str, _source_path: &str) {
|
||||
let semantics = module.semantics();
|
||||
let tree = semantics.tree();
|
||||
|
||||
|
|
@ -197,7 +200,7 @@ fn assert_type_at(module: Rc<fine::Module>, pos: usize, expected: &str, _source_
|
|||
}
|
||||
|
||||
fn assert_type_error_at(
|
||||
module: Rc<fine::Module>,
|
||||
module: Rc<Module>,
|
||||
errors: &[Rc<Error>],
|
||||
pos: usize,
|
||||
expected: &str,
|
||||
|
|
@ -256,7 +259,7 @@ fn dump_function(out: &mut String, function: &Function) -> std::fmt::Result {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn dump_module(out: &mut String, module: &Module) -> std::fmt::Result {
|
||||
fn dump_module(out: &mut String, module: &CompiledModule) -> std::fmt::Result {
|
||||
for function in module.functions() {
|
||||
dump_function(out, function)?;
|
||||
}
|
||||
|
|
@ -264,9 +267,9 @@ fn dump_module(out: &mut String, module: &Module) -> std::fmt::Result {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn assert_compiles_to(module: Rc<fine::Module>, expected: &str, source_path: &str) {
|
||||
fn assert_compiles_to(module: Rc<Module>, expected: &str, source_path: &str) {
|
||||
let semantics = module.semantics();
|
||||
let module = compile(&semantics);
|
||||
let module = compile_module(&semantics);
|
||||
|
||||
let mut actual = String::new();
|
||||
dump_module(&mut actual, &module).expect("no dumping?");
|
||||
|
|
@ -286,7 +289,7 @@ fn assert_compiles_to(module: Rc<fine::Module>, expected: &str, source_path: &st
|
|||
}
|
||||
}
|
||||
|
||||
fn assert_no_errors(module: Rc<fine::Module>, errors: &[Rc<Error>]) {
|
||||
fn assert_no_errors(module: Rc<Module>, errors: &[Rc<Error>]) {
|
||||
let semantics = module.semantics();
|
||||
|
||||
let expected_errors: &[Rc<Error>] = &[];
|
||||
|
|
@ -299,14 +302,39 @@ fn assert_no_errors(module: Rc<fine::Module>, errors: &[Rc<Error>]) {
|
|||
);
|
||||
}
|
||||
|
||||
fn assert_eval_ok(module: Rc<fine::Module>, expected: &str) {
|
||||
fn dump_runtime_error(module: &Rc<Module>, context: &Context, e: VMError) -> ! {
|
||||
let semantics = module.semantics();
|
||||
let module = compile(&semantics);
|
||||
semantics.dump_compiler_state(None);
|
||||
|
||||
let mut context = Context::new(module.clone());
|
||||
context.init().expect("Unable to initialize module");
|
||||
if let Some(module) = context.get_module(module.id()) {
|
||||
let mut actual = String::new();
|
||||
let _ = dump_module(&mut actual, &module);
|
||||
|
||||
match eval_export_fn(&mut context, "test", &[]) {
|
||||
eprintln!("{actual}");
|
||||
}
|
||||
|
||||
eprintln!("Backtrace:");
|
||||
for frame in e.stack.iter() {
|
||||
let func = frame.func();
|
||||
eprint!(" {} (", func.name());
|
||||
for arg in frame.args().iter() {
|
||||
eprint!("{:?},", arg);
|
||||
}
|
||||
eprintln!(") @ {}", frame.pc());
|
||||
}
|
||||
eprintln!();
|
||||
|
||||
panic!("error occurred while running: {:?}", e.code);
|
||||
}
|
||||
|
||||
fn assert_eval_ok(program: &Program, module: Rc<Module>, expected: &str) {
|
||||
let semantics = module.semantics();
|
||||
let mut context = Context::new();
|
||||
if let Err(e) = compile_program(&program, &mut context) {
|
||||
dump_runtime_error(&module, &context, e);
|
||||
};
|
||||
|
||||
match eval_export_fn(&mut context, module.id(), "test", &[]) {
|
||||
Ok(v) => {
|
||||
let actual = format!("{:?}", v);
|
||||
semantic_assert_eq!(
|
||||
|
|
@ -317,31 +345,11 @@ fn assert_eval_ok(module: Rc<fine::Module>, expected: &str) {
|
|||
"wrong return from test function"
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
semantics.dump_compiler_state(None);
|
||||
|
||||
let mut actual = String::new();
|
||||
let _ = dump_module(&mut actual, &module);
|
||||
|
||||
eprintln!("{actual}");
|
||||
|
||||
eprintln!("Backtrace:");
|
||||
for frame in e.stack.iter() {
|
||||
let func = frame.func();
|
||||
eprint!(" {} (", func.name());
|
||||
for arg in frame.args().iter() {
|
||||
eprint!("{:?},", arg);
|
||||
}
|
||||
eprintln!(") @ {}", frame.pc());
|
||||
}
|
||||
eprintln!();
|
||||
|
||||
panic!("error occurred while running: {:?}", e.code);
|
||||
}
|
||||
Err(e) => dump_runtime_error(&module, &context, e),
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_errors(module: Rc<fine::Module>, errors: &[Rc<Error>], expected_errors: Vec<&str>) {
|
||||
fn assert_errors(module: Rc<Module>, errors: &[Rc<Error>], expected_errors: Vec<&str>) {
|
||||
let semantics = module.semantics();
|
||||
|
||||
let errors: Vec<String> = errors.iter().map(|e| format!("{}", e)).collect();
|
||||
|
|
@ -355,7 +363,7 @@ fn assert_errors(module: Rc<fine::Module>, errors: &[Rc<Error>], expected_errors
|
|||
);
|
||||
}
|
||||
|
||||
fn assert_check_error(module: Rc<fine::Module>, errors: &[Rc<Error>], expected: &str) {
|
||||
fn assert_check_error(module: Rc<Module>, errors: &[Rc<Error>], expected: &str) {
|
||||
let semantics = module.semantics();
|
||||
|
||||
semantic_assert!(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
import "./foo.fine" as foo;
|
||||
|
||||
// NOTE: This is right here because a known miscompilation will cause us to
|
||||
// call this function instead of the actual target.
|
||||
fun wrong_function() -> string {
|
||||
"VERY WRONG"
|
||||
}
|
||||
|
||||
fun test() -> string {
|
||||
foo.hello() + " world"
|
||||
}
|
||||
|
||||
// TODO: Obviously run the code duh
|
||||
// @no-errors
|
||||
/// @eval: asdf
|
||||
// @eval: String("hello world")
|
||||
Loading…
Add table
Add a link
Reference in a new issue