Fix argument retrieval in native functions
This commit is contained in:
parent
04a1c81a2a
commit
bb345a7938
@ -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> {
|
||||||
|
@ -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)?;
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -8,4 +8,4 @@ function fib(n) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(fib(20));
|
console.log(fib(25));
|
||||||
|
@ -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))
|
||||||
|
Loading…
Reference in New Issue
Block a user