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 operator_position = self.current_position;
|
||||||
let rule = ParseRule::from(&operator.kind());
|
let rule = ParseRule::from(&operator.kind());
|
||||||
|
|
||||||
let (mut instruction, push_args_first) = match operator.kind() {
|
let (mut instruction, is_comparison) = match operator.kind() {
|
||||||
TokenKind::Plus => (Instruction::add(self.current_register, left, 0), true),
|
TokenKind::Plus => (Instruction::add(self.current_register, left, 0), false),
|
||||||
TokenKind::Minus => (Instruction::subtract(self.current_register, left, 0), true),
|
TokenKind::Minus => (Instruction::subtract(self.current_register, left, 0), false),
|
||||||
TokenKind::Star => (Instruction::multiply(self.current_register, left, 0), true),
|
TokenKind::Star => (Instruction::multiply(self.current_register, left, 0), false),
|
||||||
TokenKind::Slash => (Instruction::divide(self.current_register, left, 0), true),
|
TokenKind::Slash => (Instruction::divide(self.current_register, left, 0), false),
|
||||||
TokenKind::Percent => (Instruction::modulo(self.current_register, left, 0), true),
|
TokenKind::Percent => (Instruction::modulo(self.current_register, left, 0), false),
|
||||||
TokenKind::DoubleEqual => (Instruction::equal(true, left, 0), false),
|
TokenKind::DoubleEqual => (Instruction::equal(true, left, 0), true),
|
||||||
TokenKind::DoubleAmpersand => {
|
TokenKind::DoubleAmpersand => {
|
||||||
let and_test = Instruction::test(self.current_register, false);
|
let and_test = Instruction::test(self.current_register, false);
|
||||||
|
|
||||||
(and_test, false)
|
(and_test, true)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParseError::ExpectedTokenMultiple {
|
return Err(ParseError::ExpectedTokenMultiple {
|
||||||
@ -435,7 +435,7 @@ impl<'src> Parser<'src> {
|
|||||||
instruction.set_second_argument_to_constant();
|
instruction.set_second_argument_to_constant();
|
||||||
}
|
}
|
||||||
|
|
||||||
if push_args_first {
|
if !is_comparison {
|
||||||
if push_back_left {
|
if push_back_left {
|
||||||
self.emit_instruction(left_instruction, left_position);
|
self.emit_instruction(left_instruction, left_position);
|
||||||
}
|
}
|
||||||
@ -447,7 +447,7 @@ impl<'src> Parser<'src> {
|
|||||||
self.emit_instruction(instruction, operator_position);
|
self.emit_instruction(instruction, operator_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !push_args_first {
|
if is_comparison {
|
||||||
let push_left_first = self.current_register.saturating_sub(1) == left;
|
let push_left_first = self.current_register.saturating_sub(1) == left;
|
||||||
|
|
||||||
if push_back_left && push_left_first {
|
if push_back_left && push_left_first {
|
||||||
@ -476,23 +476,19 @@ impl<'src> Parser<'src> {
|
|||||||
allow_assignment: bool,
|
allow_assignment: bool,
|
||||||
_allow_return: bool,
|
_allow_return: bool,
|
||||||
) -> Result<(), ParseError> {
|
) -> Result<(), ParseError> {
|
||||||
let token = self.current_token.to_owned();
|
let token = self.current_token;
|
||||||
let start_position = self.current_position;
|
let start_position = self.current_position;
|
||||||
let local_index = self.parse_identifier_from(token, start_position)?;
|
let local_index = self.parse_identifier_from(token, start_position)?;
|
||||||
let is_mutable = self.chunk.get_local(local_index, start_position)?.mutable;
|
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 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()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let (mut previous_instruction, previous_position) =
|
let (mut previous_instruction, previous_position) =
|
||||||
@ -530,12 +526,8 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_identifier_from(
|
fn parse_identifier_from(&mut self, token: Token, position: Span) -> Result<u8, ParseError> {
|
||||||
&mut self,
|
if let Token::Identifier(text) = token {
|
||||||
token: TokenOwned,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<u8, ParseError> {
|
|
||||||
if let TokenOwned::Identifier(text) = token {
|
|
||||||
let identifier = Identifier::new(text);
|
let identifier = Identifier::new(text);
|
||||||
|
|
||||||
if let Ok(local_index) = self.chunk.get_local_index(&identifier, position) {
|
if let Ok(local_index) = self.chunk.get_local_index(&identifier, position) {
|
||||||
@ -616,20 +608,29 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
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.advance()?;
|
||||||
self.parse_expression()?;
|
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::Else)? {
|
||||||
if self.allow(TokenKind::If)? {
|
if self.allow(TokenKind::If)? {
|
||||||
self.parse_if(allow_assignment, false)?;
|
self.parse_if(allow_assignment, allow_return)?;
|
||||||
} else {
|
} 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -650,53 +651,18 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_statement(&mut self, allow_return: bool) -> Result<(), ParseError> {
|
fn parse_statement(&mut self, allow_return: bool) -> Result<(), ParseError> {
|
||||||
let start = self.current_position.0;
|
match self.current_token {
|
||||||
let is_expression = match self.current_token {
|
|
||||||
Token::Let => {
|
Token::Let => {
|
||||||
self.parse_let_statement(true, allow_return)?;
|
self.parse_let_statement(true, allow_return)?;
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
Token::LeftCurlyBrace => {
|
Token::LeftCurlyBrace => {
|
||||||
self.parse_block(true, true)?;
|
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()?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -712,7 +678,7 @@ impl<'src> Parser<'src> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.advance()?;
|
self.allow(TokenKind::Let)?;
|
||||||
|
|
||||||
let is_mutable = self.allow(TokenKind::Mut)?;
|
let is_mutable = self.allow(TokenKind::Mut)?;
|
||||||
let position = self.current_position;
|
let position = self.current_position;
|
||||||
|
@ -18,6 +18,7 @@ fn equality_assignment_long() {
|
|||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(13, 15)),
|
(Instruction::jump(1, true), Span(13, 15)),
|
||||||
(Instruction::load_boolean(0, true, true), Span(20, 24)),
|
(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::load_boolean(0, false, false), Span(34, 39)),
|
||||||
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
(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::jump(1, true), Span(5, 7)),
|
||||||
(Instruction::load_constant(0, 2), Span(12, 13)),
|
(Instruction::load_constant(0, 2), Span(12, 13)),
|
||||||
|
(Instruction::jump(1, true), Span(26, 26)),
|
||||||
(Instruction::load_constant(1, 3), Span(23, 24)),
|
(Instruction::load_constant(1, 3), Span(23, 24)),
|
||||||
(Instruction::r#return(1, 1), Span(0, 26)),
|
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Value::integer(1),
|
Value::integer(1),
|
||||||
@ -99,7 +100,6 @@ fn list_with_expression() {
|
|||||||
),
|
),
|
||||||
(Instruction::load_constant(2, 3), Span(11, 12)),
|
(Instruction::load_constant(2, 3), Span(11, 12)),
|
||||||
(Instruction::load_list(3, 0, 3), Span(0, 13)),
|
(Instruction::load_list(3, 0, 3), Span(0, 13)),
|
||||||
(Instruction::r#return(3, 3), Span(0, 13)),
|
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Value::integer(1),
|
Value::integer(1),
|
||||||
@ -124,7 +124,6 @@ fn list() {
|
|||||||
(Instruction::load_constant(1, 1), Span(4, 5)),
|
(Instruction::load_constant(1, 1), Span(4, 5)),
|
||||||
(Instruction::load_constant(2, 2), Span(7, 8)),
|
(Instruction::load_constant(2, 2), Span(7, 8)),
|
||||||
(Instruction::load_list(3, 0, 3), Span(0, 9)),
|
(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![Value::integer(1), Value::integer(2), Value::integer(3),],
|
||||||
vec![]
|
vec![]
|
||||||
@ -217,7 +216,6 @@ fn parentheses_precedence() {
|
|||||||
*Instruction::multiply(1, 0, 2).set_second_argument_to_constant(),
|
*Instruction::multiply(1, 0, 2).set_second_argument_to_constant(),
|
||||||
Span(8, 9)
|
Span(8, 9)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(1, 1), Span(0, 11)),
|
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2), Value::integer(3)],
|
vec![Value::integer(1), Value::integer(2), Value::integer(3)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -248,7 +246,6 @@ fn math_operator_precedence() {
|
|||||||
Span(14, 15)
|
Span(14, 15)
|
||||||
),
|
),
|
||||||
(Instruction::subtract(1, 0, 3), Span(6, 7)),
|
(Instruction::subtract(1, 0, 3), Span(6, 7)),
|
||||||
(Instruction::r#return(1, 1), Span(0, 17)),
|
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Value::integer(1),
|
Value::integer(1),
|
||||||
@ -284,10 +281,14 @@ fn and() {
|
|||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
(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::jump(1, true), Span(5, 7)),
|
||||||
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||||
(Instruction::r#return(1, 1), Span(0, 13)),
|
|
||||||
],
|
],
|
||||||
vec![],
|
vec![],
|
||||||
vec![]
|
vec![]
|
||||||
@ -300,15 +301,12 @@ fn divide() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("1 / 2"),
|
parse("1 / 2"),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![(
|
||||||
(
|
*Instruction::divide(0, 0, 1)
|
||||||
*Instruction::divide(0, 0, 1)
|
.set_first_argument_to_constant()
|
||||||
.set_first_argument_to_constant()
|
.set_second_argument_to_constant(),
|
||||||
.set_second_argument_to_constant(),
|
Span(2, 3)
|
||||||
Span(2, 3)
|
),],
|
||||||
),
|
|
||||||
(Instruction::r#return(0, 0), Span(0, 5)),
|
|
||||||
],
|
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
@ -320,15 +318,12 @@ fn multiply() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("1 * 2"),
|
parse("1 * 2"),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![(
|
||||||
(
|
*Instruction::multiply(0, 0, 1)
|
||||||
*Instruction::multiply(0, 0, 1)
|
.set_first_argument_to_constant()
|
||||||
.set_first_argument_to_constant()
|
.set_second_argument_to_constant(),
|
||||||
.set_second_argument_to_constant(),
|
Span(2, 3)
|
||||||
Span(2, 3)
|
),],
|
||||||
),
|
|
||||||
(Instruction::r#return(0, 0), Span(0, 5)),
|
|
||||||
],
|
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
@ -340,15 +335,12 @@ fn add() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("1 + 2"),
|
parse("1 + 2"),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![(
|
||||||
(
|
*Instruction::add(0, 0, 1)
|
||||||
*Instruction::add(0, 0, 1)
|
.set_first_argument_to_constant()
|
||||||
.set_first_argument_to_constant()
|
.set_second_argument_to_constant(),
|
||||||
.set_second_argument_to_constant(),
|
Span(2, 3)
|
||||||
Span(2, 3)
|
),],
|
||||||
),
|
|
||||||
(Instruction::r#return(0, 0), Span(0, 5)),
|
|
||||||
],
|
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
@ -360,15 +352,12 @@ fn subtract() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("1 - 2"),
|
parse("1 - 2"),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![(
|
||||||
(
|
*Instruction::subtract(0, 0, 1)
|
||||||
*Instruction::subtract(0, 0, 1)
|
.set_first_argument_to_constant()
|
||||||
.set_first_argument_to_constant()
|
.set_second_argument_to_constant(),
|
||||||
.set_second_argument_to_constant(),
|
Span(2, 3)
|
||||||
Span(2, 3)
|
),],
|
||||||
),
|
|
||||||
(Instruction::r#return(0, 0), Span(0, 5)),
|
|
||||||
],
|
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
@ -380,10 +369,7 @@ fn constant() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("42"),
|
parse("42"),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![(Instruction::load_constant(0, 0), Span(0, 2)),],
|
||||||
(Instruction::load_constant(0, 0), Span(0, 2)),
|
|
||||||
(Instruction::r#return(0, 0), Span(0, 2)),
|
|
||||||
],
|
|
||||||
vec![Value::integer(42)],
|
vec![Value::integer(42)],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
|
@ -292,9 +292,13 @@ impl Vm {
|
|||||||
self.ip = new_ip;
|
self.ip = new_ip;
|
||||||
}
|
}
|
||||||
Operation::Return => {
|
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