[fine] Simplify function compilation
This is simpler because we don't "discover" functions to compile as we go, we just compile all the ones we can find, and functions have pre-defined exports. This is good and useful to us as we can now refer to functions in different modules by known indices, but it *does* make me wonder what we're going to do for compiling generic specializations. The previous technique was better for that sort of thing. This is all predicated on the idea that I want to have partially-compiled modules, which I can't really say why I want it. If I'm happy to just compile things cross module in the same kind of space then it's much easier to go back to the function key way of working.
This commit is contained in:
parent
4c061fbd28
commit
ab477cd783
14 changed files with 190 additions and 214 deletions
6
fine/TODO
Normal file
6
fine/TODO
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
- 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<T : ModuleLoader>` that does the boxing, and `new` should just make the standard one
|
||||||
|
|
@ -142,20 +142,11 @@ impl std::fmt::Debug for Function {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Hash, Clone)]
|
|
||||||
struct FunctionKey {
|
|
||||||
tree: TreeRef,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Compiler<'a> {
|
struct Compiler<'a> {
|
||||||
source: &'a str,
|
source: &'a str,
|
||||||
semantics: &'a Semantics,
|
semantics: &'a Semantics,
|
||||||
syntax: &'a SyntaxTree,
|
syntax: &'a SyntaxTree,
|
||||||
|
|
||||||
function_bindings: HashMap<FunctionKey, usize>,
|
|
||||||
pending_functions: Vec<(FunctionKey, usize, Function)>,
|
|
||||||
temp_functions: Vec<Option<Rc<Function>>>,
|
|
||||||
|
|
||||||
module: Module,
|
module: Module,
|
||||||
function: Function,
|
function: Function,
|
||||||
}
|
}
|
||||||
|
|
@ -249,16 +240,33 @@ macro_rules! ice {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// macro_rules! inst_panic {
|
type CR = Result<(), &'static str>;
|
||||||
// ($($t:tt)+) => {{
|
const OK: CR = CR::Ok(());
|
||||||
// // eprintln!($($t)*);
|
|
||||||
// Instruction::Panic
|
|
||||||
// }};
|
|
||||||
// }
|
|
||||||
|
|
||||||
// macro_rules! ice {
|
fn function_from_function_decl(
|
||||||
// ($compiler:expr, $tr:expr, $($t:tt)*) => {{}};
|
source: &str,
|
||||||
// }
|
syntax: &SyntaxTree,
|
||||||
|
tree: &Tree,
|
||||||
|
) -> Result<Function, &'static str> {
|
||||||
|
// TODO: If this is a method the name should be different.
|
||||||
|
let name = tree.nth_token(1).ok_or("no id")?.as_str(source);
|
||||||
|
|
||||||
|
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<Function, &'static str> {
|
||||||
|
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(semantics: &Semantics) -> Rc<Module> {
|
pub fn compile(semantics: &Semantics) -> Rc<Module> {
|
||||||
let source = semantics.source();
|
let source = semantics.source();
|
||||||
|
|
@ -269,32 +277,44 @@ pub fn compile(semantics: &Semantics) -> Rc<Module> {
|
||||||
semantics: &semantics,
|
semantics: &semantics,
|
||||||
syntax: &syntax_tree,
|
syntax: &syntax_tree,
|
||||||
|
|
||||||
function_bindings: HashMap::new(),
|
|
||||||
pending_functions: Vec::new(),
|
|
||||||
temp_functions: Vec::new(),
|
|
||||||
|
|
||||||
module: Module::new(),
|
module: Module::new(),
|
||||||
function: Function::new("<< module >>", 0),
|
function: Function::new("<< module >>", 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut functions = vec![None; semantics.function_count() + 1];
|
||||||
if let Some(t) = semantics.tree().root() {
|
if let Some(t) = semantics.tree().root() {
|
||||||
compiler.temp_functions.push(None);
|
|
||||||
file(&mut compiler, t);
|
file(&mut compiler, t);
|
||||||
compiler.temp_functions[0] = Some(Rc::new(compiler.function));
|
let index = functions.len() - 1;
|
||||||
compiler.module.init = 0;
|
functions[index] = Some(Rc::new(compiler.function));
|
||||||
|
compiler.module.init = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some((fk, idx, func)) = compiler.pending_functions.pop() {
|
for t in semantics.tree().trees() {
|
||||||
if idx >= compiler.temp_functions.len() {
|
if let Some(function_index) = semantics.get_function_index(t) {
|
||||||
compiler.temp_functions.resize(idx + 1, None);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
compiler.function = func;
|
|
||||||
let _ = compile_function(&mut compiler, fk.tree);
|
|
||||||
compiler.temp_functions[idx] = Some(Rc::new(compiler.function));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut module = compiler.module;
|
let mut module = compiler.module;
|
||||||
for f in compiler.temp_functions {
|
for f in functions {
|
||||||
module.functions.push(f.unwrap());
|
module.functions.push(f.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -317,9 +337,6 @@ fn file(c: &mut Compiler, t: TreeRef) {
|
||||||
c.push(Instruction::Return);
|
c.push(Instruction::Return);
|
||||||
}
|
}
|
||||||
|
|
||||||
type CR = Result<(), &'static str>;
|
|
||||||
const OK: CR = CR::Ok(());
|
|
||||||
|
|
||||||
fn compile_expression(c: &mut Compiler, t: TreeRef) {
|
fn compile_expression(c: &mut Compiler, t: TreeRef) {
|
||||||
let tree = &c.syntax[t];
|
let tree = &c.syntax[t];
|
||||||
let cr = match tree.kind {
|
let cr = match tree.kind {
|
||||||
|
|
@ -656,43 +673,15 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat
|
||||||
Instruction::LoadSlot(index)
|
Instruction::LoadSlot(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
Location::Function => {
|
Location::Function => Instruction::LoadFunction(index),
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
Location::ExternalFunction => Instruction::LoadExternFunction(index),
|
Location::ExternalFunction => Instruction::LoadExternFunction(index),
|
||||||
|
|
||||||
// Must be a static don't worry about it.
|
// Must be a static don't worry about it.
|
||||||
Location::Class => return OK,
|
Location::Class => return OK,
|
||||||
|
|
||||||
// fix later
|
// Imports are handled with an instruction prefix.
|
||||||
Location::Import => ice!(c, t, "import compile not supported"),
|
Location::Import => return OK,
|
||||||
};
|
};
|
||||||
|
|
||||||
c.push(instruction);
|
c.push(instruction);
|
||||||
|
|
@ -988,24 +977,7 @@ fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> C
|
||||||
let declaration = environment.bind(identifier).ok_or("cannot bind type")?;
|
let declaration = environment.bind(identifier).ok_or("cannot bind type")?;
|
||||||
match declaration.location {
|
match declaration.location {
|
||||||
Location::Class => {
|
Location::Class => {
|
||||||
let Origin::Source(classdecl) = declaration.origin else {
|
c.push(Instruction::LoadFunction(declaration.index));
|
||||||
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"),
|
_ => return Err("unsupported type for construction"),
|
||||||
}
|
}
|
||||||
|
|
@ -1039,20 +1011,7 @@ fn compile_member_access(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
|
||||||
let typ = c.semantics.type_of(lhs);
|
let typ = c.semantics.type_of(lhs);
|
||||||
let ident = tree.nth_token(2).ok_or("no ident")?.as_str(&c.source);
|
let ident = tree.nth_token(2).ok_or("no ident")?.as_str(&c.source);
|
||||||
|
|
||||||
let environment = match &typ {
|
let environment = c.semantics.member_environment(t, &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")?;
|
let declaration = environment.bind(ident).ok_or("cannot bind")?;
|
||||||
|
|
||||||
// NOTE: If this is a method call we still don't have to do anything
|
// NOTE: If this is a method call we still don't have to do anything
|
||||||
|
|
@ -1092,10 +1051,10 @@ fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) {
|
||||||
|
|
||||||
TreeKind::Import => compile_import_statement(c, gen_value),
|
TreeKind::Import => compile_import_statement(c, gen_value),
|
||||||
TreeKind::Block => compile_block_statement(c, t, gen_value),
|
TreeKind::Block => compile_block_statement(c, t, gen_value),
|
||||||
TreeKind::ClassDecl => compile_class_declaration(c, t, tree, gen_value),
|
TreeKind::ClassDecl => compile_class_declaration(c, gen_value),
|
||||||
TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value),
|
TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value),
|
||||||
TreeKind::ForStatement => compile_for_statement(c, tree, gen_value),
|
TreeKind::ForStatement => compile_for_statement(c, tree, gen_value),
|
||||||
TreeKind::FunctionDecl => compile_function_declaration(c, t, tree, gen_value),
|
TreeKind::FunctionDecl => compile_function_declaration(c, gen_value),
|
||||||
TreeKind::IfStatement => compile_if_statement(c, tree, gen_value),
|
TreeKind::IfStatement => compile_if_statement(c, tree, gen_value),
|
||||||
TreeKind::LetStatement => compile_let_statement(c, t, tree, gen_value),
|
TreeKind::LetStatement => compile_let_statement(c, t, tree, gen_value),
|
||||||
TreeKind::ReturnStatement => compile_return_statement(c, tree),
|
TreeKind::ReturnStatement => compile_return_statement(c, tree),
|
||||||
|
|
@ -1176,32 +1135,7 @@ fn compile_let_statement(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: b
|
||||||
OK
|
OK
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR {
|
fn compile_function_declaration(c: &mut Compiler, 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 {
|
if gen_value {
|
||||||
c.push(Instruction::PushNothing);
|
c.push(Instruction::PushNothing);
|
||||||
}
|
}
|
||||||
|
|
@ -1209,26 +1143,7 @@ fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_v
|
||||||
OK
|
OK
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_class_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR {
|
fn compile_class_declaration(c: &mut Compiler, 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 {
|
if gen_value {
|
||||||
c.push(Instruction::PushNothing);
|
c.push(Instruction::PushNothing);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ pub fn process_file(file: &str) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// OK now there might be errors.
|
// OK now there might be semantic errors or whatnot.
|
||||||
if errors.len() > 0 {
|
if errors.len() > 0 {
|
||||||
for e in errors {
|
for e in errors {
|
||||||
eprintln!("{file}: {}:{}: {}", e.start.0, e.start.1, e.message);
|
eprintln!("{file}: {}:{}: {}", e.start.0, e.start.1, e.message);
|
||||||
|
|
|
||||||
|
|
@ -344,9 +344,9 @@ pub enum Location {
|
||||||
Local, // A local in an frame
|
Local, // A local in an frame
|
||||||
Slot, // A slot in an object
|
Slot, // A slot in an object
|
||||||
Module, // A global in a module
|
Module, // A global in a module
|
||||||
Function, // A function in a module (index unrelated)
|
Function, // A function in a module
|
||||||
ExternalFunction, // An external function (module unrelated)
|
ExternalFunction, // An external function (module unrelated)
|
||||||
Class, // A class in a module (index unrelated)
|
Class, // A class in a module
|
||||||
Import, // An import in a module (index unrelated)
|
Import, // An import in a module (index unrelated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -683,6 +683,9 @@ pub struct Semantics {
|
||||||
// This is what is used for binding.
|
// This is what is used for binding.
|
||||||
logical_parents: Vec<Option<TreeRef>>,
|
logical_parents: Vec<Option<TreeRef>>,
|
||||||
|
|
||||||
|
function_count: usize,
|
||||||
|
function_indices: Vec<Option<usize>>,
|
||||||
|
|
||||||
// TODO: State should be externalized instead of this refcell nonsense.
|
// TODO: State should be externalized instead of this refcell nonsense.
|
||||||
errors: RefCell<Vec<Rc<Error>>>,
|
errors: RefCell<Vec<Rc<Error>>>,
|
||||||
types: RefCell<Vec<Incremental<Type>>>,
|
types: RefCell<Vec<Incremental<Type>>>,
|
||||||
|
|
@ -706,6 +709,19 @@ impl Semantics {
|
||||||
|
|
||||||
let root_environment = Environment::new(mid, None, Location::Module);
|
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 {
|
let mut semantics = Semantics {
|
||||||
mid,
|
mid,
|
||||||
file,
|
file,
|
||||||
|
|
@ -714,6 +730,8 @@ impl Semantics {
|
||||||
lines,
|
lines,
|
||||||
import_map: OnceCell::new(),
|
import_map: OnceCell::new(),
|
||||||
logical_parents,
|
logical_parents,
|
||||||
|
function_count,
|
||||||
|
function_indices,
|
||||||
errors: RefCell::new(vec![]),
|
errors: RefCell::new(vec![]),
|
||||||
types: RefCell::new(vec![Incremental::None; tree.len()]),
|
types: RefCell::new(vec![Incremental::None; tree.len()]),
|
||||||
environments: RefCell::new(vec![Incremental::None; tree.len()]),
|
environments: RefCell::new(vec![Incremental::None; tree.len()]),
|
||||||
|
|
@ -892,6 +910,27 @@ impl Semantics {
|
||||||
Environment::error(error)
|
Environment::error(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn function_count(&self) -> usize {
|
||||||
|
self.function_count
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_function_index(&self, t: TreeRef) -> Option<usize> {
|
||||||
|
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 {
|
pub fn environment_of(&self, t: TreeRef) -> EnvironmentRef {
|
||||||
{
|
{
|
||||||
// I want to make sure that this borrow is dropped after this block.
|
// I want to make sure that this borrow is dropped after this block.
|
||||||
|
|
@ -952,7 +991,7 @@ impl Semantics {
|
||||||
name.as_str(&self.source).into(),
|
name.as_str(&self.source).into(),
|
||||||
Declaration {
|
Declaration {
|
||||||
location: Location::Function,
|
location: Location::Function,
|
||||||
index: 0,
|
index: self.function_index_of(*t),
|
||||||
module: self.mid,
|
module: self.mid,
|
||||||
origin: Origin::Source(*t),
|
origin: Origin::Source(*t),
|
||||||
exported: false,
|
exported: false,
|
||||||
|
|
@ -1007,7 +1046,7 @@ impl Semantics {
|
||||||
|
|
||||||
let declaration = Declaration {
|
let declaration = Declaration {
|
||||||
location: Location::Function,
|
location: Location::Function,
|
||||||
index: 0,
|
index: self.function_index_of(t),
|
||||||
module: self.mid,
|
module: self.mid,
|
||||||
origin: Origin::Source(t),
|
origin: Origin::Source(t),
|
||||||
exported,
|
exported,
|
||||||
|
|
@ -1024,7 +1063,7 @@ impl Semantics {
|
||||||
|
|
||||||
let declaration = Declaration {
|
let declaration = Declaration {
|
||||||
location: Location::Class,
|
location: Location::Class,
|
||||||
index: 0,
|
index: self.function_index_of(t),
|
||||||
module: self.mid,
|
module: self.mid,
|
||||||
origin: Origin::Source(t),
|
origin: Origin::Source(t),
|
||||||
exported,
|
exported,
|
||||||
|
|
@ -1393,7 +1432,7 @@ impl Semantics {
|
||||||
(&*method.name).into(),
|
(&*method.name).into(),
|
||||||
Declaration {
|
Declaration {
|
||||||
location: Location::Function,
|
location: Location::Function,
|
||||||
index: 0,
|
index: self.function_index_of(method.declaration),
|
||||||
module: self.mid,
|
module: self.mid,
|
||||||
origin: Origin::Source(method.declaration),
|
origin: Origin::Source(method.declaration),
|
||||||
exported: false,
|
exported: false,
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,11 @@ pub enum VMErrorCode {
|
||||||
SlotOutOfRange(usize, Rc<str>, usize),
|
SlotOutOfRange(usize, Rc<str>, usize),
|
||||||
#[error("internal error: the extern function with ID {0} was not registered")]
|
#[error("internal error: the extern function with ID {0} was not registered")]
|
||||||
UnregisteredExternFunction(usize),
|
UnregisteredExternFunction(usize),
|
||||||
|
|
||||||
|
#[error("the requested export was not found: {0}")]
|
||||||
|
ExportNotFound(String),
|
||||||
|
#[error("the requested export is not a function: {0}")]
|
||||||
|
ExportNotFunction(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -687,8 +692,18 @@ pub fn eval_export_fn(
|
||||||
) -> std::result::Result<StackValue, VMError> {
|
) -> std::result::Result<StackValue, VMError> {
|
||||||
let export = match c.module.exports.get(name) {
|
let export = match c.module.exports.get(name) {
|
||||||
Some(Export::Function(id)) => id,
|
Some(Export::Function(id)) => id,
|
||||||
Some(_) => todo!(),
|
Some(_) => {
|
||||||
None => todo!(),
|
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([]),
|
||||||
|
})
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let function = c.module.functions[*export].clone();
|
let function = c.module.functions[*export].clone();
|
||||||
|
|
|
||||||
|
|
@ -64,11 +64,6 @@ fun test() -> f64 {
|
||||||
// | RightBrace:'"}"'
|
// | RightBrace:'"}"'
|
||||||
// |
|
// |
|
||||||
// @compiles-to:
|
// @compiles-to:
|
||||||
// | function << module >> (0 args, 0 locals):
|
|
||||||
// | strings (0):
|
|
||||||
// | code (2):
|
|
||||||
// | 0: PushNothing
|
|
||||||
// | 1: Return
|
|
||||||
// | function foo (1 args, 0 locals):
|
// | function foo (1 args, 0 locals):
|
||||||
// | strings (0):
|
// | strings (0):
|
||||||
// | code (4):
|
// | code (4):
|
||||||
|
|
@ -80,7 +75,12 @@ fun test() -> f64 {
|
||||||
// | strings (0):
|
// | strings (0):
|
||||||
// | code (4):
|
// | code (4):
|
||||||
// | 0: PushFloat(1.0)
|
// | 0: PushFloat(1.0)
|
||||||
// | 1: LoadFunction(1)
|
// | 1: LoadFunction(0)
|
||||||
// | 2: Call(1)
|
// | 2: Call(1)
|
||||||
// | 3: Return
|
// | 3: Return
|
||||||
|
// | function << module >> (0 args, 0 locals):
|
||||||
|
// | strings (0):
|
||||||
|
// | code (2):
|
||||||
|
// | 0: PushNothing
|
||||||
|
// | 1: Return
|
||||||
// |
|
// |
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,6 @@ fun test() -> f64 {
|
||||||
// | RightBrace:'"}"'
|
// | RightBrace:'"}"'
|
||||||
// |
|
// |
|
||||||
// @compiles-to:
|
// @compiles-to:
|
||||||
// | function << module >> (0 args, 0 locals):
|
|
||||||
// | strings (0):
|
|
||||||
// | code (2):
|
|
||||||
// | 0: PushNothing
|
|
||||||
// | 1: Return
|
|
||||||
// | function test (0 args, 0 locals):
|
// | function test (0 args, 0 locals):
|
||||||
// | strings (0):
|
// | strings (0):
|
||||||
// | code (10):
|
// | code (10):
|
||||||
|
|
@ -58,4 +53,9 @@ fun test() -> f64 {
|
||||||
// | 7: FloatMultiply
|
// | 7: FloatMultiply
|
||||||
// | 8: FloatAdd
|
// | 8: FloatAdd
|
||||||
// | 9: Return
|
// | 9: Return
|
||||||
|
// | function << module >> (0 args, 0 locals):
|
||||||
|
// | strings (0):
|
||||||
|
// | code (2):
|
||||||
|
// | 0: PushNothing
|
||||||
|
// | 1: Return
|
||||||
// |
|
// |
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,6 @@ fun test() -> f64 {
|
||||||
|
|
||||||
// @no-errors
|
// @no-errors
|
||||||
// @compiles-to:
|
// @compiles-to:
|
||||||
// | function << module >> (0 args, 0 locals):
|
|
||||||
// | strings (0):
|
|
||||||
// | code (2):
|
|
||||||
// | 0: PushNothing
|
|
||||||
// | 1: Return
|
|
||||||
// | function test (0 args, 3 locals):
|
// | function test (0 args, 3 locals):
|
||||||
// | strings (0):
|
// | strings (0):
|
||||||
// | code (14):
|
// | code (14):
|
||||||
|
|
@ -30,5 +25,10 @@ fun test() -> f64 {
|
||||||
// | 11: Discard
|
// | 11: Discard
|
||||||
// | 12: LoadLocal(0)
|
// | 12: LoadLocal(0)
|
||||||
// | 13: Return
|
// | 13: Return
|
||||||
|
// | function << module >> (0 args, 0 locals):
|
||||||
|
// | strings (0):
|
||||||
|
// | code (2):
|
||||||
|
// | 0: PushNothing
|
||||||
|
// | 1: Return
|
||||||
// |
|
// |
|
||||||
// @eval: Float(2.0)
|
// @eval: Float(2.0)
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ fun test() {
|
||||||
|
|
||||||
// @no-errors
|
// @no-errors
|
||||||
// @compiles-to:
|
// @compiles-to:
|
||||||
// | function << module >> (0 args, 0 locals):
|
// | function test (0 args, 0 locals):
|
||||||
// | strings (0):
|
// | strings (0):
|
||||||
// | code (2):
|
// | code (2):
|
||||||
// | 0: PushNothing
|
// | 0: PushNothing
|
||||||
// | 1: Return
|
// | 1: Return
|
||||||
// | function test (0 args, 0 locals):
|
// | function << module >> (0 args, 0 locals):
|
||||||
// | strings (0):
|
// | strings (0):
|
||||||
// | code (2):
|
// | code (2):
|
||||||
// | 0: PushNothing
|
// | 0: PushNothing
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,6 @@ fun test() -> bool {
|
||||||
|
|
||||||
// @no-errors
|
// @no-errors
|
||||||
// @compiles-to:
|
// @compiles-to:
|
||||||
// | function << module >> (0 args, 0 locals):
|
|
||||||
// | strings (0):
|
|
||||||
// | code (2):
|
|
||||||
// | 0: PushNothing
|
|
||||||
// | 1: Return
|
|
||||||
// | function test (0 args, 0 locals):
|
// | function test (0 args, 0 locals):
|
||||||
// | strings (0):
|
// | strings (0):
|
||||||
// | code (15):
|
// | code (15):
|
||||||
|
|
@ -27,6 +22,11 @@ fun test() -> bool {
|
||||||
// | 12: PushTrue
|
// | 12: PushTrue
|
||||||
// | 13: BoolNot
|
// | 13: BoolNot
|
||||||
// | 14: Return
|
// | 14: Return
|
||||||
|
// | function << module >> (0 args, 0 locals):
|
||||||
|
// | strings (0):
|
||||||
|
// | code (2):
|
||||||
|
// | 0: PushNothing
|
||||||
|
// | 1: Return
|
||||||
// |
|
// |
|
||||||
// @eval: Bool(false)
|
// @eval: Bool(false)
|
||||||
// @type: 15 bool
|
// @type: 15 bool
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,26 @@ fun test() -> f64 {
|
||||||
// @no-errors
|
// @no-errors
|
||||||
// @eval: Float(597.0)
|
// @eval: Float(597.0)
|
||||||
// @compiles-to:
|
// @compiles-to:
|
||||||
// | function << module >> (0 args, 0 locals):
|
// | function something_static (0 args, 0 locals):
|
||||||
// | strings (0):
|
// | strings (0):
|
||||||
// | code (2):
|
// | code (2):
|
||||||
// | 0: PushNothing
|
// | 0: PushFloat(12.0)
|
||||||
// | 1: Return
|
// | 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):
|
// | function Point (6 args, 0 locals):
|
||||||
// | strings (1):
|
// | strings (1):
|
||||||
// | 0: "Point"
|
// | 0: "Point"
|
||||||
|
|
@ -60,13 +75,13 @@ fun test() -> f64 {
|
||||||
// | code (27):
|
// | code (27):
|
||||||
// | 0: PushFloat(99.0)
|
// | 0: PushFloat(99.0)
|
||||||
// | 1: PushFloat(999.0)
|
// | 1: PushFloat(999.0)
|
||||||
// | 2: LoadFunction(1)
|
// | 2: LoadFunction(2)
|
||||||
// | 3: Call(2)
|
// | 3: Call(2)
|
||||||
// | 4: PushFloat(23.0)
|
// | 4: PushFloat(23.0)
|
||||||
// | 5: PushFloat(7.0)
|
// | 5: PushFloat(7.0)
|
||||||
// | 6: LoadFunction(1)
|
// | 6: LoadFunction(2)
|
||||||
// | 7: Call(2)
|
// | 7: Call(2)
|
||||||
// | 8: LoadFunction(2)
|
// | 8: LoadFunction(3)
|
||||||
// | 9: Call(2)
|
// | 9: Call(2)
|
||||||
// | 10: StoreLocal(0)
|
// | 10: StoreLocal(0)
|
||||||
// | 11: LoadLocal(0)
|
// | 11: LoadLocal(0)
|
||||||
|
|
@ -76,33 +91,18 @@ fun test() -> f64 {
|
||||||
// | 15: LoadSlot(0)
|
// | 15: LoadSlot(0)
|
||||||
// | 16: LoadSlot(0)
|
// | 16: LoadSlot(0)
|
||||||
// | 17: LoadLocal(1)
|
// | 17: LoadLocal(1)
|
||||||
// | 18: LoadFunction(4)
|
// | 18: LoadFunction(1)
|
||||||
// | 19: Call(1)
|
// | 19: Call(1)
|
||||||
// | 20: FloatAdd
|
// | 20: FloatAdd
|
||||||
// | 21: LoadFunction(5)
|
// | 21: LoadFunction(0)
|
||||||
// | 22: Call(0)
|
// | 22: Call(0)
|
||||||
// | 23: FloatAdd
|
// | 23: FloatAdd
|
||||||
// | 24: StoreLocal(2)
|
// | 24: StoreLocal(2)
|
||||||
// | 25: LoadLocal(2)
|
// | 25: LoadLocal(2)
|
||||||
// | 26: Return
|
// | 26: Return
|
||||||
// | function square_length (1 args, 0 locals):
|
// | function << module >> (0 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):
|
// | strings (0):
|
||||||
// | code (2):
|
// | code (2):
|
||||||
// | 0: PushFloat(12.0)
|
// | 0: PushNothing
|
||||||
// | 1: Return
|
// | 1: Return
|
||||||
// |
|
// |
|
||||||
|
|
|
||||||
|
|
@ -58,11 +58,6 @@ fun test() -> f64 {
|
||||||
// | RightBrace:'"}"'
|
// | RightBrace:'"}"'
|
||||||
//
|
//
|
||||||
// @compiles-to:
|
// @compiles-to:
|
||||||
// | function << module >> (0 args, 0 locals):
|
|
||||||
// | strings (0):
|
|
||||||
// | code (2):
|
|
||||||
// | 0: PushNothing
|
|
||||||
// | 1: Return
|
|
||||||
// | function test (0 args, 0 locals):
|
// | function test (0 args, 0 locals):
|
||||||
// | strings (1):
|
// | strings (1):
|
||||||
// | 0: "discarded"
|
// | 0: "discarded"
|
||||||
|
|
@ -75,5 +70,10 @@ fun test() -> f64 {
|
||||||
// | 5: Jump(7)
|
// | 5: Jump(7)
|
||||||
// | 6: PushFloat(45.0)
|
// | 6: PushFloat(45.0)
|
||||||
// | 7: Return
|
// | 7: Return
|
||||||
|
// | function << module >> (0 args, 0 locals):
|
||||||
|
// | strings (0):
|
||||||
|
// | code (2):
|
||||||
|
// | 0: PushNothing
|
||||||
|
// | 1: Return
|
||||||
// |
|
// |
|
||||||
// @eval: Float(23.0)
|
// @eval: Float(23.0)
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,13 @@ fun test() -> f64 {
|
||||||
// | RightBrace:'"}"'
|
// | RightBrace:'"}"'
|
||||||
// |
|
// |
|
||||||
// @compiles-to:
|
// @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):
|
// | function << module >> (0 args, 0 locals):
|
||||||
// | strings (0):
|
// | strings (0):
|
||||||
// | code (12):
|
// | code (12):
|
||||||
|
|
@ -79,11 +86,4 @@ fun test() -> f64 {
|
||||||
// | 9: Discard
|
// | 9: Discard
|
||||||
// | 10: PushNothing
|
// | 10: PushNothing
|
||||||
// | 11: Return
|
// | 11: Return
|
||||||
// | function test (0 args, 0 locals):
|
|
||||||
// | strings (0):
|
|
||||||
// | code (4):
|
|
||||||
// | 0: LoadModule(0)
|
|
||||||
// | 1: LoadModule(1)
|
|
||||||
// | 2: FloatAdd
|
|
||||||
// | 3: Return
|
|
||||||
// |
|
// |
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,4 @@ fun test() -> string {
|
||||||
|
|
||||||
// TODO: Obviously run the code duh
|
// TODO: Obviously run the code duh
|
||||||
// @no-errors
|
// @no-errors
|
||||||
|
/// @eval: asdf
|
||||||
Loading…
Add table
Add a link
Reference in a new issue