Refactor to use 64-bit instructions
This commit is contained in:
parent
e04ead3848
commit
fbaf59abe2
@ -1,13 +1,13 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
fs::{read_to_string, File, OpenOptions},
|
fs::{File, OpenOptions},
|
||||||
io::{stdin, stdout, Read, Write},
|
io::{stdin, stdout, Read, Write},
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::{Args, Parser, Subcommand, ValueEnum};
|
use clap::{Args, Parser, Subcommand, ValueEnum};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use dust_lang::{compile, display_token_list, format, lex, run_source, vm::run_chunk, Chunk};
|
use dust_lang::{compile, display_token_list, format, lex, vm::run_chunk};
|
||||||
use env_logger::Target;
|
use env_logger::Target;
|
||||||
use log::{Level, LevelFilter};
|
use log::{Level, LevelFilter};
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
//! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they
|
//! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they
|
||||||
//! belong to a named function.
|
//! belong to a named function.
|
||||||
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::fmt::{self, Debug, Display, Formatter, Write};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -193,7 +193,7 @@ impl Debug for Chunk {
|
|||||||
let disassembly = self.disassembler().style(false).disassemble();
|
let disassembly = self.disassembler().style(false).disassemble();
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
write!(f, "\n",)?;
|
f.write_char('\n')?;
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(f, "{}", disassembly)
|
write!(f, "{}", disassembly)
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
//! - [`compile`], which compiles the entire input and returns a chunk
|
//! - [`compile`], which compiles the entire input and returns a chunk
|
||||||
//! - [`Compiler`], which compiles the input a token at a time while assembling a chunk
|
//! - [`Compiler`], which compiles the input a token at a time while assembling a chunk
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
mem::replace,
|
mem::replace,
|
||||||
num::{ParseFloatError, ParseIntError},
|
num::{ParseFloatError, ParseIntError},
|
||||||
@ -49,7 +48,7 @@ pub fn compile(source: &str) -> Result<Chunk, DustError> {
|
|||||||
pub struct Compiler<'src> {
|
pub struct Compiler<'src> {
|
||||||
chunk: Chunk,
|
chunk: Chunk,
|
||||||
lexer: Lexer<'src>,
|
lexer: Lexer<'src>,
|
||||||
local_declarations: HashMap<u8, u8>,
|
local_declarations: Vec<u8>,
|
||||||
|
|
||||||
current_token: Token<'src>,
|
current_token: Token<'src>,
|
||||||
current_position: Span,
|
current_position: Span,
|
||||||
@ -79,7 +78,7 @@ impl<'src> Compiler<'src> {
|
|||||||
Ok(Compiler {
|
Ok(Compiler {
|
||||||
chunk,
|
chunk,
|
||||||
lexer,
|
lexer,
|
||||||
local_declarations: HashMap::new(),
|
local_declarations: Vec::new(),
|
||||||
current_token,
|
current_token,
|
||||||
current_position,
|
current_position,
|
||||||
previous_token: Token::Eof,
|
previous_token: Token::Eof,
|
||||||
@ -119,9 +118,7 @@ impl<'src> Compiler<'src> {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(instruction, _)| {
|
.filter_map(|(instruction, _)| {
|
||||||
if instruction.yields_value() {
|
if instruction.yields_value() {
|
||||||
let to_register = instruction.a();
|
Some(instruction.a() + 1)
|
||||||
|
|
||||||
Some(to_register + 1)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -199,20 +196,21 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
let identifier = ConcreteValue::string(identifier);
|
let identifier = ConcreteValue::string(identifier);
|
||||||
let identifier_index = self.chunk.push_or_get_constant(identifier);
|
let identifier_index = self.chunk.push_or_get_constant(identifier);
|
||||||
let local_index = self.chunk.locals().len() as u8;
|
let local_index = self.chunk.locals().len();
|
||||||
|
|
||||||
self.chunk
|
self.chunk
|
||||||
.locals_mut()
|
.locals_mut()
|
||||||
.push(Local::new(identifier_index, r#type, is_mutable, scope));
|
.push(Local::new(identifier_index, r#type, is_mutable, scope));
|
||||||
self.local_declarations.insert(local_index, register_index);
|
self.local_declarations.insert(local_index, register_index);
|
||||||
|
|
||||||
(local_index, identifier_index)
|
(local_index as u8, identifier_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn redeclare_local(&mut self, local_index: u8, register_index: u8) -> Result<(), CompileError> {
|
fn redeclare_local(&mut self, local_index: u8, register_index: u8) -> Result<(), CompileError> {
|
||||||
let local = self.get_local(local_index)?;
|
let local_index = local_index as usize;
|
||||||
|
|
||||||
if !self.current_scope.contains(&local.scope) {
|
if !self.local_declarations.len() <= local_index {
|
||||||
|
let local = self.get_local(local_index as u8)?;
|
||||||
let identifier = self
|
let identifier = self
|
||||||
.chunk
|
.chunk
|
||||||
.constants()
|
.constants()
|
||||||
@ -228,7 +226,7 @@ impl<'src> Compiler<'src> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.local_declarations.insert(local_index, register_index);
|
self.local_declarations[local_index] = register_index;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -696,7 +694,10 @@ impl<'src> Compiler<'src> {
|
|||||||
let local = self.get_local(local_index)?;
|
let local = self.get_local(local_index)?;
|
||||||
is_mutable_local = local.is_mutable;
|
is_mutable_local = local.is_mutable;
|
||||||
|
|
||||||
*self.local_declarations.get(&local_index).ok_or_else(|| {
|
*self
|
||||||
|
.local_declarations
|
||||||
|
.get(local_index as usize)
|
||||||
|
.ok_or_else(|| {
|
||||||
let identifier = self
|
let identifier = self
|
||||||
.chunk
|
.chunk
|
||||||
.constants()
|
.constants()
|
||||||
@ -785,7 +786,6 @@ impl<'src> Compiler<'src> {
|
|||||||
} else {
|
} else {
|
||||||
self.next_register()
|
self.next_register()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut new_instruction = match operator {
|
let mut new_instruction = match operator {
|
||||||
Token::Plus => Instruction::add(register, left, right),
|
Token::Plus => Instruction::add(register, left, right),
|
||||||
Token::PlusEqual => Instruction::add(register, left, right),
|
Token::PlusEqual => Instruction::add(register, left, right),
|
||||||
@ -827,12 +827,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
self.emit_instruction(new_instruction, operator_position);
|
self.emit_instruction(new_instruction, operator_position);
|
||||||
|
|
||||||
if let Token::PlusEqual
|
if is_assignment {
|
||||||
| Token::MinusEqual
|
|
||||||
| Token::StarEqual
|
|
||||||
| Token::SlashEqual
|
|
||||||
| Token::PercentEqual = operator
|
|
||||||
{
|
|
||||||
self.previous_expression_type = Type::None;
|
self.previous_expression_type = Type::None;
|
||||||
} else {
|
} else {
|
||||||
self.previous_expression_type = self.get_instruction_type(&left_instruction)?;
|
self.previous_expression_type = self.get_instruction_type(&left_instruction)?;
|
||||||
@ -859,7 +854,6 @@ impl<'src> Compiler<'src> {
|
|||||||
})?;
|
})?;
|
||||||
let (push_back_left, left_is_constant, _, left) =
|
let (push_back_left, left_is_constant, _, left) =
|
||||||
self.handle_binary_argument(&left_instruction)?;
|
self.handle_binary_argument(&left_instruction)?;
|
||||||
|
|
||||||
let operator = self.current_token;
|
let operator = self.current_token;
|
||||||
let operator_position = self.current_position;
|
let operator_position = self.current_position;
|
||||||
let rule = ParseRule::from(&operator);
|
let rule = ParseRule::from(&operator);
|
||||||
@ -1028,9 +1022,10 @@ impl<'src> Compiler<'src> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let register = self.next_register() - 1;
|
|
||||||
|
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
|
let register = self.local_declarations[local_index as usize];
|
||||||
|
|
||||||
self.redeclare_local(local_index, register)?;
|
self.redeclare_local(local_index, register)?;
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
Instruction::set_local(register, local_index),
|
Instruction::set_local(register, local_index),
|
||||||
|
@ -8,7 +8,7 @@ use crate::{CompileError, DustError, LexError, Lexer, Token};
|
|||||||
pub fn format(
|
pub fn format(
|
||||||
source: &str,
|
source: &str,
|
||||||
colored: bool,
|
colored: bool,
|
||||||
indent: usize,
|
_indent: usize,
|
||||||
line_numbers: bool,
|
line_numbers: bool,
|
||||||
) -> Result<String, DustError> {
|
) -> Result<String, DustError> {
|
||||||
let lexer = Lexer::new(source);
|
let lexer = Lexer::new(source);
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
//! An operation and its arguments for the Dust virtual machine.
|
//! An operation and its arguments for the Dust virtual machine.
|
||||||
//!
|
//!
|
||||||
//! Each instruction is a 32-bit unsigned integer that is divided into five fields:
|
//! Each instruction is a 64-bit unsigned integer that is divided into five fields:
|
||||||
//! - Bits 0-6: The operation code.
|
//! - Bits 0-8: The operation code.
|
||||||
//! - Bit 7: A flag indicating whether the B argument is a constant.
|
//! - Bit 9: Boolean flag indicating whether the B argument is a constant.
|
||||||
//! - Bit 8: A flag indicating whether the C argument is a constant.
|
//! - Bit 10: Boolean flag indicating whether the C argument is a constant.
|
||||||
//! - Bits 9-16: The A argument,
|
//! - Bit 11: Boolean flag indicating whether the A argument is a local.
|
||||||
//! - Bits 17-24: The B argument.
|
//! - Bit 12: Boolean flag indicating whether the B argument is a local.
|
||||||
//! - Bits 25-32: The C argument.
|
//! - Bit 13: Boolean flag indicating whether the C argument is a local.
|
||||||
|
//! - Bits 17-32: The A argument,
|
||||||
|
//! - Bits 33-48: The B argument.
|
||||||
|
//! - Bits 49-63: The C argument.
|
||||||
//!
|
//!
|
||||||
//! Be careful when working with instructions directly. When modifying an instruction, be sure to
|
//! Be careful when working with instructions directly. When modifying an instruction, be sure to
|
||||||
//! account for the fact that setting the A, B, or C arguments to 0 will have no effect. It is
|
//! account for the fact that setting the A, B, or C arguments to 0 will have no effect. It is
|
||||||
@ -16,571 +19,486 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::{Chunk, NativeFunction, Operation};
|
use crate::{Chunk, NativeFunction, Operation};
|
||||||
|
|
||||||
|
pub struct InstructionBuilder {
|
||||||
|
operation: Operation,
|
||||||
|
a: u16,
|
||||||
|
b: u16,
|
||||||
|
c: u16,
|
||||||
|
b_is_constant: bool,
|
||||||
|
c_is_constant: bool,
|
||||||
|
a_is_local: bool,
|
||||||
|
b_is_local: bool,
|
||||||
|
c_is_local: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstructionBuilder {
|
||||||
|
pub fn build(&self) -> Instruction {
|
||||||
|
Instruction(
|
||||||
|
(self.operation as u64)
|
||||||
|
| ((self.b_is_constant as u64) << 9)
|
||||||
|
| ((self.c_is_constant as u64) << 10)
|
||||||
|
| ((self.a_is_local as u64) << 11)
|
||||||
|
| ((self.b_is_local as u64) << 12)
|
||||||
|
| ((self.c_is_local as u64) << 13)
|
||||||
|
| ((self.a as u64) << 16)
|
||||||
|
| ((self.b as u64) << 32)
|
||||||
|
| ((self.c as u64) << 48),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_a(&mut self, a: u16) -> &mut Self {
|
||||||
|
self.a = a;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_b(&mut self, b: u16) -> &mut Self {
|
||||||
|
self.b = b;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_b_to_boolean(&mut self, b: bool) -> &mut Self {
|
||||||
|
self.b = b as u16;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_c(&mut self, c: u16) -> &mut Self {
|
||||||
|
self.c = c;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_c_to_boolean(&mut self, c: bool) -> &mut Self {
|
||||||
|
self.c = c as u16;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_b_is_constant(&mut self, b_is_constant: bool) -> &mut Self {
|
||||||
|
self.b_is_constant = b_is_constant;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_c_is_constant(&mut self, c_is_constant: bool) -> &mut Self {
|
||||||
|
self.c_is_constant = c_is_constant;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_a_is_local(&mut self, a_is_local: bool) -> &mut Self {
|
||||||
|
self.a_is_local = a_is_local;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_b_is_local(&mut self, b_is_local: bool) -> &mut Self {
|
||||||
|
self.b_is_local = b_is_local;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_c_is_local(&mut self, c_is_local: bool) -> &mut Self {
|
||||||
|
self.c_is_local = c_is_local;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An operation and its arguments for the Dust virtual machine.
|
/// An operation and its arguments for the Dust virtual machine.
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
/// See the [module-level documentation](index.html) for more information.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
pub struct Instruction(u32);
|
pub struct Instruction(u64);
|
||||||
|
|
||||||
impl Instruction {
|
impl Instruction {
|
||||||
pub fn with_operation(operation: Operation) -> Instruction {
|
pub fn builder(operation: Operation) -> InstructionBuilder {
|
||||||
Instruction(operation as u32)
|
InstructionBuilder {
|
||||||
|
operation,
|
||||||
|
a: 0,
|
||||||
|
b: 0,
|
||||||
|
c: 0,
|
||||||
|
b_is_constant: false,
|
||||||
|
c_is_constant: false,
|
||||||
|
a_is_local: false,
|
||||||
|
b_is_local: false,
|
||||||
|
c_is_local: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#move(to_register: u8, from_register: u8) -> Instruction {
|
pub fn r#move(to_register: u16, from_register: u16) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Move as u32);
|
Instruction::builder(Operation::Move)
|
||||||
|
.set_a(to_register)
|
||||||
instruction.set_a(to_register);
|
.set_b(from_register)
|
||||||
instruction.set_b(from_register);
|
.build()
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(from_register: u8, to_register: u8) -> Instruction {
|
pub fn close(from_register: u16, to_register: u16) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Close as u32);
|
Instruction::builder(Operation::Close)
|
||||||
|
.set_b(from_register)
|
||||||
instruction.set_b(from_register);
|
.set_c(to_register)
|
||||||
instruction.set_c(to_register);
|
.build()
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_boolean(to_register: u8, value: bool, skip: bool) -> Instruction {
|
pub fn load_boolean(to_register: u16, value: bool, skip: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::LoadBoolean as u32);
|
Instruction::builder(Operation::LoadBoolean)
|
||||||
|
.set_a(to_register)
|
||||||
instruction.set_a(to_register);
|
.set_b_to_boolean(value)
|
||||||
instruction.set_b_to_boolean(value);
|
.set_c_to_boolean(skip)
|
||||||
instruction.set_c_to_boolean(skip);
|
.build()
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_constant(to_register: u8, constant_index: u8, skip: bool) -> Instruction {
|
pub fn load_constant(to_register: u16, constant_index: u16, skip: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::LoadConstant as u32);
|
Instruction::builder(Operation::LoadConstant)
|
||||||
|
.set_a(to_register)
|
||||||
instruction.set_a(to_register);
|
.set_b(constant_index)
|
||||||
instruction.set_b(constant_index);
|
.set_c_to_boolean(skip)
|
||||||
instruction.set_c_to_boolean(skip);
|
.build()
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_list(to_register: u8, start_register: u8) -> Instruction {
|
pub fn load_list(to_register: u16, start_register: u16) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::LoadList as u32);
|
Instruction::builder(Operation::LoadList)
|
||||||
|
.set_a(to_register)
|
||||||
instruction.set_a(to_register);
|
.set_b(start_register)
|
||||||
instruction.set_b(start_register);
|
.build()
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_self(to_register: u8) -> Instruction {
|
pub fn load_self(to_register: u16) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::LoadSelf as u32);
|
Instruction::builder(Operation::LoadSelf)
|
||||||
|
.set_a(to_register)
|
||||||
instruction.set_a(to_register);
|
.build()
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn define_local(to_register: u8, local_index: u8, is_mutable: bool) -> Instruction {
|
pub fn define_local(to_register: u16, local_index: u16, is_mutable: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::DefineLocal as u32);
|
Instruction::builder(Operation::DefineLocal)
|
||||||
|
.set_a(to_register as u16)
|
||||||
instruction.set_a(to_register);
|
.set_b(local_index as u16)
|
||||||
instruction.set_b(local_index);
|
.set_c_to_boolean(is_mutable)
|
||||||
instruction.set_c(if is_mutable { 1 } else { 0 });
|
.build()
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local(to_register: u8, local_index: u8) -> Instruction {
|
pub fn get_local(to_register: u16, local_index: u16) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::GetLocal as u32);
|
Instruction::builder(Operation::GetLocal)
|
||||||
|
.set_a(to_register)
|
||||||
instruction.set_a(to_register);
|
.set_b(local_index)
|
||||||
instruction.set_b(local_index);
|
.build()
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_local(from_register: u8, local_index: u8) -> Instruction {
|
pub fn set_local(from_register: u16, local_index: u16) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::SetLocal as u32);
|
Instruction::builder(Operation::SetLocal)
|
||||||
|
.set_a(from_register)
|
||||||
instruction.set_a(from_register);
|
.set_b(local_index)
|
||||||
instruction.set_b(local_index);
|
.build()
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
// pub fn add(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Add as u32);
|
// Instruction::builder(Operation::Add)
|
||||||
|
// .set_a(to_register)
|
||||||
|
// .set_b(left_index)
|
||||||
|
// .set_c(right_index)
|
||||||
|
// .build()
|
||||||
|
// }
|
||||||
|
|
||||||
instruction.set_a(to_register);
|
// pub fn subtract(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||||
instruction.set_b(left_index);
|
// let mut instruction = Instruction(Operation::Subtract as u32);
|
||||||
instruction.set_c(right_index);
|
|
||||||
|
|
||||||
instruction
|
// instruction.set_a(to_register);
|
||||||
|
// instruction.set_b(left_index);
|
||||||
|
// instruction.set_c(right_index);
|
||||||
|
|
||||||
|
// instruction
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn multiply(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||||
|
// let mut instruction = Instruction(Operation::Multiply as u32);
|
||||||
|
|
||||||
|
// instruction.set_a(to_register);
|
||||||
|
// instruction.set_b(left_index);
|
||||||
|
// instruction.set_c(right_index);
|
||||||
|
|
||||||
|
// instruction
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn divide(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||||
|
// let mut instruction = Instruction(Operation::Divide as u32);
|
||||||
|
|
||||||
|
// instruction.set_a(to_register);
|
||||||
|
// instruction.set_b(left_index);
|
||||||
|
// instruction.set_c(right_index);
|
||||||
|
|
||||||
|
// instruction
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn modulo(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||||
|
// let mut instruction = Instruction(Operation::Modulo as u32);
|
||||||
|
|
||||||
|
// instruction.set_a(to_register);
|
||||||
|
// instruction.set_b(left_index);
|
||||||
|
// instruction.set_c(right_index);
|
||||||
|
|
||||||
|
// instruction
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn test(test_register: u16, test_value: bool) -> Instruction {
|
||||||
|
// Instruction::builder(Operation::Test)
|
||||||
|
// .set_b(test_register)
|
||||||
|
// .set_c_to_boolean(test_value)
|
||||||
|
// .build()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn test_set(to_register: u16, argument_index: u16, test_value: bool) -> Instruction {
|
||||||
|
// Instruction::builder(Operation::TestSet)
|
||||||
|
// .set_a(to_register)
|
||||||
|
// .set_b(argument_index)
|
||||||
|
// .set_c_to_boolean(test_value)
|
||||||
|
// .build()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn equal(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction {
|
||||||
|
// let mut instruction = Instruction(Operation::Equal as u32);
|
||||||
|
|
||||||
|
// instruction.set_a_to_boolean(comparison_boolean);
|
||||||
|
// instruction.set_b(left_index);
|
||||||
|
// instruction.set_c(right_index);
|
||||||
|
|
||||||
|
// instruction
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn less(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction {
|
||||||
|
// let mut instruction = Instruction(Operation::Less as u32);
|
||||||
|
|
||||||
|
// instruction.set_a_to_boolean(comparison_boolean);
|
||||||
|
// instruction.set_b(left_index);
|
||||||
|
// instruction.set_c(right_index);
|
||||||
|
|
||||||
|
// instruction
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn less_equal(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction {
|
||||||
|
// let mut instruction = Instruction(Operation::LessEqual as u32);
|
||||||
|
|
||||||
|
// instruction.set_a_to_boolean(comparison_boolean);
|
||||||
|
// instruction.set_b(left_index);
|
||||||
|
// instruction.set_c(right_index);
|
||||||
|
|
||||||
|
// instruction
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn negate(to_register: u16, from_index: u16) -> Instruction {
|
||||||
|
// let mut instruction = Instruction(Operation::Negate as u32);
|
||||||
|
|
||||||
|
// instruction.set_a(to_register);
|
||||||
|
// instruction.set_b(from_index);
|
||||||
|
|
||||||
|
// instruction
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn not(to_register: u16, from_index: u16) -> Instruction {
|
||||||
|
// let mut instruction = Instruction(Operation::Not as u32);
|
||||||
|
|
||||||
|
// instruction.set_a(to_register);
|
||||||
|
// instruction.set_b(from_index);
|
||||||
|
|
||||||
|
// instruction
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn jump(jump_offset: u16, is_positive: bool) -> Instruction {
|
||||||
|
Instruction::builder(Operation::Jump)
|
||||||
|
.set_b(jump_offset)
|
||||||
|
.set_c_to_boolean(is_positive)
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subtract(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
pub fn call(to_register: u16, function_register: u16, argument_count: u16) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Subtract as u32);
|
Instruction::builder(Operation::Call)
|
||||||
|
.set_a(to_register)
|
||||||
instruction.set_a(to_register);
|
.set_b(function_register)
|
||||||
instruction.set_b(left_index);
|
.set_c(argument_count)
|
||||||
instruction.set_c(right_index);
|
.build()
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn multiply(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
|
||||||
let mut instruction = Instruction(Operation::Multiply as u32);
|
|
||||||
|
|
||||||
instruction.set_a(to_register);
|
|
||||||
instruction.set_b(left_index);
|
|
||||||
instruction.set_c(right_index);
|
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn divide(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
|
||||||
let mut instruction = Instruction(Operation::Divide as u32);
|
|
||||||
|
|
||||||
instruction.set_a(to_register);
|
|
||||||
instruction.set_b(left_index);
|
|
||||||
instruction.set_c(right_index);
|
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn modulo(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
|
||||||
let mut instruction = Instruction(Operation::Modulo as u32);
|
|
||||||
|
|
||||||
instruction.set_a(to_register);
|
|
||||||
instruction.set_b(left_index);
|
|
||||||
instruction.set_c(right_index);
|
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test(test_register: u8, test_value: bool) -> Instruction {
|
|
||||||
let mut instruction = Instruction(Operation::Test as u32);
|
|
||||||
|
|
||||||
instruction.set_b(test_register);
|
|
||||||
instruction.set_c_to_boolean(test_value);
|
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_set(to_register: u8, argument_index: u8, test_value: bool) -> Instruction {
|
|
||||||
let mut instruction = Instruction(Operation::TestSet as u32);
|
|
||||||
|
|
||||||
instruction.set_a(to_register);
|
|
||||||
instruction.set_b(argument_index);
|
|
||||||
instruction.set_c_to_boolean(test_value);
|
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
|
||||||
let mut instruction = Instruction(Operation::Equal as u32);
|
|
||||||
|
|
||||||
instruction.set_a_to_boolean(comparison_boolean);
|
|
||||||
instruction.set_b(left_index);
|
|
||||||
instruction.set_c(right_index);
|
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
|
||||||
let mut instruction = Instruction(Operation::Less as u32);
|
|
||||||
|
|
||||||
instruction.set_a_to_boolean(comparison_boolean);
|
|
||||||
instruction.set_b(left_index);
|
|
||||||
instruction.set_c(right_index);
|
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less_equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
|
||||||
let mut instruction = Instruction(Operation::LessEqual as u32);
|
|
||||||
|
|
||||||
instruction.set_a_to_boolean(comparison_boolean);
|
|
||||||
instruction.set_b(left_index);
|
|
||||||
instruction.set_c(right_index);
|
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn negate(to_register: u8, from_index: u8) -> Instruction {
|
|
||||||
let mut instruction = Instruction(Operation::Negate as u32);
|
|
||||||
|
|
||||||
instruction.set_a(to_register);
|
|
||||||
instruction.set_b(from_index);
|
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn not(to_register: u8, from_index: u8) -> Instruction {
|
|
||||||
let mut instruction = Instruction(Operation::Not as u32);
|
|
||||||
|
|
||||||
instruction.set_a(to_register);
|
|
||||||
instruction.set_b(from_index);
|
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn jump(jump_offset: u8, is_positive: bool) -> Instruction {
|
|
||||||
let mut instruction = Instruction(Operation::Jump as u32);
|
|
||||||
|
|
||||||
instruction.set_b(jump_offset);
|
|
||||||
instruction.set_c_to_boolean(is_positive);
|
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call(to_register: u8, function_register: u8, argument_count: u8) -> Instruction {
|
|
||||||
let mut instruction = Instruction(Operation::Call as u32);
|
|
||||||
|
|
||||||
instruction.set_a(to_register);
|
|
||||||
instruction.set_b(function_register);
|
|
||||||
instruction.set_c(argument_count);
|
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_native(
|
pub fn call_native(
|
||||||
to_register: u8,
|
to_register: u16,
|
||||||
native_fn: NativeFunction,
|
native_fn: NativeFunction,
|
||||||
argument_count: u8,
|
argument_count: u16,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::CallNative as u32);
|
Instruction::builder(Operation::CallNative)
|
||||||
let native_fn_byte = native_fn as u8;
|
.set_a(to_register)
|
||||||
|
.set_b(native_fn as u16)
|
||||||
instruction.set_a(to_register);
|
.set_c(argument_count)
|
||||||
instruction.set_b(native_fn_byte);
|
.build()
|
||||||
instruction.set_c(argument_count);
|
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#return(should_return_value: bool) -> Instruction {
|
pub fn r#return(should_return_value: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Return as u32);
|
Instruction::builder(Operation::Return)
|
||||||
|
.set_b_to_boolean(should_return_value)
|
||||||
instruction.set_b_to_boolean(should_return_value);
|
.build()
|
||||||
|
|
||||||
instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn operation(&self) -> Operation {
|
|
||||||
Operation::from((self.0 & 0b0000_0000_0011_1111) as u8)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_operation(&mut self, operation: Operation) {
|
|
||||||
self.0 |= u8::from(operation) as u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn data(&self) -> (Operation, u8, u8, u8, bool, bool) {
|
|
||||||
(
|
|
||||||
self.operation(),
|
|
||||||
self.a(),
|
|
||||||
self.b(),
|
|
||||||
self.c(),
|
|
||||||
self.b_is_constant(),
|
|
||||||
self.c_is_constant(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn a(&self) -> u8 {
|
|
||||||
(self.0 >> 24) as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn b(&self) -> u8 {
|
|
||||||
(self.0 >> 16) as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn c(&self) -> u8 {
|
|
||||||
(self.0 >> 8) as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn a_as_boolean(&self) -> bool {
|
|
||||||
self.a() != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn b_as_boolean(&self) -> bool {
|
|
||||||
self.b() != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn c_as_boolean(&self) -> bool {
|
|
||||||
self.c() != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_a_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
|
||||||
self.set_a(if boolean { 1 } else { 0 });
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_b_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
|
||||||
self.set_b(if boolean { 1 } else { 0 });
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_c_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
|
||||||
self.set_c(if boolean { 1 } else { 0 });
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_a(&mut self, to_register: u8) {
|
|
||||||
self.0 |= (to_register as u32) << 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_b(&mut self, argument: u8) {
|
|
||||||
self.0 |= (argument as u32) << 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_c(&mut self, argument: u8) {
|
|
||||||
self.0 |= (argument as u32) << 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn b_is_constant(&self) -> bool {
|
|
||||||
self.0 & 0b1000_0000 != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn c_is_constant(&self) -> bool {
|
|
||||||
self.0 & 0b0100_0000 != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_b_is_constant(&mut self) -> &mut Self {
|
|
||||||
self.0 |= 0b1000_0000;
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_c_is_constant(&mut self) -> &mut Self {
|
|
||||||
self.0 |= 0b0100_0000;
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn returns_or_panics(&self) -> bool {
|
|
||||||
match self.operation() {
|
|
||||||
Operation::Return => true,
|
|
||||||
Operation::CallNative => {
|
|
||||||
let native_function = NativeFunction::from(self.b());
|
|
||||||
|
|
||||||
matches!(native_function, NativeFunction::Panic)
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn yields_value(&self) -> bool {
|
|
||||||
matches!(
|
|
||||||
self.operation(),
|
|
||||||
Operation::Add
|
|
||||||
| Operation::Call
|
|
||||||
| Operation::CallNative
|
|
||||||
| Operation::Divide
|
|
||||||
| Operation::GetLocal
|
|
||||||
| Operation::LoadBoolean
|
|
||||||
| Operation::LoadConstant
|
|
||||||
| Operation::LoadList
|
|
||||||
| Operation::LoadSelf
|
|
||||||
| Operation::Modulo
|
|
||||||
| Operation::Multiply
|
|
||||||
| Operation::Negate
|
|
||||||
| Operation::Not
|
|
||||||
| Operation::Subtract
|
|
||||||
| Operation::TestSet
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disassembly_info(&self, chunk: &Chunk) -> String {
|
pub fn disassembly_info(&self, chunk: &Chunk) -> String {
|
||||||
|
let InstructionBuilder {
|
||||||
|
operation,
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
b_is_constant,
|
||||||
|
c_is_constant,
|
||||||
|
a_is_local,
|
||||||
|
b_is_local,
|
||||||
|
c_is_local,
|
||||||
|
} = InstructionBuilder::from(self);
|
||||||
let format_arguments = || {
|
let format_arguments = || {
|
||||||
let first_argument = if self.b_is_constant() {
|
let first_argument = if b_is_constant {
|
||||||
format!("C{}", self.b())
|
format!("C{}", b)
|
||||||
} else {
|
} else {
|
||||||
format!("R{}", self.b())
|
format!("R{}", b)
|
||||||
};
|
};
|
||||||
let second_argument = if self.c_is_constant() {
|
let second_argument = if c_is_constant {
|
||||||
format!("C{}", self.c())
|
format!("C{}", c)
|
||||||
} else {
|
} else {
|
||||||
format!("R{}", self.c())
|
format!("R{}", c)
|
||||||
};
|
};
|
||||||
|
|
||||||
(first_argument, second_argument)
|
(first_argument, second_argument)
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.operation() {
|
match operation {
|
||||||
Operation::Move => format!("R{} = R{}", self.a(), self.b()),
|
Operation::Move => format!("R{a} = R{b}"),
|
||||||
Operation::Close => {
|
Operation::Close => {
|
||||||
let from_register = self.b();
|
format!("R{b}..R{c}")
|
||||||
let to_register = self.c().saturating_sub(1);
|
|
||||||
|
|
||||||
format!("R{from_register}..=R{to_register}")
|
|
||||||
}
|
}
|
||||||
Operation::LoadBoolean => {
|
Operation::LoadBoolean => {
|
||||||
let to_register = self.a();
|
let boolean = b != 0;
|
||||||
let boolean = self.b_as_boolean();
|
let jump = c != 0;
|
||||||
let jump = self.c_as_boolean();
|
|
||||||
|
|
||||||
if jump {
|
if jump {
|
||||||
format!("R{to_register} = {boolean} && JUMP +1")
|
format!("R{a} = {boolean} && JUMP +1")
|
||||||
} else {
|
} else {
|
||||||
format!("R{to_register} = {boolean}")
|
format!("R{a} {boolean}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
let register_index = self.a();
|
let jump = c != 0;
|
||||||
let constant_index = self.b();
|
|
||||||
let jump = self.c_as_boolean();
|
|
||||||
|
|
||||||
if jump {
|
if jump {
|
||||||
format!("R{register_index} = C{constant_index} JUMP +1")
|
format!("R{a} = C{b} JUMP +1")
|
||||||
} else {
|
} else {
|
||||||
format!("R{register_index} = C{constant_index}")
|
format!("R{a} = C{b}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::LoadList => {
|
Operation::LoadList => {
|
||||||
let to_register = self.a();
|
format!("R{a} = [R{b}..=R{c}]",)
|
||||||
let first_index = self.b();
|
|
||||||
let last_index = self.c();
|
|
||||||
|
|
||||||
format!("R{to_register} = [R{first_index}..=R{last_index}]",)
|
|
||||||
}
|
}
|
||||||
Operation::LoadSelf => {
|
Operation::LoadSelf => {
|
||||||
let to_register = self.a();
|
|
||||||
let name = chunk
|
let name = chunk
|
||||||
.name()
|
.name()
|
||||||
.map(|idenifier| idenifier.as_str())
|
.map(|idenifier| idenifier.as_str())
|
||||||
.unwrap_or("self");
|
.unwrap_or("self");
|
||||||
|
|
||||||
format!("R{to_register} = {name}")
|
format!("R{a} = {name}")
|
||||||
}
|
}
|
||||||
Operation::DefineLocal => {
|
Operation::DefineLocal => {
|
||||||
let to_register = self.a();
|
format!("L{b} = R{a}")
|
||||||
let local_index = self.b();
|
|
||||||
|
|
||||||
format!("L{local_index} = R{to_register}")
|
|
||||||
}
|
}
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
let local_index = self.b();
|
format!("R{a} = L{b}")
|
||||||
|
|
||||||
format!("R{} = L{}", self.a(), local_index)
|
|
||||||
}
|
}
|
||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let local_index = self.b();
|
format!("L{b} = R{a}")
|
||||||
let register = self.a();
|
|
||||||
|
|
||||||
format!("L{local_index} = R{register}")
|
|
||||||
}
|
}
|
||||||
Operation::Add => {
|
Operation::Add => {
|
||||||
let to_register = self.a();
|
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
format!("R{to_register} = {first_argument} + {second_argument}",)
|
format!("R{a} = {first_argument} + {second_argument}",)
|
||||||
}
|
}
|
||||||
Operation::Subtract => {
|
Operation::Subtract => {
|
||||||
let to_register = self.a();
|
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
format!("R{to_register} = {first_argument} - {second_argument}",)
|
format!("R{a} = {first_argument} - {second_argument}",)
|
||||||
}
|
}
|
||||||
Operation::Multiply => {
|
Operation::Multiply => {
|
||||||
let to_register = self.a();
|
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
format!("R{to_register} = {first_argument} * {second_argument}",)
|
format!("R{a} = {first_argument} * {second_argument}",)
|
||||||
}
|
}
|
||||||
Operation::Divide => {
|
Operation::Divide => {
|
||||||
let to_register = self.a();
|
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
format!("R{to_register} = {first_argument} / {second_argument}",)
|
format!("R{a} = {first_argument} / {second_argument}",)
|
||||||
}
|
}
|
||||||
Operation::Modulo => {
|
Operation::Modulo => {
|
||||||
let to_register = self.a();
|
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
format!("R{to_register} = {first_argument} % {second_argument}",)
|
format!("R{a} = {first_argument} % {second_argument}",)
|
||||||
}
|
}
|
||||||
Operation::Test => {
|
Operation::Test => {
|
||||||
let test_register = if self.b_is_constant() {
|
let test_register = if b_is_constant {
|
||||||
format!("C{}", self.b())
|
format!("C{b}")
|
||||||
} else {
|
} else {
|
||||||
format!("R{}", self.b())
|
format!("R{b}")
|
||||||
};
|
};
|
||||||
let test_value = self.c_as_boolean();
|
let test_value = c != 0;
|
||||||
let bang = if test_value { "" } else { "!" };
|
let bang = if test_value { "" } else { "!" };
|
||||||
|
|
||||||
format!("if {bang}{test_register} {{ JUMP +1 }}",)
|
format!("if {bang}{test_register} {{ JUMP +1 }}",)
|
||||||
}
|
}
|
||||||
Operation::TestSet => {
|
Operation::TestSet => {
|
||||||
let to_register = self.a();
|
let test_register = if b_is_constant {
|
||||||
let test_register = self.b();
|
format!("C{b}")
|
||||||
let test_value = self.c_as_boolean();
|
} else {
|
||||||
|
format!("R{b}")
|
||||||
|
};
|
||||||
|
let test_value = c != 0;
|
||||||
let bang = if test_value { "" } else { "!" };
|
let bang = if test_value { "" } else { "!" };
|
||||||
|
|
||||||
format!("if {bang}R{test_register} {{ JUMP +1 }} else {{ R{to_register} = R{test_register} }}")
|
format!(
|
||||||
|
"if {bang}R{test_register} {{ JUMP +1 }} else {{ R{a} = R{test_register} }}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Operation::Equal => {
|
Operation::Equal => {
|
||||||
let comparison_symbol = if self.a_as_boolean() { "==" } else { "!=" };
|
let comparison_symbol = if a != 0 { "==" } else { "!=" };
|
||||||
|
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}")
|
format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}")
|
||||||
}
|
}
|
||||||
Operation::Less => {
|
Operation::Less => {
|
||||||
let comparison_symbol = if self.a_as_boolean() { "<" } else { ">=" };
|
let comparison_symbol = if a != 0 { "<" } else { ">=" };
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}")
|
format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}")
|
||||||
}
|
}
|
||||||
Operation::LessEqual => {
|
Operation::LessEqual => {
|
||||||
let comparison_symbol = if self.a_as_boolean() { "<=" } else { ">" };
|
let comparison_symbol = if a != 0 { "<=" } else { ">" };
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}")
|
format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}")
|
||||||
}
|
}
|
||||||
Operation::Negate => {
|
Operation::Negate => {
|
||||||
let to_register = self.a();
|
let argument = if b_is_constant {
|
||||||
let argument = if self.b_is_constant() {
|
format!("C{b}")
|
||||||
format!("C{}", self.b())
|
|
||||||
} else {
|
} else {
|
||||||
format!("R{}", self.b())
|
format!("R{b}")
|
||||||
};
|
};
|
||||||
|
|
||||||
format!("R{to_register} = -{argument}")
|
format!("R{a} = -{argument}")
|
||||||
}
|
}
|
||||||
Operation::Not => {
|
Operation::Not => {
|
||||||
let to_register = self.a();
|
let argument = if b_is_constant {
|
||||||
let argument = if self.b_is_constant() {
|
format!("C{b}")
|
||||||
format!("C{}", self.b())
|
|
||||||
} else {
|
} else {
|
||||||
format!("R{}", self.b())
|
format!("R{b}")
|
||||||
};
|
};
|
||||||
|
|
||||||
format!("R{to_register} = !{argument}")
|
format!("R{a} = !{argument}")
|
||||||
}
|
}
|
||||||
Operation::Jump => {
|
Operation::Jump => {
|
||||||
let jump_distance = self.b();
|
let is_positive = c != 0;
|
||||||
let is_positive = self.c_as_boolean();
|
|
||||||
|
|
||||||
if is_positive {
|
if is_positive {
|
||||||
format!("JUMP +{jump_distance}")
|
format!("JUMP +{b}")
|
||||||
} else {
|
} else {
|
||||||
format!("JUMP -{jump_distance}")
|
format!("JUMP -{b}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Call => {
|
Operation::Call => {
|
||||||
let to_register = self.a();
|
let argument_count = c;
|
||||||
let function_register = self.b();
|
|
||||||
let argument_count = self.c();
|
|
||||||
|
|
||||||
let mut output = format!("R{to_register} = R{function_register}(");
|
let mut output = format!("R{a} = R{b}(");
|
||||||
|
|
||||||
if argument_count != 0 {
|
if argument_count != 0 {
|
||||||
let first_argument = function_register + 1;
|
let first_argument = b + 1;
|
||||||
|
|
||||||
for (index, register) in
|
for (index, register) in
|
||||||
(first_argument..first_argument + argument_count).enumerate()
|
(first_argument..first_argument + argument_count).enumerate()
|
||||||
@ -598,18 +516,17 @@ impl Instruction {
|
|||||||
output
|
output
|
||||||
}
|
}
|
||||||
Operation::CallNative => {
|
Operation::CallNative => {
|
||||||
let to_register = self.a();
|
let native_function = NativeFunction::from(b);
|
||||||
let native_function = NativeFunction::from(self.b());
|
let argument_count = c;
|
||||||
let argument_count = self.c();
|
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
let native_function_name = native_function.as_str();
|
let native_function_name = native_function.as_str();
|
||||||
|
|
||||||
output.push_str(&format!("R{} = {}(", to_register, native_function_name));
|
output.push_str(&format!("R{a} = {}(", native_function_name));
|
||||||
|
|
||||||
if argument_count != 0 {
|
if argument_count != 0 {
|
||||||
let first_argument = to_register.saturating_sub(argument_count);
|
let first_argument = a.saturating_sub(argument_count);
|
||||||
|
|
||||||
for register in first_argument..to_register {
|
for register in first_argument..a {
|
||||||
if register != first_argument {
|
if register != first_argument {
|
||||||
output.push_str(", ");
|
output.push_str(", ");
|
||||||
}
|
}
|
||||||
@ -623,10 +540,10 @@ impl Instruction {
|
|||||||
output
|
output
|
||||||
}
|
}
|
||||||
Operation::Return => {
|
Operation::Return => {
|
||||||
let should_return_value = self.b_as_boolean();
|
let should_return_value = b != 0;
|
||||||
|
|
||||||
if should_return_value {
|
if should_return_value {
|
||||||
"->".to_string()
|
"RETURN".to_string()
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
}
|
}
|
||||||
@ -635,12 +552,6 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Instruction> for u32 {
|
|
||||||
fn from(instruction: &Instruction) -> Self {
|
|
||||||
instruction.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -719,7 +630,7 @@ mod tests {
|
|||||||
assert_eq!(instruction.operation(), Operation::DefineLocal);
|
assert_eq!(instruction.operation(), Operation::DefineLocal);
|
||||||
assert_eq!(instruction.a(), 4);
|
assert_eq!(instruction.a(), 4);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.c(), true as u8);
|
assert_eq!(instruction.c(), true as u16);
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,8 +74,8 @@ macro_rules! define_native_function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u8> for NativeFunction {
|
impl From<u16> for NativeFunction {
|
||||||
fn from(byte: u8) -> Self {
|
fn from(byte: u16) -> Self {
|
||||||
match byte {
|
match byte {
|
||||||
$(
|
$(
|
||||||
$byte => NativeFunction::$name,
|
$byte => NativeFunction::$name,
|
||||||
@ -126,7 +126,7 @@ define_native_function! {
|
|||||||
// (AssertNotEqual, 2_u8, "assert_not_equal", false),
|
// (AssertNotEqual, 2_u8, "assert_not_equal", false),
|
||||||
(
|
(
|
||||||
Panic,
|
Panic,
|
||||||
3_u8,
|
3,
|
||||||
"panic",
|
"panic",
|
||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
@ -143,7 +143,7 @@ define_native_function! {
|
|||||||
// (ToInteger, 7_u8, "to_integer", true),
|
// (ToInteger, 7_u8, "to_integer", true),
|
||||||
(
|
(
|
||||||
ToString,
|
ToString,
|
||||||
8_u8,
|
8,
|
||||||
"to_string",
|
"to_string",
|
||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
@ -204,7 +204,7 @@ define_native_function! {
|
|||||||
// (ReadFile, 49_u8, "read_file", true),
|
// (ReadFile, 49_u8, "read_file", true),
|
||||||
(
|
(
|
||||||
ReadLine,
|
ReadLine,
|
||||||
50_u8,
|
50,
|
||||||
"read_line",
|
"read_line",
|
||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
@ -220,7 +220,7 @@ define_native_function! {
|
|||||||
// (PrependFile, 54_u8, "prepend_file", false),
|
// (PrependFile, 54_u8, "prepend_file", false),
|
||||||
(
|
(
|
||||||
Write,
|
Write,
|
||||||
55_u8,
|
55,
|
||||||
"write",
|
"write",
|
||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
@ -232,7 +232,7 @@ define_native_function! {
|
|||||||
// (WriteFile, 56_u8, "write_file", false),
|
// (WriteFile, 56_u8, "write_file", false),
|
||||||
(
|
(
|
||||||
WriteLine,
|
WriteLine,
|
||||||
57_u8,
|
57,
|
||||||
"write_line",
|
"write_line",
|
||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
//! Virtual machine and errors
|
//! Virtual machine and errors
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::HashMap,
|
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
io,
|
io,
|
||||||
};
|
};
|
||||||
@ -34,7 +33,7 @@ pub struct Vm<'a> {
|
|||||||
chunk: &'a Chunk,
|
chunk: &'a Chunk,
|
||||||
stack: Vec<Register>,
|
stack: Vec<Register>,
|
||||||
parent: Option<&'a Vm<'a>>,
|
parent: Option<&'a Vm<'a>>,
|
||||||
local_definitions: HashMap<u8, u8>,
|
local_definitions: Vec<Option<u8>>,
|
||||||
|
|
||||||
ip: usize,
|
ip: usize,
|
||||||
last_assigned_register: Option<u8>,
|
last_assigned_register: Option<u8>,
|
||||||
@ -49,7 +48,7 @@ impl<'a> Vm<'a> {
|
|||||||
chunk,
|
chunk,
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
parent,
|
parent,
|
||||||
local_definitions: HashMap::new(),
|
local_definitions: vec![None; chunk.locals().len()],
|
||||||
ip: 0,
|
ip: 0,
|
||||||
last_assigned_register: None,
|
last_assigned_register: None,
|
||||||
current_position: Span(0, 0),
|
current_position: Span(0, 0),
|
||||||
@ -158,12 +157,12 @@ impl<'a> Vm<'a> {
|
|||||||
let from_register = instruction.a();
|
let from_register = instruction.a();
|
||||||
let to_local = instruction.b();
|
let to_local = instruction.b();
|
||||||
|
|
||||||
self.define_local(to_local, from_register)?;
|
self.local_definitions[to_local as usize] = Some(from_register);
|
||||||
}
|
}
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let local_index = instruction.b();
|
let local_index = instruction.b();
|
||||||
let local_register = self.local_definitions.get(&local_index).copied().ok_or(
|
let local_register = self.local_definitions[local_index as usize].ok_or(
|
||||||
VmError::UndefinedLocal {
|
VmError::UndefinedLocal {
|
||||||
local_index,
|
local_index,
|
||||||
position: self.current_position,
|
position: self.current_position,
|
||||||
@ -176,16 +175,8 @@ impl<'a> Vm<'a> {
|
|||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let from_register = instruction.a();
|
let from_register = instruction.a();
|
||||||
let to_local = instruction.b();
|
let to_local = instruction.b();
|
||||||
let local_register = self.local_definitions.get(&to_local).copied().ok_or(
|
|
||||||
VmError::UndefinedLocal {
|
|
||||||
local_index: to_local,
|
|
||||||
position: self.current_position,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
let register = Register::Pointer(Pointer::Stack(from_register));
|
|
||||||
|
|
||||||
self.define_local(to_local, from_register)?;
|
self.local_definitions[to_local as usize] = Some(from_register);
|
||||||
self.set_register(local_register, register)?;
|
|
||||||
}
|
}
|
||||||
Operation::Add => {
|
Operation::Add => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
@ -640,14 +631,6 @@ impl<'a> Vm<'a> {
|
|||||||
|
|
||||||
Ok(instruction)
|
Ok(instruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define_local(&mut self, local_index: u8, register_index: u8) -> Result<(), VmError> {
|
|
||||||
log::debug!("Define local L{}", local_index);
|
|
||||||
|
|
||||||
self.local_definitions.insert(local_index, register_index);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -23,8 +23,8 @@ fn function() {
|
|||||||
],
|
],
|
||||||
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
|
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
|
||||||
vec![
|
vec![
|
||||||
Local::new(0, Type::Integer, false, Scope::default(), 0),
|
Local::new(0, Type::Integer, false, Scope::default()),
|
||||||
Local::new(1, Type::Integer, false, Scope::default(), 1)
|
Local::new(1, Type::Integer, false, Scope::default())
|
||||||
]
|
]
|
||||||
))))
|
))))
|
||||||
);
|
);
|
||||||
@ -64,8 +64,8 @@ fn function_call() {
|
|||||||
],
|
],
|
||||||
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
|
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
|
||||||
vec![
|
vec![
|
||||||
Local::new(0, Type::Integer, false, Scope::default(), 0),
|
Local::new(0, Type::Integer, false, Scope::default()),
|
||||||
Local::new(1, Type::Integer, false, Scope::default(), 1)
|
Local::new(1, Type::Integer, false, Scope::default())
|
||||||
]
|
]
|
||||||
)),
|
)),
|
||||||
ConcreteValue::Integer(1),
|
ConcreteValue::Integer(1),
|
||||||
@ -93,6 +93,7 @@ fn function_declaration() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(0, 40)),
|
(Instruction::load_constant(0, 0, false), Span(0, 40)),
|
||||||
|
(Instruction::define_local(0, 0, false), Span(3, 6)),
|
||||||
(Instruction::r#return(false), Span(40, 40))
|
(Instruction::r#return(false), Span(40, 40))
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
@ -109,8 +110,8 @@ fn function_declaration() {
|
|||||||
],
|
],
|
||||||
vec![ConcreteValue::string("a"), ConcreteValue::string("b")],
|
vec![ConcreteValue::string("a"), ConcreteValue::string("b")],
|
||||||
vec![
|
vec![
|
||||||
Local::new(0, Type::Integer, false, Scope::default(), 0),
|
Local::new(0, Type::Integer, false, Scope::default()),
|
||||||
Local::new(1, Type::Integer, false, Scope::default(), 1)
|
Local::new(1, Type::Integer, false, Scope::default())
|
||||||
]
|
]
|
||||||
)),
|
)),
|
||||||
ConcreteValue::string("add"),
|
ConcreteValue::string("add"),
|
||||||
@ -124,7 +125,6 @@ fn function_declaration() {
|
|||||||
}),
|
}),
|
||||||
false,
|
false,
|
||||||
Scope::default(),
|
Scope::default(),
|
||||||
0
|
|
||||||
),],
|
),],
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
@ -71,7 +71,9 @@ fn variable_and() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_boolean(0, true, false), Span(8, 12)),
|
(Instruction::load_boolean(0, true, false), Span(8, 12)),
|
||||||
|
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
||||||
(Instruction::load_boolean(1, false, false), Span(22, 27)),
|
(Instruction::load_boolean(1, false, false), Span(22, 27)),
|
||||||
|
(Instruction::define_local(1, 1, false), Span(18, 19)),
|
||||||
(Instruction::get_local(2, 0), Span(29, 30)),
|
(Instruction::get_local(2, 0), Span(29, 30)),
|
||||||
(Instruction::test(2, true), Span(31, 33)),
|
(Instruction::test(2, true), Span(31, 33)),
|
||||||
(Instruction::jump(1, true), Span(31, 33)),
|
(Instruction::jump(1, true), Span(31, 33)),
|
||||||
@ -80,8 +82,8 @@ fn variable_and() {
|
|||||||
],
|
],
|
||||||
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
|
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
|
||||||
vec![
|
vec![
|
||||||
Local::new(0, Type::Boolean, false, Scope::default(), 0),
|
Local::new(0, Type::Boolean, false, Scope::default()),
|
||||||
Local::new(1, Type::Boolean, false, Scope::default(), 1),
|
Local::new(1, Type::Boolean, false, Scope::default()),
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
@ -15,6 +15,7 @@ fn r#while() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
||||||
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
(
|
(
|
||||||
*Instruction::less(true, 0, 2).set_c_is_constant(),
|
*Instruction::less(true, 0, 2).set_c_is_constant(),
|
||||||
Span(23, 24)
|
Span(23, 24)
|
||||||
@ -31,7 +32,7 @@ fn r#while() {
|
|||||||
ConcreteValue::Integer(5),
|
ConcreteValue::Integer(5),
|
||||||
ConcreteValue::Integer(1),
|
ConcreteValue::Integer(1),
|
||||||
],
|
],
|
||||||
vec![Local::new(1, Type::Integer, true, Scope::default(), 0)]
|
vec![Local::new(1, Type::Integer, true, Scope::default())]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ fn add_assign() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
||||||
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
(*Instruction::add(0, 0, 2).set_c_is_constant(), Span(17, 19)),
|
(*Instruction::add(0, 0, 2).set_c_is_constant(), Span(17, 19)),
|
||||||
(Instruction::get_local(1, 0), Span(23, 24)),
|
(Instruction::get_local(1, 0), Span(23, 24)),
|
||||||
(Instruction::r#return(true), Span(24, 24))
|
(Instruction::r#return(true), Span(24, 24))
|
||||||
@ -54,7 +55,7 @@ fn add_assign() {
|
|||||||
ConcreteValue::string("a"),
|
ConcreteValue::string("a"),
|
||||||
ConcreteValue::Integer(2)
|
ConcreteValue::Integer(2)
|
||||||
],
|
],
|
||||||
vec![Local::new(1, Type::Integer, true, Scope::default(), 0)]
|
vec![Local::new(1, Type::Integer, true, Scope::default())]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -138,6 +139,7 @@ fn divide_assign() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
||||||
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
(
|
(
|
||||||
*Instruction::divide(0, 0, 0).set_c_is_constant(),
|
*Instruction::divide(0, 0, 0).set_c_is_constant(),
|
||||||
Span(17, 19)
|
Span(17, 19)
|
||||||
@ -146,7 +148,7 @@ fn divide_assign() {
|
|||||||
(Instruction::r#return(true), Span(24, 24))
|
(Instruction::r#return(true), Span(24, 24))
|
||||||
],
|
],
|
||||||
vec![ConcreteValue::Integer(2), ConcreteValue::string("a")],
|
vec![ConcreteValue::Integer(2), ConcreteValue::string("a")],
|
||||||
vec![Local::new(1, Type::Integer, true, Scope::default(), 0)]
|
vec![Local::new(1, Type::Integer, true, Scope::default())]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -261,6 +263,7 @@ fn multiply_assign() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
||||||
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
(
|
(
|
||||||
*Instruction::multiply(0, 0, 2).set_c_is_constant(),
|
*Instruction::multiply(0, 0, 2).set_c_is_constant(),
|
||||||
Span(17, 19)
|
Span(17, 19)
|
||||||
@ -273,7 +276,7 @@ fn multiply_assign() {
|
|||||||
ConcreteValue::string("a"),
|
ConcreteValue::string("a"),
|
||||||
ConcreteValue::Integer(3)
|
ConcreteValue::Integer(3)
|
||||||
],
|
],
|
||||||
vec![Local::new(1, Type::Integer, true, Scope::default(), 0)]
|
vec![Local::new(1, Type::Integer, true, Scope::default())]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -341,6 +344,7 @@ fn subtract_assign() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(12, 14)),
|
(Instruction::load_constant(0, 0, false), Span(12, 14)),
|
||||||
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
(
|
(
|
||||||
*Instruction::subtract(0, 0, 2).set_c_is_constant(),
|
*Instruction::subtract(0, 0, 2).set_c_is_constant(),
|
||||||
Span(18, 20)
|
Span(18, 20)
|
||||||
@ -353,7 +357,7 @@ fn subtract_assign() {
|
|||||||
ConcreteValue::string("x"),
|
ConcreteValue::string("x"),
|
||||||
ConcreteValue::Integer(2)
|
ConcreteValue::Integer(2)
|
||||||
],
|
],
|
||||||
vec![Local::new(1, Type::Integer, true, Scope::default(), 0)]
|
vec![Local::new(1, Type::Integer, true, Scope::default())]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -60,11 +60,11 @@ fn block_scope() {
|
|||||||
ConcreteValue::string("e"),
|
ConcreteValue::string("e"),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Local::new(1, Type::Integer, false, Scope::new(0, 0), 0),
|
Local::new(1, Type::Integer, false, Scope::new(0, 0)),
|
||||||
Local::new(3, Type::Integer, false, Scope::new(1, 1), 0),
|
Local::new(3, Type::Integer, false, Scope::new(1, 1)),
|
||||||
Local::new(5, Type::Integer, false, Scope::new(2, 2), 0),
|
Local::new(5, Type::Integer, false, Scope::new(2, 2)),
|
||||||
Local::new(7, Type::Integer, false, Scope::new(1, 1), 0),
|
Local::new(7, Type::Integer, false, Scope::new(1, 1)),
|
||||||
Local::new(8, Type::Integer, false, Scope::new(0, 0), 0),
|
Local::new(8, Type::Integer, false, Scope::new(0, 0)),
|
||||||
]
|
]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
@ -136,15 +136,15 @@ fn multiple_block_scopes() {
|
|||||||
ConcreteValue::string("e"),
|
ConcreteValue::string("e"),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Local::new(1, Type::Integer, false, Scope::new(0, 0), 0),
|
Local::new(1, Type::Integer, false, Scope::new(0, 0)),
|
||||||
Local::new(3, Type::Integer, false, Scope::new(1, 1), 0),
|
Local::new(3, Type::Integer, false, Scope::new(1, 1)),
|
||||||
Local::new(5, Type::Integer, false, Scope::new(2, 2), 0),
|
Local::new(5, Type::Integer, false, Scope::new(2, 2)),
|
||||||
Local::new(6, Type::Integer, false, Scope::new(1, 1), 0),
|
Local::new(6, Type::Integer, false, Scope::new(1, 1)),
|
||||||
Local::new(7, Type::Integer, false, Scope::new(0, 0), 0),
|
Local::new(7, Type::Integer, false, Scope::new(0, 0)),
|
||||||
Local::new(3, Type::Integer, false, Scope::new(1, 3), 0),
|
Local::new(3, Type::Integer, false, Scope::new(1, 3)),
|
||||||
Local::new(5, Type::Integer, false, Scope::new(2, 4), 0),
|
Local::new(5, Type::Integer, false, Scope::new(2, 4)),
|
||||||
Local::new(6, Type::Integer, false, Scope::new(1, 3), 0),
|
Local::new(6, Type::Integer, false, Scope::new(1, 3)),
|
||||||
Local::new(8, Type::Integer, false, Scope::new(0, 0), 0),
|
Local::new(8, Type::Integer, false, Scope::new(0, 0)),
|
||||||
]
|
]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
@ -15,10 +15,11 @@ fn define_local() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(8, 10)),
|
(Instruction::load_constant(0, 0, false), Span(8, 10)),
|
||||||
|
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
||||||
(Instruction::r#return(false), Span(11, 11))
|
(Instruction::r#return(false), Span(11, 11))
|
||||||
],
|
],
|
||||||
vec![ConcreteValue::Integer(42), ConcreteValue::string("x")],
|
vec![ConcreteValue::Integer(42), ConcreteValue::string("x")],
|
||||||
vec![Local::new(1, Type::Integer, false, Scope::default(), 0)]
|
vec![Local::new(1, Type::Integer, false, Scope::default())]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -57,8 +58,9 @@ fn set_local() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(12, 14)),
|
(Instruction::load_constant(0, 0, false), Span(12, 14)),
|
||||||
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
(Instruction::load_constant(1, 2, false), Span(20, 22)),
|
(Instruction::load_constant(1, 2, false), Span(20, 22)),
|
||||||
(Instruction::set_local(0, 0), Span(16, 17)),
|
(Instruction::set_local(1, 0), Span(16, 17)),
|
||||||
(Instruction::get_local(2, 0), Span(24, 25)),
|
(Instruction::get_local(2, 0), Span(24, 25)),
|
||||||
(Instruction::r#return(true), Span(25, 25)),
|
(Instruction::r#return(true), Span(25, 25)),
|
||||||
],
|
],
|
||||||
@ -67,7 +69,7 @@ fn set_local() {
|
|||||||
ConcreteValue::string("x"),
|
ConcreteValue::string("x"),
|
||||||
ConcreteValue::Integer(42)
|
ConcreteValue::Integer(42)
|
||||||
],
|
],
|
||||||
vec![Local::new(1, Type::Integer, true, Scope::default(), 0)]
|
vec![Local::new(1, Type::Integer, true, Scope::default())]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user