Begin implementing control flow with if/else expressions
This commit is contained in:
parent
915340fbdb
commit
413cb70731
@ -150,6 +150,16 @@ impl Instruction {
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::Equal as u32);
|
||||
|
||||
instruction.set_destination(if comparison_boolean { 1 } else { 0 });
|
||||
instruction.set_first_argument(left_index);
|
||||
instruction.set_second_argument(right_index);
|
||||
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn negate(to_register: u8, from_index: u8) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::Negate as u32);
|
||||
|
||||
@ -168,34 +178,35 @@ impl Instruction {
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn jump(offset: u8, is_positive: bool) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::Jump as u32);
|
||||
|
||||
instruction.set_first_argument(offset);
|
||||
instruction.set_second_argument(if is_positive { 1 } else { 0 });
|
||||
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn r#return() -> Instruction {
|
||||
Instruction(Operation::Return as u32)
|
||||
}
|
||||
|
||||
pub fn set_first_argument_to_constant(&mut self) -> &mut Self {
|
||||
self.0 |= 0b1000_0000;
|
||||
|
||||
self
|
||||
pub fn operation(&self) -> Operation {
|
||||
Operation::from((self.0 & 0b0000_0000_0011_1111) as u8)
|
||||
}
|
||||
|
||||
pub fn first_argument_is_constant(&self) -> bool {
|
||||
self.0 & 0b1000_0000 != 0
|
||||
}
|
||||
|
||||
pub fn set_second_argument_to_constant(&mut self) -> &mut Self {
|
||||
self.0 |= 0b0100_0000;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn second_argument_is_constant(&self) -> bool {
|
||||
self.0 & 0b0100_0000 != 0
|
||||
pub fn set_operation(&mut self, operation: Operation) {
|
||||
self.0 |= u8::from(operation) as u32;
|
||||
}
|
||||
|
||||
pub fn destination(&self) -> u8 {
|
||||
(self.0 >> 24) as u8
|
||||
}
|
||||
|
||||
pub fn destination_as_boolean(&self) -> bool {
|
||||
(self.0 >> 24) != 0
|
||||
}
|
||||
|
||||
pub fn set_destination(&mut self, destination: u8) {
|
||||
self.0 &= 0x00FFFFFF;
|
||||
self.0 |= (destination as u32) << 24;
|
||||
@ -205,6 +216,20 @@ impl Instruction {
|
||||
(self.0 >> 16) as u8
|
||||
}
|
||||
|
||||
pub fn first_argument_is_constant(&self) -> bool {
|
||||
self.0 & 0b1000_0000 != 0
|
||||
}
|
||||
|
||||
pub fn first_argument_as_boolean(&self) -> bool {
|
||||
self.first_argument() != 0
|
||||
}
|
||||
|
||||
pub fn set_first_argument_to_constant(&mut self) -> &mut Self {
|
||||
self.0 |= 0b1000_0000;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_first_argument(&mut self, argument: u8) {
|
||||
self.0 |= (argument as u32) << 16;
|
||||
}
|
||||
@ -213,18 +238,24 @@ impl Instruction {
|
||||
(self.0 >> 8) as u8
|
||||
}
|
||||
|
||||
pub fn second_argument_is_constant(&self) -> bool {
|
||||
self.0 & 0b0100_0000 != 0
|
||||
}
|
||||
|
||||
pub fn second_argument_as_boolean(&self) -> bool {
|
||||
self.second_argument() != 0
|
||||
}
|
||||
|
||||
pub fn set_second_argument_to_constant(&mut self) -> &mut Self {
|
||||
self.0 |= 0b0100_0000;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_second_argument(&mut self, argument: u8) {
|
||||
self.0 |= (argument as u32) << 8;
|
||||
}
|
||||
|
||||
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 disassemble(&self, chunk: &Chunk) -> String {
|
||||
let mut disassembled = format!("{:16} ", self.operation().to_string());
|
||||
|
||||
@ -263,14 +294,14 @@ impl Instruction {
|
||||
}
|
||||
Operation::LoadBoolean => {
|
||||
let to_register = self.destination();
|
||||
let boolean = if self.first_argument() == 0 {
|
||||
"false"
|
||||
let boolean = self.first_argument_as_boolean();
|
||||
let skip_display = if self.second_argument_as_boolean() {
|
||||
"IP++"
|
||||
} else {
|
||||
"true"
|
||||
""
|
||||
};
|
||||
let skip = self.second_argument() != 0;
|
||||
|
||||
format!("R({to_register}) = {boolean}; if {skip} ip++",)
|
||||
format!("R({to_register}) = {boolean} {skip_display}",)
|
||||
}
|
||||
Operation::LoadConstant => {
|
||||
let constant_index = self.first_argument();
|
||||
@ -389,6 +420,37 @@ impl Instruction {
|
||||
|
||||
format!("R({destination}) = {first_argument} || {second_argument}",)
|
||||
}
|
||||
Operation::Equal => {
|
||||
let comparison_symbol = if self.destination_as_boolean() {
|
||||
"=="
|
||||
} else {
|
||||
"!="
|
||||
};
|
||||
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("if {first_argument} {comparison_symbol} {second_argument} IP++",)
|
||||
}
|
||||
Operation::Less => {
|
||||
let comparison_symbol = if self.destination_as_boolean() {
|
||||
"<"
|
||||
} else {
|
||||
">="
|
||||
};
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("if {first_argument} {comparison_symbol} {second_argument} IP++",)
|
||||
}
|
||||
Operation::LessEqual => {
|
||||
let comparison_symbol = if self.destination_as_boolean() {
|
||||
"<="
|
||||
} else {
|
||||
">"
|
||||
};
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("if {first_argument} {comparison_symbol} {second_argument} IP++",)
|
||||
}
|
||||
Operation::Negate => {
|
||||
let destination = self.destination();
|
||||
let argument = if self.first_argument_is_constant() {
|
||||
@ -409,6 +471,16 @@ impl Instruction {
|
||||
|
||||
format!("R({destination}) = !{argument}")
|
||||
}
|
||||
Operation::Jump => {
|
||||
let offset = self.first_argument();
|
||||
let positive = self.second_argument() != 0;
|
||||
|
||||
if positive {
|
||||
format!("IP += {}", offset)
|
||||
} else {
|
||||
format!("IP -= {}", offset)
|
||||
}
|
||||
}
|
||||
Operation::Return => return None,
|
||||
};
|
||||
|
||||
@ -453,6 +525,16 @@ mod tests {
|
||||
assert_eq!(instruction.second_argument(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_boolean() {
|
||||
let instruction = Instruction::load_boolean(4, true, true);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::LoadBoolean);
|
||||
assert_eq!(instruction.destination(), 4);
|
||||
assert!(instruction.first_argument_as_boolean());
|
||||
assert!(instruction.second_argument_as_boolean());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_constant() {
|
||||
let mut instruction = Instruction::load_constant(0, 1);
|
||||
@ -484,8 +566,6 @@ mod tests {
|
||||
fn add() {
|
||||
let mut instruction = Instruction::add(1, 1, 0);
|
||||
|
||||
instruction.set_operation(Operation::Add);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Add);
|
||||
@ -499,8 +579,6 @@ mod tests {
|
||||
fn subtract() {
|
||||
let mut instruction = Instruction::subtract(0, 1, 2);
|
||||
|
||||
instruction.set_operation(Operation::Subtract);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
|
||||
@ -516,8 +594,6 @@ mod tests {
|
||||
fn multiply() {
|
||||
let mut instruction = Instruction::multiply(0, 1, 2);
|
||||
|
||||
instruction.set_operation(Operation::Multiply);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
|
||||
@ -533,8 +609,6 @@ mod tests {
|
||||
fn divide() {
|
||||
let mut instruction = Instruction::divide(0, 1, 2);
|
||||
|
||||
instruction.set_operation(Operation::Divide);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
|
||||
@ -550,8 +624,6 @@ mod tests {
|
||||
fn and() {
|
||||
let mut instruction = Instruction::and(0, 1, 2);
|
||||
|
||||
instruction.set_operation(Operation::And);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
|
||||
@ -567,8 +639,6 @@ mod tests {
|
||||
fn or() {
|
||||
let mut instruction = Instruction::or(0, 1, 2);
|
||||
|
||||
instruction.set_operation(Operation::Or);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
|
||||
@ -580,12 +650,25 @@ mod tests {
|
||||
assert!(instruction.second_argument_is_constant());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal() {
|
||||
let mut instruction = Instruction::equal(true, 1, 2);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Equal);
|
||||
assert!(instruction.destination_as_boolean());
|
||||
assert_eq!(instruction.first_argument(), 1);
|
||||
assert_eq!(instruction.second_argument(), 2);
|
||||
assert!(instruction.first_argument_is_constant());
|
||||
assert!(instruction.second_argument_is_constant());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negate() {
|
||||
let mut instruction = Instruction::negate(0, 1);
|
||||
|
||||
instruction.set_operation(Operation::Negate);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
|
||||
@ -600,8 +683,6 @@ mod tests {
|
||||
fn not() {
|
||||
let mut instruction = Instruction::not(0, 1);
|
||||
|
||||
instruction.set_operation(Operation::Not);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
|
||||
@ -612,12 +693,22 @@ mod tests {
|
||||
assert!(instruction.second_argument_is_constant());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jump() {
|
||||
let mut instruction = Instruction::jump(4, true);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Jump);
|
||||
assert_eq!(instruction.first_argument(), 4);
|
||||
assert!(instruction.first_argument_as_boolean());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn r#return() {
|
||||
let mut instruction = Instruction::r#return();
|
||||
|
||||
instruction.set_operation(Operation::Return);
|
||||
|
||||
instruction.set_first_argument_to_constant();
|
||||
instruction.set_second_argument_to_constant();
|
||||
|
||||
|
@ -2,22 +2,32 @@ 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;
|
||||
const DIVIDE: u8 = 0b0000_1011;
|
||||
const MODULO: u8 = 0b0000_1100;
|
||||
const AND: u8 = 0b0000_1101;
|
||||
const OR: u8 = 0b0000_1110;
|
||||
|
||||
const EQUAL: u8 = 0b0000_1111;
|
||||
const LESS: u8 = 0b0001_0000;
|
||||
const LESS_EQUAL: u8 = 0b0001_0001;
|
||||
|
||||
const NEGATE: u8 = 0b0001_0010;
|
||||
const NOT: u8 = 0b0001_0011;
|
||||
|
||||
const JUMP: u8 = 0b0001_0100;
|
||||
const RETURN: u8 = 0b0001_0101;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Operation {
|
||||
@ -44,11 +54,17 @@ pub enum Operation {
|
||||
And = AND as isize,
|
||||
Or = OR as isize,
|
||||
|
||||
// Relational operations
|
||||
Equal = EQUAL as isize,
|
||||
Less = LESS as isize,
|
||||
LessEqual = LESS_EQUAL as isize,
|
||||
|
||||
// Unary operations
|
||||
Negate = NEGATE as isize,
|
||||
Not = NOT as isize,
|
||||
|
||||
// Control flow
|
||||
Jump = JUMP as isize,
|
||||
Return = RETURN as isize,
|
||||
}
|
||||
|
||||
@ -85,8 +101,12 @@ impl From<u8> for Operation {
|
||||
MODULO => Operation::Modulo,
|
||||
AND => Operation::And,
|
||||
OR => Operation::Or,
|
||||
EQUAL => Operation::Equal,
|
||||
LESS => Operation::Less,
|
||||
LESS_EQUAL => Operation::LessEqual,
|
||||
NEGATE => Operation::Negate,
|
||||
NOT => Operation::Not,
|
||||
JUMP => Operation::Jump,
|
||||
RETURN => Operation::Return,
|
||||
_ => {
|
||||
if cfg!(test) {
|
||||
@ -117,8 +137,12 @@ impl From<Operation> for u8 {
|
||||
Operation::Modulo => MODULO,
|
||||
Operation::And => AND,
|
||||
Operation::Or => OR,
|
||||
Operation::Equal => EQUAL,
|
||||
Operation::Less => LESS,
|
||||
Operation::LessEqual => LESS_EQUAL,
|
||||
Operation::Negate => NEGATE,
|
||||
Operation::Not => NOT,
|
||||
Operation::Jump => JUMP,
|
||||
Operation::Return => RETURN,
|
||||
}
|
||||
}
|
||||
@ -142,8 +166,12 @@ impl Display for Operation {
|
||||
Operation::Modulo => write!(f, "MODULO"),
|
||||
Operation::And => write!(f, "AND"),
|
||||
Operation::Or => write!(f, "OR"),
|
||||
Operation::Equal => write!(f, "EQUAL"),
|
||||
Operation::Less => write!(f, "LESS"),
|
||||
Operation::LessEqual => write!(f, "LESS_EQUAL"),
|
||||
Operation::Negate => write!(f, "NEGATE"),
|
||||
Operation::Not => write!(f, "NOT"),
|
||||
Operation::Jump => write!(f, "JUMP"),
|
||||
Operation::Return => write!(f, "RETURN"),
|
||||
}
|
||||
}
|
||||
|
@ -153,6 +153,7 @@ impl<'src> Parser<'src> {
|
||||
Instruction::load_boolean(self.current_register, boolean, false),
|
||||
self.previous_position,
|
||||
);
|
||||
self.increment_register()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -382,6 +383,7 @@ impl<'src> Parser<'src> {
|
||||
TokenKind::Percent => Instruction::modulo(self.current_register, left, right),
|
||||
TokenKind::DoubleAmpersand => Instruction::and(self.current_register, left, right),
|
||||
TokenKind::DoublePipe => Instruction::or(self.current_register, left, right),
|
||||
TokenKind::DoubleEqual => Instruction::equal(true, left, right),
|
||||
_ => {
|
||||
return Err(ParseError::ExpectedTokenMultiple {
|
||||
expected: &[
|
||||
@ -392,6 +394,7 @@ impl<'src> Parser<'src> {
|
||||
TokenKind::Percent,
|
||||
TokenKind::DoubleAmpersand,
|
||||
TokenKind::DoublePipe,
|
||||
TokenKind::DoubleEqual,
|
||||
],
|
||||
found: operator.to_owned(),
|
||||
position: operator_position,
|
||||
@ -418,6 +421,10 @@ impl<'src> Parser<'src> {
|
||||
self.emit_instruction(instruction, operator_position);
|
||||
self.increment_register()?;
|
||||
|
||||
if let TokenKind::DoubleEqual = operator.kind() {
|
||||
self.emit_instruction(Instruction::jump(2, true), operator_position);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -521,14 +528,17 @@ impl<'src> Parser<'src> {
|
||||
ends_with_semicolon = true;
|
||||
}
|
||||
|
||||
let end = self.current_position.1;
|
||||
|
||||
if ends_with_semicolon {
|
||||
let end = self.current_position.1;
|
||||
let end_register = self.current_register;
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::close(start_register, end_register),
|
||||
Span(start, end),
|
||||
);
|
||||
} else {
|
||||
self.emit_instruction(Instruction::r#return(), Span(start, end));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -572,6 +582,18 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_if(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||
self.advance()?;
|
||||
self.parse_expression()?;
|
||||
self.parse_block(false)?;
|
||||
|
||||
if self.allow(TokenKind::Else)? {
|
||||
self.parse_block(false)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_expression(&mut self) -> Result<(), ParseError> {
|
||||
self.parse(Precedence::None)
|
||||
}
|
||||
@ -784,9 +806,17 @@ impl From<&TokenKind> for ParseRule<'_> {
|
||||
TokenKind::Async => todo!(),
|
||||
TokenKind::Bool => todo!(),
|
||||
TokenKind::Break => todo!(),
|
||||
TokenKind::Else => todo!(),
|
||||
TokenKind::Else => ParseRule {
|
||||
prefix: None,
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::FloatKeyword => todo!(),
|
||||
TokenKind::If => todo!(),
|
||||
TokenKind::If => ParseRule {
|
||||
prefix: Some(Parser::parse_if),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Int => todo!(),
|
||||
TokenKind::Let => ParseRule {
|
||||
prefix: Some(Parser::parse_let_statement),
|
||||
@ -816,7 +846,11 @@ impl From<&TokenKind> for ParseRule<'_> {
|
||||
precedence: Precedence::LogicalAnd,
|
||||
},
|
||||
TokenKind::DoubleDot => todo!(),
|
||||
TokenKind::DoubleEqual => todo!(),
|
||||
TokenKind::DoubleEqual => ParseRule {
|
||||
prefix: None,
|
||||
infix: Some(Parser::parse_binary),
|
||||
precedence: Precedence::Equality,
|
||||
},
|
||||
TokenKind::DoublePipe => ParseRule {
|
||||
prefix: None,
|
||||
infix: Some(Parser::parse_binary),
|
||||
|
@ -77,13 +77,13 @@ impl Vm {
|
||||
}
|
||||
Operation::LoadBoolean => {
|
||||
let to_register = instruction.destination();
|
||||
let boolean = instruction.first_argument() != 0;
|
||||
let skip = instruction.second_argument() != 0;
|
||||
let boolean = instruction.first_argument_as_boolean();
|
||||
let skip = instruction.second_argument_as_boolean();
|
||||
let value = Value::boolean(boolean);
|
||||
|
||||
self.insert(value, to_register, position)?;
|
||||
|
||||
if skip {
|
||||
if boolean && skip {
|
||||
self.ip += 1;
|
||||
}
|
||||
}
|
||||
@ -201,6 +201,45 @@ impl Vm {
|
||||
|
||||
self.insert(or, instruction.destination(), position)?;
|
||||
}
|
||||
Operation::Equal => {
|
||||
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
||||
let equal = left
|
||||
.equal(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
let compare_to = instruction.destination_as_boolean();
|
||||
|
||||
if let Some(boolean) = equal.as_boolean() {
|
||||
if boolean == compare_to {
|
||||
self.ip += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Operation::Less => {
|
||||
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
||||
let less = left
|
||||
.less_than(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
let compare_to = instruction.destination() != 0;
|
||||
|
||||
if let Some(boolean) = less.as_boolean() {
|
||||
if boolean != compare_to {
|
||||
self.ip += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Operation::LessEqual => {
|
||||
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
||||
let less_equal = left
|
||||
.less_than_or_equal(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
let compare_to = instruction.destination() != 0;
|
||||
|
||||
if let Some(boolean) = less_equal.as_boolean() {
|
||||
if boolean != compare_to {
|
||||
self.ip += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Operation::Negate => {
|
||||
let value = if instruction.first_argument_is_constant() {
|
||||
self.chunk
|
||||
@ -227,6 +266,17 @@ impl Vm {
|
||||
|
||||
self.insert(not, instruction.destination(), position)?;
|
||||
}
|
||||
Operation::Jump => {
|
||||
let offset = instruction.first_argument();
|
||||
let is_positive = instruction.second_argument_as_boolean();
|
||||
let new_ip = if is_positive {
|
||||
self.ip + offset as usize
|
||||
} else {
|
||||
self.ip - offset as usize
|
||||
};
|
||||
|
||||
self.ip = new_ip;
|
||||
}
|
||||
Operation::Return => {
|
||||
let value = self.pop(position)?;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user