1
0

Refactor parser and chunk to have fewer errors and less retrospection

This commit is contained in:
Jeff 2024-10-19 12:05:20 -04:00
parent 8c79157fa7
commit 3f8b554a86
3 changed files with 281 additions and 332 deletions

View File

@ -6,7 +6,7 @@ use std::{
use colored::Colorize;
use serde::{Deserialize, Serialize};
use crate::{AnnotatedError, Identifier, Instruction, Operation, Span, Type, Value};
use crate::{AnnotatedError, Identifier, Instruction, Span, Type, Value};
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Chunk {
@ -69,63 +69,6 @@ impl Chunk {
self.instructions.insert(index, (instruction, position));
}
pub fn pop_instruction(&mut self, position: Span) -> Result<(Instruction, Span), ChunkError> {
self.instructions
.pop()
.ok_or(ChunkError::InstructionUnderflow { position })
}
pub fn get_last_instruction(
&self,
position: Span,
) -> Result<(&Instruction, &Span), ChunkError> {
let (instruction, position) = self
.instructions
.last()
.ok_or_else(|| ChunkError::InstructionUnderflow { position })?;
Ok((instruction, position))
}
pub fn get_last_n_instructions<const N: usize>(&self) -> [Option<(&Instruction, &Span)>; N] {
let mut instructions = [None; N];
for (index, (instruction, position)) in self.instructions.iter().rev().enumerate().take(N) {
instructions[index] = Some((instruction, position));
}
instructions
}
pub fn find_last_instruction(&mut self, operation: Operation) -> Option<usize> {
self.instructions
.iter()
.enumerate()
.rev()
.find_map(|(index, (instruction, _))| {
if instruction.operation() == operation {
Some(index)
} else {
None
}
})
}
pub fn get_last_operation(&self, position: Span) -> Result<Operation, ChunkError> {
self.get_last_instruction(position)
.map(|(instruction, _)| instruction.operation())
}
pub fn get_last_n_operations<const N: usize>(&self) -> [Option<Operation>; N] {
let mut operations = [None; N];
for (index, (instruction, _)) in self.instructions.iter().rev().enumerate().take(N) {
operations[index] = Some(instruction.operation());
}
operations
}
pub fn take_constants(self) -> Vec<Value> {
self.constants
}
@ -634,10 +577,6 @@ pub enum ChunkError {
offset: usize,
position: Span,
},
ConstantAlreadyUsed {
index: usize,
position: Span,
},
ConstantOverflow {
position: Span,
},
@ -645,9 +584,6 @@ pub enum ChunkError {
index: usize,
position: Span,
},
InstructionUnderflow {
position: Span,
},
LocalIndexOutOfBounds {
index: usize,
position: Span,
@ -669,10 +605,8 @@ impl AnnotatedError for ChunkError {
fn description(&self) -> &'static str {
match self {
ChunkError::InstructionIndexOfBounds { .. } => "Instruction index out of bounds",
ChunkError::ConstantAlreadyUsed { .. } => "Constant already used",
ChunkError::ConstantOverflow { .. } => "Constant overflow",
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
ChunkError::InstructionUnderflow { .. } => "Instruction underflow",
ChunkError::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
ChunkError::LocalOverflow { .. } => "Local overflow",
ChunkError::IdentifierNotFound { .. } => "Identifier not found",
@ -684,13 +618,9 @@ impl AnnotatedError for ChunkError {
ChunkError::InstructionIndexOfBounds { offset, .. } => {
Some(format!("Instruction index: {}", offset))
}
ChunkError::ConstantAlreadyUsed { index, .. } => {
Some(format!("Constant index: {}", index))
}
ChunkError::ConstantIndexOutOfBounds { index, .. } => {
Some(format!("Constant index: {}", index))
}
ChunkError::InstructionUnderflow { .. } => None,
ChunkError::LocalIndexOutOfBounds { index, .. } => {
Some(format!("Local index: {}", index))
}
@ -705,10 +635,8 @@ impl AnnotatedError for ChunkError {
fn position(&self) -> Span {
match self {
ChunkError::InstructionIndexOfBounds { position, .. } => *position,
ChunkError::ConstantAlreadyUsed { position, .. } => *position,
ChunkError::ConstantIndexOutOfBounds { position, .. } => *position,
ChunkError::IdentifierNotFound { position, .. } => *position,
ChunkError::InstructionUnderflow { position, .. } => *position,
ChunkError::LocalIndexOutOfBounds { position, .. } => *position,
ChunkError::LocalOverflow { position, .. } => *position,
ChunkError::ConstantOverflow { position, .. } => *position,

View File

@ -318,36 +318,21 @@ impl Instruction {
self
}
pub fn is_expression(&self) -> bool {
let operation = self.operation();
if operation.is_math() {
return if !self.b_is_constant() {
self.a() != self.b()
} else if !self.c_is_constant() {
self.a() != self.c()
} else {
true
};
}
pub fn yields_value(&self) -> bool {
matches!(
operation,
Operation::Not
| Operation::Negate
| Operation::Equal
| Operation::Less
| Operation::LessEqual
| Operation::Add
| Operation::Subtract
| Operation::Multiply
self.operation(),
Operation::Add
| Operation::Call
| Operation::Divide
| Operation::Modulo
| Operation::Test
| Operation::GetLocal
| Operation::LoadBoolean
| Operation::LoadConstant
| Operation::LoadList
| Operation::Modulo
| Operation::Multiply
| Operation::Negate
| Operation::Not
| Operation::Subtract
)
}

View File

@ -2,6 +2,7 @@ use std::{
fmt::{self, Display, Formatter},
mem::replace,
num::{ParseFloatError, ParseIntError},
vec,
};
use colored::Colorize;
@ -34,10 +35,11 @@ pub fn parse(source: &str) -> Result<Chunk, DustError> {
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize)]
pub struct Parser<'src> {
chunk: Chunk,
lexer: Lexer<'src>,
chunk: Chunk,
current_register: u8,
current_statement: Vec<(Instruction, Span)>,
minimum_register: u8,
current_token: Token<'src>,
current_position: Span,
@ -62,7 +64,8 @@ impl<'src> Parser<'src> {
Ok(Parser {
lexer,
chunk: Chunk::new(),
current_register: 0,
current_statement: Vec::new(),
minimum_register: 0,
current_token,
current_position,
previous_token: Token::Eof,
@ -82,18 +85,18 @@ impl<'src> Parser<'src> {
matches!(self.current_token, Token::Eof)
}
fn increment_register(&mut self) -> Result<(), ParseError> {
let current = self.current_register;
if current == u8::MAX {
Err(ParseError::RegisterOverflow {
position: self.current_position,
})
fn next_register(&mut self) -> u8 {
self.current_statement
.iter()
.rev()
.find_map(|(instruction, _)| {
if instruction.yields_value() {
Some(instruction.a() + 1)
} else {
self.current_register += 1;
Ok(())
None
}
})
.unwrap_or(self.minimum_register)
}
fn advance(&mut self) -> Result<(), ParseError> {
@ -137,23 +140,73 @@ impl<'src> Parser<'src> {
}
}
fn emit_instruction(&mut self, instruction: Instruction, position: Span) {
fn push_instruction(&mut self, instruction: Instruction, position: Span) {
log::debug!(
"Emitting {} at {}",
instruction.operation().to_string().bold(),
position.to_string()
);
self.current_statement.push((instruction, position));
}
fn commit_current_statement(&mut self) {
let next_register = self.next_register();
for (instruction, position) in self.current_statement.drain(..) {
self.chunk.push_instruction(instruction, position);
}
self.minimum_register = next_register;
}
fn get_staged_operations<const COUNT: usize>(&self) -> Option<[(Operation, Span); COUNT]> {
if self.current_statement.len() < COUNT {
return None;
}
let mut operations = [(Operation::Return, Span(0, 0)); COUNT];
for (index, (instruction, position)) in
self.current_statement.iter().rev().enumerate().take(COUNT)
{
operations[index] = (instruction.operation(), *position);
}
Some(operations)
}
fn get_staged_jump_mut(&mut self) -> Option<&mut Instruction> {
self.current_statement
.iter_mut()
.rev()
.find(|(instruction, _)| matches!(instruction.operation(), Operation::Jump))
.map(|(instruction, _)| instruction)
}
fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), ParseError> {
self.latest_value_type = Some(value.r#type());
let constant_index = self.chunk.push_constant(value, position)?;
let register = self.next_register();
self.emit_instruction(
Instruction::load_constant(self.current_register, constant_index, false),
if let Some(instruction) = self
.current_statement
.last()
.map(|(instruction, _)| instruction)
{
if let Operation::LoadConstant = instruction.operation() {
self.push_instruction(
Instruction::load_constant(instruction.a(), constant_index, false),
position,
);
return Ok(());
}
}
self.push_instruction(
Instruction::load_constant(register, constant_index, false),
position,
);
@ -167,14 +220,26 @@ impl<'src> Parser<'src> {
self.advance()?;
let boolean = text.parse::<bool>().unwrap();
let register = if let Some(instruction) = self
.current_statement
.last()
.map(|(instruction, _)| instruction)
{
if let Operation::LoadBoolean = instruction.operation() {
instruction.a()
} else {
self.next_register()
}
} else {
self.next_register()
};
self.emit_instruction(
Instruction::load_boolean(self.current_register, boolean, false),
self.push_instruction(
Instruction::load_boolean(register, boolean, false),
position,
);
self.latest_value_type = Some(Type::Boolean);
self.parsed_expression = true;
Ok(())
} else {
@ -198,8 +263,6 @@ impl<'src> Parser<'src> {
self.emit_constant(value, position)?;
self.parsed_expression = true;
Ok(())
} else {
Err(ParseError::ExpectedToken {
@ -220,8 +283,6 @@ impl<'src> Parser<'src> {
self.emit_constant(value, position)?;
self.parsed_expression = true;
Ok(())
} else {
Err(ParseError::ExpectedToken {
@ -248,8 +309,6 @@ impl<'src> Parser<'src> {
self.emit_constant(value, position)?;
self.parsed_expression = true;
Ok(())
} else {
Err(ParseError::ExpectedToken {
@ -276,8 +335,6 @@ impl<'src> Parser<'src> {
self.emit_constant(value, position)?;
self.parsed_expression = true;
Ok(())
} else {
Err(ParseError::ExpectedToken {
@ -298,8 +355,6 @@ impl<'src> Parser<'src> {
self.emit_constant(value, position)?;
self.parsed_expression = true;
Ok(())
} else {
Err(ParseError::ExpectedToken {
@ -315,8 +370,6 @@ impl<'src> Parser<'src> {
self.parse_expression()?;
self.expect(Token::RightParenthesis)?;
self.parsed_expression = true;
Ok(())
}
@ -328,7 +381,12 @@ impl<'src> Parser<'src> {
self.parse_expression()?;
let (previous_instruction, previous_position) =
self.chunk.pop_instruction(self.current_position)?;
self.current_statement
.pop()
.ok_or_else(|| ParseError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let (push_back, is_constant, argument) = {
match previous_instruction.operation() {
@ -346,12 +404,13 @@ impl<'src> Parser<'src> {
};
if push_back {
self.increment_register()?;
self.push_instruction(previous_instruction, previous_position);
}
let register = self.next_register();
let mut instruction = match operator.kind() {
TokenKind::Bang => Instruction::not(self.current_register, argument),
TokenKind::Minus => Instruction::negate(self.current_register, argument),
TokenKind::Bang => Instruction::not(register, argument),
TokenKind::Minus => Instruction::negate(register, argument),
_ => {
return Err(ParseError::ExpectedTokenMultiple {
expected: &[TokenKind::Bang, TokenKind::Minus],
@ -365,13 +424,7 @@ impl<'src> Parser<'src> {
instruction.set_b_is_constant();
}
if push_back {
self.emit_instruction(previous_instruction, previous_position);
}
self.emit_instruction(instruction, operator_position);
self.parsed_expression = true;
self.push_instruction(instruction, operator_position);
Ok(())
}
@ -415,18 +468,25 @@ impl<'src> Parser<'src> {
fn parse_math_binary(&mut self) -> Result<(), ParseError> {
let (left_instruction, left_position) =
self.chunk.pop_instruction(self.current_position)?;
self.current_statement
.pop()
.ok_or_else(|| ParseError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let (push_back_left, left_is_constant, left_is_mutable_local, left) =
self.handle_binary_argument(&left_instruction)?;
if push_back_left {
self.push_instruction(left_instruction, left_position);
}
let operator = self.current_token;
let operator_position = self.current_position;
let rule = ParseRule::from(&operator);
if let TokenKind::PlusEqual
| TokenKind::MinusEqual
| TokenKind::StarEqual
| TokenKind::SlashEqual = operator.kind()
if let Token::PlusEqual | Token::MinusEqual | Token::StarEqual | Token::SlashEqual =
operator
{
if !left_is_mutable_local {
return Err(ParseError::ExpectedMutableVariable {
@ -440,32 +500,36 @@ impl<'src> Parser<'src> {
self.parse_sub_expression(&rule.precedence)?;
let (right_instruction, right_position) =
self.chunk.pop_instruction(self.current_position)?;
let (push_back_right, right_is_constant, right_is_mutable_local, right) =
self.current_statement
.pop()
.ok_or_else(|| ParseError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let (push_back_right, right_is_constant, _, right) =
self.handle_binary_argument(&right_instruction)?;
if push_back_right {
self.push_instruction(right_instruction, right_position);
}
let register = if left_is_mutable_local {
left
} else if right_is_mutable_local {
right
} else {
let current = self.current_register;
self.increment_register()?;
current
self.next_register()
};
let (mut new_instruction, is_expression) = match operator.kind() {
TokenKind::Plus => (Instruction::add(register, left, right), true),
TokenKind::PlusEqual => (Instruction::add(register, left, right), false),
TokenKind::Minus => (Instruction::subtract(register, left, right), true),
TokenKind::MinusEqual => (Instruction::subtract(register, left, right), false),
TokenKind::Star => (Instruction::multiply(register, left, right), true),
TokenKind::StarEqual => (Instruction::multiply(register, left, right), false),
TokenKind::Slash => (Instruction::divide(register, left, right), true),
TokenKind::SlashEqual => (Instruction::divide(register, left, right), false),
TokenKind::Percent => (Instruction::modulo(register, left, right), true),
let mut new_instruction = match operator.kind() {
TokenKind::Plus => Instruction::add(register, left, right),
TokenKind::PlusEqual => Instruction::add(register, left, right),
TokenKind::Minus => Instruction::subtract(register, left, right),
TokenKind::MinusEqual => Instruction::subtract(register, left, right),
TokenKind::Star => Instruction::multiply(register, left, right),
TokenKind::StarEqual => Instruction::multiply(register, left, right),
TokenKind::Slash => Instruction::divide(register, left, right),
TokenKind::SlashEqual => Instruction::divide(register, left, right),
TokenKind::Percent => Instruction::modulo(register, left, right),
TokenKind::PercentEqual => Instruction::modulo(register, left, right),
_ => {
return Err(ParseError::ExpectedTokenMultiple {
expected: &[
@ -478,6 +542,7 @@ impl<'src> Parser<'src> {
TokenKind::Slash,
TokenKind::SlashEqual,
TokenKind::Percent,
TokenKind::PercentEqual,
],
found: operator.to_owned(),
position: operator_position,
@ -485,8 +550,6 @@ impl<'src> Parser<'src> {
}
};
self.parsed_expression = is_expression;
if left_is_constant {
new_instruction.set_b_is_constant();
}
@ -495,50 +558,15 @@ impl<'src> Parser<'src> {
new_instruction.set_c_is_constant();
}
let mut instructions = if !push_back_left && !push_back_right {
self.emit_instruction(new_instruction, operator_position);
return Ok(());
} else if push_back_right && !push_back_left {
vec![
(right_instruction, right_position),
(new_instruction, operator_position),
]
} else if push_back_left && !push_back_right {
vec![
(left_instruction, left_position),
(new_instruction, operator_position),
]
} else {
vec![
(new_instruction, operator_position),
(left_instruction, left_position),
(right_instruction, right_position),
]
};
while let Ok(operation) = self.chunk.get_last_operation(operator_position) {
if operation.is_math() {
let (instruction, position) = self.chunk.pop_instruction(self.current_position)?;
instructions.push((instruction, position));
} else {
break;
}
}
instructions.sort_by_key(|(instruction, _)| instruction.a());
for (instruction, position) in instructions {
self.emit_instruction(instruction, position);
}
self.push_instruction(new_instruction, operator_position);
Ok(())
}
fn parse_comparison_binary(&mut self) -> Result<(), ParseError> {
if let [Some(Operation::Jump), Some(Operation::Equal) | Some(Operation::Less) | Some(Operation::LessEqual)] =
self.chunk.get_last_n_operations()
if let Some(
[(Operation::Jump, _), (Operation::Equal, _) | (Operation::Less, _) | (Operation::LessEqual, _)],
) = self.get_staged_operations()
{
return Err(ParseError::CannotChainComparison {
position: self.current_position,
@ -546,7 +574,12 @@ impl<'src> Parser<'src> {
}
let (left_instruction, left_position) =
self.chunk.pop_instruction(self.current_position)?;
self.current_statement
.pop()
.ok_or_else(|| ParseError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let (push_back_left, left_is_constant, _, left) =
self.handle_binary_argument(&left_instruction)?;
@ -558,7 +591,12 @@ impl<'src> Parser<'src> {
self.parse_sub_expression(&rule.precedence)?;
let (right_instruction, right_position) =
self.chunk.pop_instruction(self.current_position)?;
self.current_statement
.pop()
.ok_or_else(|| ParseError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let (push_back_right, right_is_constant, _, right) =
self.handle_binary_argument(&right_instruction)?;
@ -586,8 +624,6 @@ impl<'src> Parser<'src> {
}
};
self.parsed_expression = true;
if left_is_constant {
instruction.set_b_is_constant();
}
@ -597,22 +633,27 @@ impl<'src> Parser<'src> {
}
if push_back_left {
self.emit_instruction(left_instruction, left_position);
self.push_instruction(left_instruction, left_position);
}
if push_back_right {
self.emit_instruction(right_instruction, right_position);
self.push_instruction(right_instruction, right_position);
}
self.emit_instruction(instruction, operator_position);
self.emit_instruction(Instruction::jump(1, true), operator_position);
self.push_instruction(instruction, operator_position);
self.push_instruction(Instruction::jump(1, true), operator_position);
Ok(())
}
fn parse_logical_binary(&mut self) -> Result<(), ParseError> {
let (left_instruction, left_position) =
self.chunk.pop_instruction(self.current_position)?;
self.current_statement
.pop()
.ok_or_else(|| ParseError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let operator = self.current_token;
let operator_position = self.current_position;
@ -630,20 +671,12 @@ impl<'src> Parser<'src> {
}
};
self.increment_register()?;
self.advance()?;
self.push_instruction(left_instruction, left_position);
self.push_instruction(instruction, operator_position);
self.push_instruction(Instruction::jump(1, true), operator_position);
self.parse_sub_expression(&rule.precedence)?;
let (right_instruction, right_position) =
self.chunk.pop_instruction(self.current_position)?;
self.emit_instruction(left_instruction, left_position);
self.emit_instruction(instruction, operator_position);
self.emit_instruction(Instruction::jump(1, true), operator_position);
self.emit_instruction(right_instruction, right_position);
self.parsed_expression = true;
Ok(())
}
@ -676,8 +709,13 @@ impl<'src> Parser<'src> {
self.parse_expression()?;
let (mut previous_instruction, previous_position) =
self.chunk.pop_instruction(self.current_position)?;
let (mut previous_instruction, previous_position) = self
.current_statement
.pop()
.ok_or_else(|| ParseError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
if previous_instruction.operation().is_math() {
let register_index = self
@ -688,26 +726,25 @@ impl<'src> Parser<'src> {
log::trace!("Condensing SET_LOCAL to binary math expression");
previous_instruction.set_a(register_index);
self.emit_instruction(previous_instruction, self.current_position);
self.push_instruction(previous_instruction, self.current_position);
return Ok(());
}
self.emit_instruction(previous_instruction, previous_position);
self.emit_instruction(
Instruction::set_local(self.current_register, local_index),
let register = self.next_register();
self.push_instruction(previous_instruction, previous_position);
self.push_instruction(
Instruction::set_local(register, local_index),
start_position,
);
self.increment_register()?;
self.parsed_expression = false;
} else {
self.emit_instruction(
Instruction::get_local(self.current_register, local_index),
let register = self.next_register();
self.push_instruction(
Instruction::get_local(register, local_index),
self.previous_position,
);
self.parsed_expression = true;
}
Ok(())
@ -771,20 +808,19 @@ impl<'src> Parser<'src> {
self.advance()?;
let start_register = self.current_register;
let start_register = self.next_register();
while !self.allow(Token::RightSquareBrace)? && !self.is_eof() {
let next_register = self.current_register;
let expected_register = self.next_register();
self.parse_expression()?;
self.commit_current_statement();
if let Operation::LoadConstant = self.chunk.get_last_operation(self.current_position)? {
self.increment_register()?;
}
let actual_register = self.next_register() - 1;
if next_register != self.current_register.saturating_sub(1) {
self.emit_instruction(
Instruction::close(next_register, self.current_register.saturating_sub(1)),
if expected_register < actual_register {
self.push_instruction(
Instruction::close(expected_register, actual_register),
self.current_position,
);
}
@ -792,16 +828,14 @@ impl<'src> Parser<'src> {
self.allow(Token::Comma)?;
}
let end_register = self.current_register.saturating_sub(1);
let to_register = self.next_register();
let end_register = to_register.saturating_sub(1);
let end = self.current_position.1;
self.emit_instruction(
Instruction::load_list(self.current_register, start_register, end_register),
self.push_instruction(
Instruction::load_list(to_register, start_register, end_register),
Span(start, end),
);
self.increment_register()?;
self.parsed_expression = true;
Ok(())
}
@ -810,17 +844,6 @@ impl<'src> Parser<'src> {
self.advance()?;
self.parse_expression()?;
let length = self.chunk.len();
let is_explicit_boolean =
matches!(self.previous_token, Token::Boolean(_)) && length == self.chunk.len() - 1;
if is_explicit_boolean {
self.emit_instruction(
Instruction::test(self.current_register, false),
self.current_position,
);
}
let block_allowed = Allowed {
assignment: allowed.assignment,
explicit_return: allowed.explicit_return,
@ -831,40 +854,53 @@ impl<'src> Parser<'src> {
self.parse_block(block_allowed)?;
}
let last_operation = self.chunk.get_last_operation(self.current_position)?;
let last_operation = self
.current_statement
.last()
.map(|(instruction, _)| instruction.operation());
if let (Operation::LoadConstant | Operation::LoadBoolean, Token::Else) =
if let (Some(Operation::LoadConstant) | Some(Operation::LoadBoolean), Token::Else) =
(last_operation, self.current_token)
{
let (mut load_constant, load_constant_position) =
self.chunk.pop_instruction(self.current_position)?;
let (mut load_constant, load_constant_position) = self
.current_statement
.pop()
.ok_or_else(|| ParseError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
load_constant.set_c_to_boolean(true);
self.emit_instruction(load_constant, load_constant_position);
self.push_instruction(load_constant, load_constant_position);
}
if self.allow(Token::Else)? {
if let Token::If = self.current_token {
self.parse_if(allowed)?;
return self.parse_if(allowed);
}
if let Token::LeftCurlyBrace = self.current_token {
self.parse_block(block_allowed)?;
self.parsed_expression = true;
}
} else {
self.parsed_expression = false;
return Ok(());
}
return Err(ParseError::ExpectedTokenMultiple {
expected: &[TokenKind::If, TokenKind::LeftCurlyBrace],
found: self.current_token.to_owned(),
position: self.current_position,
});
}
self.commit_current_statement();
Ok(())
}
fn parse_while(&mut self, allowed: Allowed) -> Result<(), ParseError> {
self.advance()?;
let jump_start = self.chunk.len();
let jump_start = self.chunk.len() + self.current_statement.len();
self.parse_expression()?;
self.parse_block(Allowed {
@ -873,24 +909,18 @@ impl<'src> Parser<'src> {
implicit_return: false,
})?;
let jump_end = self.chunk.len();
let jump_end = self.chunk.len() + self.current_statement.len();
let jump_distance = jump_end.abs_diff(jump_start) as u8;
let jump_back = Instruction::jump(jump_distance, false);
let jump_over_index = self.chunk.find_last_instruction(Operation::Jump);
if let Some(index) = jump_over_index {
let (_, jump_over_position) = self.chunk.remove_instruction(index);
let jump_over = Instruction::jump(jump_distance - 1, true);
self.chunk
.insert_instruction(index, jump_over, jump_over_position);
if let Some(jump_over) = self.get_staged_jump_mut() {
*jump_over = Instruction::jump(jump_distance - 1, true);
}
self.commit_current_statement();
self.chunk
.insert_instruction(jump_end, jump_back, self.current_position);
self.parsed_expression = false;
Ok(())
}
@ -904,40 +934,45 @@ impl<'src> Parser<'src> {
},
)?;
let previous_instructions = self.chunk.get_last_n_instructions();
let parsed_expression = !self.current_statement.is_empty();
let end_of_statement = matches!(
self.current_token,
Token::Eof | Token::RightCurlyBrace | Token::Semicolon
);
if let [Some((jump, _)), Some((comparison, comparison_position))] = previous_instructions {
if let (Operation::Jump, Operation::Equal | Operation::Less | Operation::LessEqual) =
(jump.operation(), comparison.operation())
if end_of_statement || context == Context::Assignment {
if let Some(
[(Operation::Jump, _), (Operation::Equal | Operation::Less | Operation::LessEqual, comparison_position)],
) = self.get_staged_operations()
{
if matches!(self.current_token, Token::Eof | Token::RightCurlyBrace)
|| context == Context::Assignment
{
let comparison_position = *comparison_position;
let register = self.next_register();
self.emit_instruction(
Instruction::load_boolean(self.current_register, true, true),
self.push_instruction(
Instruction::load_boolean(register, true, true),
comparison_position,
);
self.emit_instruction(
Instruction::load_boolean(self.current_register, false, false),
self.push_instruction(
Instruction::load_boolean(register, false, false),
comparison_position,
);
}
}
}
let end_of_statement = matches!(self.current_token, Token::Eof | Token::RightCurlyBrace);
let has_semicolon = self.allow(Token::Semicolon)?;
let returned = self.chunk.get_last_operation(self.current_position)? == Operation::Return;
let returned = self
.current_statement
.last()
.map(|(instruction, _)| matches!(instruction.operation(), Operation::Return))
.unwrap_or(false);
if allowed.implicit_return
&& self.parsed_expression
&& parsed_expression
&& end_of_statement
&& !has_semicolon
&& !returned
{
self.emit_instruction(Instruction::r#return(true), self.current_position);
self.push_instruction(Instruction::r#return(true), self.current_position);
self.commit_current_statement();
}
Ok(())
@ -988,9 +1023,8 @@ impl<'src> Parser<'src> {
};
let end = self.current_position.1;
self.emit_instruction(Instruction::r#return(has_return_value), Span(start, end));
self.parsed_expression = false;
self.push_instruction(Instruction::r#return(has_return_value), Span(start, end));
self.commit_current_statement();
Ok(())
}
@ -1027,10 +1061,9 @@ impl<'src> Parser<'src> {
} else {
None
};
let register = self.current_register;
let register = self.next_register();
self.expect(Token::Equal)?;
self.parse_statement(
Allowed {
assignment: false,
@ -1040,20 +1073,16 @@ impl<'src> Parser<'src> {
Context::Assignment,
)?;
if self.current_register == register {
self.increment_register()?;
}
let local_index = self
.chunk
.declare_local(identifier, r#type, is_mutable, register, position)?;
let register = self.next_register().saturating_sub(1);
self.emit_instruction(
Instruction::define_local(self.current_register - 1, local_index, is_mutable),
self.push_instruction(
Instruction::define_local(register, local_index, is_mutable),
position,
);
self.parsed_expression = false;
self.commit_current_statement();
Ok(())
}
@ -1098,15 +1127,23 @@ impl<'src> Parser<'src> {
value_parameters = Some(vec![(parameter.clone(), r#type.clone())]);
};
let register = value_parameters
.as_ref()
.map(|values| values.len() as u8 - 1)
.unwrap_or(0);
function_parser.chunk.declare_local(
parameter,
Some(r#type),
is_mutable,
function_parser.current_register,
register,
Span(start, end),
)?;
function_parser.increment_register()?;
if let Some(values) = &value_parameters {
function_parser.minimum_register = values.len() as u8;
}
function_parser.allow(Token::Comma)?;
}
@ -1156,15 +1193,18 @@ impl<'src> Parser<'src> {
self.lexer.skip_to(function_end);
self.emit_constant(function, Span(function_start, function_end))?;
self.parsed_expression = true;
Ok(())
}
fn parse_call(&mut self) -> Result<(), ParseError> {
let last_instruction = self.chunk.get_last_instruction(self.current_position)?.0;
let (last_instruction, _) = self.current_statement.last().copied().ok_or_else(|| {
ParseError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
}
})?;
if !last_instruction.is_expression() {
if !last_instruction.yields_value() {
return Err(ParseError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
@ -1175,9 +1215,7 @@ impl<'src> Parser<'src> {
self.advance()?;
let function_register = self.current_register;
self.increment_register()?;
let function_register = self.next_register();
while !self.allow(Token::RightParenthesis)? {
self.parse_expression()?;
@ -1185,14 +1223,12 @@ impl<'src> Parser<'src> {
}
let end = self.current_position.1;
let register = self.next_register();
self.emit_instruction(
Instruction::call(self.current_register, function_register),
self.push_instruction(
Instruction::call(register, function_register),
Span(start, end),
);
self.increment_register()?;
self.parsed_expression = true;
Ok(())
}