diff --git a/fine/build.rs b/fine/build.rs index 1b3c7b1e..9938438a 100644 --- a/fine/build.rs +++ b/fine/build.rs @@ -107,7 +107,7 @@ fn generate_test_for_file(path: PathBuf) -> String { } else if let Some(line) = line.strip_prefix("@eval:") { let expected = line.trim(); assertions.push(quote! { - crate::assert_eval_ok(_module.clone(), #expected); + crate::assert_eval_ok(&program, _module.clone(), #expected); }); } else if let Some(line) = line.strip_prefix("@check-error:") { let expected = line.trim(); @@ -139,8 +139,8 @@ fn generate_test_for_file(path: PathBuf) -> String { #disabled fn #name() { let source : std::rc::Rc = #contents.into(); - let mut runtime = crate::test_runtime(#display_path, source.clone()); - let (_errors, _module) = runtime.load_module("__test__").unwrap(); + let mut program = crate::test_runtime(#display_path, source.clone()); + let (_errors, _module) = program.load_module("__test__").unwrap(); #(#assertions)* } diff --git a/fine/src/compiler.rs b/fine/src/compiler.rs index dbb4a5c2..09a095f8 100644 --- a/fine/src/compiler.rs +++ b/fine/src/compiler.rs @@ -3,7 +3,9 @@ use std::rc::Rc; use crate::{ parser::{Child, SyntaxTree, Tree, TreeKind, TreeRef}, - semantics::{string_constant_to_string, Declaration, Location, Origin, Semantics, Type}, + semantics::{ + string_constant_to_string, Declaration, Location, ModuleId, Origin, Semantics, Type, + }, tokens::TokenKind, }; @@ -62,6 +64,8 @@ pub enum Instruction { StoreSlot(usize), StringAdd, NewList(usize), + + ModulePrefix(ModuleId), } pub enum Export { @@ -69,23 +73,31 @@ pub enum Export { Global(usize), } -pub struct Module { +pub struct CompiledModule { + pub id: ModuleId, pub functions: Vec>, // Functions pub globals: usize, // The number of global variables pub exports: HashMap, // Exports by name pub init: usize, // The index of the initialization function + pub deps: Vec, // Modules I depend on } -impl Module { - pub fn new() -> Self { - Module { +impl CompiledModule { + pub fn new(id: ModuleId) -> Self { + CompiledModule { + id, functions: Vec::new(), globals: 0, exports: HashMap::new(), init: 0, + deps: Vec::new(), } } + pub fn init_function(&self) -> &Rc { + &self.functions[self.init] + } + pub fn functions(&self) -> &[Rc] { &self.functions } @@ -147,7 +159,7 @@ struct Compiler<'a> { semantics: &'a Semantics, syntax: &'a SyntaxTree, - module: Module, + module: CompiledModule, function: Function, } @@ -268,7 +280,7 @@ fn function_from_class_decl(source: &str, tree: &Tree) -> Result Rc { +pub fn compile_module(semantics: &Semantics) -> Rc { let source = semantics.source(); let syntax_tree = semantics.tree(); @@ -277,7 +289,7 @@ pub fn compile(semantics: &Semantics) -> Rc { semantics: &semantics, syntax: &syntax_tree, - module: Module::new(), + module: CompiledModule::new(semantics.mid()), function: Function::new("<< module >>", 0), }; @@ -318,6 +330,7 @@ pub fn compile(semantics: &Semantics) -> Rc { module.functions.push(f.unwrap()); } + module.deps.append(&mut semantics.import_ids()); Rc::new(module) } @@ -650,8 +663,6 @@ fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> C } fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declaration) -> CR { - // TODO: Handle load of non-local value. - let index = declaration.index; let instruction = match declaration.location { Location::Local => { @@ -665,7 +676,12 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat Instruction::LoadArgument(index) } Location::Module => { - compiler_assert!(c, t, index < c.module.globals); + if declaration.module != c.semantics.mid() { + // TODO: Assert here too? + c.push(Instruction::ModulePrefix(declaration.module)); + } else { + compiler_assert!(c, t, index < c.module.globals); + } Instruction::LoadModule(index) } Location::Slot => { @@ -673,7 +689,12 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat Instruction::LoadSlot(index) } - Location::Function => Instruction::LoadFunction(index), + Location::Function => { + if declaration.module != c.semantics.mid() { + c.push(Instruction::ModulePrefix(declaration.module)); + } + Instruction::LoadFunction(index) + } Location::ExternalFunction => Instruction::LoadExternFunction(index), @@ -1059,6 +1080,8 @@ fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) { TreeKind::LetStatement => compile_let_statement(c, t, tree, gen_value), TreeKind::ReturnStatement => compile_return_statement(c, tree), TreeKind::WhileStatement => compile_while_statement(c, tree, gen_value), + TreeKind::Export => compile_export_statement(c, tree, gen_value), + TreeKind::ExportList => OK, _ => ice!(c, t, "unsupported statement tree kind {:?}", tree.kind), }; @@ -1071,6 +1094,11 @@ fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) { } } +fn compile_export_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR { + compile_statement(c, tree.nth_tree(1).ok_or("nothing to export")?, gen_value); + OK +} + fn compile_if_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR { compile_expression(c, tree.nth_tree(0).ok_or("no expr")?); if !gen_value { @@ -1120,7 +1148,9 @@ fn compile_let_statement(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: b Instruction::StoreLocal(index) } Location::Module => { - if index >= c.module.globals { + if declaration.module != c.semantics.mid() { + c.push(Instruction::ModulePrefix(declaration.module)); + } else if index >= c.module.globals { c.module.globals = index + 1; } Instruction::StoreModule(index) diff --git a/fine/src/lib.rs b/fine/src/lib.rs index c80463e2..b914ca6e 100644 --- a/fine/src/lib.rs +++ b/fine/src/lib.rs @@ -1,181 +1,51 @@ -use std::{collections::HashMap, fs, path::PathBuf, rc::Rc}; +use std::{path::PathBuf, rc::Rc}; -use compiler::compile; -use parser::parse; -use semantics::{check, Error, ImportRecord, ModuleId, Semantics}; +use compiler::compile_module; +use program::{Module, Program, StandardModuleLoader}; use vm::{eval, Context}; pub mod compiler; pub mod parser; +pub mod program; pub mod semantics; pub mod tokens; pub mod vm; -pub enum ModuleSource { - SourceText(String), -} +fn load_module( + program: &Program, + module: &Rc, + context: &mut Context, +) -> Result<(), vm::VMError> { + if !context.loaded(module.id()) { + let semantics = module.semantics(); + let module = compile_module(&semantics); + context.set_module(module.clone()); -#[derive(Debug)] -pub enum ModuleLoadError { - IO(String, std::io::Error), -} - -pub trait ModuleLoader { - fn normalize_module_name(&self, source: &str, name: String) -> String; - fn load_module(&self, name: &String) -> Result; -} - -pub struct StandardModuleLoader { - base_path: PathBuf, -} - -impl StandardModuleLoader { - pub fn new(base_path: PathBuf) -> Self { - StandardModuleLoader { base_path } - } -} - -impl ModuleLoader for StandardModuleLoader { - fn normalize_module_name(&self, source: &str, name: String) -> String { - let p = self.base_path.join(source).join(name.clone()); - let result = match std::fs::canonicalize(&p) { - Ok(p) => match p.into_os_string().into_string() { - Ok(s) => s, - Err(_e) => name.clone(), - }, - Err(_e) => name.clone(), - }; - result - } - - fn load_module(&self, name: &String) -> Result { - match fs::read_to_string(name) { - Ok(c) => Ok(ModuleSource::SourceText(c)), - Err(e) => Err(ModuleLoadError::IO(name.clone(), e)), - } - } -} - -pub struct Module { - id: ModuleId, - semantics: Rc, -} - -impl Module { - pub fn id(&self) -> ModuleId { - self.id - } - - pub fn semantics(&self) -> Rc { - self.semantics.clone() - } -} - -pub struct Runtime { - next_module_id: u64, - modules: HashMap>, - loader: Box, -} - -impl Runtime { - pub fn new(loader: Box) -> Self { - Runtime { - next_module_id: 0, - modules: HashMap::new(), - loader, - } - } - - pub fn load_module( - &mut self, - name: &str, - ) -> Result<(Vec>, Rc), ModuleLoadError> { - let mut init_pending = HashMap::new(); - let mut names = Vec::new(); - let name = self.loader.normalize_module_name("", name.to_string()); - names.push(name.clone()); - - let mut id_assign = self.next_module_id; - - while let Some(name) = names.pop() { - if self.modules.contains_key(&name) || init_pending.contains_key(&name) { - // Either already loaded or pending load. - continue; - } - - // TODO: Errors here are bad! Remember, run everything! - match self.loader.load_module(&name)? { - ModuleSource::SourceText(source) => { - let mid = ModuleId::from(id_assign); - id_assign += 1; - - let source: Rc = source.into(); - let (tree, lines) = parse(&source); - let semantics = Rc::new(Semantics::new( - mid, - name.clone().into(), - source, - tree, - lines, - )); - - let mut normalized_imports = Vec::new(); - for import in semantics.imports() { - let normalized = self.loader.normalize_module_name(&name, import.clone()); - - names.push(normalized.clone()); - normalized_imports.push((import, normalized)); - } - - init_pending.insert(name, (mid, normalized_imports, semantics)); - } + for dep in module.deps.iter() { + if let Some(dep_mod) = program.get_module(dep) { + load_module(program, dep_mod, context)?; } } - for (_, (_, imports, semantics)) in init_pending.iter() { - let mut import_table = HashMap::new(); - for (import, normalized) in imports.iter() { - // NOTE: We look up the load(ed|ing) module here by normalized name, because that's how - // we track it... - let target = if let Some(module) = self.modules.get(&*normalized) { - ImportRecord { - name: normalized.clone(), - module_id: module.id(), - semantics: Rc::downgrade(&module.semantics), - } - } else { - let (module_id, _, semantics) = init_pending.get(&*normalized).unwrap(); - ImportRecord { - name: normalized.clone(), - module_id: *module_id, - semantics: Rc::downgrade(semantics), - } - }; - - // ...but we set it into the import table here with the name - // that the source code used, for more better binding. - import_table.insert(import.clone(), target); - } - semantics.set_imports(import_table); - } - - let mut errors = Vec::new(); - for (name, (id, _, semantics)) in init_pending.into_iter() { - check(&semantics); - errors.append(&mut semantics.snapshot_errors()); - self.modules.insert(name, Rc::new(Module { id, semantics })); - } - self.next_module_id = id_assign; - - let result = self.modules.get(&name).unwrap().clone(); - Ok((errors, result)) + eval(context, module.id, module.init, &[])?; } + + Ok(()) +} + +pub fn compile_program(program: &Program, context: &mut Context) -> Result<(), vm::VMError> { + for module in program.modules() { + load_module(program, module, context)?; + } + + Ok(()) } pub fn process_file(file: &str) { - let mut runtime = Runtime::new(Box::new(StandardModuleLoader::new(PathBuf::from(".")))); + let mut program = Program::new(Box::new(StandardModuleLoader::new(PathBuf::from(".")))); - let (errors, module) = match runtime.load_module(file) { + // Load the program text + let (errors, _) = match program.load_module(file) { Ok(r) => r, Err(_) => { eprintln!("Error loading module"); @@ -191,16 +61,10 @@ pub fn process_file(file: &str) { return; } - // shrug - let semantics = module.semantics(); - let module = compile(&semantics); - let main_function = module.functions[module.init].clone(); - - let mut context = Context::new(module.clone()); - match eval(&mut context, main_function, vec![]) { - Ok(v) => { - println!("{:?}", v); - } + // This is weird, why run the init function as main? Maybe just run main? + let mut context = Context::new(); + match compile_program(&program, &mut context) { + Ok(_) => {} Err(e) => { eprintln!("{:?}", e); } diff --git a/fine/src/program.rs b/fine/src/program.rs new file mode 100644 index 00000000..48d0a913 --- /dev/null +++ b/fine/src/program.rs @@ -0,0 +1,196 @@ +use std::{collections::HashMap, fs, path::PathBuf, rc::Rc}; + +use crate::parser::parse; +use crate::semantics::{check, Error, ImportRecord, ModuleId, Semantics}; + +pub enum ModuleSource { + SourceText(String), +} + +#[derive(Debug)] +pub enum ModuleLoadError { + IO(String, std::io::Error), +} + +pub trait ModuleLoader { + fn normalize_module_name(&self, source: &str, name: String) -> String; + fn load_module(&self, name: &String) -> Result; +} + +pub struct StandardModuleLoader { + base_path: PathBuf, +} + +impl StandardModuleLoader { + pub fn new(base_path: PathBuf) -> Self { + StandardModuleLoader { base_path } + } +} + +impl ModuleLoader for StandardModuleLoader { + fn normalize_module_name(&self, source: &str, name: String) -> String { + let p = self.base_path.join(source).join(name.clone()); + let result = match std::fs::canonicalize(&p) { + Ok(p) => match p.into_os_string().into_string() { + Ok(s) => s, + Err(_e) => name.clone(), + }, + Err(_e) => name.clone(), + }; + result + } + + fn load_module(&self, name: &String) -> Result { + match fs::read_to_string(name) { + Ok(c) => Ok(ModuleSource::SourceText(c)), + Err(e) => Err(ModuleLoadError::IO(name.clone(), e)), + } + } +} + +pub struct Module { + id: ModuleId, + semantics: Rc, +} + +impl Module { + pub fn id(&self) -> ModuleId { + self.id + } + + pub fn semantics(&self) -> Rc { + self.semantics.clone() + } +} + +struct PendingModule { + mid: ModuleId, + imports: Vec<(String, String)>, // (raw, normalized) + semantics: Rc, +} + +pub struct Program { + next_module_id: u64, + modules: HashMap>, + modules_by_id: HashMap>, + loader: Box, +} + +impl Program { + pub fn new(loader: Box) -> Self { + Program { + next_module_id: 0, + modules: HashMap::new(), + modules_by_id: HashMap::new(), + loader, + } + } + + pub fn modules(&self) -> impl Iterator> { + self.modules.values() + } + + pub fn get_module(&self, id: &ModuleId) -> Option<&Rc> { + self.modules_by_id.get(id) + } + + pub fn load_module( + &mut self, + name: &str, + ) -> Result<(Vec>, Rc), ModuleLoadError> { + let mut init_pending = HashMap::new(); + let mut names = Vec::new(); + let name = self.loader.normalize_module_name("", name.to_string()); + names.push(name.clone()); + + let mut id_assign = self.next_module_id; + + while let Some(name) = names.pop() { + if self.modules.contains_key(&name) || init_pending.contains_key(&name) { + // Either already loaded or pending load. + continue; + } + + // TODO: Errors here are bad! Remember, run everything! + match self.loader.load_module(&name)? { + ModuleSource::SourceText(source) => { + let mid = ModuleId::from(id_assign); + id_assign += 1; + + let source: Rc = source.into(); + let (tree, lines) = parse(&source); + let semantics = Rc::new(Semantics::new( + mid, + name.clone().into(), + source, + tree, + lines, + )); + + let mut imports = Vec::new(); + for import in semantics.imports() { + let normalized = self.loader.normalize_module_name(&name, import.clone()); + + names.push(normalized.clone()); + imports.push((import, normalized)); + } + + init_pending.insert( + name, + PendingModule { + semantics, + mid, + imports, + }, + ); + } + } + } + + for (_, pending) in init_pending.iter() { + let mut import_table = HashMap::new(); + for (import, normalized) in pending.imports.iter() { + // NOTE: We look up the load(ed|ing) module here by normalized name, because that's how + // we track it... + let target = if let Some(module) = self.modules.get(&*normalized) { + ImportRecord { + name: normalized.clone(), + module_id: module.id(), + semantics: Rc::downgrade(&module.semantics), + } + } else { + let other = init_pending.get(&*normalized).unwrap(); + ImportRecord { + name: normalized.clone(), + module_id: other.mid, + semantics: Rc::downgrade(&other.semantics), + } + }; + + // ...but we set it into the import table here with the name + // that the source code used, for more better binding. + import_table.insert(import.clone(), target); + } + + // Now tell the semantics object about its import table. + pending.semantics.set_imports(import_table); + } + + let mut errors = Vec::new(); + for (name, pending) in init_pending.into_iter() { + check(&pending.semantics); + errors.append(&mut pending.semantics.snapshot_errors()); + + let module = Rc::new(Module { + id: pending.mid, + semantics: pending.semantics, + }); + self.modules.insert(name, module.clone()); + self.modules_by_id.insert(pending.mid, module); + } + self.next_module_id = id_assign; + + let result = self.modules.get(&name).unwrap().clone(); + Ok((errors, result)) + } +} diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index 585ec172..976fd202 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -116,6 +116,12 @@ impl From for ModuleId { } } +impl fmt::Display for ModuleId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "#{}", self.0) + } +} + #[derive(Clone, Debug)] pub struct ImportRecord { pub name: String, @@ -762,6 +768,12 @@ impl Semantics { self.import_map.set(imports).expect("imports already set"); } + pub fn import_ids(&self) -> Vec { + // TODO: Pull from by_name when we go global + let import_map = self.import_map.get().unwrap(); + import_map.by_id.keys().map(|id| *id).collect() + } + pub fn import_by_id(&self, mid: ModuleId) -> Option> { // TODO: ACTUALLY THIS IS WRONG, WE NEED THE GLOBAL MAP HERE, NOT THE LOCAL ONE. let import_map = self.import_map.get()?; @@ -815,6 +827,10 @@ impl Semantics { } } + pub fn mid(&self) -> ModuleId { + self.mid + } + fn report_error_span(&self, start_pos: usize, end_pos: usize, error: T) -> Rc where T: ToString, diff --git a/fine/src/vm.rs b/fine/src/vm.rs index 25d761ea..d176cba1 100644 --- a/fine/src/vm.rs +++ b/fine/src/vm.rs @@ -1,11 +1,13 @@ +use core::fmt; use std::cell::{Cell, RefCell}; +use std::collections::HashMap; use std::rc::Rc; use crate::compiler::{ - Export, Function, Instruction, Module, EXTERN_BUILTIN_LIST_GET_ITERATOR, + CompiledModule, Export, Function, Instruction, EXTERN_BUILTIN_LIST_GET_ITERATOR, EXTERN_BUILTIN_LIST_ITERATOR_NEXT, EXTERN_BUILTIN_NOOP, }; -use crate::semantics::Type; +use crate::semantics::{ModuleId, Type}; use thiserror::Error; #[derive(Error, Debug)] @@ -49,6 +51,8 @@ pub enum VMErrorCode { ExportNotFound(String), #[error("the requested export is not a function: {0}")] ExportNotFunction(String), + #[error("a module with id {0} has not been loaded")] + ModuleNotFound(ModuleId), } #[derive(Debug)] @@ -85,6 +89,21 @@ pub struct ListIterator { index: Cell, } +#[derive(Clone)] +pub enum FuncValue { + Function(Rc, Rc), + ExternFunction(usize), +} + +impl fmt::Debug for FuncValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FuncValue::Function(m, func) => write!(f, "fuction #{:?}:{:?}", m, func), + FuncValue::ExternFunction(i) => write!(f, "external {i}"), + } + } +} + #[derive(Clone, Debug)] pub enum StackValue { Nothing, @@ -92,8 +111,7 @@ pub enum StackValue { Float(f64), Int(i64), String(Rc), - Function(Rc), - ExternFunction(usize), + Function(Box), Object(Rc), List(Rc>), ListIterator(Rc), @@ -136,13 +154,9 @@ impl StackValue { } } -enum FuncValue { - Function(Rc), - ExternFunction(usize), -} - #[derive(Debug)] pub struct Frame { + module: Rc, func: Rc, args: Vec, locals: Vec, @@ -151,11 +165,12 @@ pub struct Frame { } impl Frame { - fn from_function(func: Rc, args: Vec) -> Self { + fn from_function(module: Rc, func: Rc, args: Vec) -> Self { let mut locals = Vec::new(); locals.resize(func.locals(), StackValue::Nothing); Frame { + module, func, args, locals, @@ -164,8 +179,12 @@ impl Frame { } } - pub fn func(&self) -> Rc { - self.func.clone() + pub fn module(&self) -> &Rc { + &self.module + } + + pub fn func(&self) -> &Rc { + &self.func } pub fn args(&self) -> &[StackValue] { @@ -235,12 +254,12 @@ impl Frame { self.push_value(StackValue::String(v)) } - fn push_function(&mut self, v: Rc) { - self.push_value(StackValue::Function(v)); + fn push_function(&mut self, m: Rc, v: Rc) { + self.push_value(StackValue::Function(Box::new(FuncValue::Function(m, v)))); } fn push_extern_function(&mut self, v: usize) { - self.push_value(StackValue::ExternFunction(v)); + self.push_value(StackValue::Function(Box::new(FuncValue::ExternFunction(v)))); } fn push_object(&mut self, v: Rc) { @@ -297,55 +316,86 @@ impl Frame { fn pop_function(&mut self) -> Result { match self.pop_value()? { - StackValue::Function(f) => Ok(FuncValue::Function(f)), - StackValue::ExternFunction(i) => Ok(FuncValue::ExternFunction(i)), + StackValue::Function(f) => Ok(*f), v => Err(VMErrorCode::StackExpectedFunction(v)), } } } -pub struct Context { - module: Rc, - globals: Vec, +pub struct RuntimeModule { + code: Rc, + globals: RefCell>, } -impl Context { - pub fn new(module: Rc) -> Context { - let mut globals = Vec::new(); - globals.resize(module.globals, StackValue::Nothing); - Context { module, globals } +impl fmt::Debug for RuntimeModule { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "#{}", self.code.id) } +} - pub fn init(&mut self) -> std::result::Result<(), VMError> { - let init_index = self.module.init; - let init_fn = self.module.functions[init_index].clone(); - eval(self, init_fn, vec![])?; - Ok(()) +impl RuntimeModule { + fn new(code: Rc) -> RuntimeModule { + let mut globals = Vec::new(); + globals.resize(code.globals, StackValue::Nothing); + RuntimeModule { + code, + globals: RefCell::new(globals), + } } fn get_global(&self, i: usize) -> Result { self.globals + .borrow() .get(i) .map(|v| v.clone()) .ok_or_else(|| VMErrorCode::GlobalOutOfRange(i)) // TODO: Test } - fn set_global(&mut self, i: usize, v: StackValue) -> Result<()> { - if i >= self.globals.len() { + fn set_global(&self, i: usize, v: StackValue) -> Result<()> { + let mut globals = self.globals.borrow_mut(); + if i >= globals.len() { Err(VMErrorCode::GlobalOutOfRange(i)) // TODO: Test } else { - self.globals[i] = v; + globals[i] = v; Ok(()) } } fn get_function(&self, i: usize) -> Result> { - let functions = self.module.functions(); + let functions = self.code.functions(); functions .get(i) .map(|v| v.clone()) .ok_or_else(|| VMErrorCode::FunctionOutOfRange(i)) // TODO: Test } +} + +pub struct Context { + modules: HashMap>, + module_prefix: Option, +} + +impl Context { + pub fn new() -> Context { + Context { + modules: HashMap::new(), + module_prefix: None, + } + } + + pub fn loaded(&self, module: ModuleId) -> bool { + self.modules.contains_key(&module) + } + + pub fn get_module(&self, module: ModuleId) -> Option<&Rc> { + self.modules.get(&module).map(|rm| &rm.code) + } + + pub fn set_module(&mut self, module: Rc) { + let id = module.id; + let runtime_module = Rc::new(RuntimeModule::new(module)); + self.modules.insert(id, runtime_module.clone()); + } fn call_extern_function(&self, index: usize, args: &[StackValue]) -> Result { match index { @@ -398,6 +448,8 @@ fn eval_one( f: &mut Frame, stack: &mut Vec, ) -> Result { + let module_prefix = c.module_prefix.take(); + match instruction { Instruction::Panic(index) => { let v = f @@ -462,7 +514,15 @@ fn eval_one( f.push_value(v); } Instruction::LoadModule(i) => { - let v = c.get_global(i)?; + let module = module_prefix + .map(|id| { + c.modules + .get(&id) + .ok_or_else(|| VMErrorCode::ModuleNotFound(id)) + }) + .unwrap_or(Ok(&f.module))?; + + let v = module.get_global(i)?; f.push_value(v); } Instruction::PushFalse => { @@ -491,7 +551,15 @@ fn eval_one( } Instruction::StoreModule(i) => { let v = f.pop_value()?; - c.set_global(i, v)?; + let module = module_prefix + .map(|id| { + c.modules + .get(&id) + .ok_or_else(|| VMErrorCode::ModuleNotFound(id)) + }) + .unwrap_or(Ok(&f.module))?; + + module.set_global(i, v)?; } Instruction::StoreSlot(i) => { let o = f.pop_object()?; @@ -499,8 +567,16 @@ fn eval_one( o.values.borrow_mut()[i] = v; } Instruction::LoadFunction(i) => { - let v = c.get_function(i)?; - f.push_function(v); + let module = module_prefix + .map(|id| { + c.modules + .get(&id) + .ok_or_else(|| VMErrorCode::ModuleNotFound(id)) + }) + .unwrap_or(Ok(&f.module))?; + + let v = module.get_function(i)?; + f.push_function(module.clone(), v); } Instruction::LoadExternFunction(i) => { f.push_extern_function(i); @@ -512,8 +588,8 @@ fn eval_one( args.push(f.pop_value()?); } match function { - FuncValue::Function(func) => { - let mut frame = Frame::from_function(func, args); + FuncValue::Function(module, func) => { + let mut frame = Frame::from_function(module, func, args); std::mem::swap(&mut frame, f); frame.pc = *index; stack.push(frame); @@ -538,8 +614,8 @@ fn eval_one( let x = f.pop_string()?; let y = f.pop_string()?; - let mut new_string = x.to_string(); - new_string.push_str(&y); + let mut new_string = y.to_string(); + new_string.push_str(&x); f.push_string(new_string.into()); } @@ -629,18 +705,22 @@ fn eval_one( } f.push_list(Rc::new(v)); } + Instruction::ModulePrefix(mid) => { + c.module_prefix = Some(mid); + } } Ok(Flow::Continue) } -pub fn eval( +fn eval_core( c: &mut Context, + module: Rc, function: Rc, args: Vec, ) -> std::result::Result { let mut stack = Vec::new(); - let mut f = Frame::from_function(function, args); + let mut f = Frame::from_function(module, function, args); let mut index = 0; loop { @@ -685,12 +765,45 @@ pub fn eval( } } +pub fn eval( + c: &mut Context, + module: ModuleId, + function: usize, + args: &[StackValue], +) -> std::result::Result { + let Some(module) = c.modules.get(&module) else { + return Err(VMError { + code: VMErrorCode::ModuleNotFound(module), + stack: Box::new([]), + }); + }; + + let Some(function) = module.code.functions.get(function) else { + return Err(VMError { + code: VMErrorCode::FunctionOutOfRange(function), + stack: Box::new([]), + }); + }; + + let function = function.clone(); + let args = args.iter().map(|a| a.clone()).collect(); + eval_core(c, module.clone(), function, args) +} + pub fn eval_export_fn( c: &mut Context, + module: ModuleId, name: &str, args: &[StackValue], ) -> std::result::Result { - let export = match c.module.exports.get(name) { + let Some(module) = c.modules.get(&module) else { + return Err(VMError { + code: VMErrorCode::ModuleNotFound(module), + stack: Box::new([]), + }); + }; + + let export = match module.code.exports.get(name) { Some(Export::Function(id)) => id, Some(_) => { return Err(VMError { @@ -706,7 +819,7 @@ pub fn eval_export_fn( } }; - let function = c.module.functions[*export].clone(); + let function = module.code.functions[*export].clone(); let args = args.iter().map(|a| a.clone()).collect(); - eval(c, function, args) + eval_core(c, module.clone(), function, args) } diff --git a/fine/tests/example_tests.rs b/fine/tests/example_tests.rs index fd829613..eede64c1 100644 --- a/fine/tests/example_tests.rs +++ b/fine/tests/example_tests.rs @@ -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) -> Runtime { - Runtime::new(TestLoader::new(_source_path.into(), source)) +fn test_runtime(_source_path: &str, source: Rc) -> Program { + Program::new(TestLoader::new(_source_path.into(), source)) } -fn assert_type_at(module: Rc, pos: usize, expected: &str, _source_path: &str) { +fn assert_type_at(module: Rc, 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, pos: usize, expected: &str, _source_ } fn assert_type_error_at( - module: Rc, + module: Rc, errors: &[Rc], 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, expected: &str, source_path: &str) { +fn assert_compiles_to(module: Rc, 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, expected: &str, source_path: &st } } -fn assert_no_errors(module: Rc, errors: &[Rc]) { +fn assert_no_errors(module: Rc, errors: &[Rc]) { let semantics = module.semantics(); let expected_errors: &[Rc] = &[]; @@ -299,14 +302,39 @@ fn assert_no_errors(module: Rc, errors: &[Rc]) { ); } -fn assert_eval_ok(module: Rc, expected: &str) { +fn dump_runtime_error(module: &Rc, 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, 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, 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, errors: &[Rc], expected_errors: Vec<&str>) { +fn assert_errors(module: Rc, errors: &[Rc], expected_errors: Vec<&str>) { let semantics = module.semantics(); let errors: Vec = errors.iter().map(|e| format!("{}", e)).collect(); @@ -355,7 +363,7 @@ fn assert_errors(module: Rc, errors: &[Rc], expected_errors ); } -fn assert_check_error(module: Rc, errors: &[Rc], expected: &str) { +fn assert_check_error(module: Rc, errors: &[Rc], expected: &str) { let semantics = module.semantics(); semantic_assert!( diff --git a/fine/tests/modules/import.fine b/fine/tests/modules/import.fine index f583592c..9b9ec5c0 100644 --- a/fine/tests/modules/import.fine +++ b/fine/tests/modules/import.fine @@ -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 \ No newline at end of file +// @eval: String("hello world") \ No newline at end of file