Add mutable variables
This commit is contained in:
parent
2cb03297c5
commit
85b95a56aa
@ -144,6 +144,7 @@ impl Chunk {
|
|||||||
pub fn declare_local(
|
pub fn declare_local(
|
||||||
&mut self,
|
&mut self,
|
||||||
identifier: Identifier,
|
identifier: Identifier,
|
||||||
|
mutable: bool,
|
||||||
register_index: u8,
|
register_index: u8,
|
||||||
position: Span,
|
position: Span,
|
||||||
) -> Result<u8, ChunkError> {
|
) -> Result<u8, ChunkError> {
|
||||||
@ -154,6 +155,7 @@ impl Chunk {
|
|||||||
} else {
|
} else {
|
||||||
self.locals.push(Local::new(
|
self.locals.push(Local::new(
|
||||||
identifier,
|
identifier,
|
||||||
|
mutable,
|
||||||
self.scope_depth,
|
self.scope_depth,
|
||||||
Some(register_index),
|
Some(register_index),
|
||||||
));
|
));
|
||||||
@ -236,14 +238,21 @@ impl PartialEq for Chunk {
|
|||||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Local {
|
pub struct Local {
|
||||||
pub identifier: Identifier,
|
pub identifier: Identifier,
|
||||||
|
pub mutable: bool,
|
||||||
pub depth: usize,
|
pub depth: usize,
|
||||||
pub register_index: Option<u8>,
|
pub register_index: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Local {
|
impl Local {
|
||||||
pub fn new(identifier: Identifier, depth: usize, register_index: Option<u8>) -> Self {
|
pub fn new(
|
||||||
|
identifier: Identifier,
|
||||||
|
mutable: bool,
|
||||||
|
depth: usize,
|
||||||
|
register_index: Option<u8>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
identifier,
|
identifier,
|
||||||
|
mutable,
|
||||||
depth,
|
depth,
|
||||||
register_index,
|
register_index,
|
||||||
}
|
}
|
||||||
@ -278,8 +287,8 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
"",
|
"",
|
||||||
"Locals",
|
"Locals",
|
||||||
"------",
|
"------",
|
||||||
"INDEX IDENTIFIER DEPTH REGISTER",
|
"INDEX IDENTIFIER MUTABLE DEPTH REGISTER",
|
||||||
"----- ---------- ----- --------",
|
"----- ---------- ------- ----- --------",
|
||||||
];
|
];
|
||||||
|
|
||||||
/// The default width of the disassembly output. To correctly align the output, this should be
|
/// The default width of the disassembly output. To correctly align the output, this should be
|
||||||
@ -384,6 +393,7 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
identifier,
|
identifier,
|
||||||
depth,
|
depth,
|
||||||
register_index,
|
register_index,
|
||||||
|
mutable,
|
||||||
},
|
},
|
||||||
) in self.chunk.locals.iter().enumerate()
|
) in self.chunk.locals.iter().enumerate()
|
||||||
{
|
{
|
||||||
@ -392,8 +402,9 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
.map(|value| value.to_string())
|
.map(|value| value.to_string())
|
||||||
.unwrap_or_else(|| "empty".to_string());
|
.unwrap_or_else(|| "empty".to_string());
|
||||||
let identifier_display = identifier.as_str();
|
let identifier_display = identifier.as_str();
|
||||||
let local_display =
|
let local_display = format!(
|
||||||
format!("{index:<5} {identifier_display:<10} {depth:<5} {register_display:<8}");
|
"{index:<5} {identifier_display:<10} {mutable:<7} {depth:<5} {register_display:<8}"
|
||||||
|
);
|
||||||
|
|
||||||
disassembled.push_str(¢er(&local_display));
|
disassembled.push_str(¢er(&local_display));
|
||||||
}
|
}
|
||||||
|
@ -42,11 +42,12 @@ impl Instruction {
|
|||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declare_local(to_register: u8, variable_index: u8) -> Instruction {
|
pub fn define_local(to_register: u8, variable_index: u8, is_mutable: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::DeclareLocal as u32);
|
let mut instruction = Instruction(Operation::DefineLocal as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_destination(to_register);
|
||||||
instruction.set_first_argument(variable_index);
|
instruction.set_first_argument(variable_index);
|
||||||
|
instruction.set_second_argument(if is_mutable { 1 } else { 0 });
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -284,7 +285,7 @@ impl Instruction {
|
|||||||
destination, first_index, last_index
|
destination, first_index, last_index
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Operation::DeclareLocal => {
|
Operation::DefineLocal => {
|
||||||
let local_index = self.first_argument();
|
let local_index = self.first_argument();
|
||||||
let identifier_display = if let Some(chunk) = chunk {
|
let identifier_display = if let Some(chunk) = chunk {
|
||||||
match chunk.get_identifier(local_index) {
|
match chunk.get_identifier(local_index) {
|
||||||
@ -433,7 +434,7 @@ pub enum Operation {
|
|||||||
LoadList = LOAD_LIST as isize,
|
LoadList = LOAD_LIST as isize,
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
DeclareLocal = DECLARE_LOCAL as isize,
|
DefineLocal = DECLARE_LOCAL as isize,
|
||||||
GetLocal = GET_LOCAL as isize,
|
GetLocal = GET_LOCAL as isize,
|
||||||
SetLocal = SET_LOCAL as isize,
|
SetLocal = SET_LOCAL as isize,
|
||||||
|
|
||||||
@ -476,7 +477,7 @@ impl From<u8> for Operation {
|
|||||||
CLOSE => Operation::Close,
|
CLOSE => Operation::Close,
|
||||||
LOAD_CONSTANT => Operation::LoadConstant,
|
LOAD_CONSTANT => Operation::LoadConstant,
|
||||||
LOAD_LIST => Operation::LoadList,
|
LOAD_LIST => Operation::LoadList,
|
||||||
DECLARE_LOCAL => Operation::DeclareLocal,
|
DECLARE_LOCAL => Operation::DefineLocal,
|
||||||
GET_LOCAL => Operation::GetLocal,
|
GET_LOCAL => Operation::GetLocal,
|
||||||
SET_LOCAL => Operation::SetLocal,
|
SET_LOCAL => Operation::SetLocal,
|
||||||
ADD => Operation::Add,
|
ADD => Operation::Add,
|
||||||
@ -501,7 +502,7 @@ impl From<Operation> for u8 {
|
|||||||
Operation::Close => CLOSE,
|
Operation::Close => CLOSE,
|
||||||
Operation::LoadConstant => LOAD_CONSTANT,
|
Operation::LoadConstant => LOAD_CONSTANT,
|
||||||
Operation::LoadList => LOAD_LIST,
|
Operation::LoadList => LOAD_LIST,
|
||||||
Operation::DeclareLocal => DECLARE_LOCAL,
|
Operation::DefineLocal => DECLARE_LOCAL,
|
||||||
Operation::GetLocal => GET_LOCAL,
|
Operation::GetLocal => GET_LOCAL,
|
||||||
Operation::SetLocal => SET_LOCAL,
|
Operation::SetLocal => SET_LOCAL,
|
||||||
Operation::Add => ADD,
|
Operation::Add => ADD,
|
||||||
@ -525,7 +526,7 @@ impl Display for Operation {
|
|||||||
Operation::Close => write!(f, "CLOSE"),
|
Operation::Close => write!(f, "CLOSE"),
|
||||||
Operation::LoadConstant => write!(f, "LOAD_CONSTANT"),
|
Operation::LoadConstant => write!(f, "LOAD_CONSTANT"),
|
||||||
Operation::LoadList => write!(f, "LOAD_LIST"),
|
Operation::LoadList => write!(f, "LOAD_LIST"),
|
||||||
Operation::DeclareLocal => write!(f, "DECLARE_LOCAL"),
|
Operation::DefineLocal => write!(f, "DEFINE_LOCAL"),
|
||||||
Operation::GetLocal => write!(f, "GET_LOCAL"),
|
Operation::GetLocal => write!(f, "GET_LOCAL"),
|
||||||
Operation::SetLocal => write!(f, "SET_LOCAL"),
|
Operation::SetLocal => write!(f, "SET_LOCAL"),
|
||||||
Operation::Add => write!(f, "ADD"),
|
Operation::Add => write!(f, "ADD"),
|
||||||
@ -585,16 +586,15 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn declare_local() {
|
fn declare_local() {
|
||||||
let mut instruction = Instruction::declare_local(0, 1);
|
let mut instruction = Instruction::define_local(0, 1, true);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_first_argument_to_constant();
|
||||||
instruction.set_second_argument_to_constant();
|
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::DeclareLocal);
|
assert_eq!(instruction.operation(), Operation::DefineLocal);
|
||||||
assert_eq!(instruction.destination(), 0);
|
assert_eq!(instruction.destination(), 0);
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.first_argument(), 1);
|
||||||
|
assert_eq!(instruction.second_argument(), true as u8);
|
||||||
assert!(instruction.first_argument_is_constant());
|
assert!(instruction.first_argument_is_constant());
|
||||||
assert!(instruction.second_argument_is_constant());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -295,12 +295,22 @@ impl<'src> Parser<'src> {
|
|||||||
let mut left_is_constant = false;
|
let mut left_is_constant = false;
|
||||||
let left = match left_instruction.operation() {
|
let left = match left_instruction.operation() {
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
|
log::trace!(
|
||||||
|
"Condensing {} to binary expression",
|
||||||
|
left_instruction.operation()
|
||||||
|
);
|
||||||
|
|
||||||
left_is_constant = true;
|
left_is_constant = true;
|
||||||
|
|
||||||
self.decrement_register()?;
|
self.decrement_register()?;
|
||||||
left_instruction.first_argument()
|
left_instruction.first_argument()
|
||||||
}
|
}
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
|
log::trace!(
|
||||||
|
"Condensing {} to binary expression",
|
||||||
|
left_instruction.operation()
|
||||||
|
);
|
||||||
|
|
||||||
self.decrement_register()?;
|
self.decrement_register()?;
|
||||||
left_instruction.first_argument()
|
left_instruction.first_argument()
|
||||||
}
|
}
|
||||||
@ -330,12 +340,22 @@ impl<'src> Parser<'src> {
|
|||||||
let mut right_is_constant = false;
|
let mut right_is_constant = false;
|
||||||
let right = match right_instruction.operation() {
|
let right = match right_instruction.operation() {
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
|
log::trace!(
|
||||||
|
"Condensing {} to binary expression",
|
||||||
|
right_instruction.operation()
|
||||||
|
);
|
||||||
|
|
||||||
right_is_constant = true;
|
right_is_constant = true;
|
||||||
|
|
||||||
self.decrement_register()?;
|
self.decrement_register()?;
|
||||||
right_instruction.first_argument()
|
right_instruction.first_argument()
|
||||||
}
|
}
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
|
log::trace!(
|
||||||
|
"Condensing {} to binary expression",
|
||||||
|
right_instruction.operation()
|
||||||
|
);
|
||||||
|
|
||||||
self.decrement_register()?;
|
self.decrement_register()?;
|
||||||
right_instruction.first_argument()
|
right_instruction.first_argument()
|
||||||
}
|
}
|
||||||
@ -423,6 +443,8 @@ impl<'src> Parser<'src> {
|
|||||||
.register_index;
|
.register_index;
|
||||||
|
|
||||||
if let Some(register_index) = previous_register {
|
if let Some(register_index) = previous_register {
|
||||||
|
log::trace!("Condensing SET_LOCAL to binary expression");
|
||||||
|
|
||||||
previous_instruction.set_destination(register_index);
|
previous_instruction.set_destination(register_index);
|
||||||
self.emit_instruction(previous_instruction, self.current_position);
|
self.emit_instruction(previous_instruction, self.current_position);
|
||||||
} else {
|
} else {
|
||||||
@ -592,6 +614,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
|
let is_mutable = self.allow(TokenKind::Mut)?;
|
||||||
let position = self.current_position;
|
let position = self.current_position;
|
||||||
let identifier = if let Token::Identifier(text) = self.current_token {
|
let identifier = if let Token::Identifier(text) = self.current_token {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
@ -609,11 +632,14 @@ impl<'src> Parser<'src> {
|
|||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let register = self.chunk.get_last_instruction(position)?.0.destination();
|
let register = self.chunk.get_last_instruction(position)?.0.destination();
|
||||||
let local_index = self
|
let local_index =
|
||||||
.chunk
|
self.chunk
|
||||||
.declare_local(identifier, register, self.current_position)?;
|
.declare_local(identifier, is_mutable, register, self.current_position)?;
|
||||||
|
|
||||||
self.emit_instruction(Instruction::declare_local(register, local_index), position);
|
self.emit_instruction(
|
||||||
|
Instruction::define_local(register, local_index, is_mutable),
|
||||||
|
position,
|
||||||
|
);
|
||||||
self.allow(TokenKind::Semicolon)?;
|
self.allow(TokenKind::Semicolon)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -824,8 +850,16 @@ impl From<&TokenKind> for ParseRule<'_> {
|
|||||||
precedence: Precedence::Term,
|
precedence: Precedence::Term,
|
||||||
},
|
},
|
||||||
TokenKind::MinusEqual => todo!(),
|
TokenKind::MinusEqual => todo!(),
|
||||||
TokenKind::Mut => todo!(),
|
TokenKind::Mut => ParseRule {
|
||||||
TokenKind::Percent => todo!(),
|
prefix: None,
|
||||||
|
infix: None,
|
||||||
|
precedence: Precedence::None,
|
||||||
|
},
|
||||||
|
TokenKind::Percent => ParseRule {
|
||||||
|
prefix: None,
|
||||||
|
infix: Some(Parser::parse_binary),
|
||||||
|
precedence: Precedence::Factor,
|
||||||
|
},
|
||||||
TokenKind::Plus => ParseRule {
|
TokenKind::Plus => ParseRule {
|
||||||
prefix: None,
|
prefix: None,
|
||||||
infix: Some(Parser::parse_binary),
|
infix: Some(Parser::parse_binary),
|
||||||
|
@ -71,17 +71,17 @@ fn block_scope() {
|
|||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0), Span(17, 18)),
|
(Instruction::load_constant(0, 0), Span(17, 18)),
|
||||||
(Instruction::declare_local(0, 0), Span(13, 14)),
|
(Instruction::define_local(0, 0, false), Span(13, 14)),
|
||||||
(Instruction::load_constant(1, 1), Span(50, 52)),
|
(Instruction::load_constant(1, 1), Span(50, 52)),
|
||||||
(Instruction::declare_local(1, 1), Span(46, 47)),
|
(Instruction::define_local(1, 1, false), Span(46, 47)),
|
||||||
(Instruction::load_constant(2, 2), Span(92, 93)),
|
(Instruction::load_constant(2, 2), Span(92, 93)),
|
||||||
(Instruction::declare_local(2, 2), Span(88, 89)),
|
(Instruction::define_local(2, 2, false), Span(88, 89)),
|
||||||
(Instruction::close(2, 3), Span(84, 124)),
|
(Instruction::close(2, 3), Span(84, 124)),
|
||||||
(Instruction::load_constant(3, 3), Span(129, 130)),
|
(Instruction::load_constant(3, 3), Span(129, 130)),
|
||||||
(Instruction::declare_local(3, 3), Span(125, 126)),
|
(Instruction::define_local(3, 3, false), Span(125, 126)),
|
||||||
(Instruction::close(1, 4), Span(42, 153)),
|
(Instruction::close(1, 4), Span(42, 153)),
|
||||||
(Instruction::load_constant(4, 4), Span(158, 159)),
|
(Instruction::load_constant(4, 4), Span(158, 159)),
|
||||||
(Instruction::declare_local(4, 4), Span(154, 155)),
|
(Instruction::define_local(4, 4, false), Span(154, 155)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Value::integer(0),
|
Value::integer(0),
|
||||||
@ -91,11 +91,11 @@ fn block_scope() {
|
|||||||
Value::integer(1)
|
Value::integer(1)
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Local::new(Identifier::new("a"), 0, Some(0)),
|
Local::new(Identifier::new("a"), false, 0, Some(0)),
|
||||||
Local::new(Identifier::new("b"), 1, Some(1)),
|
Local::new(Identifier::new("b"), false, 1, Some(1)),
|
||||||
Local::new(Identifier::new("c"), 2, Some(2)),
|
Local::new(Identifier::new("c"), false, 2, Some(2)),
|
||||||
Local::new(Identifier::new("d"), 1, Some(3)),
|
Local::new(Identifier::new("d"), false, 1, Some(3)),
|
||||||
Local::new(Identifier::new("e"), 0, Some(4)),
|
Local::new(Identifier::new("e"), false, 0, Some(4)),
|
||||||
]
|
]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
@ -113,12 +113,12 @@ fn set_local() {
|
|||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0), Span(8, 10)),
|
(Instruction::load_constant(0, 0), Span(8, 10)),
|
||||||
(Instruction::declare_local(0, 0), Span(4, 5)),
|
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
||||||
(Instruction::load_constant(1, 1), Span(16, 18)),
|
(Instruction::load_constant(1, 1), Span(16, 18)),
|
||||||
(Instruction::set_local(1, 0), Span(12, 13)),
|
(Instruction::set_local(1, 0), Span(12, 13)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(41), Value::integer(42)],
|
vec![Value::integer(41), Value::integer(42)],
|
||||||
vec![Local::new(Identifier::new("x"), 0, Some(0)),]
|
vec![Local::new(Identifier::new("x"), false, 0, Some(0)),]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -191,10 +191,10 @@ fn declare_local() {
|
|||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0), Span(8, 10)),
|
(Instruction::load_constant(0, 0), Span(8, 10)),
|
||||||
(Instruction::declare_local(0, 0), Span(4, 5)),
|
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42)],
|
vec![Value::integer(42)],
|
||||||
vec![Local::new(Identifier::new("x"), 0, Some(0))]
|
vec![Local::new(Identifier::new("x"), false, 0, Some(0))]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Debug, Display, Formatter},
|
||||||
ops::{Range, RangeInclusive},
|
ops::{Range, RangeInclusive},
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
};
|
};
|
||||||
@ -50,7 +50,6 @@ use crate::{EnumType, FunctionType, Identifier, RangeableType, StructType, Type}
|
|||||||
/// Dust value representation
|
/// Dust value representation
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation][self] for more.
|
/// See the [module-level documentation][self] for more.
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Raw(ValueData),
|
Raw(ValueData),
|
||||||
Reference(Arc<ValueData>),
|
Reference(Arc<ValueData>),
|
||||||
@ -127,6 +126,8 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_reference(self) -> Self {
|
pub fn into_reference(self) -> Self {
|
||||||
|
log::trace!("Converting to reference: {self:?}");
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Value::Raw(data) => Value::Reference(Arc::new(data)),
|
Value::Raw(data) => Value::Reference(Arc::new(data)),
|
||||||
Value::Reference(_) => self,
|
Value::Reference(_) => self,
|
||||||
@ -140,8 +141,16 @@ impl Value {
|
|||||||
|
|
||||||
pub fn into_mutable(self) -> Self {
|
pub fn into_mutable(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Value::Raw(data) => Value::Mutable(Arc::new(RwLock::new(data))),
|
Value::Raw(data) => {
|
||||||
Value::Reference(data) => Value::Mutable(Arc::new(RwLock::new(data.as_ref().clone()))),
|
log::trace!("Converting to mutable: {data:?}");
|
||||||
|
|
||||||
|
Value::Mutable(Arc::new(RwLock::new(data)))
|
||||||
|
}
|
||||||
|
Value::Reference(data) => {
|
||||||
|
log::trace!("Converting to mutable: {data:?}");
|
||||||
|
|
||||||
|
Value::Mutable(Arc::new(RwLock::new(data.as_ref().clone())))
|
||||||
|
}
|
||||||
Value::Mutable(_) => self,
|
Value::Mutable(_) => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -818,6 +827,20 @@ impl Clone for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for Value {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Value::Raw(data) => write!(f, "Value::Raw({data:?})"),
|
||||||
|
Value::Reference(data) => write!(f, "Value::Reference({data:?})"),
|
||||||
|
Value::Mutable(data) => {
|
||||||
|
let data = data.read().unwrap();
|
||||||
|
|
||||||
|
write!(f, "Value::Mutable({data:?})")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Eq for Value {}
|
impl Eq for Value {}
|
||||||
|
|
||||||
impl PartialEq for Value {
|
impl PartialEq for Value {
|
||||||
|
@ -35,6 +35,7 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> Result<Option<Value>, VmError> {
|
pub fn run(&mut self) -> Result<Option<Value>, VmError> {
|
||||||
|
// DRY helper closure to take a constant or clone a register
|
||||||
let take_constants_or_clone = |vm: &mut Vm,
|
let take_constants_or_clone = |vm: &mut Vm,
|
||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
position: Span|
|
position: Span|
|
||||||
@ -107,7 +108,7 @@ impl Vm {
|
|||||||
|
|
||||||
self.insert(Value::list(list), to_register, position)?;
|
self.insert(Value::list(list), to_register, position)?;
|
||||||
}
|
}
|
||||||
Operation::DeclareLocal => {
|
Operation::DefineLocal => {
|
||||||
let from_register = instruction.destination();
|
let from_register = instruction.destination();
|
||||||
let to_local = instruction.first_argument();
|
let to_local = instruction.first_argument();
|
||||||
|
|
||||||
@ -122,10 +123,21 @@ impl Vm {
|
|||||||
self.insert(value, register_index, position)?;
|
self.insert(value, register_index, position)?;
|
||||||
}
|
}
|
||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let from_register = instruction.destination();
|
let register_index = instruction.destination();
|
||||||
let to_local = instruction.first_argument();
|
let local_index = instruction.first_argument();
|
||||||
|
let local = self.chunk.get_local(local_index, position)?.clone();
|
||||||
|
let value = self.clone_as_variable(local, position)?;
|
||||||
|
let new_value = if instruction.first_argument_is_constant() {
|
||||||
|
self.chunk.take_constant(register_index, position)?
|
||||||
|
} else {
|
||||||
|
self.clone(register_index, position)?
|
||||||
|
};
|
||||||
|
|
||||||
self.chunk.define_local(to_local, from_register, position)?;
|
value
|
||||||
|
.mutate(new_value)
|
||||||
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
|
self.insert(value, register_index, position)?;
|
||||||
}
|
}
|
||||||
Operation::Add => {
|
Operation::Add => {
|
||||||
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
||||||
@ -258,19 +270,40 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clone_mutable(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if let Some(register) = self.register_stack.get_mut(index) {
|
||||||
|
let cloneable = if let Some(value) = register.take() {
|
||||||
|
value.into_mutable()
|
||||||
|
} else {
|
||||||
|
return Err(VmError::EmptyRegister { index, position });
|
||||||
|
};
|
||||||
|
|
||||||
|
*register = Some(cloneable.clone());
|
||||||
|
|
||||||
|
Ok(cloneable)
|
||||||
|
} else {
|
||||||
|
Err(VmError::RegisterIndexOutOfBounds { position })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn clone_as_variable(&mut self, local: Local, position: Span) -> Result<Value, VmError> {
|
fn clone_as_variable(&mut self, local: Local, position: Span) -> Result<Value, VmError> {
|
||||||
let index = if let Some(index) = local.register_index {
|
let index = if let Some(index) = local.register_index {
|
||||||
index
|
index
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::UndefinedVariable {
|
return Err(VmError::UndefinedVariable {
|
||||||
identifier: local.identifier.clone(),
|
identifier: local.identifier,
|
||||||
position,
|
position,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
let clone_result = self.clone(index, position);
|
let clone_result = if local.mutable {
|
||||||
|
self.clone_mutable(index, position)
|
||||||
|
} else {
|
||||||
|
self.clone(index, position)
|
||||||
|
};
|
||||||
|
|
||||||
match clone_result {
|
match clone_result {
|
||||||
Ok(value) => Ok(value),
|
|
||||||
Err(VmError::EmptyRegister { .. }) => Err(VmError::UndefinedVariable {
|
Err(VmError::EmptyRegister { .. }) => Err(VmError::UndefinedVariable {
|
||||||
identifier: local.identifier,
|
identifier: local.identifier,
|
||||||
position,
|
position,
|
||||||
@ -334,8 +367,8 @@ pub enum VmError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<ChunkError> for VmError {
|
impl From<ChunkError> for VmError {
|
||||||
fn from(v: ChunkError) -> Self {
|
fn from(error: ChunkError) -> Self {
|
||||||
Self::Chunk(v)
|
Self::Chunk(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user