diff --git a/fine/src/compiler.rs b/fine/src/compiler.rs index a98f411e..50173529 100644 --- a/fine/src/compiler.rs +++ b/fine/src/compiler.rs @@ -7,6 +7,12 @@ use crate::{ tokens::TokenKind, }; +pub const EXTERN_BUILTIN_NOOP: usize = 0; +pub const EXTERN_BUILTIN_LIST_GET_ITERATOR: usize = 1; +pub const EXTERN_BUILTIN_LIST_ITERATOR_NEXT: usize = 2; + +pub const EXTERN_USER_FIRST: usize = 100000; + // TODO: If I were cool this would by actual bytecode. // But I'm not cool. #[derive(Debug, Clone, Copy)] @@ -55,6 +61,7 @@ pub enum Instruction { StoreModule(usize), StoreSlot(usize), StringAdd, + NewList(usize), } pub enum Export { @@ -153,10 +160,6 @@ struct Compiler<'a> { } impl<'a> Compiler<'a> { - pub fn type_of(&self, t: TreeRef) -> Type { - self.semantics.type_of(t) - } - fn add_string(&mut self, result: String) -> usize { let index = self.function.strings.len(); self.function.strings.push(result.into()); @@ -307,6 +310,8 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) { TreeKind::GroupingExpression => compile_grouping(c, tree), TreeKind::Identifier => compile_identifier_expression(c, t, tree), TreeKind::IsExpression => compile_is_expression(c, tree), + TreeKind::ListConstructor => compile_list_constructor(c, tree), + TreeKind::ListConstructorElement => compile_list_constructor_element(c, tree), TreeKind::LiteralExpression => compile_literal(c, t, tree), TreeKind::MemberAccess => compile_member_access(c, t, tree), TreeKind::NewObjectExpression => compile_new_object_expression(c, t, tree), @@ -322,7 +327,7 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) { fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { let tok = tr.nth_token(0)?; - match c.type_of(t) { + match c.semantics.type_of(t) { Type::F64 => c.push(Instruction::PushFloat(tok.as_str().parse().unwrap())), Type::Bool => c.push(if tok.kind == TokenKind::True { Instruction::PushTrue @@ -969,12 +974,31 @@ fn compile_self_reference(c: &mut Compiler) -> CR { OK } +fn compile_list_constructor(c: &mut Compiler, tree: &Tree) -> CR { + let mut children: Vec<_> = tree + .children_of_kind(c.syntax, TreeKind::ListConstructorElement) + .collect(); + children.reverse(); + let count = children.len(); + for child in children { + compile_expression(c, child); + } + c.push(Instruction::NewList(count)); + OK +} + +fn compile_list_constructor_element(c: &mut Compiler, tree: &Tree) -> CR { + compile_expression(c, tree.nth_tree(0)?); + OK +} + fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) { let tree = &c.semantics.tree()[t]; let cr = match tree.kind { TreeKind::Block => compile_block_statement(c, t, 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, t, tree, gen_value), TreeKind::IfStatement => compile_if_statement(c, tree, gen_value), TreeKind::LetStatement => compile_let_statement(c, t, tree, gen_value), @@ -1180,3 +1204,86 @@ fn compile_return_statement(c: &mut Compiler, tree: &Tree) -> CR { c.push(Instruction::Return); OK } + +fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR { + // Figure out the variable. + let vt = tree.nth_tree(1)?; + let var = &c.syntax[vt]; + let id = var.nth_token(0)?; + + let body = tree.nth_tree(4)?; + let env = c.semantics.environment_of(body); + let Some(variable_decl) = env.bind(id) else { + ice!(c, body, "Unable to bind {id} in loop body"); + }; + let variable_slot = match variable_decl { + Declaration::Variable { + location, index, .. + } => { + compiler_assert_eq!( + c, + vt, + *location, + Location::Local, + "expected loop variable to be local" + ); + + *index + } + _ => ice!(c, vt, "loop variable was not a variable"), + }; + + // Figure out the generator. + let iterable = tree.nth_tree(3)?; + compile_expression(c, iterable); + + // call 'get_iterator' + let iterable_typ = c.semantics.type_of(iterable); + match iterable_typ { + Type::List(_) => { + c.push(Instruction::LoadExternFunction( + EXTERN_BUILTIN_LIST_GET_ITERATOR, + )); + c.push(Instruction::Call(1)); + } + _ => return None, // TODO: Bind and call get_iterator() on type of iterable + } + + // iterate + // (Stack is clear except for the iterator.) + + let loop_top = c.push(Instruction::Dup); // Save the iterator + match iterable_typ { + Type::List(_) => { + c.push(Instruction::LoadExternFunction( + EXTERN_BUILTIN_LIST_ITERATOR_NEXT, + )); + } + _ => return None, // TODO: Bind and call next() on type of iterator + } + c.push(Instruction::Call(1)); // Call 'next' + + c.push(Instruction::Dup); // Save the result + c.push(Instruction::IsNothing); // Check to see if iteration is done + let jump_to_end = c.push(Instruction::JumpTrue(0)); + c.push(Instruction::StoreLocal(variable_slot)); // Store the value + + // (Stack is clear except for the iterator.) + + compile_statement(c, body, false); // Run the body + + // (Stack is clear except for the iterator.) + + c.push(Instruction::Jump(loop_top)); + + // Clean up the loop. + c.patch(jump_to_end, |i| Instruction::JumpTrue(i)); + c.push(Instruction::Discard); // Drop the unused value + c.push(Instruction::Discard); // Drop the iterator + + if gen_value { + c.push(Instruction::PushNothing); + } + + OK +} diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index b51a56db..ea571030 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -318,6 +318,7 @@ impl ExternalFunctionId { } } +#[derive(Clone, Debug)] pub enum Declaration { Variable { declaration: TreeRef, diff --git a/fine/src/vm.rs b/fine/src/vm.rs index c9295e9b..91660a03 100644 --- a/fine/src/vm.rs +++ b/fine/src/vm.rs @@ -1,7 +1,10 @@ -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::rc::Rc; -use crate::compiler::{Export, Function, Instruction, Module}; +use crate::compiler::{ + Export, Function, Instruction, Module, EXTERN_BUILTIN_LIST_GET_ITERATOR, + EXTERN_BUILTIN_LIST_ITERATOR_NEXT, EXTERN_BUILTIN_NOOP, +}; use crate::semantics::Type; use thiserror::Error; @@ -33,8 +36,14 @@ pub enum VMErrorCode { StackExpectedFunction(StackValue), #[error("internal error: stack type mismatch ({0:?} is not object)")] StackExpectedObject(StackValue), + #[error("internal error: stack type mismatch ({0:?} is not list)")] + StackExpectedList(StackValue), + #[error("internal error: stack type mismatch ({0:?} is not list iterator)")] + StackExpectedListIterator(StackValue), #[error("internal error: slot {0} was out of range for object (type {1} with {2} slots)")] SlotOutOfRange(usize, Rc, usize), + #[error("internal error: the extern function with ID {0} was not registered")] + UnregisteredExternFunction(usize), } #[derive(Debug)] @@ -65,6 +74,12 @@ impl Object { } } +#[derive(Clone, Debug)] +pub struct ListIterator { + list: Rc>, + index: Cell, +} + #[derive(Clone, Debug)] pub enum StackValue { Nothing, @@ -75,6 +90,8 @@ pub enum StackValue { Function(Rc), ExternFunction(usize), Object(Rc), + List(Rc>), + ListIterator(Rc), } impl StackValue { @@ -229,6 +246,10 @@ impl Frame { self.push_value(StackValue::Int(v)); } + fn push_list(&mut self, value: Rc>) { + self.push_value(StackValue::List(value)); + } + fn get_argument(&self, i: usize) -> Result { self.args .get(i) @@ -320,6 +341,43 @@ impl Context { .map(|v| v.clone()) .ok_or_else(|| VMErrorCode::FunctionOutOfRange(i)) // TODO: Test } + + fn call_extern_function(&self, index: usize, args: &[StackValue]) -> Result { + match index { + EXTERN_BUILTIN_NOOP => Ok(StackValue::Nothing), + EXTERN_BUILTIN_LIST_GET_ITERATOR => { + let Some(list_value) = args.get(0) else { + return Err(VMErrorCode::ArgumentOutOfRange(0)); + }; + let StackValue::List(list) = list_value else { + return Err(VMErrorCode::StackExpectedList(list_value.clone())); + }; + + Ok(StackValue::ListIterator(Rc::new(ListIterator { + list: list.clone(), + index: Cell::new(0), + }))) + } + EXTERN_BUILTIN_LIST_ITERATOR_NEXT => { + let Some(iter_value) = args.get(0) else { + return Err(VMErrorCode::ArgumentOutOfRange(0)); + }; + let StackValue::ListIterator(li) = iter_value else { + return Err(VMErrorCode::StackExpectedListIterator(iter_value.clone())); + }; + + let index = li.index.get(); + if index >= li.list.len() { + Ok(StackValue::Nothing) + } else { + let result = li.list[index].clone(); + li.index.set(index + 1); + Ok(result) + } + } + _ => Err(VMErrorCode::UnregisteredExternFunction(index)), + } + } } enum Flow { @@ -451,7 +509,9 @@ fn eval_one( stack.push(frame); *index = 0; } - FuncValue::ExternFunction(_) => todo!(), + FuncValue::ExternFunction(i) => { + f.push_value(c.call_extern_function(i, &args)?); + } } } Instruction::Return => match stack.pop() { @@ -552,6 +612,13 @@ fn eval_one( let v = f.pop_value()?; f.push_bool(v.is_nothing()); } + Instruction::NewList(c) => { + let mut v = Vec::with_capacity(c); + for _ in 0..c { + v.push(f.pop_value()?); + } + f.push_list(Rc::new(v)); + } } Ok(Flow::Continue) @@ -571,14 +638,14 @@ pub fn eval( let instruction = instructions[index]; // { - // eprint!("{index}: {instruction:?} ["); - // for val in f.stack.iter().rev().take(3) { - // eprint!("{val:?} "); - // } + // eprint!("{index}: ["); // if f.stack.len() > 3 { // eprint!("..."); // } - // eprintln!("]"); + // for val in f.stack.iter().take(3) { + // eprint!("{val:?} "); + // } + // eprintln!("] => {instruction:?}"); // } index += 1; diff --git a/fine/tests/expression/lists.fine b/fine/tests/expression/lists.fine index 7e6159bc..34d4add1 100644 --- a/fine/tests/expression/lists.fine +++ b/fine/tests/expression/lists.fine @@ -11,6 +11,6 @@ fun test() -> f64 { sum(val) } -// @ignore WIP // @no-errors -// @type: 155 list \ No newline at end of file +// @type: 155 list +// @eval: Float(6.0) \ No newline at end of file