diff --git a/fine/TODO b/fine/TODO deleted file mode 100644 index 33d3eccf..00000000 --- a/fine/TODO +++ /dev/null @@ -1,6 +0,0 @@ -- The Export enum is stupid I think, for runtime modules. Why do we even have them? We should just put all the names in, like Declaration {} but for runtime. - -- Module IDs must be globally resolvable from within a given semantics object -- When adding PANIC instructions, push a diagnostic that I find if I can find one instead of a hard-coded string. - -- runtime should have `new` with 0 args and `with_loader` that does the boxing, and `new` should just make the standard one \ No newline at end of file diff --git a/fine/build.rs b/fine/build.rs index 9938438a..1b3c7b1e 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(&program, _module.clone(), #expected); + crate::assert_eval_ok(_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 program = crate::test_runtime(#display_path, source.clone()); - let (_errors, _module) = program.load_module("__test__").unwrap(); + let mut runtime = crate::test_runtime(#display_path, source.clone()); + let (_errors, _module) = runtime.load_module("__test__").unwrap(); #(#assertions)* } diff --git a/fine/src/compiler.rs b/fine/src/compiler.rs index 09a095f8..0086b35c 100644 --- a/fine/src/compiler.rs +++ b/fine/src/compiler.rs @@ -3,9 +3,7 @@ use std::rc::Rc; use crate::{ parser::{Child, SyntaxTree, Tree, TreeKind, TreeRef}, - semantics::{ - string_constant_to_string, Declaration, Location, ModuleId, Origin, Semantics, Type, - }, + semantics::{string_constant_to_string, Declaration, Location, Origin, Semantics, Type}, tokens::TokenKind, }; @@ -64,8 +62,6 @@ pub enum Instruction { StoreSlot(usize), StringAdd, NewList(usize), - - ModulePrefix(ModuleId), } pub enum Export { @@ -73,31 +69,23 @@ pub enum Export { Global(usize), } -pub struct CompiledModule { - pub id: ModuleId, +pub struct Module { 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 CompiledModule { - pub fn new(id: ModuleId) -> Self { - CompiledModule { - id, +impl Module { + pub fn new() -> Self { + Module { 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 } @@ -154,12 +142,21 @@ impl std::fmt::Debug for Function { } } +#[derive(Eq, PartialEq, Hash, Clone)] +struct FunctionKey { + tree: TreeRef, +} + struct Compiler<'a> { source: &'a str, semantics: &'a Semantics, syntax: &'a SyntaxTree, - module: CompiledModule, + function_bindings: HashMap, + pending_functions: Vec<(FunctionKey, usize, Function)>, + temp_functions: Vec>>, + + module: Module, function: Function, } @@ -252,35 +249,18 @@ macro_rules! ice { }} } -type CR = Result<(), &'static str>; -const OK: CR = CR::Ok(()); +// macro_rules! inst_panic { +// ($($t:tt)+) => {{ +// // eprintln!($($t)*); +// Instruction::Panic +// }}; +// } -fn function_from_function_decl( - source: &str, - syntax: &SyntaxTree, - tree: &Tree, -) -> Result { - // TODO: If this is a method the name should be different. - let name = tree.nth_token(1).ok_or("no id")?.as_str(source); +// macro_rules! ice { +// ($compiler:expr, $tr:expr, $($t:tt)*) => {{}}; +// } - let param_list = tree - .child_tree_of_kind(syntax, TreeKind::ParamList) - .ok_or("no paramlist")?; - let param_count = param_list.children.len() - 2; - - Ok(Function::new(name, param_count)) -} - -fn function_from_class_decl(source: &str, tree: &Tree) -> Result { - let name = tree.nth_token(1).ok_or("no name")?.as_str(source); - - // TODO: I think this is incorrect! - let field_count = tree.children.len() - 2; - - Ok(Function::new(name, field_count)) -} - -pub fn compile_module(semantics: &Semantics) -> Rc { +pub fn compile(semantics: &Semantics) -> Rc { let source = semantics.source(); let syntax_tree = semantics.tree(); @@ -289,48 +269,35 @@ pub fn compile_module(semantics: &Semantics) -> Rc { semantics: &semantics, syntax: &syntax_tree, - module: CompiledModule::new(semantics.mid()), + function_bindings: HashMap::new(), + pending_functions: Vec::new(), + temp_functions: Vec::new(), + + module: Module::new(), function: Function::new("<< module >>", 0), }; - let mut functions = vec![None; semantics.function_count() + 1]; if let Some(t) = semantics.tree().root() { + compiler.temp_functions.push(None); file(&mut compiler, t); - let index = functions.len() - 1; - functions[index] = Some(Rc::new(compiler.function)); - compiler.module.init = index; + compiler.temp_functions[0] = Some(Rc::new(compiler.function)); + compiler.module.init = 0; } - for t in semantics.tree().trees() { - if let Some(function_index) = semantics.get_function_index(t) { - let tree = &semantics.tree()[t]; - let function = match tree.kind { - TreeKind::FunctionDecl => function_from_function_decl(&source, &syntax_tree, tree), - TreeKind::ClassDecl => function_from_class_decl(&source, tree), - _ => Err("don't know how to make a function of this"), - }; - - if let Ok(function) = function { - compiler.function = function; - - let _ = compile_function(&mut compiler, t); - - let function = Rc::new(compiler.function); - compiler - .module - .exports - .insert(function.name.clone(), Export::Function(function_index)); - functions[function_index] = Some(function); - } + while let Some((fk, idx, func)) = compiler.pending_functions.pop() { + if idx >= compiler.temp_functions.len() { + compiler.temp_functions.resize(idx + 1, None); } + compiler.function = func; + let _ = compile_function(&mut compiler, fk.tree); + compiler.temp_functions[idx] = Some(Rc::new(compiler.function)); } let mut module = compiler.module; - for f in functions { + for f in compiler.temp_functions { module.functions.push(f.unwrap()); } - module.deps.append(&mut semantics.import_ids()); Rc::new(module) } @@ -350,6 +317,9 @@ fn file(c: &mut Compiler, t: TreeRef) { c.push(Instruction::Return); } +type CR = Result<(), &'static str>; +const OK: CR = CR::Ok(()); + fn compile_expression(c: &mut Compiler, t: TreeRef) { let tree = &c.syntax[t]; let cr = match tree.kind { @@ -663,6 +633,8 @@ 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 => { @@ -676,12 +648,7 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat Instruction::LoadArgument(index) } Location::Module => { - 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); - } + compiler_assert!(c, t, index < c.module.globals); Instruction::LoadModule(index) } Location::Slot => { @@ -690,9 +657,32 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat } Location::Function => { - if declaration.module != c.semantics.mid() { - c.push(Instruction::ModulePrefix(declaration.module)); - } + // TODO: Assert declaration is local + let Origin::Source(ft) = declaration.origin else { + ice!(c, t, "Function location but external origin?"); + }; + let key = FunctionKey { tree: ft }; + let index = match c.function_bindings.get(&key) { + Some(index) => *index, + None => { + let tree = &c.syntax[ft]; + compiler_assert_eq!(c, t, tree.kind, TreeKind::FunctionDecl); + + compile_function_declaration(c, ft, tree, false)?; + + match c.function_bindings.get(&key) { + Some(index) => *index, + None => { + ice!( + c, + t, + "did not compile the function with key {:?}!", + declaration + ) + } + } + } + }; Instruction::LoadFunction(index) } @@ -701,8 +691,8 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat // Must be a static don't worry about it. Location::Class => return OK, - // Imports are handled with an instruction prefix. - Location::Import => return OK, + // fix later + Location::Import => ice!(c, t, "import compile not supported"), }; c.push(instruction); @@ -998,7 +988,24 @@ fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> C let declaration = environment.bind(identifier).ok_or("cannot bind type")?; match declaration.location { Location::Class => { - c.push(Instruction::LoadFunction(declaration.index)); + let Origin::Source(classdecl) = declaration.origin else { + ice!(c, t, "this class declaration has no source?"); + }; + let key = FunctionKey { tree: classdecl }; + let index = match c.function_bindings.get(&key) { + Some(index) => *index, + None => { + let tree = &c.syntax[classdecl]; + compiler_assert_eq!(c, t, tree.kind, TreeKind::ClassDecl); + + compile_class_declaration(c, t, tree, false)?; + + *c.function_bindings + .get(&key) + .expect("did not compile the class constructor!") + } + }; + c.push(Instruction::LoadFunction(index)); } _ => return Err("unsupported type for construction"), } @@ -1032,7 +1039,20 @@ fn compile_member_access(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR { let typ = c.semantics.type_of(lhs); let ident = tree.nth_token(2).ok_or("no ident")?.as_str(&c.source); - let environment = c.semantics.member_environment(t, &typ); + let environment = match &typ { + Type::Object(mid, ct, _) => { + let class = c.semantics.class_of(*mid, *ct); + class.env.clone() + } + Type::Class(mid, ct, _) => { + let class = c.semantics.class_of(*mid, *ct); + class.static_env.clone() + } + _ => { + c.push_panic("cannot get environment of {typ}"); + return Err("cannot get environment"); + } + }; let declaration = environment.bind(ident).ok_or("cannot bind")?; // NOTE: If this is a method call we still don't have to do anything @@ -1072,16 +1092,14 @@ fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) { TreeKind::Import => compile_import_statement(c, gen_value), TreeKind::Block => compile_block_statement(c, t, gen_value), - TreeKind::ClassDecl => compile_class_declaration(c, gen_value), + TreeKind::ClassDecl => compile_class_declaration(c, t, tree, gen_value), TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value), TreeKind::ForStatement => compile_for_statement(c, tree, gen_value), - TreeKind::FunctionDecl => compile_function_declaration(c, gen_value), + TreeKind::FunctionDecl => compile_function_declaration(c, t, tree, gen_value), TreeKind::IfStatement => compile_if_statement(c, tree, gen_value), 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), }; @@ -1094,11 +1112,6 @@ 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 { @@ -1148,9 +1161,7 @@ fn compile_let_statement(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: b Instruction::StoreLocal(index) } Location::Module => { - if declaration.module != c.semantics.mid() { - c.push(Instruction::ModulePrefix(declaration.module)); - } else if index >= c.module.globals { + if index >= c.module.globals { c.module.globals = index + 1; } Instruction::StoreModule(index) @@ -1165,7 +1176,32 @@ fn compile_let_statement(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: b OK } -fn compile_function_declaration(c: &mut Compiler, gen_value: bool) -> CR { +fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR { + // Only compile a given function once. + // + // TODO: When it's time for generics, this should only actually compile + // if we have no unbound type variables. + let fk = FunctionKey { tree: t }; + if !c.function_bindings.contains_key(&fk) { + // TODO: If this is a method the name should be different. + let name = tree.nth_token(1).ok_or("no id")?.as_str(&c.source); + + let param_list = tree + .child_tree_of_kind(&c.syntax, TreeKind::ParamList) + .ok_or("no paramlist")?; + let param_count = param_list.children.len() - 2; + + let function_index = c.temp_functions.len(); + c.temp_functions.push(None); + + c.pending_functions + .push((fk.clone(), function_index, Function::new(name, param_count))); + c.function_bindings.insert(fk, function_index); + c.module + .exports + .insert(name.to_string(), Export::Function(function_index)); + } + if gen_value { c.push(Instruction::PushNothing); } @@ -1173,7 +1209,26 @@ fn compile_function_declaration(c: &mut Compiler, gen_value: bool) -> CR { OK } -fn compile_class_declaration(c: &mut Compiler, gen_value: bool) -> CR { +fn compile_class_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR { + // Only compile a given function once. + // Classes get compiled as constructor functions which get called. + let fk = FunctionKey { tree: t }; + if !c.function_bindings.contains_key(&fk) { + let name = tree.nth_token(1).ok_or("no name")?.as_str(&c.source); + + let field_count = tree.children.len() - 2; + + let function_index = c.temp_functions.len(); + c.temp_functions.push(None); + + c.pending_functions + .push((fk.clone(), function_index, Function::new(name, field_count))); + c.function_bindings.insert(fk, function_index); + c.module + .exports + .insert(name.to_string(), Export::Function(function_index)); + } + if gen_value { c.push(Instruction::PushNothing); } diff --git a/fine/src/lib.rs b/fine/src/lib.rs index b914ca6e..2a4fc39d 100644 --- a/fine/src/lib.rs +++ b/fine/src/lib.rs @@ -1,51 +1,181 @@ -use std::{path::PathBuf, rc::Rc}; +use std::{collections::HashMap, fs, path::PathBuf, rc::Rc}; -use compiler::compile_module; -use program::{Module, Program, StandardModuleLoader}; +use compiler::compile; +use parser::parse; +use semantics::{check, Error, ImportRecord, ModuleId, Semantics}; use vm::{eval, Context}; pub mod compiler; pub mod parser; -pub mod program; pub mod semantics; pub mod tokens; pub mod vm; -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()); +pub enum ModuleSource { + SourceText(String), +} - for dep in module.deps.iter() { - if let Some(dep_mod) = program.get_module(dep) { - load_module(program, dep_mod, context)?; +#[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)); + } } } - eval(context, module.id, module.init, &[])?; + 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)) } - - 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 program = Program::new(Box::new(StandardModuleLoader::new(PathBuf::from(".")))); + let mut runtime = Runtime::new(Box::new(StandardModuleLoader::new(PathBuf::from(".")))); - // Load the program text - let (errors, _) = match program.load_module(file) { + let (errors, module) = match runtime.load_module(file) { Ok(r) => r, Err(_) => { eprintln!("Error loading module"); @@ -53,7 +183,7 @@ pub fn process_file(file: &str) { } }; - // OK now there might be semantic errors or whatnot. + // OK now there might be errors. if errors.len() > 0 { for e in errors { eprintln!("{file}: {}:{}: {}", e.start.0, e.start.1, e.message); @@ -61,10 +191,16 @@ pub fn process_file(file: &str) { return; } - // 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(_) => {} + // 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); + } Err(e) => { eprintln!("{:?}", e); } diff --git a/fine/src/program.rs b/fine/src/program.rs deleted file mode 100644 index 48d0a913..00000000 --- a/fine/src/program.rs +++ /dev/null @@ -1,196 +0,0 @@ -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 976fd202..d65b85c6 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -116,12 +116,6 @@ 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, @@ -350,9 +344,9 @@ pub enum Location { Local, // A local in an frame Slot, // A slot in an object Module, // A global in a module - Function, // A function in a module + Function, // A function in a module (index unrelated) ExternalFunction, // An external function (module unrelated) - Class, // A class in a module + Class, // A class in a module (index unrelated) Import, // An import in a module (index unrelated) } @@ -689,9 +683,6 @@ pub struct Semantics { // This is what is used for binding. logical_parents: Vec>, - function_count: usize, - function_indices: Vec>, - // TODO: State should be externalized instead of this refcell nonsense. errors: RefCell>>, types: RefCell>>, @@ -715,19 +706,6 @@ impl Semantics { let root_environment = Environment::new(mid, None, Location::Module); - let mut function_count = 0; - let mut function_indices = vec![None; tree.len()]; - for t in tree.trees() { - let tree = &tree[t]; - match tree.kind { - TreeKind::FunctionDecl | TreeKind::ClassDecl => { - function_indices[t.index()] = Some(function_count); - function_count += 1; - } - _ => {} - } - } - let mut semantics = Semantics { mid, file, @@ -736,8 +714,6 @@ impl Semantics { lines, import_map: OnceCell::new(), logical_parents, - function_count, - function_indices, errors: RefCell::new(vec![]), types: RefCell::new(vec![Incremental::None; tree.len()]), environments: RefCell::new(vec![Incremental::None; tree.len()]), @@ -768,12 +744,6 @@ 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()?; @@ -827,10 +797,6 @@ 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, @@ -926,27 +892,6 @@ impl Semantics { Environment::error(error) } - pub fn function_count(&self) -> usize { - self.function_count - } - - pub fn get_function_index(&self, t: TreeRef) -> Option { - let index = t.index(); - if index >= self.function_indices.len() { - None - } else { - self.function_indices[t.index()] - } - } - - pub fn function_index_of(&self, t: TreeRef) -> usize { - let Some(index) = self.function_indices[t.index()] else { - self.internal_compiler_error(Some(t), "Why didn't I get a function index for this?"); - }; - - index - } - pub fn environment_of(&self, t: TreeRef) -> EnvironmentRef { { // I want to make sure that this borrow is dropped after this block. @@ -1007,7 +952,7 @@ impl Semantics { name.as_str(&self.source).into(), Declaration { location: Location::Function, - index: self.function_index_of(*t), + index: 0, module: self.mid, origin: Origin::Source(*t), exported: false, @@ -1062,7 +1007,7 @@ impl Semantics { let declaration = Declaration { location: Location::Function, - index: self.function_index_of(t), + index: 0, module: self.mid, origin: Origin::Source(t), exported, @@ -1079,7 +1024,7 @@ impl Semantics { let declaration = Declaration { location: Location::Class, - index: self.function_index_of(t), + index: 0, module: self.mid, origin: Origin::Source(t), exported, @@ -1448,7 +1393,7 @@ impl Semantics { (&*method.name).into(), Declaration { location: Location::Function, - index: self.function_index_of(method.declaration), + index: 0, module: self.mid, origin: Origin::Source(method.declaration), exported: false, diff --git a/fine/src/vm.rs b/fine/src/vm.rs index d176cba1..032a8c7e 100644 --- a/fine/src/vm.rs +++ b/fine/src/vm.rs @@ -1,13 +1,11 @@ -use core::fmt; use std::cell::{Cell, RefCell}; -use std::collections::HashMap; use std::rc::Rc; use crate::compiler::{ - CompiledModule, Export, Function, Instruction, EXTERN_BUILTIN_LIST_GET_ITERATOR, + Export, Function, Instruction, Module, EXTERN_BUILTIN_LIST_GET_ITERATOR, EXTERN_BUILTIN_LIST_ITERATOR_NEXT, EXTERN_BUILTIN_NOOP, }; -use crate::semantics::{ModuleId, Type}; +use crate::semantics::Type; use thiserror::Error; #[derive(Error, Debug)] @@ -46,13 +44,6 @@ pub enum VMErrorCode { SlotOutOfRange(usize, Rc, usize), #[error("internal error: the extern function with ID {0} was not registered")] UnregisteredExternFunction(usize), - - #[error("the requested export was not found: {0}")] - 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)] @@ -89,21 +80,6 @@ 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, @@ -111,7 +87,8 @@ pub enum StackValue { Float(f64), Int(i64), String(Rc), - Function(Box), + Function(Rc), + ExternFunction(usize), Object(Rc), List(Rc>), ListIterator(Rc), @@ -154,9 +131,13 @@ impl StackValue { } } +enum FuncValue { + Function(Rc), + ExternFunction(usize), +} + #[derive(Debug)] pub struct Frame { - module: Rc, func: Rc, args: Vec, locals: Vec, @@ -165,12 +146,11 @@ pub struct Frame { } impl Frame { - fn from_function(module: Rc, func: Rc, args: Vec) -> Self { + fn from_function(func: Rc, args: Vec) -> Self { let mut locals = Vec::new(); locals.resize(func.locals(), StackValue::Nothing); Frame { - module, func, args, locals, @@ -179,12 +159,8 @@ impl Frame { } } - pub fn module(&self) -> &Rc { - &self.module - } - - pub fn func(&self) -> &Rc { - &self.func + pub fn func(&self) -> Rc { + self.func.clone() } pub fn args(&self) -> &[StackValue] { @@ -254,12 +230,12 @@ impl Frame { self.push_value(StackValue::String(v)) } - fn push_function(&mut self, m: Rc, v: Rc) { - self.push_value(StackValue::Function(Box::new(FuncValue::Function(m, v)))); + fn push_function(&mut self, v: Rc) { + self.push_value(StackValue::Function(v)); } fn push_extern_function(&mut self, v: usize) { - self.push_value(StackValue::Function(Box::new(FuncValue::ExternFunction(v)))); + self.push_value(StackValue::ExternFunction(v)); } fn push_object(&mut self, v: Rc) { @@ -316,86 +292,55 @@ impl Frame { fn pop_function(&mut self) -> Result { match self.pop_value()? { - StackValue::Function(f) => Ok(*f), + StackValue::Function(f) => Ok(FuncValue::Function(f)), + StackValue::ExternFunction(i) => Ok(FuncValue::ExternFunction(i)), v => Err(VMErrorCode::StackExpectedFunction(v)), } } } -pub struct RuntimeModule { - code: Rc, - globals: RefCell>, +pub struct Context { + module: Rc, + globals: Vec, } -impl fmt::Debug for RuntimeModule { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "#{}", self.code.id) - } -} - -impl RuntimeModule { - fn new(code: Rc) -> RuntimeModule { +impl Context { + pub fn new(module: Rc) -> Context { let mut globals = Vec::new(); - globals.resize(code.globals, StackValue::Nothing); - RuntimeModule { - code, - globals: RefCell::new(globals), - } + globals.resize(module.globals, StackValue::Nothing); + Context { module, globals } + } + + 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(()) } 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(&self, i: usize, v: StackValue) -> Result<()> { - let mut globals = self.globals.borrow_mut(); - if i >= globals.len() { + fn set_global(&mut self, i: usize, v: StackValue) -> Result<()> { + if i >= self.globals.len() { Err(VMErrorCode::GlobalOutOfRange(i)) // TODO: Test } else { - globals[i] = v; + self.globals[i] = v; Ok(()) } } fn get_function(&self, i: usize) -> Result> { - let functions = self.code.functions(); + let functions = self.module.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 { @@ -448,8 +393,6 @@ 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 @@ -514,15 +457,7 @@ fn eval_one( f.push_value(v); } Instruction::LoadModule(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)?; + let v = c.get_global(i)?; f.push_value(v); } Instruction::PushFalse => { @@ -551,15 +486,7 @@ fn eval_one( } Instruction::StoreModule(i) => { let v = f.pop_value()?; - 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)?; + c.set_global(i, v)?; } Instruction::StoreSlot(i) => { let o = f.pop_object()?; @@ -567,16 +494,8 @@ fn eval_one( o.values.borrow_mut()[i] = v; } Instruction::LoadFunction(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_function(i)?; - f.push_function(module.clone(), v); + let v = c.get_function(i)?; + f.push_function(v); } Instruction::LoadExternFunction(i) => { f.push_extern_function(i); @@ -588,8 +507,8 @@ fn eval_one( args.push(f.pop_value()?); } match function { - FuncValue::Function(module, func) => { - let mut frame = Frame::from_function(module, func, args); + FuncValue::Function(func) => { + let mut frame = Frame::from_function(func, args); std::mem::swap(&mut frame, f); frame.pc = *index; stack.push(frame); @@ -614,8 +533,8 @@ fn eval_one( let x = f.pop_string()?; let y = f.pop_string()?; - let mut new_string = y.to_string(); - new_string.push_str(&x); + let mut new_string = x.to_string(); + new_string.push_str(&y); f.push_string(new_string.into()); } @@ -705,22 +624,18 @@ fn eval_one( } f.push_list(Rc::new(v)); } - Instruction::ModulePrefix(mid) => { - c.module_prefix = Some(mid); - } } Ok(Flow::Continue) } -fn eval_core( +pub fn eval( c: &mut Context, - module: Rc, function: Rc, args: Vec, ) -> std::result::Result { let mut stack = Vec::new(); - let mut f = Frame::from_function(module, function, args); + let mut f = Frame::from_function(function, args); let mut index = 0; loop { @@ -765,61 +680,18 @@ fn eval_core( } } -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 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) { + let export = match c.module.exports.get(name) { Some(Export::Function(id)) => id, - Some(_) => { - return Err(VMError { - code: VMErrorCode::ExportNotFunction(name.to_string()), - stack: Box::new([]), - }) - } - None => { - return Err(VMError { - code: VMErrorCode::ExportNotFound(name.to_string()), - stack: Box::new([]), - }) - } + Some(_) => todo!(), + None => todo!(), }; - let function = module.code.functions[*export].clone(); + let function = c.module.functions[*export].clone(); let args = args.iter().map(|a| a.clone()).collect(); - eval_core(c, module.clone(), function, args) + eval(c, function, args) } diff --git a/fine/tests/example_tests.rs b/fine/tests/example_tests.rs index eede64c1..fd829613 100644 --- a/fine/tests/example_tests.rs +++ b/fine/tests/example_tests.rs @@ -1,10 +1,7 @@ -use fine::compile_program; -use fine::compiler::{compile_module, CompiledModule, Function}; -use fine::program::{ - Module, ModuleLoadError, ModuleLoader, ModuleSource, Program, StandardModuleLoader, -}; +use fine::compiler::{compile, Function, Module}; use fine::semantics::{Error, Type}; -use fine::vm::{eval_export_fn, Context, VMError}; +use fine::vm::{eval_export_fn, Context}; +use fine::{ModuleLoadError, ModuleLoader, ModuleSource, Runtime, StandardModuleLoader}; use pretty_assertions::assert_eq; use std::fmt::Write as _; @@ -171,11 +168,11 @@ impl ModuleLoader for TestLoader { } } -fn test_runtime(_source_path: &str, source: Rc) -> Program { - Program::new(TestLoader::new(_source_path.into(), source)) +fn test_runtime(_source_path: &str, source: Rc) -> Runtime { + Runtime::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(); @@ -200,7 +197,7 @@ fn assert_type_at(module: Rc, pos: usize, expected: &str, _source_path: } fn assert_type_error_at( - module: Rc, + module: Rc, errors: &[Rc], pos: usize, expected: &str, @@ -259,7 +256,7 @@ fn dump_function(out: &mut String, function: &Function) -> std::fmt::Result { Ok(()) } -fn dump_module(out: &mut String, module: &CompiledModule) -> std::fmt::Result { +fn dump_module(out: &mut String, module: &Module) -> std::fmt::Result { for function in module.functions() { dump_function(out, function)?; } @@ -267,9 +264,9 @@ fn dump_module(out: &mut String, module: &CompiledModule) -> 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_module(&semantics); + let module = compile(&semantics); let mut actual = String::new(); dump_module(&mut actual, &module).expect("no dumping?"); @@ -289,7 +286,7 @@ fn assert_compiles_to(module: Rc, expected: &str, source_path: &str) { } } -fn assert_no_errors(module: Rc, errors: &[Rc]) { +fn assert_no_errors(module: Rc, errors: &[Rc]) { let semantics = module.semantics(); let expected_errors: &[Rc] = &[]; @@ -302,39 +299,14 @@ fn assert_no_errors(module: Rc, errors: &[Rc]) { ); } -fn dump_runtime_error(module: &Rc, context: &Context, e: VMError) -> ! { +fn assert_eval_ok(module: Rc, expected: &str) { let semantics = module.semantics(); - semantics.dump_compiler_state(None); + let module = compile(&semantics); - if let Some(module) = context.get_module(module.id()) { - let mut actual = String::new(); - let _ = dump_module(&mut actual, &module); + let mut context = Context::new(module.clone()); + context.init().expect("Unable to initialize 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); -} - -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", &[]) { + match eval_export_fn(&mut context, "test", &[]) { Ok(v) => { let actual = format!("{:?}", v); semantic_assert_eq!( @@ -345,11 +317,31 @@ fn assert_eval_ok(program: &Program, module: Rc, expected: &str) { "wrong return from test function" ); } - Err(e) => dump_runtime_error(&module, &context, e), + 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); + } } } -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(); @@ -363,7 +355,7 @@ fn assert_errors(module: Rc, errors: &[Rc], expected_errors: Vec< ); } -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/expression/argument.fine b/fine/tests/expression/argument.fine index 9b7453fe..a432d24e 100644 --- a/fine/tests/expression/argument.fine +++ b/fine/tests/expression/argument.fine @@ -64,6 +64,11 @@ fun test() -> f64 { // | RightBrace:'"}"' // | // @compiles-to: +// | function << module >> (0 args, 0 locals): +// | strings (0): +// | code (2): +// | 0: PushNothing +// | 1: Return // | function foo (1 args, 0 locals): // | strings (0): // | code (4): @@ -75,12 +80,7 @@ fun test() -> f64 { // | strings (0): // | code (4): // | 0: PushFloat(1.0) -// | 1: LoadFunction(0) +// | 1: LoadFunction(1) // | 2: Call(1) // | 3: Return -// | function << module >> (0 args, 0 locals): -// | strings (0): -// | code (2): -// | 0: PushNothing -// | 1: Return // | diff --git a/fine/tests/expression/arithmetic.fine b/fine/tests/expression/arithmetic.fine index 0a57ae32..03639511 100644 --- a/fine/tests/expression/arithmetic.fine +++ b/fine/tests/expression/arithmetic.fine @@ -40,6 +40,11 @@ fun test() -> f64 { // | RightBrace:'"}"' // | // @compiles-to: +// | function << module >> (0 args, 0 locals): +// | strings (0): +// | code (2): +// | 0: PushNothing +// | 1: Return // | function test (0 args, 0 locals): // | strings (0): // | code (10): @@ -53,9 +58,4 @@ fun test() -> f64 { // | 7: FloatMultiply // | 8: FloatAdd // | 9: Return -// | function << module >> (0 args, 0 locals): -// | strings (0): -// | code (2): -// | 0: PushNothing -// | 1: Return // | diff --git a/fine/tests/expression/assignment.fine b/fine/tests/expression/assignment.fine index 4f822871..56497b75 100644 --- a/fine/tests/expression/assignment.fine +++ b/fine/tests/expression/assignment.fine @@ -8,6 +8,11 @@ fun test() -> f64 { // @no-errors // @compiles-to: +// | function << module >> (0 args, 0 locals): +// | strings (0): +// | code (2): +// | 0: PushNothing +// | 1: Return // | function test (0 args, 3 locals): // | strings (0): // | code (14): @@ -25,10 +30,5 @@ fun test() -> f64 { // | 11: Discard // | 12: LoadLocal(0) // | 13: Return -// | function << module >> (0 args, 0 locals): -// | strings (0): -// | code (2): -// | 0: PushNothing -// | 1: Return // | // @eval: Float(2.0) diff --git a/fine/tests/expression/block.fine b/fine/tests/expression/block.fine index 867b2275..c6785fa2 100644 --- a/fine/tests/expression/block.fine +++ b/fine/tests/expression/block.fine @@ -4,12 +4,12 @@ fun test() { // @no-errors // @compiles-to: -// | function test (0 args, 0 locals): +// | function << module >> (0 args, 0 locals): // | strings (0): // | code (2): // | 0: PushNothing // | 1: Return -// | function << module >> (0 args, 0 locals): +// | function test (0 args, 0 locals): // | strings (0): // | code (2): // | 0: PushNothing diff --git a/fine/tests/expression/boolean.fine b/fine/tests/expression/boolean.fine index 01615b02..5ae7741b 100644 --- a/fine/tests/expression/boolean.fine +++ b/fine/tests/expression/boolean.fine @@ -4,6 +4,11 @@ fun test() -> bool { // @no-errors // @compiles-to: +// | function << module >> (0 args, 0 locals): +// | strings (0): +// | code (2): +// | 0: PushNothing +// | 1: Return // | function test (0 args, 0 locals): // | strings (0): // | code (15): @@ -22,11 +27,6 @@ fun test() -> bool { // | 12: PushTrue // | 13: BoolNot // | 14: Return -// | function << module >> (0 args, 0 locals): -// | strings (0): -// | code (2): -// | 0: PushNothing -// | 1: Return // | // @eval: Bool(false) // @type: 15 bool diff --git a/fine/tests/expression/class.fine b/fine/tests/expression/class.fine index 3bf8e056..0b04c979 100644 --- a/fine/tests/expression/class.fine +++ b/fine/tests/expression/class.fine @@ -30,26 +30,11 @@ fun test() -> f64 { // @no-errors // @eval: Float(597.0) // @compiles-to: -// | function something_static (0 args, 0 locals): +// | function << module >> (0 args, 0 locals): // | strings (0): // | code (2): -// | 0: PushFloat(12.0) +// | 0: PushNothing // | 1: Return -// | function square_length (1 args, 0 locals): -// | strings (0): -// | code (12): -// | 0: LoadArgument(0) -// | 1: LoadSlot(0) -// | 2: LoadArgument(0) -// | 3: LoadSlot(0) -// | 4: FloatMultiply -// | 5: LoadArgument(0) -// | 6: LoadSlot(1) -// | 7: LoadArgument(0) -// | 8: LoadSlot(1) -// | 9: FloatMultiply -// | 10: FloatAdd -// | 11: Return // | function Point (6 args, 0 locals): // | strings (1): // | 0: "Point" @@ -75,13 +60,13 @@ fun test() -> f64 { // | code (27): // | 0: PushFloat(99.0) // | 1: PushFloat(999.0) -// | 2: LoadFunction(2) +// | 2: LoadFunction(1) // | 3: Call(2) // | 4: PushFloat(23.0) // | 5: PushFloat(7.0) -// | 6: LoadFunction(2) +// | 6: LoadFunction(1) // | 7: Call(2) -// | 8: LoadFunction(3) +// | 8: LoadFunction(2) // | 9: Call(2) // | 10: StoreLocal(0) // | 11: LoadLocal(0) @@ -91,18 +76,33 @@ fun test() -> f64 { // | 15: LoadSlot(0) // | 16: LoadSlot(0) // | 17: LoadLocal(1) -// | 18: LoadFunction(1) +// | 18: LoadFunction(4) // | 19: Call(1) // | 20: FloatAdd -// | 21: LoadFunction(0) +// | 21: LoadFunction(5) // | 22: Call(0) // | 23: FloatAdd // | 24: StoreLocal(2) // | 25: LoadLocal(2) // | 26: Return -// | function << module >> (0 args, 0 locals): +// | function square_length (1 args, 0 locals): +// | strings (0): +// | code (12): +// | 0: LoadArgument(0) +// | 1: LoadSlot(0) +// | 2: LoadArgument(0) +// | 3: LoadSlot(0) +// | 4: FloatMultiply +// | 5: LoadArgument(0) +// | 6: LoadSlot(1) +// | 7: LoadArgument(0) +// | 8: LoadSlot(1) +// | 9: FloatMultiply +// | 10: FloatAdd +// | 11: Return +// | function something_static (0 args, 0 locals): // | strings (0): // | code (2): -// | 0: PushNothing +// | 0: PushFloat(12.0) // | 1: Return // | diff --git a/fine/tests/expression/conditional.fine b/fine/tests/expression/conditional.fine index 24c0d09c..cad9fa10 100644 --- a/fine/tests/expression/conditional.fine +++ b/fine/tests/expression/conditional.fine @@ -58,6 +58,11 @@ fun test() -> f64 { // | RightBrace:'"}"' // // @compiles-to: +// | function << module >> (0 args, 0 locals): +// | strings (0): +// | code (2): +// | 0: PushNothing +// | 1: Return // | function test (0 args, 0 locals): // | strings (1): // | 0: "discarded" @@ -70,10 +75,5 @@ fun test() -> f64 { // | 5: Jump(7) // | 6: PushFloat(45.0) // | 7: Return -// | function << module >> (0 args, 0 locals): -// | strings (0): -// | code (2): -// | 0: PushNothing -// | 1: Return // | // @eval: Float(23.0) diff --git a/fine/tests/expression/variable.fine b/fine/tests/expression/variable.fine index 34e39dc6..e7335ac8 100644 --- a/fine/tests/expression/variable.fine +++ b/fine/tests/expression/variable.fine @@ -64,13 +64,6 @@ fun test() -> f64 { // | RightBrace:'"}"' // | // @compiles-to: -// | function test (0 args, 0 locals): -// | strings (0): -// | code (4): -// | 0: LoadModule(0) -// | 1: LoadModule(1) -// | 2: FloatAdd -// | 3: Return // | function << module >> (0 args, 0 locals): // | strings (0): // | code (12): @@ -86,4 +79,11 @@ fun test() -> f64 { // | 9: Discard // | 10: PushNothing // | 11: Return +// | function test (0 args, 0 locals): +// | strings (0): +// | code (4): +// | 0: LoadModule(0) +// | 1: LoadModule(1) +// | 2: FloatAdd +// | 3: Return // | diff --git a/fine/tests/modules/import.fine b/fine/tests/modules/import.fine index 9b9ec5c0..da98ad17 100644 --- a/fine/tests/modules/import.fine +++ b/fine/tests/modules/import.fine @@ -1,14 +1,8 @@ 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: String("hello world") \ No newline at end of file