From 40d4f48878c625fbd5e956759851536d29a0b373 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Thu, 26 Dec 2024 18:41:54 -0500 Subject: [PATCH] notes and comments --- bytecode_thoughts.md | 8 ++++++++ src/compiler.rs | 21 +++++++++++++++++---- src/value.rs | 4 ++-- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/bytecode_thoughts.md b/bytecode_thoughts.md index 4a1d66b..fd19518 100644 --- a/bytecode_thoughts.md +++ b/bytecode_thoughts.md @@ -229,3 +229,11 @@ struct StackFrame<'a> { This gives us a way to access everything we need: where to return to, the root of the stack, the chunk (function->chunk), the closures (function->closures). +### 2024-12-26 +One particular concern here, which needs some work: recursion is challenging. + +In particular, the issue is that if, as I have been planning, a function closes over all its values at the moment it is compiled, the only value type that requires updating is a function. A function can be declared but not yet defined, and then when another function that uses that function is defined, the closed-over value will be to the declaration but not the definition. + +One way to handle this, I think is using `std::cell::OnceCell`. Rather than a `RefCell`, `OnceCell` has no runtime overhead. Instead, what happens is you effectively put a `None` in the cell. Then, once you have the value you want to put in there, you call `set` on the `OnceCell`, and it does what it needs to. + +This allows for the closures to be closed over right after compilation. diff --git a/src/compiler.rs b/src/compiler.rs index 1d817b9..56501f0 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -4,6 +4,7 @@ use crate::value::*; use chumsky::prelude::SimpleSpan; use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::FromPrimitive; +use std::cell::OnceCell; use std::rc::Rc; #[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)] @@ -434,23 +435,35 @@ impl Chunk { } } Fn(name, body, doc) => { + // first, declare the function + // TODO: or, check if the function has already been declared! + let init_val = Value::Fn(Rc::new(OnceCell::new())); + self.emit_constant(init_val); + self.bind(name); + + // compile the function let mut chunk = Chunk::new(body, self.name, self.src); chunk.compile(); if crate::DEBUG_COMPILE { println!("==function: {name}=="); chunk.disassemble(); } + let lfn = crate::value::LFn { name, doc: *doc, chunk, + closed: vec![], }; - let fn_val = Value::Fn(Rc::new(lfn)); - self.emit_constant(fn_val); - self.bind(name); + + // TODO: close over everything accessed in the function + + // TODO: pull the function off the stack, and set the OnceCell. } FnDeclaration(name) => { - todo!() + let lfn = Value::Fn(Rc::new(OnceCell::new())); + self.emit_constant(lfn); + self.bind(name); } FnBody(clauses) => { self.emit_op(Op::ResetMatch); diff --git a/src/value.rs b/src/value.rs index d5ddd38..fcf0ce9 100644 --- a/src/value.rs +++ b/src/value.rs @@ -2,7 +2,7 @@ use crate::compiler::Chunk; use crate::parser::Ast; use crate::spans::Spanned; use imbl::{HashMap, Vector}; -use std::cell::RefCell; +use std::cell::{OnceCell, RefCell}; use std::rc::Rc; #[derive(Clone, Debug, PartialEq)] @@ -37,7 +37,7 @@ pub enum Value { List(Box>), Dict(Box>), Box(Rc>), - Fn(Rc), + Fn(Rc>), } impl std::fmt::Display for Value {