Pass tests
This commit is contained in:
parent
dee09d3583
commit
d4a8a65096
@ -383,17 +383,17 @@ impl<'src> Parser<'src> {
|
||||
let operator_position = self.current_position;
|
||||
let rule = ParseRule::from(&operator.kind());
|
||||
|
||||
let (mut instruction, push_args_first) = match operator.kind() {
|
||||
TokenKind::Plus => (Instruction::add(self.current_register, left, 0), true),
|
||||
TokenKind::Minus => (Instruction::subtract(self.current_register, left, 0), true),
|
||||
TokenKind::Star => (Instruction::multiply(self.current_register, left, 0), true),
|
||||
TokenKind::Slash => (Instruction::divide(self.current_register, left, 0), true),
|
||||
TokenKind::Percent => (Instruction::modulo(self.current_register, left, 0), true),
|
||||
TokenKind::DoubleEqual => (Instruction::equal(true, left, 0), false),
|
||||
let (mut instruction, is_comparison) = match operator.kind() {
|
||||
TokenKind::Plus => (Instruction::add(self.current_register, left, 0), false),
|
||||
TokenKind::Minus => (Instruction::subtract(self.current_register, left, 0), false),
|
||||
TokenKind::Star => (Instruction::multiply(self.current_register, left, 0), false),
|
||||
TokenKind::Slash => (Instruction::divide(self.current_register, left, 0), false),
|
||||
TokenKind::Percent => (Instruction::modulo(self.current_register, left, 0), false),
|
||||
TokenKind::DoubleEqual => (Instruction::equal(true, left, 0), true),
|
||||
TokenKind::DoubleAmpersand => {
|
||||
let and_test = Instruction::test(self.current_register, false);
|
||||
|
||||
(and_test, false)
|
||||
(and_test, true)
|
||||
}
|
||||
_ => {
|
||||
return Err(ParseError::ExpectedTokenMultiple {
|
||||
@ -435,7 +435,7 @@ impl<'src> Parser<'src> {
|
||||
instruction.set_second_argument_to_constant();
|
||||
}
|
||||
|
||||
if push_args_first {
|
||||
if !is_comparison {
|
||||
if push_back_left {
|
||||
self.emit_instruction(left_instruction, left_position);
|
||||
}
|
||||
@ -447,7 +447,7 @@ impl<'src> Parser<'src> {
|
||||
self.emit_instruction(instruction, operator_position);
|
||||
}
|
||||
|
||||
if !push_args_first {
|
||||
if is_comparison {
|
||||
let push_left_first = self.current_register.saturating_sub(1) == left;
|
||||
|
||||
if push_back_left && push_left_first {
|
||||
@ -476,23 +476,19 @@ impl<'src> Parser<'src> {
|
||||
allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
let token = self.current_token.to_owned();
|
||||
let token = self.current_token;
|
||||
let start_position = self.current_position;
|
||||
let local_index = self.parse_identifier_from(token, start_position)?;
|
||||
let is_mutable = self.chunk.get_local(local_index, start_position)?.mutable;
|
||||
|
||||
self.advance()?;
|
||||
if !is_mutable {
|
||||
return Err(ParseError::CannotMutateImmutableVariable {
|
||||
identifier: self.chunk.get_identifier(local_index).cloned().unwrap(),
|
||||
position: start_position,
|
||||
});
|
||||
}
|
||||
|
||||
if allow_assignment && self.allow(TokenKind::Equal)? {
|
||||
if !is_mutable {
|
||||
let identifier = self.chunk.get_identifier(local_index).cloned().unwrap();
|
||||
|
||||
return Err(ParseError::CannotMutateImmutableVariable {
|
||||
identifier,
|
||||
position: start_position,
|
||||
});
|
||||
}
|
||||
|
||||
self.parse_expression()?;
|
||||
|
||||
let (mut previous_instruction, previous_position) =
|
||||
@ -530,12 +526,8 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_identifier_from(
|
||||
&mut self,
|
||||
token: TokenOwned,
|
||||
position: Span,
|
||||
) -> Result<u8, ParseError> {
|
||||
if let TokenOwned::Identifier(text) = token {
|
||||
fn parse_identifier_from(&mut self, token: Token, position: Span) -> Result<u8, ParseError> {
|
||||
if let Token::Identifier(text) = token {
|
||||
let identifier = Identifier::new(text);
|
||||
|
||||
if let Ok(local_index) = self.chunk.get_local_index(&identifier, position) {
|
||||
@ -616,20 +608,29 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_if(&mut self, allow_assignment: bool, _allow_return: bool) -> Result<(), ParseError> {
|
||||
fn parse_if(&mut self, allow_assignment: bool, allow_return: bool) -> Result<(), ParseError> {
|
||||
self.advance()?;
|
||||
self.parse_expression()?;
|
||||
self.parse_block(allow_assignment, allow_return)?;
|
||||
|
||||
self.parse_block(allow_assignment, false)?;
|
||||
let jump_start = self.current_register;
|
||||
let jump_index = self.chunk.len();
|
||||
|
||||
if self.allow(TokenKind::Else)? {
|
||||
if self.allow(TokenKind::If)? {
|
||||
self.parse_if(allow_assignment, false)?;
|
||||
self.parse_if(allow_assignment, allow_return)?;
|
||||
} else {
|
||||
self.parse_block(allow_assignment, false)?;
|
||||
self.parse_block(allow_assignment, allow_return)?;
|
||||
}
|
||||
}
|
||||
|
||||
let jump_end = self.current_register;
|
||||
let jump_distance = (jump_end - jump_start).max(1);
|
||||
let jump = Instruction::jump(jump_distance, true);
|
||||
|
||||
self.chunk
|
||||
.insert_instruction(jump_index, jump, self.current_position);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -650,53 +651,18 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
fn parse_statement(&mut self, allow_return: bool) -> Result<(), ParseError> {
|
||||
let start = self.current_position.0;
|
||||
let is_expression = match self.current_token {
|
||||
match self.current_token {
|
||||
Token::Let => {
|
||||
self.parse_let_statement(true, allow_return)?;
|
||||
|
||||
false
|
||||
}
|
||||
Token::LeftCurlyBrace => {
|
||||
self.parse_block(true, true)?;
|
||||
|
||||
let last_operation = self.chunk.get_last_operation(self.current_position)?;
|
||||
let ends_in_return = last_operation == Operation::Return;
|
||||
|
||||
if ends_in_return {
|
||||
self.chunk.pop_instruction(self.current_position)?;
|
||||
}
|
||||
|
||||
ends_in_return
|
||||
}
|
||||
_ => {
|
||||
self.parse_expression()?;
|
||||
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
if !allow_return || !is_expression {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (last_instruction, _) = *self.chunk.get_previous(self.current_position)?;
|
||||
let ends_in_assignment = matches!(
|
||||
last_instruction.operation(),
|
||||
Operation::DefineLocal | Operation::SetLocal
|
||||
);
|
||||
let has_semicolon = self.allow(TokenKind::Semicolon)?;
|
||||
|
||||
if !ends_in_assignment && !has_semicolon {
|
||||
let end = self.previous_position.1;
|
||||
let return_register = last_instruction.destination();
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::r#return(return_register, return_register),
|
||||
Span(start, end),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -712,7 +678,7 @@ impl<'src> Parser<'src> {
|
||||
});
|
||||
}
|
||||
|
||||
self.advance()?;
|
||||
self.allow(TokenKind::Let)?;
|
||||
|
||||
let is_mutable = self.allow(TokenKind::Mut)?;
|
||||
let position = self.current_position;
|
||||
|
@ -18,6 +18,7 @@ fn equality_assignment_long() {
|
||||
),
|
||||
(Instruction::jump(1, true), Span(13, 15)),
|
||||
(Instruction::load_boolean(0, true, true), Span(20, 24)),
|
||||
(Instruction::jump(1, true), Span(41, 42)),
|
||||
(Instruction::load_boolean(0, false, false), Span(34, 39)),
|
||||
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
||||
],
|
||||
@ -68,8 +69,8 @@ fn if_else_expression() {
|
||||
),
|
||||
(Instruction::jump(1, true), Span(5, 7)),
|
||||
(Instruction::load_constant(0, 2), Span(12, 13)),
|
||||
(Instruction::jump(1, true), Span(26, 26)),
|
||||
(Instruction::load_constant(1, 3), Span(23, 24)),
|
||||
(Instruction::r#return(1, 1), Span(0, 26)),
|
||||
],
|
||||
vec![
|
||||
Value::integer(1),
|
||||
@ -99,7 +100,6 @@ fn list_with_expression() {
|
||||
),
|
||||
(Instruction::load_constant(2, 3), Span(11, 12)),
|
||||
(Instruction::load_list(3, 0, 3), Span(0, 13)),
|
||||
(Instruction::r#return(3, 3), Span(0, 13)),
|
||||
],
|
||||
vec![
|
||||
Value::integer(1),
|
||||
@ -124,7 +124,6 @@ fn list() {
|
||||
(Instruction::load_constant(1, 1), Span(4, 5)),
|
||||
(Instruction::load_constant(2, 2), Span(7, 8)),
|
||||
(Instruction::load_list(3, 0, 3), Span(0, 9)),
|
||||
(Instruction::r#return(3, 3), Span(0, 9)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2), Value::integer(3),],
|
||||
vec![]
|
||||
@ -217,7 +216,6 @@ fn parentheses_precedence() {
|
||||
*Instruction::multiply(1, 0, 2).set_second_argument_to_constant(),
|
||||
Span(8, 9)
|
||||
),
|
||||
(Instruction::r#return(1, 1), Span(0, 11)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2), Value::integer(3)],
|
||||
vec![]
|
||||
@ -248,7 +246,6 @@ fn math_operator_precedence() {
|
||||
Span(14, 15)
|
||||
),
|
||||
(Instruction::subtract(1, 0, 3), Span(6, 7)),
|
||||
(Instruction::r#return(1, 1), Span(0, 17)),
|
||||
],
|
||||
vec![
|
||||
Value::integer(1),
|
||||
@ -284,10 +281,14 @@ fn and() {
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
||||
(Instruction::test(0, true), Span(5, 7)),
|
||||
(
|
||||
*Instruction::test(0, true)
|
||||
.set_second_argument_to_constant()
|
||||
.set_first_argument_to_constant(),
|
||||
Span(5, 7)
|
||||
),
|
||||
(Instruction::jump(1, true), Span(5, 7)),
|
||||
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||
(Instruction::r#return(1, 1), Span(0, 13)),
|
||||
],
|
||||
vec![],
|
||||
vec![]
|
||||
@ -300,15 +301,12 @@ 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(0, 0), Span(0, 5)),
|
||||
],
|
||||
vec![(
|
||||
*Instruction::divide(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
))
|
||||
@ -320,15 +318,12 @@ 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(0, 0), Span(0, 5)),
|
||||
],
|
||||
vec![(
|
||||
*Instruction::multiply(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
))
|
||||
@ -340,15 +335,12 @@ fn add() {
|
||||
assert_eq!(
|
||||
parse("1 + 2"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(
|
||||
*Instruction::add(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::r#return(0, 0), Span(0, 5)),
|
||||
],
|
||||
vec![(
|
||||
*Instruction::add(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
))
|
||||
@ -360,15 +352,12 @@ fn subtract() {
|
||||
assert_eq!(
|
||||
parse("1 - 2"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(
|
||||
*Instruction::subtract(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::r#return(0, 0), Span(0, 5)),
|
||||
],
|
||||
vec![(
|
||||
*Instruction::subtract(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
))
|
||||
@ -380,10 +369,7 @@ fn constant() {
|
||||
assert_eq!(
|
||||
parse("42"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0), Span(0, 2)),
|
||||
(Instruction::r#return(0, 0), Span(0, 2)),
|
||||
],
|
||||
vec![(Instruction::load_constant(0, 0), Span(0, 2)),],
|
||||
vec![Value::integer(42)],
|
||||
vec![]
|
||||
))
|
||||
|
@ -292,9 +292,13 @@ impl Vm {
|
||||
self.ip = new_ip;
|
||||
}
|
||||
Operation::Return => {
|
||||
let return_value = self.pop(position)?;
|
||||
let start_register = instruction.destination();
|
||||
let end_register = instruction.first_argument();
|
||||
let return_value_count = end_register - start_register;
|
||||
|
||||
return Ok(Some(return_value));
|
||||
if return_value_count == 1 {
|
||||
return Ok(Some(self.take(start_register, position)?));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user