Add &&, || and ! to the language; Add tests
This commit is contained in:
parent
00555785e3
commit
6ca96bc1dc
@ -99,6 +99,36 @@ impl Instruction {
|
|||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn modulo(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
||||||
|
let mut instruction = Instruction(Operation::Modulo as u32);
|
||||||
|
|
||||||
|
instruction.set_destination(to_register);
|
||||||
|
instruction.set_first_argument(left_index);
|
||||||
|
instruction.set_second_argument(right_index);
|
||||||
|
|
||||||
|
instruction
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn and(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
||||||
|
let mut instruction = Instruction(Operation::And as u32);
|
||||||
|
|
||||||
|
instruction.set_destination(to_register);
|
||||||
|
instruction.set_first_argument(left_index);
|
||||||
|
instruction.set_second_argument(right_index);
|
||||||
|
|
||||||
|
instruction
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn or(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
||||||
|
let mut instruction = Instruction(Operation::Or as u32);
|
||||||
|
|
||||||
|
instruction.set_destination(to_register);
|
||||||
|
instruction.set_first_argument(left_index);
|
||||||
|
instruction.set_second_argument(right_index);
|
||||||
|
|
||||||
|
instruction
|
||||||
|
}
|
||||||
|
|
||||||
pub fn negate(to_register: u8, from_index: u8) -> Instruction {
|
pub fn negate(to_register: u8, from_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Negate as u32);
|
let mut instruction = Instruction(Operation::Negate as u32);
|
||||||
|
|
||||||
@ -108,6 +138,15 @@ impl Instruction {
|
|||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn not(to_register: u8, from_index: u8) -> Instruction {
|
||||||
|
let mut instruction = Instruction(Operation::Not as u32);
|
||||||
|
|
||||||
|
instruction.set_destination(to_register);
|
||||||
|
instruction.set_first_argument(from_index);
|
||||||
|
|
||||||
|
instruction
|
||||||
|
}
|
||||||
|
|
||||||
pub fn r#return() -> Instruction {
|
pub fn r#return() -> Instruction {
|
||||||
Instruction(Operation::Return as u32)
|
Instruction(Operation::Return as u32)
|
||||||
}
|
}
|
||||||
@ -306,6 +345,51 @@ impl Instruction {
|
|||||||
|
|
||||||
format!("R({destination}) = {first_argument} / {second_argument}",)
|
format!("R({destination}) = {first_argument} / {second_argument}",)
|
||||||
}
|
}
|
||||||
|
Operation::Modulo => {
|
||||||
|
let destination = self.destination();
|
||||||
|
let first_argument = if self.first_argument_is_constant() {
|
||||||
|
format!("C({})", self.first_argument())
|
||||||
|
} else {
|
||||||
|
format!("R({})", self.first_argument())
|
||||||
|
};
|
||||||
|
let second_argument = if self.second_argument_is_constant() {
|
||||||
|
format!("C({})", self.second_argument())
|
||||||
|
} else {
|
||||||
|
format!("R({})", self.second_argument())
|
||||||
|
};
|
||||||
|
|
||||||
|
format!("R({destination}) = {first_argument} % {second_argument}",)
|
||||||
|
}
|
||||||
|
Operation::And => {
|
||||||
|
let destination = self.destination();
|
||||||
|
let first_argument = if self.first_argument_is_constant() {
|
||||||
|
format!("C({})", self.first_argument())
|
||||||
|
} else {
|
||||||
|
format!("R({})", self.first_argument())
|
||||||
|
};
|
||||||
|
let second_argument = if self.second_argument_is_constant() {
|
||||||
|
format!("C({})", self.second_argument())
|
||||||
|
} else {
|
||||||
|
format!("R({})", self.second_argument())
|
||||||
|
};
|
||||||
|
|
||||||
|
format!("R({destination}) = {first_argument} && {second_argument}",)
|
||||||
|
}
|
||||||
|
Operation::Or => {
|
||||||
|
let destination = self.destination();
|
||||||
|
let first_argument = if self.first_argument_is_constant() {
|
||||||
|
format!("C({})", self.first_argument())
|
||||||
|
} else {
|
||||||
|
format!("R({})", self.first_argument())
|
||||||
|
};
|
||||||
|
let second_argument = if self.second_argument_is_constant() {
|
||||||
|
format!("C({})", self.second_argument())
|
||||||
|
} else {
|
||||||
|
format!("R({})", self.second_argument())
|
||||||
|
};
|
||||||
|
|
||||||
|
format!("R({destination}) = {first_argument} || {second_argument}",)
|
||||||
|
}
|
||||||
Operation::Negate => {
|
Operation::Negate => {
|
||||||
let destination = self.destination();
|
let destination = self.destination();
|
||||||
let argument = if self.first_argument_is_constant() {
|
let argument = if self.first_argument_is_constant() {
|
||||||
@ -316,6 +400,16 @@ impl Instruction {
|
|||||||
|
|
||||||
format!("R({destination}) = -{argument}")
|
format!("R({destination}) = -{argument}")
|
||||||
}
|
}
|
||||||
|
Operation::Not => {
|
||||||
|
let destination = self.destination();
|
||||||
|
let argument = if self.first_argument_is_constant() {
|
||||||
|
format!("C({})", self.first_argument())
|
||||||
|
} else {
|
||||||
|
format!("R({})", self.first_argument())
|
||||||
|
};
|
||||||
|
|
||||||
|
format!("R({destination}) = !{argument}")
|
||||||
|
}
|
||||||
Operation::Return => return None,
|
Operation::Return => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -342,9 +436,13 @@ const SET_LOCAL: u8 = 0b0000_0101;
|
|||||||
const ADD: u8 = 0b0000_0110;
|
const ADD: u8 = 0b0000_0110;
|
||||||
const SUBTRACT: u8 = 0b0000_0111;
|
const SUBTRACT: u8 = 0b0000_0111;
|
||||||
const MULTIPLY: u8 = 0b0000_1000;
|
const MULTIPLY: u8 = 0b0000_1000;
|
||||||
const DIVIDE: u8 = 0b0000_1001;
|
const MODULO: u8 = 0b0000_1001;
|
||||||
const NEGATE: u8 = 0b0000_1010;
|
const AND: u8 = 0b0000_1010;
|
||||||
const RETURN: u8 = 0b0000_1011;
|
const OR: u8 = 0b0000_1011;
|
||||||
|
const DIVIDE: u8 = 0b0000_1100;
|
||||||
|
const NEGATE: u8 = 0b0000_1101;
|
||||||
|
const NOT: u8 = 0b0000_1110;
|
||||||
|
const RETURN: u8 = 0b0000_1111;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Operation {
|
pub enum Operation {
|
||||||
@ -365,9 +463,13 @@ pub enum Operation {
|
|||||||
Subtract = SUBTRACT as isize,
|
Subtract = SUBTRACT as isize,
|
||||||
Multiply = MULTIPLY as isize,
|
Multiply = MULTIPLY as isize,
|
||||||
Divide = DIVIDE as isize,
|
Divide = DIVIDE as isize,
|
||||||
|
Modulo = MODULO as isize,
|
||||||
|
And = AND as isize,
|
||||||
|
Or = OR as isize,
|
||||||
|
|
||||||
// Unary operations
|
// Unary operations
|
||||||
Negate = NEGATE as isize,
|
Negate = NEGATE as isize,
|
||||||
|
Not = NOT as isize,
|
||||||
|
|
||||||
// Control flow
|
// Control flow
|
||||||
Return = RETURN as isize,
|
Return = RETURN as isize,
|
||||||
@ -377,7 +479,13 @@ impl Operation {
|
|||||||
pub fn is_binary(&self) -> bool {
|
pub fn is_binary(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
Operation::Add | Operation::Subtract | Operation::Multiply | Operation::Divide
|
Operation::Add
|
||||||
|
| Operation::Subtract
|
||||||
|
| Operation::Multiply
|
||||||
|
| Operation::Divide
|
||||||
|
| Operation::Modulo
|
||||||
|
| Operation::And
|
||||||
|
| Operation::Or
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -395,8 +503,13 @@ impl From<u8> for Operation {
|
|||||||
SUBTRACT => Operation::Subtract,
|
SUBTRACT => Operation::Subtract,
|
||||||
MULTIPLY => Operation::Multiply,
|
MULTIPLY => Operation::Multiply,
|
||||||
DIVIDE => Operation::Divide,
|
DIVIDE => Operation::Divide,
|
||||||
|
MODULO => Operation::Modulo,
|
||||||
|
AND => Operation::And,
|
||||||
|
OR => Operation::Or,
|
||||||
NEGATE => Operation::Negate,
|
NEGATE => Operation::Negate,
|
||||||
_ => Operation::Return,
|
NOT => Operation::Not,
|
||||||
|
RETURN => Operation::Return,
|
||||||
|
_ => panic!("Invalid operation byte: {}", byte),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -414,7 +527,11 @@ impl From<Operation> for u8 {
|
|||||||
Operation::Subtract => SUBTRACT,
|
Operation::Subtract => SUBTRACT,
|
||||||
Operation::Multiply => MULTIPLY,
|
Operation::Multiply => MULTIPLY,
|
||||||
Operation::Divide => DIVIDE,
|
Operation::Divide => DIVIDE,
|
||||||
|
Operation::Modulo => MODULO,
|
||||||
|
Operation::And => AND,
|
||||||
|
Operation::Or => OR,
|
||||||
Operation::Negate => NEGATE,
|
Operation::Negate => NEGATE,
|
||||||
|
Operation::Not => NOT,
|
||||||
Operation::Return => RETURN,
|
Operation::Return => RETURN,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -433,7 +550,11 @@ impl Display for Operation {
|
|||||||
Operation::Subtract => write!(f, "SUBTRACT"),
|
Operation::Subtract => write!(f, "SUBTRACT"),
|
||||||
Operation::Multiply => write!(f, "MULTIPLY"),
|
Operation::Multiply => write!(f, "MULTIPLY"),
|
||||||
Operation::Divide => write!(f, "DIVIDE"),
|
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::Negate => write!(f, "NEGATE"),
|
||||||
|
Operation::Not => write!(f, "NOT"),
|
||||||
Operation::Return => write!(f, "RETURN"),
|
Operation::Return => write!(f, "RETURN"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -564,6 +685,40 @@ mod tests {
|
|||||||
assert!(instruction.second_argument_is_constant());
|
assert!(instruction.second_argument_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
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();
|
||||||
|
|
||||||
|
assert_eq!(instruction.operation(), Operation::And);
|
||||||
|
assert_eq!(instruction.destination(), 0);
|
||||||
|
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 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();
|
||||||
|
|
||||||
|
assert_eq!(instruction.operation(), Operation::Or);
|
||||||
|
assert_eq!(instruction.destination(), 0);
|
||||||
|
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]
|
#[test]
|
||||||
fn negate() {
|
fn negate() {
|
||||||
let mut instruction = Instruction::negate(0, 1);
|
let mut instruction = Instruction::negate(0, 1);
|
||||||
@ -580,6 +735,22 @@ mod tests {
|
|||||||
assert!(instruction.second_argument_is_constant());
|
assert!(instruction.second_argument_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
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();
|
||||||
|
|
||||||
|
assert_eq!(instruction.operation(), Operation::Not);
|
||||||
|
assert_eq!(instruction.destination(), 0);
|
||||||
|
assert_eq!(instruction.first_argument(), 1);
|
||||||
|
assert!(instruction.first_argument_is_constant());
|
||||||
|
assert!(instruction.second_argument_is_constant());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn r#return() {
|
fn r#return() {
|
||||||
let mut instruction = Instruction::r#return();
|
let mut instruction = Instruction::r#return();
|
||||||
|
@ -279,8 +279,8 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
fn parse_binary(&mut self) -> Result<(), ParseError> {
|
fn parse_binary(&mut self) -> Result<(), ParseError> {
|
||||||
let operator_position = self.current_position;
|
let operator_position = self.current_position;
|
||||||
let operator = self.current_token.kind();
|
let operator = self.current_token;
|
||||||
let rule = ParseRule::from(&operator);
|
let rule = ParseRule::from(&operator.kind());
|
||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
@ -330,11 +330,14 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut instruction = match operator {
|
let mut instruction = match operator.kind() {
|
||||||
TokenKind::Plus => Instruction::add(self.current_register, left, right),
|
TokenKind::Plus => Instruction::add(self.current_register, left, right),
|
||||||
TokenKind::Minus => Instruction::subtract(self.current_register, left, right),
|
TokenKind::Minus => Instruction::subtract(self.current_register, left, right),
|
||||||
TokenKind::Star => Instruction::multiply(self.current_register, left, right),
|
TokenKind::Star => Instruction::multiply(self.current_register, left, right),
|
||||||
TokenKind::Slash => Instruction::divide(self.current_register, left, right),
|
TokenKind::Slash => Instruction::divide(self.current_register, left, right),
|
||||||
|
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),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParseError::ExpectedTokenMultiple {
|
return Err(ParseError::ExpectedTokenMultiple {
|
||||||
expected: vec![
|
expected: vec![
|
||||||
@ -343,7 +346,7 @@ impl<'src> Parser<'src> {
|
|||||||
TokenKind::Star,
|
TokenKind::Star,
|
||||||
TokenKind::Slash,
|
TokenKind::Slash,
|
||||||
],
|
],
|
||||||
found: self.previous_token.to_owned(),
|
found: operator.to_owned(),
|
||||||
position: operator_position,
|
position: operator_position,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -448,6 +451,7 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_block(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
pub fn parse_block(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||||
|
self.advance()?;
|
||||||
self.chunk.begin_scope();
|
self.chunk.begin_scope();
|
||||||
|
|
||||||
while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() {
|
while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() {
|
||||||
@ -467,13 +471,11 @@ impl<'src> Parser<'src> {
|
|||||||
let start = self.current_position.0;
|
let start = self.current_position.0;
|
||||||
let (is_expression_statement, contains_block) = match self.current_token {
|
let (is_expression_statement, contains_block) = match self.current_token {
|
||||||
Token::Let => {
|
Token::Let => {
|
||||||
self.advance()?;
|
|
||||||
self.parse_let_statement(true)?;
|
self.parse_let_statement(true)?;
|
||||||
|
|
||||||
(false, false)
|
(false, false)
|
||||||
}
|
}
|
||||||
Token::LeftCurlyBrace => {
|
Token::LeftCurlyBrace => {
|
||||||
self.advance()?;
|
|
||||||
self.parse_block(true)?;
|
self.parse_block(true)?;
|
||||||
|
|
||||||
(true, true)
|
(true, true)
|
||||||
@ -495,7 +497,7 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_let_statement(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_let_statement(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||||
self.allow(TokenKind::Let)?;
|
self.advance()?;
|
||||||
|
|
||||||
let position = self.current_position;
|
let position = self.current_position;
|
||||||
let identifier = if let Token::Identifier(text) = self.current_token {
|
let identifier = if let Token::Identifier(text) = self.current_token {
|
||||||
@ -692,7 +694,11 @@ impl From<&TokenKind> for ParseRule<'_> {
|
|||||||
TokenKind::Str => todo!(),
|
TokenKind::Str => todo!(),
|
||||||
TokenKind::While => todo!(),
|
TokenKind::While => todo!(),
|
||||||
TokenKind::BangEqual => todo!(),
|
TokenKind::BangEqual => todo!(),
|
||||||
TokenKind::Bang => todo!(),
|
TokenKind::Bang => ParseRule {
|
||||||
|
prefix: Some(Parser::parse_unary),
|
||||||
|
infix: None,
|
||||||
|
precedence: Precedence::Unary,
|
||||||
|
},
|
||||||
TokenKind::Colon => todo!(),
|
TokenKind::Colon => todo!(),
|
||||||
TokenKind::Comma => todo!(),
|
TokenKind::Comma => todo!(),
|
||||||
TokenKind::Dot => todo!(),
|
TokenKind::Dot => todo!(),
|
||||||
@ -703,7 +709,11 @@ impl From<&TokenKind> for ParseRule<'_> {
|
|||||||
},
|
},
|
||||||
TokenKind::DoubleDot => todo!(),
|
TokenKind::DoubleDot => todo!(),
|
||||||
TokenKind::DoubleEqual => todo!(),
|
TokenKind::DoubleEqual => todo!(),
|
||||||
TokenKind::DoublePipe => todo!(),
|
TokenKind::DoublePipe => ParseRule {
|
||||||
|
prefix: None,
|
||||||
|
infix: Some(Parser::parse_binary),
|
||||||
|
precedence: Precedence::LogicalOr,
|
||||||
|
},
|
||||||
TokenKind::Equal => ParseRule {
|
TokenKind::Equal => ParseRule {
|
||||||
prefix: None,
|
prefix: None,
|
||||||
infix: None,
|
infix: None,
|
||||||
|
@ -148,15 +148,60 @@ fn declare_local() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn constant() {
|
fn and() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("42"),
|
parse("1 && 2"),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0), Span(0, 2)),
|
(
|
||||||
(Instruction::r#return(), Span(0, 2)),
|
*Instruction::and(0, 0, 1)
|
||||||
|
.set_first_argument_to_constant()
|
||||||
|
.set_second_argument_to_constant(),
|
||||||
|
Span(2, 4)
|
||||||
|
),
|
||||||
|
(Instruction::r#return(), Span(0, 6)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
|
vec![]
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn divide() {
|
||||||
|
assert_eq!(
|
||||||
|
parse("1 / 2"),
|
||||||
|
Ok(Chunk::with_data(
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
*Instruction::divide(0, 0, 1)
|
||||||
|
.set_first_argument_to_constant()
|
||||||
|
.set_second_argument_to_constant(),
|
||||||
|
Span(2, 3)
|
||||||
|
),
|
||||||
|
(Instruction::r#return(), Span(0, 5)),
|
||||||
|
],
|
||||||
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
|
vec![]
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiply() {
|
||||||
|
assert_eq!(
|
||||||
|
parse("1 * 2"),
|
||||||
|
Ok(Chunk::with_data(
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
*Instruction::multiply(0, 0, 1)
|
||||||
|
.set_first_argument_to_constant()
|
||||||
|
.set_second_argument_to_constant(),
|
||||||
|
Span(2, 3)
|
||||||
|
),
|
||||||
|
(Instruction::r#return(), Span(0, 5)),
|
||||||
|
],
|
||||||
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
@ -201,3 +246,18 @@ fn subtract() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn constant() {
|
||||||
|
assert_eq!(
|
||||||
|
parse("42"),
|
||||||
|
Ok(Chunk::with_data(
|
||||||
|
vec![
|
||||||
|
(Instruction::load_constant(0, 0), Span(0, 2)),
|
||||||
|
(Instruction::r#return(), Span(0, 2)),
|
||||||
|
],
|
||||||
|
vec![Value::integer(42)],
|
||||||
|
vec![]
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
/// Source code token.
|
/// Source code token.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum Token<'src> {
|
pub enum Token<'src> {
|
||||||
// End of file
|
// End of file
|
||||||
Eof,
|
Eof,
|
||||||
|
@ -149,6 +149,57 @@ impl Vm {
|
|||||||
|
|
||||||
self.insert(quotient, instruction.destination(), position)?;
|
self.insert(quotient, instruction.destination(), position)?;
|
||||||
}
|
}
|
||||||
|
Operation::Modulo => {
|
||||||
|
let left = self.take_constant_or_clone_register(
|
||||||
|
instruction.first_argument(),
|
||||||
|
instruction.first_argument_is_constant(),
|
||||||
|
position,
|
||||||
|
)?;
|
||||||
|
let right = self.take_constant_or_clone_register(
|
||||||
|
instruction.second_argument(),
|
||||||
|
instruction.second_argument_is_constant(),
|
||||||
|
position,
|
||||||
|
)?;
|
||||||
|
let remainder = left
|
||||||
|
.modulo(&right)
|
||||||
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
|
self.insert(remainder, instruction.destination(), position)?;
|
||||||
|
}
|
||||||
|
Operation::And => {
|
||||||
|
let left = self.take_constant_or_clone_register(
|
||||||
|
instruction.first_argument(),
|
||||||
|
instruction.first_argument_is_constant(),
|
||||||
|
position,
|
||||||
|
)?;
|
||||||
|
let right = self.take_constant_or_clone_register(
|
||||||
|
instruction.second_argument(),
|
||||||
|
instruction.second_argument_is_constant(),
|
||||||
|
position,
|
||||||
|
)?;
|
||||||
|
let result = left
|
||||||
|
.and(&right)
|
||||||
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
|
self.insert(result, instruction.destination(), position)?;
|
||||||
|
}
|
||||||
|
Operation::Or => {
|
||||||
|
let left = self.take_constant_or_clone_register(
|
||||||
|
instruction.first_argument(),
|
||||||
|
instruction.first_argument_is_constant(),
|
||||||
|
position,
|
||||||
|
)?;
|
||||||
|
let right = self.take_constant_or_clone_register(
|
||||||
|
instruction.second_argument(),
|
||||||
|
instruction.second_argument_is_constant(),
|
||||||
|
position,
|
||||||
|
)?;
|
||||||
|
let result = left
|
||||||
|
.or(&right)
|
||||||
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
|
self.insert(result, instruction.destination(), position)?;
|
||||||
|
}
|
||||||
Operation::Negate => {
|
Operation::Negate => {
|
||||||
let value = self.take_constant_or_clone_register(
|
let value = self.take_constant_or_clone_register(
|
||||||
instruction.first_argument(),
|
instruction.first_argument(),
|
||||||
@ -161,6 +212,18 @@ impl Vm {
|
|||||||
|
|
||||||
self.insert(negated, instruction.destination(), position)?;
|
self.insert(negated, instruction.destination(), position)?;
|
||||||
}
|
}
|
||||||
|
Operation::Not => {
|
||||||
|
let value = self.take_constant_or_clone_register(
|
||||||
|
instruction.first_argument(),
|
||||||
|
instruction.first_argument_is_constant(),
|
||||||
|
position,
|
||||||
|
)?;
|
||||||
|
let result = value
|
||||||
|
.not()
|
||||||
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
|
self.insert(result, instruction.destination(), position)?;
|
||||||
|
}
|
||||||
Operation::Return => {
|
Operation::Return => {
|
||||||
let value = self.pop(position)?;
|
let value = self.pop(position)?;
|
||||||
|
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
use dust_lang::*;
|
use dust_lang::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn long_math() {
|
||||||
|
assert_eq!(run("1 + 2 * 3 - 4 / 2"), Ok(Some(Value::integer(5))));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add() {
|
fn add() {
|
||||||
assert_eq!(run("1 + 2"), Ok(Some(Value::integer(3))));
|
assert_eq!(run("1 + 2"), Ok(Some(Value::integer(3))));
|
||||||
|
Loading…
Reference in New Issue
Block a user