1
0

Fix argument retrieval in native functions

This commit is contained in:
Jeff 2024-11-07 03:05:32 -05:00
parent 04a1c81a2a
commit bb345a7938
6 changed files with 121 additions and 49 deletions

View File

@ -95,21 +95,21 @@ impl<'src> Compiler<'src> {
} }
fn next_register(&mut self) -> u8 { fn next_register(&mut self) -> u8 {
let current = self self.chunk
.chunk
.instructions() .instructions()
.iter() .iter()
.rev() .rev()
.find_map(|(instruction, _)| { .find_map(|(instruction, _)| {
if instruction.yields_value() { if instruction.yields_value() {
Some(instruction.a()) let previous = instruction.a();
let next = previous.overflowing_add(1).0;
Some(next)
} else { } else {
None None
} }
}) })
.unwrap_or(self.minimum_register); .unwrap_or(self.minimum_register)
current.overflowing_add(1).0
} }
fn advance(&mut self) -> Result<(), CompileError> { fn advance(&mut self) -> Result<(), CompileError> {

View File

@ -245,7 +245,7 @@ impl NativeFunction {
message.push(' '); message.push(' ');
} }
let argument = vm.get_register(argument_index, position)?; let argument = vm.open_register(argument_index, position)?;
message.push_str(&argument.to_string()); message.push_str(&argument.to_string());
} }
@ -264,7 +264,7 @@ impl NativeFunction {
let mut string = String::new(); let mut string = String::new();
for argument_index in 0..argument_count { for argument_index in 0..argument_count {
let argument = vm.get_register(argument_index, position)?; let argument = vm.open_register(argument_index, position)?;
string.push_str(&argument.to_string()); string.push_str(&argument.to_string());
} }
@ -305,7 +305,7 @@ impl NativeFunction {
stdout.write(b" ").map_err(map_err)?; stdout.write(b" ").map_err(map_err)?;
} }
let argument_string = vm.get_register(argument_index, position)?.to_string(); let argument_string = vm.open_register(argument_index, position)?.to_string();
stdout stdout
.write_all(argument_string.as_bytes()) .write_all(argument_string.as_bytes())
@ -323,19 +323,23 @@ impl NativeFunction {
}) })
}; };
let first_argument = to_register.saturating_sub(argument_count); let first_index = to_register.saturating_sub(argument_count);
let last_argument = to_register.saturating_sub(1); let arguments = vm.open_nonempty_registers(first_index..to_register, position)?;
for argument_index in first_argument..=last_argument { for (index, argument) in arguments.into_iter().enumerate() {
if argument_index != 0 { if index != 0 {
stdout.write(b" ").map_err(map_err)?; stdout.write(b" ").map_err(map_err)?;
} }
let argument_string = vm.get_register(argument_index, position)?.to_string(); if let Value::Concrete(ConcreteValue::String(string)) = argument {
let bytes = string.as_bytes();
stdout stdout.write_all(bytes).map_err(map_err)?;
.write_all(argument_string.as_bytes()) } else {
.map_err(map_err)?; let bytes = argument.to_string().into_bytes();
stdout.write_all(&bytes).map_err(map_err)?;
}
} }
stdout.write(b"\n").map_err(map_err)?; stdout.write(b"\n").map_err(map_err)?;

View File

@ -486,6 +486,10 @@ impl Function {
&self.chunk &self.chunk
} }
pub fn chunk_mut(&mut self) -> &mut Chunk {
&mut self.chunk
}
pub fn take_chunk(self) -> Chunk { pub fn take_chunk(self) -> Chunk {
self.chunk self.chunk
} }

View File

@ -2,6 +2,7 @@
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
ops::Range,
}; };
use crate::{ use crate::{
@ -11,8 +12,8 @@ use crate::{
}; };
pub fn run(source: &str) -> Result<Option<Value>, DustError> { pub fn run(source: &str) -> Result<Option<Value>, DustError> {
let chunk = compile(source)?; let mut chunk = compile(source)?;
let mut vm = Vm::new(chunk, None); let mut vm = Vm::new(&mut chunk, None);
vm.run() vm.run()
.map(|option| option.cloned()) .map(|option| option.cloned())
@ -31,18 +32,18 @@ pub fn run_and_display_output(source: &str) {
/// ///
/// See the [module-level documentation](index.html) for more information. /// See the [module-level documentation](index.html) for more information.
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub struct Vm<'parent> { pub struct Vm<'chunk, 'parent> {
ip: usize, ip: usize,
chunk: Chunk, chunk: &'chunk mut Chunk,
stack: Vec<Register>, stack: Vec<Register>,
last_assigned_register: Option<u8>, last_assigned_register: Option<u8>,
parent: Option<&'parent Vm<'parent>>, parent: Option<&'parent Vm<'chunk, 'parent>>,
} }
impl<'parent> Vm<'parent> { impl<'chunk, 'parent> Vm<'chunk, 'parent> {
const STACK_LIMIT: usize = u16::MAX as usize; const STACK_LIMIT: usize = u16::MAX as usize;
pub fn new(chunk: Chunk, parent: Option<&'parent Vm<'parent>>) -> Self { pub fn new(chunk: &'chunk mut Chunk, parent: Option<&'parent Vm<'chunk, 'parent>>) -> Self {
Self { Self {
ip: 0, ip: 0,
chunk, chunk,
@ -62,12 +63,12 @@ impl<'parent> Vm<'parent> {
let left = if instruction.b_is_constant() { let left = if instruction.b_is_constant() {
vm.get_constant(instruction.b(), position)? vm.get_constant(instruction.b(), position)?
} else { } else {
vm.get_register(instruction.b(), position)? vm.open_register(instruction.b(), position)?
}; };
let right = if instruction.c_is_constant() { let right = if instruction.c_is_constant() {
vm.get_constant(instruction.c(), position)? vm.get_constant(instruction.c(), position)?
} else { } else {
vm.get_register(instruction.c(), position)? vm.open_register(instruction.c(), position)?
}; };
Ok((left, right)) Ok((left, right))
@ -79,7 +80,7 @@ impl<'parent> Vm<'parent> {
self.ip - 1, self.ip - 1,
position, position,
instruction.operation(), instruction.operation(),
instruction.disassembly_info(&self.chunk) instruction.disassembly_info(self.chunk)
); );
match instruction.operation() { match instruction.operation() {
@ -143,7 +144,7 @@ impl<'parent> Vm<'parent> {
let start_register = instruction.b(); let start_register = instruction.b();
let item_type = (start_register..to_register) let item_type = (start_register..to_register)
.find_map(|register_index| { .find_map(|register_index| {
if let Ok(value) = self.get_register(register_index, position) { if let Ok(value) = self.open_register(register_index, position) {
Some(value.r#type()) Some(value.r#type())
} else { } else {
None None
@ -238,7 +239,7 @@ impl<'parent> Vm<'parent> {
Operation::Test => { Operation::Test => {
let register = instruction.a(); let register = instruction.a();
let test_value = instruction.c_as_boolean(); let test_value = instruction.c_as_boolean();
let value = self.get_register(register, position)?; let value = self.open_register(register, position)?;
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value { let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
*boolean *boolean
} else { } else {
@ -366,7 +367,7 @@ impl<'parent> Vm<'parent> {
let value = if instruction.b_is_constant() { let value = if instruction.b_is_constant() {
self.get_constant(instruction.b(), position)? self.get_constant(instruction.b(), position)?
} else { } else {
self.get_register(instruction.b(), position)? self.open_register(instruction.b(), position)?
}; };
let negated = value let negated = value
.negate() .negate()
@ -378,7 +379,7 @@ impl<'parent> Vm<'parent> {
let value = if instruction.b_is_constant() { let value = if instruction.b_is_constant() {
self.get_constant(instruction.b(), position)? self.get_constant(instruction.b(), position)?
} else { } else {
self.get_register(instruction.b(), position)? self.open_register(instruction.b(), position)?
}; };
let not = value let not = value
.not() .not()
@ -400,17 +401,17 @@ impl<'parent> Vm<'parent> {
let to_register = instruction.a(); let to_register = instruction.a();
let function_register = instruction.b(); let function_register = instruction.b();
let argument_count = instruction.c(); let argument_count = instruction.c();
let value = self.get_register(function_register, position)?.clone(); let value = self.open_register(function_register, position)?.clone();
let function = if let Value::Concrete(ConcreteValue::Function(function)) = value let mut function =
{ if let Value::Concrete(ConcreteValue::Function(function)) = value {
function function
} else { } else {
return Err(VmError::ExpectedFunction { return Err(VmError::ExpectedFunction {
found: value, found: value,
position, position,
}); });
}; };
let mut function_vm = Vm::new(function.take_chunk(), Some(self)); let mut function_vm = Vm::new(function.chunk_mut(), Some(self));
let first_argument_index = function_register + 1; let first_argument_index = function_register + 1;
for argument_index in for argument_index in
@ -449,7 +450,7 @@ impl<'parent> Vm<'parent> {
} }
let return_value = if let Some(register_index) = self.last_assigned_register { let return_value = if let Some(register_index) = self.last_assigned_register {
self.get_register(register_index, position)? self.open_register(register_index, position)?
} else { } else {
return Err(VmError::StackUnderflow { position }); return Err(VmError::StackUnderflow { position });
}; };
@ -496,7 +497,7 @@ impl<'parent> Vm<'parent> {
let difference = to_register - length; let difference = to_register - length;
for index in 0..difference { for index in 0..difference {
log::trace!("Set R{index} to empty"); log::trace!("Set R{index} to {register}");
self.stack.push(Register::Empty); self.stack.push(Register::Empty);
} }
@ -516,7 +517,7 @@ impl<'parent> Vm<'parent> {
.map_err(|error| VmError::Chunk { error, position }) .map_err(|error| VmError::Chunk { error, position })
} }
pub fn get_register(&self, register_index: u8, position: Span) -> Result<&Value, VmError> { pub fn open_register(&self, register_index: u8, position: Span) -> Result<&Value, VmError> {
let register_index = register_index as usize; let register_index = register_index as usize;
let register = let register =
self.stack self.stack
@ -528,7 +529,7 @@ impl<'parent> Vm<'parent> {
match register { match register {
Register::Value(value) => Ok(value), Register::Value(value) => Ok(value),
Register::StackPointer(register_index) => self.get_register(*register_index, position), Register::StackPointer(register_index) => self.open_register(*register_index, position),
Register::ConstantPointer(constant_index) => { Register::ConstantPointer(constant_index) => {
self.get_constant(*constant_index, position) self.get_constant(*constant_index, position)
} }
@ -538,7 +539,7 @@ impl<'parent> Vm<'parent> {
.as_ref() .as_ref()
.ok_or(VmError::ExpectedParent { position })?; .ok_or(VmError::ExpectedParent { position })?;
parent.get_register(*register_index, position) parent.open_register(*register_index, position)
} }
Register::ParentConstantPointer(constant_index) => { Register::ParentConstantPointer(constant_index) => {
let parent = self let parent = self
@ -555,6 +556,62 @@ impl<'parent> Vm<'parent> {
} }
} }
pub fn open_nonempty_registers(
&self,
register_index_range: Range<u8>,
position: Span,
) -> Result<Vec<&Value>, VmError> {
let mut values = Vec::with_capacity(register_index_range.len());
for register_index in register_index_range.clone() {
let register_index = register_index as usize;
let register = self.stack.get(register_index).ok_or_else(|| {
VmError::RegisterIndexOutOfBounds {
index: register_index,
position,
}
})?;
let value = match register {
Register::Value(value) => value,
Register::StackPointer(register_index) => {
self.open_register(*register_index, position)?
}
Register::ConstantPointer(constant_index) => {
self.get_constant(*constant_index, position)?
}
Register::ParentStackPointer(register_index) => {
let parent = self
.parent
.as_ref()
.ok_or(VmError::ExpectedParent { position })?;
parent.open_register(*register_index, position)?
}
Register::ParentConstantPointer(constant_index) => {
let parent = self
.parent
.as_ref()
.ok_or(VmError::ExpectedParent { position })?;
parent.get_constant(*constant_index, position)?
}
Register::Empty => continue,
};
values.push(value);
}
if values.is_empty() {
Err(VmError::EmptyRegisters {
indexes: register_index_range,
position,
})
} else {
Ok(values)
}
}
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> { fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
self.chunk self.chunk
.expect_not_poisoned() .expect_not_poisoned()
@ -637,6 +694,7 @@ pub enum VmError {
// Register errors // Register errors
EmptyRegister { index: usize, position: Span }, EmptyRegister { index: usize, position: Span },
EmptyRegisters { indexes: Range<u8>, position: Span },
RegisterIndexOutOfBounds { index: usize, position: Span }, RegisterIndexOutOfBounds { index: usize, position: Span },
// Execution errors // Execution errors
@ -659,6 +717,7 @@ impl AnnotatedError for VmError {
match self { match self {
Self::Chunk { .. } => "Chunk error", Self::Chunk { .. } => "Chunk error",
Self::EmptyRegister { .. } => "Empty register", Self::EmptyRegister { .. } => "Empty register",
Self::EmptyRegisters { .. } => "Empty registers",
Self::ExpectedBoolean { .. } => "Expected boolean", Self::ExpectedBoolean { .. } => "Expected boolean",
Self::ExpectedFunction { .. } => "Expected function", Self::ExpectedFunction { .. } => "Expected function",
Self::ExpectedParent { .. } => "Expected parent", Self::ExpectedParent { .. } => "Expected parent",
@ -674,6 +733,10 @@ impl AnnotatedError for VmError {
match self { match self {
Self::Chunk { error, .. } => Some(error.to_string()), Self::Chunk { error, .. } => Some(error.to_string()),
Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")), Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")),
Self::EmptyRegisters { indexes: range, .. } => Some(format!(
"Registers R{} to R{} are empty",
range.start, range.end
)),
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")), Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
Self::RegisterIndexOutOfBounds { index, .. } => { Self::RegisterIndexOutOfBounds { index, .. } => {
Some(format!("Register {index} does not exist")) Some(format!("Register {index} does not exist"))
@ -688,6 +751,7 @@ impl AnnotatedError for VmError {
match self { match self {
Self::Chunk { position, .. } => *position, Self::Chunk { position, .. } => *position,
Self::EmptyRegister { position, .. } => *position, Self::EmptyRegister { position, .. } => *position,
Self::EmptyRegisters { position, .. } => *position,
Self::ExpectedBoolean { position, .. } => *position, Self::ExpectedBoolean { position, .. } => *position,
Self::ExpectedFunction { position, .. } => *position, Self::ExpectedFunction { position, .. } => *position,
Self::ExpectedParent { position } => *position, Self::ExpectedParent { position } => *position,

View File

@ -8,4 +8,4 @@ function fib(n) {
} }
} }
console.log(fib(20)); console.log(fib(25));

View File

@ -5,4 +5,4 @@ fn fib (n: int) -> int {
fib(n - 1) + fib(n - 2) fib(n - 1) + fib(n - 2)
} }
fib(20) write_line(fib(25))