Add LoadBoolean; Refactor; Improve disassembly output
This commit is contained in:
parent
85b95a56aa
commit
915340fbdb
@ -262,7 +262,7 @@ impl Local {
|
|||||||
pub struct ChunkDisassembler<'a> {
|
pub struct ChunkDisassembler<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
chunk: &'a Chunk,
|
chunk: &'a Chunk,
|
||||||
width: usize,
|
width: Option<usize>,
|
||||||
styled: bool,
|
styled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,16 +271,16 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
"",
|
"",
|
||||||
"Instructions",
|
"Instructions",
|
||||||
"------------",
|
"------------",
|
||||||
"OFFSET OPERATION INFO POSITION",
|
"INDEX OPERATION INFO POSITION",
|
||||||
"------ -------------- ------------------------- --------",
|
"----- -------------- ------------------------- --------",
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONSTANT_HEADER: [&'static str; 5] = [
|
const CONSTANT_HEADER: [&'static str; 5] = [
|
||||||
"",
|
"",
|
||||||
"Constants",
|
"Constants",
|
||||||
"---------",
|
"---------",
|
||||||
"INDEX KIND VALUE",
|
"INDEX VALUE ",
|
||||||
"----- ----- -----",
|
"----- ---------",
|
||||||
];
|
];
|
||||||
|
|
||||||
const LOCAL_HEADER: [&'static str; 5] = [
|
const LOCAL_HEADER: [&'static str; 5] = [
|
||||||
@ -291,21 +291,29 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
"----- ---------- ------- ----- --------",
|
"----- ---------- ------- ----- --------",
|
||||||
];
|
];
|
||||||
|
|
||||||
/// 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
|
||||||
/// set to the width of the longest line that the disassembler is guaranteed to produce.
|
/// return the width of the longest line that the disassembler is guaranteed to produce.
|
||||||
const DEFAULT_WIDTH: usize = Self::INSTRUCTION_HEADER[4].len() + 1;
|
pub fn default_width(styled: bool) -> usize {
|
||||||
|
let longest_line = Self::INSTRUCTION_HEADER[4];
|
||||||
|
|
||||||
|
if styled {
|
||||||
|
longest_line.bold().chars().count()
|
||||||
|
} else {
|
||||||
|
longest_line.chars().count()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(name: &'a str, chunk: &'a Chunk) -> Self {
|
pub fn new(name: &'a str, chunk: &'a Chunk) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
chunk,
|
chunk,
|
||||||
width: Self::DEFAULT_WIDTH,
|
width: None,
|
||||||
styled: false,
|
styled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn width(&mut self, width: usize) -> &mut Self {
|
pub fn width(&mut self, width: usize) -> &mut Self {
|
||||||
self.width = width;
|
self.width = Some(width);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -317,7 +325,11 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn disassemble(&self) -> String {
|
pub fn disassemble(&self) -> String {
|
||||||
let center = |line: &str| format!("{line:^width$}\n", width = self.width);
|
let width = self
|
||||||
|
.width
|
||||||
|
.unwrap_or_else(|| Self::default_width(self.styled))
|
||||||
|
+ 1;
|
||||||
|
let center = |line: &str| format!("{line:^width$}\n");
|
||||||
let style = |line: String| {
|
let style = |line: String| {
|
||||||
if self.styled {
|
if self.styled {
|
||||||
line.bold().to_string()
|
line.bold().to_string()
|
||||||
@ -326,10 +338,10 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut disassembled = String::with_capacity(self.predict_length());
|
let mut disassembly = String::with_capacity(self.predict_length());
|
||||||
let name_line = style(center(self.name));
|
let name_line = style(center(self.name));
|
||||||
|
|
||||||
disassembled.push_str(&name_line);
|
disassembly.push_str(&name_line);
|
||||||
|
|
||||||
let info_line = center(&format!(
|
let info_line = center(&format!(
|
||||||
"{} instructions, {} constants, {} locals",
|
"{} instructions, {} constants, {} locals",
|
||||||
@ -339,52 +351,53 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
));
|
));
|
||||||
let styled_info_line = {
|
let styled_info_line = {
|
||||||
if self.styled {
|
if self.styled {
|
||||||
info_line.bold().dimmed().to_string()
|
info_line.dimmed().to_string()
|
||||||
} else {
|
} else {
|
||||||
info_line
|
info_line
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
disassembled.push_str(&styled_info_line);
|
disassembly.push_str(&styled_info_line);
|
||||||
|
|
||||||
for line in Self::INSTRUCTION_HEADER {
|
for line in Self::INSTRUCTION_HEADER {
|
||||||
disassembled.push_str(&style(center(line)));
|
disassembly.push_str(&style(center(line)));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (offset, (instruction, position)) in self.chunk.instructions.iter().enumerate() {
|
for (index, (instruction, position)) in self.chunk.instructions.iter().enumerate() {
|
||||||
let position = position.to_string();
|
let position = position.to_string();
|
||||||
let operation = instruction.operation().to_string();
|
let operation = instruction.operation().to_string();
|
||||||
let info_option = instruction.disassembly_info(Some(self.chunk));
|
let info_option = instruction.disassembly_info(Some(self.chunk));
|
||||||
|
|
||||||
let instruction_display = if let Some(info) = info_option {
|
let instruction_display = if let Some(info) = info_option {
|
||||||
format!("{offset:<6} {operation:14} {info:25} {position:8}")
|
format!("{index:<5} {operation:14} {info:25} {position:8}")
|
||||||
} else {
|
} else {
|
||||||
format!("{offset:<6} {operation:14} {:25} {position:8}", " ")
|
format!("{index:<5} {operation:14} {:25} {position:8}", " ")
|
||||||
};
|
};
|
||||||
|
|
||||||
disassembled.push_str(¢er(&instruction_display));
|
disassembly.push_str(¢er(&instruction_display));
|
||||||
}
|
}
|
||||||
|
|
||||||
for line in Self::CONSTANT_HEADER {
|
for line in Self::CONSTANT_HEADER {
|
||||||
disassembled.push_str(&style(center(line)));
|
disassembly.push_str(&style(center(line)));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (index, value_option) in self.chunk.constants.iter().enumerate() {
|
for (index, value_option) in self.chunk.constants.iter().enumerate() {
|
||||||
let value_kind_display = if let Some(value) = value_option {
|
|
||||||
value.kind().to_string()
|
|
||||||
} else {
|
|
||||||
"empty".to_string()
|
|
||||||
};
|
|
||||||
let value_display = value_option
|
let value_display = value_option
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|value| value.to_string())
|
.map(|value| value.to_string())
|
||||||
.unwrap_or_else(|| "empty".to_string());
|
.unwrap_or("empty".to_string());
|
||||||
let constant_display = format!("{index:<5} {value_kind_display:<5} {value_display:<5}");
|
let elipsis = if value_display.len() > 9 { "..." } else { "" };
|
||||||
|
let constant_display = if value_display.len() > 9 {
|
||||||
|
format!("{index:<5} {value_display:^7.7}{elipsis}")
|
||||||
|
} else {
|
||||||
|
format!("{index:<5} {value_display:10}")
|
||||||
|
};
|
||||||
|
|
||||||
disassembled.push_str(¢er(&constant_display));
|
disassembly.push_str(¢er(&constant_display));
|
||||||
}
|
}
|
||||||
|
|
||||||
for line in Self::LOCAL_HEADER {
|
for line in Self::LOCAL_HEADER {
|
||||||
disassembled.push_str(&style(center(line)));
|
disassembly.push_str(&style(center(line)));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (
|
for (
|
||||||
@ -403,14 +416,14 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
.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 = format!(
|
let local_display = format!(
|
||||||
"{index:<5} {identifier_display:<10} {mutable:<7} {depth:<5} {register_display:<8}"
|
"{index:<5} {identifier_display:10} {mutable:7} {depth:<5} {register_display:8}"
|
||||||
);
|
);
|
||||||
|
|
||||||
disassembled.push_str(¢er(&local_display));
|
disassembly.push_str(¢er(&local_display));
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected_length = self.predict_length();
|
let expected_length = self.predict_length();
|
||||||
let actual_length = disassembled.len();
|
let actual_length = disassembly.len();
|
||||||
|
|
||||||
if !self.styled && expected_length != actual_length {
|
if !self.styled && expected_length != actual_length {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
@ -424,7 +437,7 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
disassembled
|
disassembly
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Predicts the capacity of the disassembled output. This is used to pre-allocate the string
|
/// Predicts the capacity of the disassembled output. This is used to pre-allocate the string
|
||||||
@ -440,15 +453,21 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
/// the ANSI escape codes will make the result too low. It still works as a lower bound in that
|
/// the ANSI escape codes will make the result too low. It still works as a lower bound in that
|
||||||
/// case.
|
/// case.
|
||||||
fn predict_length(&self) -> usize {
|
fn predict_length(&self) -> usize {
|
||||||
const EXTRA_LINES: usize = 2; // There is one empty line after the name of the chunk
|
const EXTRA_LINES: usize = 2; // There is one info line and one empty line after the name
|
||||||
|
|
||||||
let static_line_count =
|
let static_line_count = Self::INSTRUCTION_HEADER.len()
|
||||||
Self::INSTRUCTION_HEADER.len() + Self::CONSTANT_HEADER.len() + Self::LOCAL_HEADER.len();
|
+ Self::CONSTANT_HEADER.len()
|
||||||
|
+ Self::LOCAL_HEADER.len()
|
||||||
|
+ EXTRA_LINES;
|
||||||
let dynamic_line_count =
|
let dynamic_line_count =
|
||||||
self.chunk.instructions.len() + self.chunk.constants.len() + self.chunk.locals.len();
|
self.chunk.instructions.len() + self.chunk.constants.len() + self.chunk.locals.len();
|
||||||
let total_line_count = static_line_count + dynamic_line_count + EXTRA_LINES;
|
let total_line_count = static_line_count + dynamic_line_count;
|
||||||
|
let width = self
|
||||||
|
.width
|
||||||
|
.unwrap_or_else(|| Self::default_width(self.styled))
|
||||||
|
+ 1;
|
||||||
|
|
||||||
total_line_count * (self.width + 1)
|
total_line_count * width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Chunk, Span};
|
use crate::{Chunk, Operation, Span};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct Instruction(u32);
|
pub struct Instruction(u32);
|
||||||
@ -24,6 +24,16 @@ impl Instruction {
|
|||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_boolean(to_register: u8, value: bool, skip: bool) -> Instruction {
|
||||||
|
let mut instruction = Instruction(Operation::LoadBoolean as u32);
|
||||||
|
|
||||||
|
instruction.set_destination(to_register);
|
||||||
|
instruction.set_first_argument(if value { 1 } else { 0 });
|
||||||
|
instruction.set_second_argument(if skip { 1 } else { 0 });
|
||||||
|
|
||||||
|
instruction
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_constant(to_register: u8, constant_index: u8) -> Instruction {
|
pub fn load_constant(to_register: u8, constant_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::LoadConstant as u32);
|
let mut instruction = Instruction(Operation::LoadConstant as u32);
|
||||||
|
|
||||||
@ -251,6 +261,17 @@ impl Instruction {
|
|||||||
|
|
||||||
format!("R({from_register})..=R({to_register})")
|
format!("R({from_register})..=R({to_register})")
|
||||||
}
|
}
|
||||||
|
Operation::LoadBoolean => {
|
||||||
|
let to_register = self.destination();
|
||||||
|
let boolean = if self.first_argument() == 0 {
|
||||||
|
"false"
|
||||||
|
} else {
|
||||||
|
"true"
|
||||||
|
};
|
||||||
|
let skip = self.second_argument() != 0;
|
||||||
|
|
||||||
|
format!("R({to_register}) = {boolean}; if {skip} ip++",)
|
||||||
|
}
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
let constant_index = self.first_argument();
|
let constant_index = self.first_argument();
|
||||||
|
|
||||||
@ -405,144 +426,6 @@ impl Display for Instruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MOVE: u8 = 0b0000_0000;
|
|
||||||
const CLOSE: u8 = 0b000_0001;
|
|
||||||
const LOAD_CONSTANT: u8 = 0b0000_0010;
|
|
||||||
const LOAD_LIST: u8 = 0b0000_0011;
|
|
||||||
const DECLARE_LOCAL: u8 = 0b0000_0100;
|
|
||||||
const GET_LOCAL: u8 = 0b0000_0101;
|
|
||||||
const SET_LOCAL: u8 = 0b0000_0110;
|
|
||||||
const ADD: u8 = 0b0000_0111;
|
|
||||||
const SUBTRACT: u8 = 0b0000_1000;
|
|
||||||
const MULTIPLY: u8 = 0b0000_1001;
|
|
||||||
const MODULO: u8 = 0b0000_1010;
|
|
||||||
const AND: u8 = 0b0000_1011;
|
|
||||||
const OR: u8 = 0b0000_1100;
|
|
||||||
const DIVIDE: u8 = 0b0000_1101;
|
|
||||||
const NEGATE: u8 = 0b0000_1110;
|
|
||||||
const NOT: u8 = 0b0000_1111;
|
|
||||||
const RETURN: u8 = 0b0001_0000;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum Operation {
|
|
||||||
// Stack manipulation
|
|
||||||
Move = MOVE as isize,
|
|
||||||
Close = CLOSE as isize,
|
|
||||||
|
|
||||||
// Value loading
|
|
||||||
LoadConstant = LOAD_CONSTANT as isize,
|
|
||||||
LoadList = LOAD_LIST as isize,
|
|
||||||
|
|
||||||
// Variables
|
|
||||||
DefineLocal = DECLARE_LOCAL as isize,
|
|
||||||
GetLocal = GET_LOCAL as isize,
|
|
||||||
SetLocal = SET_LOCAL as isize,
|
|
||||||
|
|
||||||
// Binary operations
|
|
||||||
Add = ADD as isize,
|
|
||||||
Subtract = SUBTRACT as isize,
|
|
||||||
Multiply = MULTIPLY as isize,
|
|
||||||
Divide = DIVIDE as isize,
|
|
||||||
Modulo = MODULO as isize,
|
|
||||||
And = AND as isize,
|
|
||||||
Or = OR as isize,
|
|
||||||
|
|
||||||
// Unary operations
|
|
||||||
Negate = NEGATE as isize,
|
|
||||||
Not = NOT as isize,
|
|
||||||
|
|
||||||
// Control flow
|
|
||||||
Return = RETURN as isize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Operation {
|
|
||||||
pub fn is_binary(&self) -> bool {
|
|
||||||
matches!(
|
|
||||||
self,
|
|
||||||
Operation::Add
|
|
||||||
| Operation::Subtract
|
|
||||||
| Operation::Multiply
|
|
||||||
| Operation::Divide
|
|
||||||
| Operation::Modulo
|
|
||||||
| Operation::And
|
|
||||||
| Operation::Or
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u8> for Operation {
|
|
||||||
fn from(byte: u8) -> Self {
|
|
||||||
match byte {
|
|
||||||
MOVE => Operation::Move,
|
|
||||||
CLOSE => Operation::Close,
|
|
||||||
LOAD_CONSTANT => Operation::LoadConstant,
|
|
||||||
LOAD_LIST => Operation::LoadList,
|
|
||||||
DECLARE_LOCAL => Operation::DefineLocal,
|
|
||||||
GET_LOCAL => Operation::GetLocal,
|
|
||||||
SET_LOCAL => Operation::SetLocal,
|
|
||||||
ADD => Operation::Add,
|
|
||||||
SUBTRACT => Operation::Subtract,
|
|
||||||
MULTIPLY => Operation::Multiply,
|
|
||||||
DIVIDE => Operation::Divide,
|
|
||||||
MODULO => Operation::Modulo,
|
|
||||||
AND => Operation::And,
|
|
||||||
OR => Operation::Or,
|
|
||||||
NEGATE => Operation::Negate,
|
|
||||||
NOT => Operation::Not,
|
|
||||||
RETURN => Operation::Return,
|
|
||||||
_ => panic!("Invalid operation byte: {}", byte),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Operation> for u8 {
|
|
||||||
fn from(operation: Operation) -> Self {
|
|
||||||
match operation {
|
|
||||||
Operation::Move => MOVE,
|
|
||||||
Operation::Close => CLOSE,
|
|
||||||
Operation::LoadConstant => LOAD_CONSTANT,
|
|
||||||
Operation::LoadList => LOAD_LIST,
|
|
||||||
Operation::DefineLocal => DECLARE_LOCAL,
|
|
||||||
Operation::GetLocal => GET_LOCAL,
|
|
||||||
Operation::SetLocal => SET_LOCAL,
|
|
||||||
Operation::Add => ADD,
|
|
||||||
Operation::Subtract => SUBTRACT,
|
|
||||||
Operation::Multiply => MULTIPLY,
|
|
||||||
Operation::Divide => DIVIDE,
|
|
||||||
Operation::Modulo => MODULO,
|
|
||||||
Operation::And => AND,
|
|
||||||
Operation::Or => OR,
|
|
||||||
Operation::Negate => NEGATE,
|
|
||||||
Operation::Not => NOT,
|
|
||||||
Operation::Return => RETURN,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Operation {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Operation::Move => write!(f, "MOVE"),
|
|
||||||
Operation::Close => write!(f, "CLOSE"),
|
|
||||||
Operation::LoadConstant => write!(f, "LOAD_CONSTANT"),
|
|
||||||
Operation::LoadList => write!(f, "LOAD_LIST"),
|
|
||||||
Operation::DefineLocal => write!(f, "DEFINE_LOCAL"),
|
|
||||||
Operation::GetLocal => write!(f, "GET_LOCAL"),
|
|
||||||
Operation::SetLocal => write!(f, "SET_LOCAL"),
|
|
||||||
Operation::Add => write!(f, "ADD"),
|
|
||||||
Operation::Subtract => write!(f, "SUBTRACT"),
|
|
||||||
Operation::Multiply => write!(f, "MULTIPLY"),
|
|
||||||
Operation::Divide => write!(f, "DIVIDE"),
|
|
||||||
Operation::Modulo => write!(f, "MODULO"),
|
|
||||||
Operation::And => write!(f, "AND"),
|
|
||||||
Operation::Or => write!(f, "OR"),
|
|
||||||
Operation::Negate => write!(f, "NEGATE"),
|
|
||||||
Operation::Not => write!(f, "NOT"),
|
|
||||||
Operation::Return => write!(f, "RETURN"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -4,6 +4,7 @@ mod dust_error;
|
|||||||
mod identifier;
|
mod identifier;
|
||||||
mod instruction;
|
mod instruction;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
|
mod operation;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod token;
|
mod token;
|
||||||
mod r#type;
|
mod r#type;
|
||||||
@ -16,8 +17,9 @@ pub use chunk::{Chunk, ChunkError, Local};
|
|||||||
pub use constructor::Constructor;
|
pub use constructor::Constructor;
|
||||||
pub use dust_error::{AnnotatedError, DustError};
|
pub use dust_error::{AnnotatedError, DustError};
|
||||||
pub use identifier::Identifier;
|
pub use identifier::Identifier;
|
||||||
pub use instruction::{Instruction, Operation};
|
pub use instruction::Instruction;
|
||||||
pub use lexer::{lex, LexError, Lexer};
|
pub use lexer::{lex, LexError, Lexer};
|
||||||
|
pub use operation::Operation;
|
||||||
pub use parser::{parse, ParseError, Parser};
|
pub use parser::{parse, ParseError, Parser};
|
||||||
pub use r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
|
pub use r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
|
||||||
pub use token::{Token, TokenKind, TokenOwned};
|
pub use token::{Token, TokenKind, TokenOwned};
|
||||||
|
150
dust-lang/src/operation.rs
Normal file
150
dust-lang/src/operation.rs
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
|
const MOVE: u8 = 0b0000_0000;
|
||||||
|
const CLOSE: u8 = 0b000_0001;
|
||||||
|
const LOAD_BOOLEAN: u8 = 0b0000_0010;
|
||||||
|
const LOAD_CONSTANT: u8 = 0b0000_0011;
|
||||||
|
const LOAD_LIST: u8 = 0b0000_0100;
|
||||||
|
const DECLARE_LOCAL: u8 = 0b0000_0101;
|
||||||
|
const GET_LOCAL: u8 = 0b0000_0110;
|
||||||
|
const SET_LOCAL: u8 = 0b0000_0111;
|
||||||
|
const ADD: u8 = 0b0000_1000;
|
||||||
|
const SUBTRACT: u8 = 0b0000_1001;
|
||||||
|
const MULTIPLY: u8 = 0b0000_1010;
|
||||||
|
const MODULO: u8 = 0b0000_1011;
|
||||||
|
const AND: u8 = 0b0000_1100;
|
||||||
|
const OR: u8 = 0b0000_1101;
|
||||||
|
const DIVIDE: u8 = 0b0000_1110;
|
||||||
|
const NEGATE: u8 = 0b0000_1111;
|
||||||
|
const NOT: u8 = 0b0001_0000;
|
||||||
|
const RETURN: u8 = 0b0001_0001;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum Operation {
|
||||||
|
// Stack manipulation
|
||||||
|
Move = MOVE as isize,
|
||||||
|
Close = CLOSE as isize,
|
||||||
|
|
||||||
|
// Value loading
|
||||||
|
LoadBoolean = LOAD_BOOLEAN as isize,
|
||||||
|
LoadConstant = LOAD_CONSTANT as isize,
|
||||||
|
LoadList = LOAD_LIST as isize,
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
DefineLocal = DECLARE_LOCAL as isize,
|
||||||
|
GetLocal = GET_LOCAL as isize,
|
||||||
|
SetLocal = SET_LOCAL as isize,
|
||||||
|
|
||||||
|
// Binary operations
|
||||||
|
Add = ADD as isize,
|
||||||
|
Subtract = SUBTRACT as isize,
|
||||||
|
Multiply = MULTIPLY as isize,
|
||||||
|
Divide = DIVIDE as isize,
|
||||||
|
Modulo = MODULO as isize,
|
||||||
|
And = AND as isize,
|
||||||
|
Or = OR as isize,
|
||||||
|
|
||||||
|
// Unary operations
|
||||||
|
Negate = NEGATE as isize,
|
||||||
|
Not = NOT as isize,
|
||||||
|
|
||||||
|
// Control flow
|
||||||
|
Return = RETURN as isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Operation {
|
||||||
|
pub fn is_binary(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Operation::Add
|
||||||
|
| Operation::Subtract
|
||||||
|
| Operation::Multiply
|
||||||
|
| Operation::Divide
|
||||||
|
| Operation::Modulo
|
||||||
|
| Operation::And
|
||||||
|
| Operation::Or
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for Operation {
|
||||||
|
fn from(byte: u8) -> Self {
|
||||||
|
match byte {
|
||||||
|
MOVE => Operation::Move,
|
||||||
|
CLOSE => Operation::Close,
|
||||||
|
LOAD_BOOLEAN => Operation::LoadBoolean,
|
||||||
|
LOAD_CONSTANT => Operation::LoadConstant,
|
||||||
|
LOAD_LIST => Operation::LoadList,
|
||||||
|
DECLARE_LOCAL => Operation::DefineLocal,
|
||||||
|
GET_LOCAL => Operation::GetLocal,
|
||||||
|
SET_LOCAL => Operation::SetLocal,
|
||||||
|
ADD => Operation::Add,
|
||||||
|
SUBTRACT => Operation::Subtract,
|
||||||
|
MULTIPLY => Operation::Multiply,
|
||||||
|
DIVIDE => Operation::Divide,
|
||||||
|
MODULO => Operation::Modulo,
|
||||||
|
AND => Operation::And,
|
||||||
|
OR => Operation::Or,
|
||||||
|
NEGATE => Operation::Negate,
|
||||||
|
NOT => Operation::Not,
|
||||||
|
RETURN => Operation::Return,
|
||||||
|
_ => {
|
||||||
|
if cfg!(test) {
|
||||||
|
panic!("Invalid operation byte: {}", byte)
|
||||||
|
} else {
|
||||||
|
Operation::Return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Operation> for u8 {
|
||||||
|
fn from(operation: Operation) -> Self {
|
||||||
|
match operation {
|
||||||
|
Operation::Move => MOVE,
|
||||||
|
Operation::Close => CLOSE,
|
||||||
|
Operation::LoadBoolean => LOAD_BOOLEAN,
|
||||||
|
Operation::LoadConstant => LOAD_CONSTANT,
|
||||||
|
Operation::LoadList => LOAD_LIST,
|
||||||
|
Operation::DefineLocal => DECLARE_LOCAL,
|
||||||
|
Operation::GetLocal => GET_LOCAL,
|
||||||
|
Operation::SetLocal => SET_LOCAL,
|
||||||
|
Operation::Add => ADD,
|
||||||
|
Operation::Subtract => SUBTRACT,
|
||||||
|
Operation::Multiply => MULTIPLY,
|
||||||
|
Operation::Divide => DIVIDE,
|
||||||
|
Operation::Modulo => MODULO,
|
||||||
|
Operation::And => AND,
|
||||||
|
Operation::Or => OR,
|
||||||
|
Operation::Negate => NEGATE,
|
||||||
|
Operation::Not => NOT,
|
||||||
|
Operation::Return => RETURN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Operation {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Operation::Move => write!(f, "MOVE"),
|
||||||
|
Operation::Close => write!(f, "CLOSE"),
|
||||||
|
Operation::LoadBoolean => write!(f, "LOAD_BOOLEAN"),
|
||||||
|
Operation::LoadConstant => write!(f, "LOAD_CONSTANT"),
|
||||||
|
Operation::LoadList => write!(f, "LOAD_LIST"),
|
||||||
|
Operation::DefineLocal => write!(f, "DEFINE_LOCAL"),
|
||||||
|
Operation::GetLocal => write!(f, "GET_LOCAL"),
|
||||||
|
Operation::SetLocal => write!(f, "SET_LOCAL"),
|
||||||
|
Operation::Add => write!(f, "ADD"),
|
||||||
|
Operation::Subtract => write!(f, "SUBTRACT"),
|
||||||
|
Operation::Multiply => write!(f, "MULTIPLY"),
|
||||||
|
Operation::Divide => write!(f, "DIVIDE"),
|
||||||
|
Operation::Modulo => write!(f, "MODULO"),
|
||||||
|
Operation::And => write!(f, "AND"),
|
||||||
|
Operation::Or => write!(f, "OR"),
|
||||||
|
Operation::Negate => write!(f, "NEGATE"),
|
||||||
|
Operation::Not => write!(f, "NOT"),
|
||||||
|
Operation::Return => write!(f, "RETURN"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -148,9 +148,11 @@ impl<'src> Parser<'src> {
|
|||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let boolean = text.parse::<bool>().unwrap();
|
let boolean = text.parse::<bool>().unwrap();
|
||||||
let value = Value::boolean(boolean);
|
|
||||||
|
|
||||||
self.emit_constant(value)?;
|
self.emit_instruction(
|
||||||
|
Instruction::load_boolean(self.current_register, boolean, false),
|
||||||
|
self.previous_position,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1287,7 +1287,7 @@ impl Display for ValueData {
|
|||||||
ValueData::Range(range_value) => {
|
ValueData::Range(range_value) => {
|
||||||
write!(f, "{range_value}")
|
write!(f, "{range_value}")
|
||||||
}
|
}
|
||||||
ValueData::String(string) => write!(f, "\"{string}\""),
|
ValueData::String(string) => write!(f, "{string}"),
|
||||||
ValueData::Struct(r#struct) => write!(f, "{struct}"),
|
ValueData::Struct(r#struct) => write!(f, "{struct}"),
|
||||||
ValueData::Tuple(fields) => {
|
ValueData::Tuple(fields) => {
|
||||||
write!(f, "(")?;
|
write!(f, "(")?;
|
||||||
|
@ -50,12 +50,6 @@ impl Vm {
|
|||||||
vm.chunk
|
vm.chunk
|
||||||
.take_constant(instruction.second_argument(), position)?
|
.take_constant(instruction.second_argument(), position)?
|
||||||
} else {
|
} else {
|
||||||
if let Operation::GetLocal = instruction.operation() {
|
|
||||||
println!("GetLocal: {}", instruction);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("{}", instruction);
|
|
||||||
|
|
||||||
vm.clone(instruction.second_argument(), position)?
|
vm.clone(instruction.second_argument(), position)?
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -81,6 +75,18 @@ impl Vm {
|
|||||||
self.register_stack[register_index as usize] = None;
|
self.register_stack[register_index as usize] = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Operation::LoadBoolean => {
|
||||||
|
let to_register = instruction.destination();
|
||||||
|
let boolean = instruction.first_argument() != 0;
|
||||||
|
let skip = instruction.second_argument() != 0;
|
||||||
|
let value = Value::boolean(boolean);
|
||||||
|
|
||||||
|
self.insert(value, to_register, position)?;
|
||||||
|
|
||||||
|
if skip {
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
let to_register = instruction.destination();
|
let to_register = instruction.destination();
|
||||||
let from_constant = instruction.first_argument();
|
let from_constant = instruction.first_argument();
|
||||||
|
Loading…
Reference in New Issue
Block a user