Implement better closing for blocks
This commit is contained in:
parent
0a16c5e0ca
commit
fa4c2d48a3
@ -101,6 +101,10 @@ impl Chunk {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn locals(&self) -> &[Local] {
|
||||
&self.locals
|
||||
}
|
||||
|
||||
pub fn get_local(&self, index: u8, position: Span) -> Result<&Local, ChunkError> {
|
||||
let index = index as usize;
|
||||
|
||||
|
@ -304,6 +304,12 @@ impl<'src> Parser<'src> {
|
||||
self.decrement_register()?;
|
||||
left_instruction.first_argument()
|
||||
}
|
||||
Operation::Close => {
|
||||
return Err(ParseError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: self.previous_position,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
push_back_left = true;
|
||||
|
||||
@ -311,12 +317,11 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
};
|
||||
|
||||
let operator_position = self.current_position;
|
||||
let operator = self.current_token;
|
||||
let operator_position = self.current_position;
|
||||
let rule = ParseRule::from(&operator.kind());
|
||||
|
||||
self.advance()?;
|
||||
|
||||
self.parse(rule.precedence.increment())?;
|
||||
|
||||
let (right_instruction, right_position) =
|
||||
@ -334,6 +339,12 @@ impl<'src> Parser<'src> {
|
||||
self.decrement_register()?;
|
||||
right_instruction.first_argument()
|
||||
}
|
||||
Operation::Close => {
|
||||
return Err(ParseError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: self.previous_position,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
push_back_right = true;
|
||||
|
||||
@ -431,7 +442,7 @@ impl<'src> Parser<'src> {
|
||||
} else {
|
||||
self.emit_instruction(
|
||||
Instruction::get_local(self.current_register, local_index),
|
||||
self.current_position,
|
||||
self.previous_position,
|
||||
);
|
||||
self.increment_register()?;
|
||||
}
|
||||
@ -468,12 +479,34 @@ impl<'src> Parser<'src> {
|
||||
self.advance()?;
|
||||
self.chunk.begin_scope();
|
||||
|
||||
let start = self.current_position.0;
|
||||
let start_register = self.current_register;
|
||||
let mut ends_with_semicolon = false;
|
||||
|
||||
while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() {
|
||||
self.parse_statement()?;
|
||||
|
||||
if self.previous_token == Token::Semicolon {
|
||||
ends_with_semicolon = true;
|
||||
}
|
||||
}
|
||||
|
||||
self.chunk.end_scope();
|
||||
|
||||
if self.current_token == Token::Semicolon {
|
||||
ends_with_semicolon = true;
|
||||
}
|
||||
|
||||
if ends_with_semicolon {
|
||||
let end = self.current_position.1;
|
||||
let end_register = self.current_register;
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::close(start_register, end_register),
|
||||
Span(start, end),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -521,30 +554,31 @@ impl<'src> Parser<'src> {
|
||||
|
||||
fn parse_statement(&mut self) -> Result<(), ParseError> {
|
||||
let start = self.current_position.0;
|
||||
let (is_expression_statement, contains_block) = match self.current_token {
|
||||
let is_expression = match self.current_token {
|
||||
Token::Let => {
|
||||
self.parse_let_statement(true)?;
|
||||
|
||||
(false, false)
|
||||
false
|
||||
}
|
||||
Token::LeftCurlyBrace => {
|
||||
self.parse_block(true)?;
|
||||
|
||||
(true, true)
|
||||
self.previous_token != Token::Semicolon
|
||||
}
|
||||
_ => {
|
||||
self.parse_expression()?;
|
||||
|
||||
(true, false)
|
||||
true
|
||||
}
|
||||
};
|
||||
let has_semicolon = self.allow(TokenKind::Semicolon)?;
|
||||
|
||||
if is_expression_statement && !contains_block && !has_semicolon {
|
||||
if (!has_semicolon || is_expression) && self.is_eof() {
|
||||
let end = self.previous_position.1;
|
||||
|
||||
self.emit_instruction(Instruction::r#return(), Span(start, end))
|
||||
self.emit_instruction(Instruction::r#return(), Span(start, end));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -580,6 +614,7 @@ impl<'src> Parser<'src> {
|
||||
.declare_local(identifier, register, self.current_position)?;
|
||||
|
||||
self.emit_instruction(Instruction::declare_local(register, local_index), position);
|
||||
self.allow(TokenKind::Semicolon)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
dust_error::AnnotatedError, parse, Chunk, ChunkError, DustError, Identifier, Instruction,
|
||||
Operation, Span, Value, ValueError,
|
||||
Local, Operation, Span, Value, ValueError,
|
||||
};
|
||||
|
||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||
@ -49,6 +49,12 @@ impl Vm {
|
||||
vm.chunk
|
||||
.take_constant(instruction.second_argument(), position)?
|
||||
} else {
|
||||
if let Operation::GetLocal = instruction.operation() {
|
||||
println!("GetLocal: {}", instruction);
|
||||
}
|
||||
|
||||
println!("{}", instruction);
|
||||
|
||||
vm.clone(instruction.second_argument(), position)?
|
||||
};
|
||||
|
||||
@ -110,15 +116,8 @@ impl Vm {
|
||||
Operation::GetLocal => {
|
||||
let register_index = instruction.destination();
|
||||
let local_index = instruction.first_argument();
|
||||
let local = self.chunk.get_local(local_index, position)?;
|
||||
let value = if let Some(value_index) = &local.register_index {
|
||||
self.clone(*value_index, position)?
|
||||
} else {
|
||||
return Err(VmError::UndefinedVariable {
|
||||
identifier: local.identifier.clone(),
|
||||
position,
|
||||
});
|
||||
};
|
||||
let local = self.chunk.get_local(local_index, position)?.clone();
|
||||
let value = self.clone_as_variable(local, position)?;
|
||||
|
||||
self.insert(value, register_index, position)?;
|
||||
}
|
||||
@ -170,19 +169,19 @@ impl Vm {
|
||||
}
|
||||
Operation::And => {
|
||||
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
||||
let result = left
|
||||
let and = left
|
||||
.and(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.insert(result, instruction.destination(), position)?;
|
||||
self.insert(and, instruction.destination(), position)?;
|
||||
}
|
||||
Operation::Or => {
|
||||
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
||||
let result = left
|
||||
let or = left
|
||||
.or(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.insert(result, instruction.destination(), position)?;
|
||||
self.insert(or, instruction.destination(), position)?;
|
||||
}
|
||||
Operation::Negate => {
|
||||
let value = if instruction.first_argument_is_constant() {
|
||||
@ -204,11 +203,11 @@ impl Vm {
|
||||
} else {
|
||||
self.clone(instruction.first_argument(), position)?
|
||||
};
|
||||
let result = value
|
||||
let not = value
|
||||
.not()
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.insert(result, instruction.destination(), position)?;
|
||||
self.insert(not, instruction.destination(), position)?;
|
||||
}
|
||||
Operation::Return => {
|
||||
let value = self.pop(position)?;
|
||||
@ -259,6 +258,27 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
|
||||
fn clone_as_variable(&mut self, local: Local, position: Span) -> Result<Value, VmError> {
|
||||
let index = if let Some(index) = local.register_index {
|
||||
index
|
||||
} else {
|
||||
return Err(VmError::UndefinedVariable {
|
||||
identifier: local.identifier.clone(),
|
||||
position,
|
||||
});
|
||||
};
|
||||
let clone_result = self.clone(index, position);
|
||||
|
||||
match clone_result {
|
||||
Ok(value) => Ok(value),
|
||||
Err(VmError::EmptyRegister { .. }) => Err(VmError::UndefinedVariable {
|
||||
identifier: local.identifier,
|
||||
position,
|
||||
}),
|
||||
_ => clone_result,
|
||||
}
|
||||
}
|
||||
|
||||
fn pop(&mut self, position: Span) -> Result<Value, VmError> {
|
||||
if let Some(register) = self.register_stack.pop() {
|
||||
let value = register.ok_or(VmError::EmptyRegister {
|
||||
@ -300,9 +320,6 @@ pub enum VmError {
|
||||
StackUnderflow {
|
||||
position: Span,
|
||||
},
|
||||
UndeclaredVariable {
|
||||
position: Span,
|
||||
},
|
||||
UndefinedVariable {
|
||||
identifier: Identifier,
|
||||
position: Span,
|
||||
@ -334,10 +351,9 @@ impl AnnotatedError for VmError {
|
||||
Self::InvalidInstruction { .. } => "Invalid instruction",
|
||||
Self::StackOverflow { .. } => "Stack overflow",
|
||||
Self::StackUnderflow { .. } => "Stack underflow",
|
||||
Self::UndeclaredVariable { .. } => "Undeclared variable",
|
||||
Self::UndefinedVariable { .. } => "Undefined variable",
|
||||
Self::Chunk(error) => error.description(),
|
||||
Self::Value { .. } => "Value error",
|
||||
Self::Chunk(error) => error.description(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,7 +376,6 @@ impl AnnotatedError for VmError {
|
||||
Self::InvalidInstruction { position, .. } => *position,
|
||||
Self::StackUnderflow { position } => *position,
|
||||
Self::StackOverflow { position } => *position,
|
||||
Self::UndeclaredVariable { position } => *position,
|
||||
Self::UndefinedVariable { position, .. } => *position,
|
||||
Self::Chunk(error) => error.position(),
|
||||
Self::Value { position, .. } => *position,
|
||||
|
Loading…
Reference in New Issue
Block a user