Continue implemnting functions; Begin adding types
This commit is contained in:
parent
bdc34cb10e
commit
259721f6cb
@ -3,7 +3,10 @@ use std::fmt::{self, Debug, Display, Formatter};
|
||||
use colored::Colorize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{AnnotatedError, Identifier, Instruction, Operation, Span, Value};
|
||||
use crate::{
|
||||
value::ValueKind, AnnotatedError, Function, Identifier, Instruction, Operation, Span, Type,
|
||||
Value,
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Chunk {
|
||||
@ -214,18 +217,20 @@ impl Chunk {
|
||||
pub fn declare_local(
|
||||
&mut self,
|
||||
identifier: Identifier,
|
||||
mutable: bool,
|
||||
r#type: Option<Type>,
|
||||
is_mutable: bool,
|
||||
register_index: u8,
|
||||
position: Span,
|
||||
) -> Result<u8, ChunkError> {
|
||||
let starting_length = self.locals.len();
|
||||
|
||||
if starting_length + 1 > (u8::MAX as usize) {
|
||||
Err(ChunkError::IdentifierOverflow { position })
|
||||
Err(ChunkError::LocalOverflow { position })
|
||||
} else {
|
||||
self.locals.push(Local::new(
|
||||
identifier,
|
||||
mutable,
|
||||
r#type,
|
||||
is_mutable,
|
||||
self.scope_depth,
|
||||
Some(register_index),
|
||||
));
|
||||
@ -308,6 +313,7 @@ impl PartialEq for Chunk {
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Local {
|
||||
pub identifier: Identifier,
|
||||
pub r#type: Option<Type>,
|
||||
pub is_mutable: bool,
|
||||
pub depth: usize,
|
||||
pub register_index: Option<u8>,
|
||||
@ -316,12 +322,14 @@ pub struct Local {
|
||||
impl Local {
|
||||
pub fn new(
|
||||
identifier: Identifier,
|
||||
r#type: Option<Type>,
|
||||
mutable: bool,
|
||||
depth: usize,
|
||||
register_index: Option<u8>,
|
||||
) -> Self {
|
||||
Self {
|
||||
identifier,
|
||||
r#type,
|
||||
is_mutable: mutable,
|
||||
depth,
|
||||
register_index,
|
||||
@ -334,6 +342,7 @@ pub struct ChunkDisassembler<'a> {
|
||||
chunk: &'a Chunk,
|
||||
width: Option<usize>,
|
||||
styled: bool,
|
||||
indent: usize,
|
||||
}
|
||||
|
||||
impl<'a> ChunkDisassembler<'a> {
|
||||
@ -357,8 +366,8 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
"",
|
||||
"Locals",
|
||||
"------",
|
||||
"INDEX IDENTIFIER MUTABLE DEPTH REGISTER",
|
||||
"----- ---------- ------- ----- --------",
|
||||
"INDEX IDENTIFIER TYPE MUTABLE DEPTH REGISTER",
|
||||
"----- ---------- -------- ------- ----- --------",
|
||||
];
|
||||
|
||||
/// The default width of the disassembly output. To correctly align the output, this should
|
||||
@ -375,6 +384,7 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
chunk,
|
||||
width: None,
|
||||
styled: false,
|
||||
indent: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,6 +400,12 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn indent(&mut self, indent: usize) -> &mut Self {
|
||||
self.indent = indent;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn disassemble(&self) -> String {
|
||||
let width = self.width.unwrap_or_else(Self::default_width);
|
||||
let center = |line: &str| format!("{line:^width$}\n");
|
||||
@ -402,9 +418,16 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
};
|
||||
|
||||
let mut disassembly = String::with_capacity(self.predict_length());
|
||||
let mut push_line = |line: &str| {
|
||||
for _ in 0..self.indent {
|
||||
disassembly.push_str(" ");
|
||||
}
|
||||
|
||||
disassembly.push_str(line);
|
||||
};
|
||||
let name_line = style(center(self.name));
|
||||
|
||||
disassembly.push_str(&name_line);
|
||||
push_line(&name_line);
|
||||
|
||||
let info_line = center(&format!(
|
||||
"{} instructions, {} constants, {} locals",
|
||||
@ -420,10 +443,10 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
disassembly.push_str(&styled_info_line);
|
||||
push_line(&styled_info_line);
|
||||
|
||||
for line in Self::INSTRUCTION_HEADER {
|
||||
disassembly.push_str(&style(center(line)));
|
||||
push_line(&style(center(line)));
|
||||
}
|
||||
|
||||
for (index, (instruction, position)) in self.chunk.instructions.iter().enumerate() {
|
||||
@ -454,11 +477,11 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
"{index:<5} {bytecode:<08X} {operation:15} {info:25} {jump_offset:8} {position:8}"
|
||||
);
|
||||
|
||||
disassembly.push_str(¢er(&instruction_display));
|
||||
push_line(¢er(&instruction_display));
|
||||
}
|
||||
|
||||
for line in Self::CONSTANT_HEADER {
|
||||
disassembly.push_str(&style(center(line)));
|
||||
push_line(&style(center(line)));
|
||||
}
|
||||
|
||||
for (index, value_option) in self.chunk.constants.iter().enumerate() {
|
||||
@ -474,17 +497,34 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
format!("{index:<5} {value_display:<trucated_length$}")
|
||||
};
|
||||
|
||||
disassembly.push_str(¢er(&constant_display));
|
||||
push_line(¢er(&constant_display));
|
||||
|
||||
if let Some(chunk) = value_option.as_ref().and_then(|value| match value {
|
||||
Value::Raw(value_data) => value_data.as_function().map(|function| function.body()),
|
||||
Value::Reference(arc) => arc.as_function().map(|function| function.body()),
|
||||
Value::Mutable(arc) => todo!(),
|
||||
}) {
|
||||
let mut function_disassembler = chunk.disassembler("function");
|
||||
|
||||
function_disassembler
|
||||
.styled(self.styled)
|
||||
.indent(self.indent + 1);
|
||||
|
||||
let function_disassembly = function_disassembler.disassemble();
|
||||
|
||||
push_line(&function_disassembly);
|
||||
}
|
||||
}
|
||||
|
||||
for line in Self::LOCAL_HEADER {
|
||||
disassembly.push_str(&style(center(line)));
|
||||
push_line(&style(center(line)));
|
||||
}
|
||||
|
||||
for (
|
||||
index,
|
||||
Local {
|
||||
identifier,
|
||||
r#type,
|
||||
depth,
|
||||
register_index,
|
||||
is_mutable: mutable,
|
||||
@ -496,11 +536,15 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
.map(|value| value.to_string())
|
||||
.unwrap_or_else(|| "empty".to_string());
|
||||
let identifier_display = identifier.as_str();
|
||||
let type_display = r#type
|
||||
.as_ref()
|
||||
.map(|r#type| r#type.to_string())
|
||||
.unwrap_or("unknown".to_string());
|
||||
let local_display = format!(
|
||||
"{index:<5} {identifier_display:10} {mutable:7} {depth:<5} {register_display:8}"
|
||||
"{index:<5} {identifier_display:10} {type_display:8} {mutable:7} {depth:<5} {register_display:8}"
|
||||
);
|
||||
|
||||
disassembly.push_str(¢er(&local_display));
|
||||
push_line(¢er(&local_display));
|
||||
}
|
||||
|
||||
let expected_length = self.predict_length();
|
||||
@ -573,7 +617,7 @@ pub enum ChunkError {
|
||||
index: usize,
|
||||
position: Span,
|
||||
},
|
||||
IdentifierOverflow {
|
||||
LocalOverflow {
|
||||
position: Span,
|
||||
},
|
||||
IdentifierNotFound {
|
||||
@ -594,8 +638,8 @@ impl AnnotatedError for ChunkError {
|
||||
ChunkError::ConstantOverflow { .. } => "Constant overflow",
|
||||
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
||||
ChunkError::InstructionUnderflow { .. } => "Instruction underflow",
|
||||
ChunkError::LocalIndexOutOfBounds { .. } => "Identifier index out of bounds",
|
||||
ChunkError::IdentifierOverflow { .. } => "Identifier overflow",
|
||||
ChunkError::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
|
||||
ChunkError::LocalOverflow { .. } => "Local overflow",
|
||||
ChunkError::IdentifierNotFound { .. } => "Identifier not found",
|
||||
}
|
||||
}
|
||||
@ -611,13 +655,13 @@ impl AnnotatedError for ChunkError {
|
||||
}
|
||||
ChunkError::InstructionUnderflow { .. } => None,
|
||||
ChunkError::LocalIndexOutOfBounds { index, .. } => {
|
||||
Some(format!("Identifier index: {}", index))
|
||||
Some(format!("Local index: {}", index))
|
||||
}
|
||||
ChunkError::IdentifierNotFound { identifier, .. } => {
|
||||
Some(format!("Identifier: {}", identifier))
|
||||
}
|
||||
ChunkError::IdentifierOverflow { .. } => Some("Identifier overflow".to_string()),
|
||||
ChunkError::ConstantOverflow { .. } => Some("Constant overflow".to_string()),
|
||||
ChunkError::LocalOverflow { .. } => None,
|
||||
ChunkError::ConstantOverflow { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -629,7 +673,7 @@ impl AnnotatedError for ChunkError {
|
||||
ChunkError::IdentifierNotFound { position, .. } => *position,
|
||||
ChunkError::InstructionUnderflow { position, .. } => *position,
|
||||
ChunkError::LocalIndexOutOfBounds { position, .. } => *position,
|
||||
ChunkError::IdentifierOverflow { position, .. } => *position,
|
||||
ChunkError::LocalOverflow { position, .. } => *position,
|
||||
ChunkError::ConstantOverflow { position, .. } => *position,
|
||||
}
|
||||
}
|
||||
|
@ -93,6 +93,10 @@ impl<'src> Lexer<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn skip_to(&mut self, position: usize) {
|
||||
self.position = position;
|
||||
}
|
||||
|
||||
/// Produce the next token.
|
||||
pub fn next_token(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
||||
self.skip_whitespace();
|
||||
@ -539,6 +543,7 @@ impl<'src> Lexer<'src> {
|
||||
"loop" => Token::Loop,
|
||||
"map" => Token::Map,
|
||||
"mut" => Token::Mut,
|
||||
"str" => Token::Str,
|
||||
"struct" => Token::Struct,
|
||||
"true" => Token::Boolean("true"),
|
||||
"while" => Token::While,
|
||||
|
@ -8,7 +8,7 @@ use colored::Colorize;
|
||||
|
||||
use crate::{
|
||||
AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, LexError, Lexer,
|
||||
Operation, Span, Token, TokenKind, TokenOwned, Value,
|
||||
Operation, Span, Token, TokenKind, TokenOwned, Type, Value,
|
||||
};
|
||||
|
||||
pub fn parse(source: &str) -> Result<Chunk, DustError> {
|
||||
@ -17,7 +17,11 @@ pub fn parse(source: &str) -> Result<Chunk, DustError> {
|
||||
|
||||
while !parser.is_eof() {
|
||||
parser
|
||||
.parse_statement(true)
|
||||
.parse_statement(Allowed {
|
||||
assignment: true,
|
||||
explicit_return: false,
|
||||
implicit_return: true,
|
||||
})
|
||||
.map_err(|error| DustError::Parse { error, source })?;
|
||||
}
|
||||
|
||||
@ -39,9 +43,8 @@ impl<'src> Parser<'src> {
|
||||
pub fn new(mut lexer: Lexer<'src>) -> Result<Self, ParseError> {
|
||||
let (current_token, current_position) = lexer.next_token()?;
|
||||
|
||||
log::info!("Begin chunk");
|
||||
log::info!(
|
||||
"{} at {}",
|
||||
"Begin chunk with {} at {}",
|
||||
current_token.to_string().bold(),
|
||||
current_position.to_string()
|
||||
);
|
||||
@ -81,7 +84,7 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
fn decrement_register(&mut self) -> Result<(), ParseError> {
|
||||
fn _decrement_register(&mut self) -> Result<(), ParseError> {
|
||||
let current = self.current_register;
|
||||
|
||||
if current == 0 {
|
||||
@ -140,8 +143,7 @@ impl<'src> Parser<'src> {
|
||||
self.chunk.push_instruction(instruction, position);
|
||||
}
|
||||
|
||||
fn emit_constant(&mut self, value: Value) -> Result<(), ParseError> {
|
||||
let position = self.previous_position;
|
||||
fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), ParseError> {
|
||||
let constant_index = self.chunk.push_constant(value, position)?;
|
||||
|
||||
self.emit_instruction(
|
||||
@ -152,11 +154,7 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_boolean(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
fn parse_boolean(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||
let boolean_text = if let Token::Boolean(text) = self.current_token {
|
||||
text
|
||||
} else {
|
||||
@ -171,7 +169,6 @@ impl<'src> Parser<'src> {
|
||||
let boolean = boolean_text.parse::<bool>().unwrap();
|
||||
|
||||
self.advance()?;
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, boolean, false),
|
||||
position,
|
||||
@ -180,11 +177,9 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_byte(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
fn parse_byte(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||
let position = self.current_position;
|
||||
|
||||
if let Token::Byte(text) = self.current_token {
|
||||
self.advance()?;
|
||||
|
||||
@ -195,33 +190,29 @@ impl<'src> Parser<'src> {
|
||||
})?;
|
||||
let value = Value::byte(byte);
|
||||
|
||||
self.emit_constant(value)?;
|
||||
self.emit_constant(value, position)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_character(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
fn parse_character(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||
let position = self.current_position;
|
||||
|
||||
if let Token::Character(character) = self.current_token {
|
||||
self.advance()?;
|
||||
|
||||
let value = Value::character(character);
|
||||
|
||||
self.emit_constant(value)?;
|
||||
self.emit_constant(value, position)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_float(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
fn parse_float(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||
let position = self.current_position;
|
||||
|
||||
if let Token::Float(text) = self.current_token {
|
||||
self.advance()?;
|
||||
|
||||
@ -233,17 +224,15 @@ impl<'src> Parser<'src> {
|
||||
})?;
|
||||
let value = Value::float(float);
|
||||
|
||||
self.emit_constant(value)?;
|
||||
self.emit_constant(value, position)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_integer(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
fn parse_integer(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||
let position = self.current_position;
|
||||
|
||||
if let Token::Integer(text) = self.current_token {
|
||||
self.advance()?;
|
||||
|
||||
@ -255,43 +244,33 @@ impl<'src> Parser<'src> {
|
||||
})?;
|
||||
let value = Value::integer(integer);
|
||||
|
||||
self.emit_constant(value)?;
|
||||
self.emit_constant(value, position)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_string(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
fn parse_string(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||
let position = self.current_position;
|
||||
|
||||
if let Token::String(text) = self.current_token {
|
||||
self.advance()?;
|
||||
|
||||
let value = Value::string(text);
|
||||
|
||||
self.emit_constant(value)?;
|
||||
self.emit_constant(value, position)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_grouped(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
fn parse_grouped(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||
self.allow(TokenKind::LeftParenthesis)?;
|
||||
self.parse(Precedence::Assignment)?; // Do not allow assignment
|
||||
self.expect(TokenKind::RightParenthesis)
|
||||
}
|
||||
|
||||
fn parse_unary(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
fn parse_unary(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||
let operator = self.current_token;
|
||||
let operator_position = self.current_position;
|
||||
|
||||
@ -428,6 +407,7 @@ impl<'src> Parser<'src> {
|
||||
|
||||
current
|
||||
};
|
||||
|
||||
let mut new_instruction = match operator.kind() {
|
||||
TokenKind::Plus => Instruction::add(register, left, right),
|
||||
TokenKind::PlusEqual => Instruction::add(register, left, right),
|
||||
@ -605,11 +585,7 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_variable(
|
||||
&mut self,
|
||||
allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
fn parse_variable(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
||||
let token = self.current_token;
|
||||
let start_position = self.current_position;
|
||||
|
||||
@ -617,7 +593,7 @@ impl<'src> Parser<'src> {
|
||||
|
||||
let local_index = self.parse_identifier_from(token, start_position)?;
|
||||
|
||||
if allow_assignment && self.allow(TokenKind::Equal)? {
|
||||
if allowed.assignment && self.allow(TokenKind::Equal)? {
|
||||
let is_mutable = self
|
||||
.chunk
|
||||
.get_local(local_index, start_position)?
|
||||
@ -687,16 +663,31 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_block(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
self.expect(TokenKind::LeftCurlyBrace)?;
|
||||
fn parse_type_from(&mut self, token: Token, position: Span) -> Result<Type, ParseError> {
|
||||
match token {
|
||||
Token::Bool => Ok(Type::Boolean),
|
||||
Token::FloatKeyword => Ok(Type::Float),
|
||||
Token::Int => Ok(Type::Integer),
|
||||
Token::Str => Ok(Type::String { length: None }),
|
||||
_ => Err(ParseError::ExpectedTokenMultiple {
|
||||
expected: &[
|
||||
TokenKind::Bool,
|
||||
TokenKind::FloatKeyword,
|
||||
TokenKind::Int,
|
||||
TokenKind::Str,
|
||||
],
|
||||
found: self.current_token.to_owned(),
|
||||
position,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_block(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
||||
self.advance()?;
|
||||
self.chunk.begin_scope();
|
||||
|
||||
while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() {
|
||||
self.parse_statement(_allow_return)?;
|
||||
self.parse_statement(allowed)?;
|
||||
}
|
||||
|
||||
self.chunk.end_scope();
|
||||
@ -704,11 +695,7 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_list(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
fn parse_list(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||
let start = self.current_position.0;
|
||||
|
||||
self.advance()?;
|
||||
@ -745,7 +732,8 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_if(&mut self, allow_assignment: bool, allow_return: bool) -> Result<(), ParseError> {
|
||||
fn parse_if(&mut self, mut allowed: Allowed) -> Result<(), ParseError> {
|
||||
allowed.implicit_return = false;
|
||||
let length = self.chunk.len();
|
||||
|
||||
self.advance()?;
|
||||
@ -762,7 +750,7 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
if let Token::LeftCurlyBrace = self.current_token {
|
||||
self.parse_block(allow_assignment, allow_return)?;
|
||||
self.parse_block(allowed)?;
|
||||
}
|
||||
|
||||
let last_operation = self.chunk.get_last_operation()?;
|
||||
@ -780,28 +768,25 @@ impl<'src> Parser<'src> {
|
||||
|
||||
if self.allow(TokenKind::Else)? {
|
||||
if let Token::If = self.current_token {
|
||||
self.parse_if(allow_assignment, allow_return)?;
|
||||
self.parse_if(allowed)?;
|
||||
}
|
||||
|
||||
if let Token::LeftCurlyBrace = self.current_token {
|
||||
self.parse_block(allow_assignment, allow_return)?;
|
||||
self.parse_block(allowed)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_while(
|
||||
&mut self,
|
||||
allow_assignment: bool,
|
||||
allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
fn parse_while(&mut self, mut allowed: Allowed) -> Result<(), ParseError> {
|
||||
self.advance()?;
|
||||
|
||||
allowed.implicit_return = false;
|
||||
let jump_start = self.chunk.len();
|
||||
|
||||
self.parse(Precedence::Assignment)?; // Do not allow assignment
|
||||
self.parse_block(allow_assignment, allow_return)?;
|
||||
self.parse_block(allowed)?;
|
||||
|
||||
let jump_end = self.chunk.len();
|
||||
let jump_distance = jump_end.abs_diff(jump_start) as u8;
|
||||
@ -847,20 +832,27 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_statement(&mut self, allow_return: bool) -> Result<(), ParseError> {
|
||||
fn parse_statement(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
||||
match self.current_token {
|
||||
Token::Let => {
|
||||
self.parse_let_statement(true, allow_return)?;
|
||||
self.parse_let_statement(Allowed {
|
||||
assignment: true,
|
||||
explicit_return: false,
|
||||
implicit_return: false,
|
||||
})?;
|
||||
self.allow(TokenKind::Semicolon)?;
|
||||
}
|
||||
Token::LeftCurlyBrace => {
|
||||
self.parse_block(true, true)?;
|
||||
self.parse_block(allowed)?;
|
||||
self.allow(TokenKind::Semicolon)?;
|
||||
}
|
||||
_ => {
|
||||
self.parse_expression()?;
|
||||
|
||||
if !self.allow(TokenKind::Semicolon)? && self.is_eof() {
|
||||
if allowed.implicit_return
|
||||
&& !self.allow(TokenKind::Semicolon)?
|
||||
&& (self.is_eof() || matches!(self.current_token, Token::RightCurlyBrace))
|
||||
{
|
||||
self.emit_instruction(Instruction::r#return(), self.current_position);
|
||||
}
|
||||
}
|
||||
@ -869,19 +861,15 @@ impl<'src> Parser<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_let_statement(
|
||||
&mut self,
|
||||
allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
if !allow_assignment {
|
||||
fn parse_let_statement(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
||||
if !allowed.assignment {
|
||||
return Err(ParseError::ExpectedExpression {
|
||||
found: self.current_token.to_owned(),
|
||||
position: self.current_position,
|
||||
});
|
||||
}
|
||||
|
||||
self.allow(TokenKind::Let)?;
|
||||
self.expect(TokenKind::Let)?;
|
||||
|
||||
let is_mutable = self.allow(TokenKind::Mut)?;
|
||||
let position = self.current_position;
|
||||
@ -896,40 +884,104 @@ impl<'src> Parser<'src> {
|
||||
position: self.current_position,
|
||||
});
|
||||
};
|
||||
let explicit_type = if self.allow(TokenKind::Colon)? {
|
||||
Some(self.parse_type_from(self.current_token, self.current_position)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let register = self.current_register;
|
||||
|
||||
self.expect(TokenKind::Equal)?;
|
||||
self.parse_expression()?;
|
||||
|
||||
if self.current_register == register {
|
||||
self.increment_register()?;
|
||||
}
|
||||
|
||||
let (previous_instruction, previous_position) = self.chunk.get_last_instruction()?;
|
||||
|
||||
if let Operation::LoadConstant | Operation::LoadBoolean | Operation::Add =
|
||||
previous_instruction.operation()
|
||||
{
|
||||
let register = previous_instruction.a();
|
||||
let local_index =
|
||||
self.chunk
|
||||
.declare_local(identifier, is_mutable, register, *previous_position)?;
|
||||
let local_index = self.chunk.declare_local(
|
||||
identifier,
|
||||
explicit_type,
|
||||
is_mutable,
|
||||
register,
|
||||
*previous_position,
|
||||
)?;
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::define_local(register, local_index, is_mutable),
|
||||
position,
|
||||
);
|
||||
self.allow(TokenKind::Semicolon)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_function(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
self.advance()?;
|
||||
|
||||
self.expect(TokenKind::LeftParenthesis)?;
|
||||
|
||||
fn parse_function(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||
let function_start = self.current_position.0;
|
||||
let mut function_parser = Parser::new(self.lexer)?;
|
||||
|
||||
self.expect(TokenKind::RightParenthesis)?;
|
||||
function_parser.expect(TokenKind::LeftParenthesis)?;
|
||||
|
||||
function_parser.parse_block(false, true)?;
|
||||
while function_parser.current_token != Token::RightParenthesis {
|
||||
let start = function_parser.current_position.0;
|
||||
let is_mutable = function_parser.allow(TokenKind::Mut)?;
|
||||
let parameter = if let Token::Identifier(text) = function_parser.current_token {
|
||||
function_parser.advance()?;
|
||||
|
||||
Identifier::new(text)
|
||||
} else {
|
||||
return Err(ParseError::ExpectedToken {
|
||||
expected: TokenKind::Identifier,
|
||||
found: function_parser.current_token.to_owned(),
|
||||
position: function_parser.current_position,
|
||||
});
|
||||
};
|
||||
|
||||
function_parser.expect(TokenKind::Colon)?;
|
||||
|
||||
let r#type = function_parser.parse_type_from(
|
||||
function_parser.current_token,
|
||||
function_parser.current_position,
|
||||
)?;
|
||||
|
||||
function_parser.advance()?;
|
||||
|
||||
let end = function_parser.current_position.1;
|
||||
let local_index = function_parser.chunk.declare_local(
|
||||
parameter,
|
||||
Some(r#type),
|
||||
is_mutable,
|
||||
function_parser.current_register,
|
||||
Span(start, end),
|
||||
)?;
|
||||
|
||||
function_parser.chunk.define_local(
|
||||
local_index,
|
||||
function_parser.current_register,
|
||||
Span(start, end),
|
||||
)?;
|
||||
function_parser.increment_register()?;
|
||||
function_parser.allow(TokenKind::Comma)?;
|
||||
}
|
||||
|
||||
function_parser.advance()?;
|
||||
function_parser.expect(TokenKind::LeftCurlyBrace)?;
|
||||
|
||||
while function_parser.current_token != Token::RightCurlyBrace {
|
||||
function_parser.parse_statement(Allowed {
|
||||
assignment: true,
|
||||
explicit_return: true,
|
||||
implicit_return: true,
|
||||
})?;
|
||||
}
|
||||
|
||||
function_parser.advance()?;
|
||||
|
||||
self.previous_token = function_parser.previous_token;
|
||||
self.previous_position = function_parser.previous_position;
|
||||
@ -937,8 +989,10 @@ impl<'src> Parser<'src> {
|
||||
self.current_position = function_parser.current_position;
|
||||
|
||||
let function = Value::function(function_parser.take_chunk());
|
||||
let function_end = self.current_position.1;
|
||||
|
||||
self.emit_constant(function)?;
|
||||
self.lexer.skip_to(function_end);
|
||||
self.emit_constant(function, Span(function_start, function_end))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -954,7 +1008,14 @@ impl<'src> Parser<'src> {
|
||||
self.current_token.to_string().bold(),
|
||||
);
|
||||
|
||||
prefix_parser(self, allow_assignment, allow_return)?;
|
||||
prefix_parser(
|
||||
self,
|
||||
Allowed {
|
||||
assignment: true,
|
||||
explicit_return: allow_return,
|
||||
implicit_return: allow_return,
|
||||
},
|
||||
)?;
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
@ -1056,7 +1117,14 @@ impl Display for Precedence {
|
||||
}
|
||||
}
|
||||
|
||||
type PrefixFunction<'a> = fn(&mut Parser<'a>, bool, bool) -> Result<(), ParseError>;
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Allowed {
|
||||
assignment: bool,
|
||||
explicit_return: bool,
|
||||
implicit_return: bool,
|
||||
}
|
||||
|
||||
type PrefixFunction<'a> = fn(&mut Parser<'a>, Allowed) -> Result<(), ParseError>;
|
||||
type InfixFunction<'a> = fn(&mut Parser<'a>) -> Result<(), ParseError>;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -1374,19 +1442,19 @@ impl AnnotatedError for ParseError {
|
||||
fn details(&self) -> Option<String> {
|
||||
match self {
|
||||
Self::CannotMutateImmutableVariable { identifier, .. } => {
|
||||
Some(format!("Cannot mutate immutable variable \"{identifier}\""))
|
||||
Some(format!("Cannot mutate immutable variable {identifier}"))
|
||||
}
|
||||
Self::ExpectedExpression { found, .. } => Some(format!("Found \"{found}\"")),
|
||||
Self::ExpectedExpression { found, .. } => Some(format!("Found {found}")),
|
||||
Self::ExpectedToken {
|
||||
expected, found, ..
|
||||
} => Some(format!("Expected \"{expected}\", found \"{found}\"")),
|
||||
} => Some(format!("Expected {expected} but found {found}")),
|
||||
Self::ExpectedTokenMultiple {
|
||||
expected, found, ..
|
||||
} => {
|
||||
let mut details = String::from("Expected");
|
||||
|
||||
for (index, token) in expected.iter().enumerate() {
|
||||
details.push_str(&format!(" \"{token}\""));
|
||||
details.push_str(&format!(" {token}"));
|
||||
|
||||
if index < expected.len() - 2 {
|
||||
details.push_str(", ");
|
||||
@ -1397,18 +1465,18 @@ impl AnnotatedError for ParseError {
|
||||
}
|
||||
}
|
||||
|
||||
details.push_str(&format!(" found \"{found}\""));
|
||||
details.push_str(&format!(" but found {found}"));
|
||||
|
||||
Some(details)
|
||||
}
|
||||
Self::ExpectedMutableVariable { found, .. } => {
|
||||
Some(format!("Expected mutable variable, found \"{found}\""))
|
||||
Some(format!("Expected mutable variable, found {found}"))
|
||||
}
|
||||
Self::InvalidAssignmentTarget { found, .. } => {
|
||||
Some(format!("Invalid assignment target, found \"{found}\""))
|
||||
Some(format!("Invalid assignment target, found {found}"))
|
||||
}
|
||||
Self::UndefinedVariable { identifier, .. } => {
|
||||
Some(format!("Undefined variable \"{identifier}\""))
|
||||
Some(format!("Undefined variable {identifier}"))
|
||||
}
|
||||
Self::RegisterOverflow { .. } => None,
|
||||
Self::RegisterUnderflow { .. } => None,
|
||||
@ -1430,10 +1498,10 @@ impl AnnotatedError for ParseError {
|
||||
Self::UndefinedVariable { position, .. } => *position,
|
||||
Self::RegisterOverflow { position } => *position,
|
||||
Self::RegisterUnderflow { position } => *position,
|
||||
Self::Chunk(error) => error.position(),
|
||||
Self::Lex(error) => error.position(),
|
||||
Self::ParseFloatError { position, .. } => *position,
|
||||
Self::ParseIntError { position, .. } => *position,
|
||||
Self::Chunk(error) => error.position(),
|
||||
Self::Lex(error) => error.position(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ use serde::{
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
|
||||
use crate::{Chunk, EnumType, FunctionType, Identifier, RangeableType, StructType, Type};
|
||||
use crate::{Chunk, EnumType, Identifier, RangeableType, StructType, Type};
|
||||
|
||||
/// Dust value representation
|
||||
///
|
||||
@ -1063,6 +1063,14 @@ impl ValueData {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_function(&self) -> Option<&Function> {
|
||||
if let ValueData::Function(function) = self {
|
||||
Some(function)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_rangeable(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
@ -1452,6 +1460,10 @@ pub struct Function {
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn body(&self) -> &Chunk {
|
||||
&self.body
|
||||
}
|
||||
|
||||
pub fn call(
|
||||
self,
|
||||
_type_arguments: Option<Vec<Type>>,
|
||||
|
@ -482,7 +482,7 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
|
||||
fn pop(&mut self, position: Span) -> Result<Value, VmError> {
|
||||
fn _pop(&mut self, position: Span) -> Result<Value, VmError> {
|
||||
if let Some(register) = self.register_stack.pop() {
|
||||
let value = register.ok_or(VmError::EmptyRegister {
|
||||
index: self.register_stack.len().saturating_sub(1),
|
||||
|
@ -39,7 +39,7 @@ fn add_assign() {
|
||||
(Instruction::r#return(), Span(24, 24))
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![Local::new(Identifier::new("a"), true, 0, Some(0))]
|
||||
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))]
|
||||
))
|
||||
);
|
||||
|
||||
@ -105,11 +105,11 @@ fn block_scope() {
|
||||
Value::integer(1)
|
||||
],
|
||||
vec![
|
||||
Local::new(Identifier::new("a"), false, 0, Some(0)),
|
||||
Local::new(Identifier::new("b"), false, 1, Some(1)),
|
||||
Local::new(Identifier::new("c"), false, 2, Some(2)),
|
||||
Local::new(Identifier::new("d"), false, 1, Some(3)),
|
||||
Local::new(Identifier::new("e"), false, 0, Some(4)),
|
||||
Local::new(Identifier::new("a"), None, false, 0, Some(0)),
|
||||
Local::new(Identifier::new("b"), None, false, 1, Some(1)),
|
||||
Local::new(Identifier::new("c"), None, false, 2, Some(2)),
|
||||
Local::new(Identifier::new("d"), None, false, 1, Some(3)),
|
||||
Local::new(Identifier::new("e"), None, false, 0, Some(4)),
|
||||
]
|
||||
)),
|
||||
);
|
||||
@ -148,7 +148,7 @@ fn define_local() {
|
||||
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
||||
],
|
||||
vec![Value::integer(42)],
|
||||
vec![Local::new(Identifier::new("x"), false, 0, Some(0))]
|
||||
vec![Local::new(Identifier::new("x"), None, false, 0, Some(0))]
|
||||
)),
|
||||
);
|
||||
|
||||
@ -197,7 +197,7 @@ fn divide_assign() {
|
||||
(Instruction::r#return(), Span(24, 24))
|
||||
],
|
||||
vec![Value::integer(2), Value::integer(2)],
|
||||
vec![Local::new(Identifier::new("a"), true, 0, Some(0))]
|
||||
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))]
|
||||
))
|
||||
);
|
||||
|
||||
@ -261,7 +261,7 @@ fn equality_assignment_long() {
|
||||
(Instruction::r#return(), Span(44, 44)),
|
||||
],
|
||||
vec![Value::integer(4), Value::integer(4)],
|
||||
vec![Local::new(Identifier::new("a"), false, 0, Some(0))]
|
||||
vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))]
|
||||
)),
|
||||
);
|
||||
|
||||
@ -290,7 +290,7 @@ fn equality_assignment_short() {
|
||||
(Instruction::r#return(), Span(17, 17)),
|
||||
],
|
||||
vec![Value::integer(4), Value::integer(4)],
|
||||
vec![Local::new(Identifier::new("a"), false, 0, Some(0))]
|
||||
vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))]
|
||||
)),
|
||||
);
|
||||
|
||||
@ -686,7 +686,7 @@ fn multiply_assign() {
|
||||
(Instruction::r#return(), Span(23, 23))
|
||||
],
|
||||
vec![Value::integer(2), Value::integer(3)],
|
||||
vec![Local::new(Identifier::new("a"), true, 0, Some(0)),]
|
||||
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0)),]
|
||||
))
|
||||
);
|
||||
|
||||
@ -825,7 +825,7 @@ fn set_local() {
|
||||
(Instruction::r#return(), Span(25, 25)),
|
||||
],
|
||||
vec![Value::integer(41), Value::integer(42)],
|
||||
vec![Local::new(Identifier::new("x"), true, 0, Some(0)),]
|
||||
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
|
||||
)),
|
||||
);
|
||||
|
||||
@ -874,7 +874,7 @@ fn subtract_assign() {
|
||||
(Instruction::r#return(), Span(25, 25)),
|
||||
],
|
||||
vec![Value::integer(42), Value::integer(2)],
|
||||
vec![Local::new(Identifier::new("x"), true, 0, Some(0)),]
|
||||
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
|
||||
)),
|
||||
);
|
||||
|
||||
@ -901,8 +901,8 @@ fn variable_and() {
|
||||
],
|
||||
vec![],
|
||||
vec![
|
||||
Local::new(Identifier::new("a"), false, 0, Some(0)),
|
||||
Local::new(Identifier::new("b"), false, 0, Some(1)),
|
||||
Local::new(Identifier::new("a"), None, false, 0, Some(0)),
|
||||
Local::new(Identifier::new("b"), None, false, 0, Some(1)),
|
||||
]
|
||||
))
|
||||
);
|
||||
@ -931,7 +931,7 @@ fn r#while() {
|
||||
(Instruction::r#return(), Span(42, 42)),
|
||||
],
|
||||
vec![Value::integer(0), Value::integer(5), Value::integer(1),],
|
||||
vec![Local::new(Identifier::new("x"), true, 0, Some(0)),]
|
||||
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
|
||||
)),
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user