Refactor parser and chunk to have fewer errors and less retrospection
This commit is contained in:
parent
8c79157fa7
commit
3f8b554a86
@ -6,7 +6,7 @@ use std::{
|
|||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use serde::{Deserialize, Serialize};
|
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)]
|
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
@ -69,63 +69,6 @@ impl Chunk {
|
|||||||
self.instructions.insert(index, (instruction, position));
|
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> {
|
pub fn take_constants(self) -> Vec<Value> {
|
||||||
self.constants
|
self.constants
|
||||||
}
|
}
|
||||||
@ -634,10 +577,6 @@ pub enum ChunkError {
|
|||||||
offset: usize,
|
offset: usize,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
ConstantAlreadyUsed {
|
|
||||||
index: usize,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ConstantOverflow {
|
ConstantOverflow {
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
@ -645,9 +584,6 @@ pub enum ChunkError {
|
|||||||
index: usize,
|
index: usize,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
InstructionUnderflow {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
LocalIndexOutOfBounds {
|
LocalIndexOutOfBounds {
|
||||||
index: usize,
|
index: usize,
|
||||||
position: Span,
|
position: Span,
|
||||||
@ -669,10 +605,8 @@ impl AnnotatedError for ChunkError {
|
|||||||
fn description(&self) -> &'static str {
|
fn description(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
ChunkError::InstructionIndexOfBounds { .. } => "Instruction index out of bounds",
|
ChunkError::InstructionIndexOfBounds { .. } => "Instruction index out of bounds",
|
||||||
ChunkError::ConstantAlreadyUsed { .. } => "Constant already used",
|
|
||||||
ChunkError::ConstantOverflow { .. } => "Constant overflow",
|
ChunkError::ConstantOverflow { .. } => "Constant overflow",
|
||||||
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
||||||
ChunkError::InstructionUnderflow { .. } => "Instruction underflow",
|
|
||||||
ChunkError::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
|
ChunkError::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
|
||||||
ChunkError::LocalOverflow { .. } => "Local overflow",
|
ChunkError::LocalOverflow { .. } => "Local overflow",
|
||||||
ChunkError::IdentifierNotFound { .. } => "Identifier not found",
|
ChunkError::IdentifierNotFound { .. } => "Identifier not found",
|
||||||
@ -684,13 +618,9 @@ impl AnnotatedError for ChunkError {
|
|||||||
ChunkError::InstructionIndexOfBounds { offset, .. } => {
|
ChunkError::InstructionIndexOfBounds { offset, .. } => {
|
||||||
Some(format!("Instruction index: {}", offset))
|
Some(format!("Instruction index: {}", offset))
|
||||||
}
|
}
|
||||||
ChunkError::ConstantAlreadyUsed { index, .. } => {
|
|
||||||
Some(format!("Constant index: {}", index))
|
|
||||||
}
|
|
||||||
ChunkError::ConstantIndexOutOfBounds { index, .. } => {
|
ChunkError::ConstantIndexOutOfBounds { index, .. } => {
|
||||||
Some(format!("Constant index: {}", index))
|
Some(format!("Constant index: {}", index))
|
||||||
}
|
}
|
||||||
ChunkError::InstructionUnderflow { .. } => None,
|
|
||||||
ChunkError::LocalIndexOutOfBounds { index, .. } => {
|
ChunkError::LocalIndexOutOfBounds { index, .. } => {
|
||||||
Some(format!("Local index: {}", index))
|
Some(format!("Local index: {}", index))
|
||||||
}
|
}
|
||||||
@ -705,10 +635,8 @@ impl AnnotatedError for ChunkError {
|
|||||||
fn position(&self) -> Span {
|
fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
ChunkError::InstructionIndexOfBounds { position, .. } => *position,
|
ChunkError::InstructionIndexOfBounds { position, .. } => *position,
|
||||||
ChunkError::ConstantAlreadyUsed { position, .. } => *position,
|
|
||||||
ChunkError::ConstantIndexOutOfBounds { position, .. } => *position,
|
ChunkError::ConstantIndexOutOfBounds { position, .. } => *position,
|
||||||
ChunkError::IdentifierNotFound { position, .. } => *position,
|
ChunkError::IdentifierNotFound { position, .. } => *position,
|
||||||
ChunkError::InstructionUnderflow { position, .. } => *position,
|
|
||||||
ChunkError::LocalIndexOutOfBounds { position, .. } => *position,
|
ChunkError::LocalIndexOutOfBounds { position, .. } => *position,
|
||||||
ChunkError::LocalOverflow { position, .. } => *position,
|
ChunkError::LocalOverflow { position, .. } => *position,
|
||||||
ChunkError::ConstantOverflow { position, .. } => *position,
|
ChunkError::ConstantOverflow { position, .. } => *position,
|
||||||
|
@ -318,36 +318,21 @@ impl Instruction {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_expression(&self) -> bool {
|
pub fn yields_value(&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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
matches!(
|
matches!(
|
||||||
operation,
|
self.operation(),
|
||||||
Operation::Not
|
Operation::Add
|
||||||
| Operation::Negate
|
| Operation::Call
|
||||||
| Operation::Equal
|
|
||||||
| Operation::Less
|
|
||||||
| Operation::LessEqual
|
|
||||||
| Operation::Add
|
|
||||||
| Operation::Subtract
|
|
||||||
| Operation::Multiply
|
|
||||||
| Operation::Divide
|
| Operation::Divide
|
||||||
| Operation::Modulo
|
|
||||||
| Operation::Test
|
|
||||||
| Operation::GetLocal
|
| Operation::GetLocal
|
||||||
| Operation::LoadBoolean
|
| Operation::LoadBoolean
|
||||||
| Operation::LoadConstant
|
| Operation::LoadConstant
|
||||||
| Operation::LoadList
|
| Operation::LoadList
|
||||||
|
| Operation::Modulo
|
||||||
|
| Operation::Multiply
|
||||||
|
| Operation::Negate
|
||||||
|
| Operation::Not
|
||||||
|
| Operation::Subtract
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ use std::{
|
|||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
mem::replace,
|
mem::replace,
|
||||||
num::{ParseFloatError, ParseIntError},
|
num::{ParseFloatError, ParseIntError},
|
||||||
|
vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
@ -34,10 +35,11 @@ pub fn parse(source: &str) -> Result<Chunk, DustError> {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize)]
|
||||||
pub struct Parser<'src> {
|
pub struct Parser<'src> {
|
||||||
chunk: Chunk,
|
|
||||||
lexer: Lexer<'src>,
|
lexer: Lexer<'src>,
|
||||||
|
chunk: Chunk,
|
||||||
|
|
||||||
current_register: u8,
|
current_statement: Vec<(Instruction, Span)>,
|
||||||
|
minimum_register: u8,
|
||||||
|
|
||||||
current_token: Token<'src>,
|
current_token: Token<'src>,
|
||||||
current_position: Span,
|
current_position: Span,
|
||||||
@ -62,7 +64,8 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(Parser {
|
Ok(Parser {
|
||||||
lexer,
|
lexer,
|
||||||
chunk: Chunk::new(),
|
chunk: Chunk::new(),
|
||||||
current_register: 0,
|
current_statement: Vec::new(),
|
||||||
|
minimum_register: 0,
|
||||||
current_token,
|
current_token,
|
||||||
current_position,
|
current_position,
|
||||||
previous_token: Token::Eof,
|
previous_token: Token::Eof,
|
||||||
@ -82,18 +85,18 @@ impl<'src> Parser<'src> {
|
|||||||
matches!(self.current_token, Token::Eof)
|
matches!(self.current_token, Token::Eof)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increment_register(&mut self) -> Result<(), ParseError> {
|
fn next_register(&mut self) -> u8 {
|
||||||
let current = self.current_register;
|
self.current_statement
|
||||||
|
.iter()
|
||||||
if current == u8::MAX {
|
.rev()
|
||||||
Err(ParseError::RegisterOverflow {
|
.find_map(|(instruction, _)| {
|
||||||
position: self.current_position,
|
if instruction.yields_value() {
|
||||||
|
Some(instruction.a() + 1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
} else {
|
.unwrap_or(self.minimum_register)
|
||||||
self.current_register += 1;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn advance(&mut self) -> Result<(), ParseError> {
|
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!(
|
log::debug!(
|
||||||
"Emitting {} at {}",
|
"Emitting {} at {}",
|
||||||
instruction.operation().to_string().bold(),
|
instruction.operation().to_string().bold(),
|
||||||
position.to_string()
|
position.to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
self.chunk.push_instruction(instruction, position);
|
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> {
|
fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), ParseError> {
|
||||||
self.latest_value_type = Some(value.r#type());
|
self.latest_value_type = Some(value.r#type());
|
||||||
|
|
||||||
let constant_index = self.chunk.push_constant(value, position)?;
|
let constant_index = self.chunk.push_constant(value, position)?;
|
||||||
|
let register = self.next_register();
|
||||||
|
|
||||||
self.emit_instruction(
|
if let Some(instruction) = self
|
||||||
Instruction::load_constant(self.current_register, constant_index, false),
|
.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,
|
position,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -167,14 +220,26 @@ impl<'src> Parser<'src> {
|
|||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let boolean = text.parse::<bool>().unwrap();
|
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(
|
self.push_instruction(
|
||||||
Instruction::load_boolean(self.current_register, boolean, false),
|
Instruction::load_boolean(register, boolean, false),
|
||||||
position,
|
position,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.latest_value_type = Some(Type::Boolean);
|
self.latest_value_type = Some(Type::Boolean);
|
||||||
self.parsed_expression = true;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -198,8 +263,6 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
self.parsed_expression = true;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(ParseError::ExpectedToken {
|
Err(ParseError::ExpectedToken {
|
||||||
@ -220,8 +283,6 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
self.parsed_expression = true;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(ParseError::ExpectedToken {
|
Err(ParseError::ExpectedToken {
|
||||||
@ -248,8 +309,6 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
self.parsed_expression = true;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(ParseError::ExpectedToken {
|
Err(ParseError::ExpectedToken {
|
||||||
@ -276,8 +335,6 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
self.parsed_expression = true;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(ParseError::ExpectedToken {
|
Err(ParseError::ExpectedToken {
|
||||||
@ -298,8 +355,6 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
self.parsed_expression = true;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(ParseError::ExpectedToken {
|
Err(ParseError::ExpectedToken {
|
||||||
@ -315,8 +370,6 @@ impl<'src> Parser<'src> {
|
|||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
self.expect(Token::RightParenthesis)?;
|
self.expect(Token::RightParenthesis)?;
|
||||||
|
|
||||||
self.parsed_expression = true;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,7 +381,12 @@ impl<'src> Parser<'src> {
|
|||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let (previous_instruction, previous_position) =
|
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) = {
|
let (push_back, is_constant, argument) = {
|
||||||
match previous_instruction.operation() {
|
match previous_instruction.operation() {
|
||||||
@ -346,12 +404,13 @@ impl<'src> Parser<'src> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if push_back {
|
if push_back {
|
||||||
self.increment_register()?;
|
self.push_instruction(previous_instruction, previous_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let register = self.next_register();
|
||||||
let mut instruction = match operator.kind() {
|
let mut instruction = match operator.kind() {
|
||||||
TokenKind::Bang => Instruction::not(self.current_register, argument),
|
TokenKind::Bang => Instruction::not(register, argument),
|
||||||
TokenKind::Minus => Instruction::negate(self.current_register, argument),
|
TokenKind::Minus => Instruction::negate(register, argument),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParseError::ExpectedTokenMultiple {
|
return Err(ParseError::ExpectedTokenMultiple {
|
||||||
expected: &[TokenKind::Bang, TokenKind::Minus],
|
expected: &[TokenKind::Bang, TokenKind::Minus],
|
||||||
@ -365,13 +424,7 @@ impl<'src> Parser<'src> {
|
|||||||
instruction.set_b_is_constant();
|
instruction.set_b_is_constant();
|
||||||
}
|
}
|
||||||
|
|
||||||
if push_back {
|
self.push_instruction(instruction, operator_position);
|
||||||
self.emit_instruction(previous_instruction, previous_position);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.emit_instruction(instruction, operator_position);
|
|
||||||
|
|
||||||
self.parsed_expression = true;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -415,18 +468,25 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
fn parse_math_binary(&mut self) -> Result<(), ParseError> {
|
fn parse_math_binary(&mut self) -> Result<(), ParseError> {
|
||||||
let (left_instruction, left_position) =
|
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) =
|
let (push_back_left, left_is_constant, left_is_mutable_local, left) =
|
||||||
self.handle_binary_argument(&left_instruction)?;
|
self.handle_binary_argument(&left_instruction)?;
|
||||||
|
|
||||||
|
if push_back_left {
|
||||||
|
self.push_instruction(left_instruction, left_position);
|
||||||
|
}
|
||||||
|
|
||||||
let operator = self.current_token;
|
let operator = self.current_token;
|
||||||
let operator_position = self.current_position;
|
let operator_position = self.current_position;
|
||||||
let rule = ParseRule::from(&operator);
|
let rule = ParseRule::from(&operator);
|
||||||
|
|
||||||
if let TokenKind::PlusEqual
|
if let Token::PlusEqual | Token::MinusEqual | Token::StarEqual | Token::SlashEqual =
|
||||||
| TokenKind::MinusEqual
|
operator
|
||||||
| TokenKind::StarEqual
|
|
||||||
| TokenKind::SlashEqual = operator.kind()
|
|
||||||
{
|
{
|
||||||
if !left_is_mutable_local {
|
if !left_is_mutable_local {
|
||||||
return Err(ParseError::ExpectedMutableVariable {
|
return Err(ParseError::ExpectedMutableVariable {
|
||||||
@ -440,32 +500,36 @@ impl<'src> Parser<'src> {
|
|||||||
self.parse_sub_expression(&rule.precedence)?;
|
self.parse_sub_expression(&rule.precedence)?;
|
||||||
|
|
||||||
let (right_instruction, right_position) =
|
let (right_instruction, right_position) =
|
||||||
self.chunk.pop_instruction(self.current_position)?;
|
self.current_statement
|
||||||
let (push_back_right, right_is_constant, right_is_mutable_local, right) =
|
.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)?;
|
self.handle_binary_argument(&right_instruction)?;
|
||||||
|
|
||||||
|
if push_back_right {
|
||||||
|
self.push_instruction(right_instruction, right_position);
|
||||||
|
}
|
||||||
|
|
||||||
let register = if left_is_mutable_local {
|
let register = if left_is_mutable_local {
|
||||||
left
|
left
|
||||||
} else if right_is_mutable_local {
|
|
||||||
right
|
|
||||||
} else {
|
} else {
|
||||||
let current = self.current_register;
|
self.next_register()
|
||||||
|
|
||||||
self.increment_register()?;
|
|
||||||
|
|
||||||
current
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let (mut new_instruction, is_expression) = match operator.kind() {
|
let mut new_instruction = match operator.kind() {
|
||||||
TokenKind::Plus => (Instruction::add(register, left, right), true),
|
TokenKind::Plus => Instruction::add(register, left, right),
|
||||||
TokenKind::PlusEqual => (Instruction::add(register, left, right), false),
|
TokenKind::PlusEqual => Instruction::add(register, left, right),
|
||||||
TokenKind::Minus => (Instruction::subtract(register, left, right), true),
|
TokenKind::Minus => Instruction::subtract(register, left, right),
|
||||||
TokenKind::MinusEqual => (Instruction::subtract(register, left, right), false),
|
TokenKind::MinusEqual => Instruction::subtract(register, left, right),
|
||||||
TokenKind::Star => (Instruction::multiply(register, left, right), true),
|
TokenKind::Star => Instruction::multiply(register, left, right),
|
||||||
TokenKind::StarEqual => (Instruction::multiply(register, left, right), false),
|
TokenKind::StarEqual => Instruction::multiply(register, left, right),
|
||||||
TokenKind::Slash => (Instruction::divide(register, left, right), true),
|
TokenKind::Slash => Instruction::divide(register, left, right),
|
||||||
TokenKind::SlashEqual => (Instruction::divide(register, left, right), false),
|
TokenKind::SlashEqual => Instruction::divide(register, left, right),
|
||||||
TokenKind::Percent => (Instruction::modulo(register, left, right), true),
|
TokenKind::Percent => Instruction::modulo(register, left, right),
|
||||||
|
TokenKind::PercentEqual => Instruction::modulo(register, left, right),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParseError::ExpectedTokenMultiple {
|
return Err(ParseError::ExpectedTokenMultiple {
|
||||||
expected: &[
|
expected: &[
|
||||||
@ -478,6 +542,7 @@ impl<'src> Parser<'src> {
|
|||||||
TokenKind::Slash,
|
TokenKind::Slash,
|
||||||
TokenKind::SlashEqual,
|
TokenKind::SlashEqual,
|
||||||
TokenKind::Percent,
|
TokenKind::Percent,
|
||||||
|
TokenKind::PercentEqual,
|
||||||
],
|
],
|
||||||
found: operator.to_owned(),
|
found: operator.to_owned(),
|
||||||
position: operator_position,
|
position: operator_position,
|
||||||
@ -485,8 +550,6 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.parsed_expression = is_expression;
|
|
||||||
|
|
||||||
if left_is_constant {
|
if left_is_constant {
|
||||||
new_instruction.set_b_is_constant();
|
new_instruction.set_b_is_constant();
|
||||||
}
|
}
|
||||||
@ -495,50 +558,15 @@ impl<'src> Parser<'src> {
|
|||||||
new_instruction.set_c_is_constant();
|
new_instruction.set_c_is_constant();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut instructions = if !push_back_left && !push_back_right {
|
self.push_instruction(new_instruction, operator_position);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_comparison_binary(&mut self) -> Result<(), ParseError> {
|
fn parse_comparison_binary(&mut self) -> Result<(), ParseError> {
|
||||||
if let [Some(Operation::Jump), Some(Operation::Equal) | Some(Operation::Less) | Some(Operation::LessEqual)] =
|
if let Some(
|
||||||
self.chunk.get_last_n_operations()
|
[(Operation::Jump, _), (Operation::Equal, _) | (Operation::Less, _) | (Operation::LessEqual, _)],
|
||||||
|
) = self.get_staged_operations()
|
||||||
{
|
{
|
||||||
return Err(ParseError::CannotChainComparison {
|
return Err(ParseError::CannotChainComparison {
|
||||||
position: self.current_position,
|
position: self.current_position,
|
||||||
@ -546,7 +574,12 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (left_instruction, left_position) =
|
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) =
|
let (push_back_left, left_is_constant, _, left) =
|
||||||
self.handle_binary_argument(&left_instruction)?;
|
self.handle_binary_argument(&left_instruction)?;
|
||||||
|
|
||||||
@ -558,7 +591,12 @@ impl<'src> Parser<'src> {
|
|||||||
self.parse_sub_expression(&rule.precedence)?;
|
self.parse_sub_expression(&rule.precedence)?;
|
||||||
|
|
||||||
let (right_instruction, right_position) =
|
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) =
|
let (push_back_right, right_is_constant, _, right) =
|
||||||
self.handle_binary_argument(&right_instruction)?;
|
self.handle_binary_argument(&right_instruction)?;
|
||||||
|
|
||||||
@ -586,8 +624,6 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.parsed_expression = true;
|
|
||||||
|
|
||||||
if left_is_constant {
|
if left_is_constant {
|
||||||
instruction.set_b_is_constant();
|
instruction.set_b_is_constant();
|
||||||
}
|
}
|
||||||
@ -597,22 +633,27 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if push_back_left {
|
if push_back_left {
|
||||||
self.emit_instruction(left_instruction, left_position);
|
self.push_instruction(left_instruction, left_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
if push_back_right {
|
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.push_instruction(instruction, operator_position);
|
||||||
self.emit_instruction(Instruction::jump(1, true), operator_position);
|
self.push_instruction(Instruction::jump(1, true), operator_position);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_logical_binary(&mut self) -> Result<(), ParseError> {
|
fn parse_logical_binary(&mut self) -> Result<(), ParseError> {
|
||||||
let (left_instruction, left_position) =
|
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 = self.current_token;
|
||||||
let operator_position = self.current_position;
|
let operator_position = self.current_position;
|
||||||
@ -630,20 +671,12 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.increment_register()?;
|
|
||||||
self.advance()?;
|
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)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -676,8 +709,13 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let (mut previous_instruction, previous_position) =
|
let (mut previous_instruction, previous_position) = self
|
||||||
self.chunk.pop_instruction(self.current_position)?;
|
.current_statement
|
||||||
|
.pop()
|
||||||
|
.ok_or_else(|| ParseError::ExpectedExpression {
|
||||||
|
found: self.previous_token.to_owned(),
|
||||||
|
position: self.previous_position,
|
||||||
|
})?;
|
||||||
|
|
||||||
if previous_instruction.operation().is_math() {
|
if previous_instruction.operation().is_math() {
|
||||||
let register_index = self
|
let register_index = self
|
||||||
@ -688,26 +726,25 @@ impl<'src> Parser<'src> {
|
|||||||
log::trace!("Condensing SET_LOCAL to binary math expression");
|
log::trace!("Condensing SET_LOCAL to binary math expression");
|
||||||
|
|
||||||
previous_instruction.set_a(register_index);
|
previous_instruction.set_a(register_index);
|
||||||
self.emit_instruction(previous_instruction, self.current_position);
|
self.push_instruction(previous_instruction, self.current_position);
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.emit_instruction(previous_instruction, previous_position);
|
let register = self.next_register();
|
||||||
self.emit_instruction(
|
|
||||||
Instruction::set_local(self.current_register, local_index),
|
self.push_instruction(previous_instruction, previous_position);
|
||||||
|
self.push_instruction(
|
||||||
|
Instruction::set_local(register, local_index),
|
||||||
start_position,
|
start_position,
|
||||||
);
|
);
|
||||||
self.increment_register()?;
|
|
||||||
|
|
||||||
self.parsed_expression = false;
|
|
||||||
} else {
|
} else {
|
||||||
self.emit_instruction(
|
let register = self.next_register();
|
||||||
Instruction::get_local(self.current_register, local_index),
|
|
||||||
|
self.push_instruction(
|
||||||
|
Instruction::get_local(register, local_index),
|
||||||
self.previous_position,
|
self.previous_position,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.parsed_expression = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -771,20 +808,19 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let start_register = self.current_register;
|
let start_register = self.next_register();
|
||||||
|
|
||||||
while !self.allow(Token::RightSquareBrace)? && !self.is_eof() {
|
while !self.allow(Token::RightSquareBrace)? && !self.is_eof() {
|
||||||
let next_register = self.current_register;
|
let expected_register = self.next_register();
|
||||||
|
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
self.commit_current_statement();
|
||||||
|
|
||||||
if let Operation::LoadConstant = self.chunk.get_last_operation(self.current_position)? {
|
let actual_register = self.next_register() - 1;
|
||||||
self.increment_register()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if next_register != self.current_register.saturating_sub(1) {
|
if expected_register < actual_register {
|
||||||
self.emit_instruction(
|
self.push_instruction(
|
||||||
Instruction::close(next_register, self.current_register.saturating_sub(1)),
|
Instruction::close(expected_register, actual_register),
|
||||||
self.current_position,
|
self.current_position,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -792,16 +828,14 @@ impl<'src> Parser<'src> {
|
|||||||
self.allow(Token::Comma)?;
|
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;
|
let end = self.current_position.1;
|
||||||
|
|
||||||
self.emit_instruction(
|
self.push_instruction(
|
||||||
Instruction::load_list(self.current_register, start_register, end_register),
|
Instruction::load_list(to_register, start_register, end_register),
|
||||||
Span(start, end),
|
Span(start, end),
|
||||||
);
|
);
|
||||||
self.increment_register()?;
|
|
||||||
|
|
||||||
self.parsed_expression = true;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -810,17 +844,6 @@ impl<'src> Parser<'src> {
|
|||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.parse_expression()?;
|
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 {
|
let block_allowed = Allowed {
|
||||||
assignment: allowed.assignment,
|
assignment: allowed.assignment,
|
||||||
explicit_return: allowed.explicit_return,
|
explicit_return: allowed.explicit_return,
|
||||||
@ -831,40 +854,53 @@ impl<'src> Parser<'src> {
|
|||||||
self.parse_block(block_allowed)?;
|
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)
|
(last_operation, self.current_token)
|
||||||
{
|
{
|
||||||
let (mut load_constant, load_constant_position) =
|
let (mut load_constant, load_constant_position) = self
|
||||||
self.chunk.pop_instruction(self.current_position)?;
|
.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);
|
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 self.allow(Token::Else)? {
|
||||||
if let Token::If = self.current_token {
|
if let Token::If = self.current_token {
|
||||||
self.parse_if(allowed)?;
|
return self.parse_if(allowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Token::LeftCurlyBrace = self.current_token {
|
if let Token::LeftCurlyBrace = self.current_token {
|
||||||
self.parse_block(block_allowed)?;
|
self.parse_block(block_allowed)?;
|
||||||
|
|
||||||
self.parsed_expression = true;
|
return Ok(());
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.parsed_expression = false;
|
return Err(ParseError::ExpectedTokenMultiple {
|
||||||
|
expected: &[TokenKind::If, TokenKind::LeftCurlyBrace],
|
||||||
|
found: self.current_token.to_owned(),
|
||||||
|
position: self.current_position,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.commit_current_statement();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_while(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
fn parse_while(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let jump_start = self.chunk.len();
|
let jump_start = self.chunk.len() + self.current_statement.len();
|
||||||
|
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
self.parse_block(Allowed {
|
self.parse_block(Allowed {
|
||||||
@ -873,24 +909,18 @@ impl<'src> Parser<'src> {
|
|||||||
implicit_return: false,
|
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_distance = jump_end.abs_diff(jump_start) as u8;
|
||||||
let jump_back = Instruction::jump(jump_distance, false);
|
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 {
|
if let Some(jump_over) = self.get_staged_jump_mut() {
|
||||||
let (_, jump_over_position) = self.chunk.remove_instruction(index);
|
*jump_over = Instruction::jump(jump_distance - 1, true);
|
||||||
let jump_over = Instruction::jump(jump_distance - 1, true);
|
|
||||||
|
|
||||||
self.chunk
|
|
||||||
.insert_instruction(index, jump_over, jump_over_position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.commit_current_statement();
|
||||||
self.chunk
|
self.chunk
|
||||||
.insert_instruction(jump_end, jump_back, self.current_position);
|
.insert_instruction(jump_end, jump_back, self.current_position);
|
||||||
|
|
||||||
self.parsed_expression = false;
|
|
||||||
|
|
||||||
Ok(())
|
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 end_of_statement || context == Context::Assignment {
|
||||||
if let (Operation::Jump, Operation::Equal | Operation::Less | Operation::LessEqual) =
|
if let Some(
|
||||||
(jump.operation(), comparison.operation())
|
[(Operation::Jump, _), (Operation::Equal | Operation::Less | Operation::LessEqual, comparison_position)],
|
||||||
|
) = self.get_staged_operations()
|
||||||
{
|
{
|
||||||
if matches!(self.current_token, Token::Eof | Token::RightCurlyBrace)
|
let register = self.next_register();
|
||||||
|| context == Context::Assignment
|
|
||||||
{
|
|
||||||
let comparison_position = *comparison_position;
|
|
||||||
|
|
||||||
self.emit_instruction(
|
self.push_instruction(
|
||||||
Instruction::load_boolean(self.current_register, true, true),
|
Instruction::load_boolean(register, true, true),
|
||||||
comparison_position,
|
comparison_position,
|
||||||
);
|
);
|
||||||
self.emit_instruction(
|
self.push_instruction(
|
||||||
Instruction::load_boolean(self.current_register, false, false),
|
Instruction::load_boolean(register, false, false),
|
||||||
comparison_position,
|
comparison_position,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let end_of_statement = matches!(self.current_token, Token::Eof | Token::RightCurlyBrace);
|
|
||||||
let has_semicolon = self.allow(Token::Semicolon)?;
|
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
|
if allowed.implicit_return
|
||||||
&& self.parsed_expression
|
&& parsed_expression
|
||||||
&& end_of_statement
|
&& end_of_statement
|
||||||
&& !has_semicolon
|
&& !has_semicolon
|
||||||
&& !returned
|
&& !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(())
|
Ok(())
|
||||||
@ -988,9 +1023,8 @@ impl<'src> Parser<'src> {
|
|||||||
};
|
};
|
||||||
let end = self.current_position.1;
|
let end = self.current_position.1;
|
||||||
|
|
||||||
self.emit_instruction(Instruction::r#return(has_return_value), Span(start, end));
|
self.push_instruction(Instruction::r#return(has_return_value), Span(start, end));
|
||||||
|
self.commit_current_statement();
|
||||||
self.parsed_expression = false;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1027,10 +1061,9 @@ impl<'src> Parser<'src> {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let register = self.current_register;
|
let register = self.next_register();
|
||||||
|
|
||||||
self.expect(Token::Equal)?;
|
self.expect(Token::Equal)?;
|
||||||
|
|
||||||
self.parse_statement(
|
self.parse_statement(
|
||||||
Allowed {
|
Allowed {
|
||||||
assignment: false,
|
assignment: false,
|
||||||
@ -1040,20 +1073,16 @@ impl<'src> Parser<'src> {
|
|||||||
Context::Assignment,
|
Context::Assignment,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if self.current_register == register {
|
|
||||||
self.increment_register()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let local_index = self
|
let local_index = self
|
||||||
.chunk
|
.chunk
|
||||||
.declare_local(identifier, r#type, is_mutable, register, position)?;
|
.declare_local(identifier, r#type, is_mutable, register, position)?;
|
||||||
|
let register = self.next_register().saturating_sub(1);
|
||||||
|
|
||||||
self.emit_instruction(
|
self.push_instruction(
|
||||||
Instruction::define_local(self.current_register - 1, local_index, is_mutable),
|
Instruction::define_local(register, local_index, is_mutable),
|
||||||
position,
|
position,
|
||||||
);
|
);
|
||||||
|
self.commit_current_statement();
|
||||||
self.parsed_expression = false;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1098,15 +1127,23 @@ impl<'src> Parser<'src> {
|
|||||||
value_parameters = Some(vec![(parameter.clone(), r#type.clone())]);
|
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(
|
function_parser.chunk.declare_local(
|
||||||
parameter,
|
parameter,
|
||||||
Some(r#type),
|
Some(r#type),
|
||||||
is_mutable,
|
is_mutable,
|
||||||
function_parser.current_register,
|
register,
|
||||||
Span(start, end),
|
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)?;
|
function_parser.allow(Token::Comma)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1156,15 +1193,18 @@ impl<'src> Parser<'src> {
|
|||||||
self.lexer.skip_to(function_end);
|
self.lexer.skip_to(function_end);
|
||||||
self.emit_constant(function, Span(function_start, function_end))?;
|
self.emit_constant(function, Span(function_start, function_end))?;
|
||||||
|
|
||||||
self.parsed_expression = true;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_call(&mut self) -> Result<(), ParseError> {
|
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 {
|
return Err(ParseError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
@ -1175,9 +1215,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let function_register = self.current_register;
|
let function_register = self.next_register();
|
||||||
|
|
||||||
self.increment_register()?;
|
|
||||||
|
|
||||||
while !self.allow(Token::RightParenthesis)? {
|
while !self.allow(Token::RightParenthesis)? {
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
@ -1185,14 +1223,12 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let end = self.current_position.1;
|
let end = self.current_position.1;
|
||||||
|
let register = self.next_register();
|
||||||
|
|
||||||
self.emit_instruction(
|
self.push_instruction(
|
||||||
Instruction::call(self.current_register, function_register),
|
Instruction::call(register, function_register),
|
||||||
Span(start, end),
|
Span(start, end),
|
||||||
);
|
);
|
||||||
self.increment_register()?;
|
|
||||||
|
|
||||||
self.parsed_expression = true;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user