xbasic 0.3.2

A library that allows adding a scripting language onto your project with ease. This lets your users write their own arbitrary logic.
Documentation
use crate::error_handler::ErrorHandler;
use crate::function::{FunctionDefinition, FunctionType};
use crate::tokens::{Token, TokenType};

pub(crate) struct Resolver<'a> {
	tokens: Vec<Token>,
	current: usize,
	functions: Vec<FunctionDefinition>,
	error_handler: &'a mut ErrorHandler,
}

impl<'a> Resolver<'a> {
	pub(crate) fn new(tokens: Vec<Token>, error_handler: &'a mut ErrorHandler) -> Self {
		Self {
			tokens,
			current: 0,
			functions: Vec::new(),
			error_handler,
		}
	}

	pub(crate) fn resolve(
		&mut self,
		function_definitions: &[FunctionDefinition],
	) -> Option<Vec<FunctionDefinition>> {
		self.functions = function_definitions.to_owned();
		while !self.is_at_end() {
			self.advance();
			if self.previous().token_type == TokenType::Function {
				// TODO is 256 functions enough?
				// May want to bump this to 65536 functions
				if self.functions.len() >= 256 {
					// Kind of gross
					// Clone is b/c we're borrowing mutably and immutably at the same time
					self.error_handler.error_token(
						&self.previous().clone(),
						"Cannot have more than 256 functions",
					);
					return None;
				}

				let func = self.function()?;
				self.functions.push(func);
			}
		}
		Some(self.functions.clone())
	}

	fn function(&mut self) -> Option<FunctionDefinition> {
		// Read in identifier
		let name = self.consume(TokenType::Identifier, "Expected identifier after FUNCTION.")?;

		// Confirm name isn't already in use
		for func in &self.functions {
			if func.name == name.lexeme {
				self.error_handler.error_token(
					&name,
					&format!("Function is already defined on line {}.", func.line),
				);
				return None;
			}
		}

		// Read in (
		self.consume(TokenType::LeftParen, "Expected '(' after function name.")?;

		// Count parameters
		let mut arity = 0;
		if self.peek().token_type != TokenType::RightParen {
			let mut another = true;
			while !self.is_at_end() && another {
				if arity == 255 {
					self.error_handler
						.error_token(&name, "Function cannot have more than 255 parameters.");
					return None;
				}
				arity += 1;
				self.consume(TokenType::Identifier, "Expected parameter name.")?;
				another = self.match_type(TokenType::Comma);
			}
		}

		// Read in )
		self.consume(
			TokenType::RightParen,
			"Expected ')' after function parameters.",
		)?;

		// Keep going until we read in "end function"
		while !self.is_at_end()
			&& (self.peek().token_type != TokenType::End
				|| self.peek_next().token_type != TokenType::Function)
		{
			self.advance();
		}

		self.consume(
			TokenType::End,
			"Expected END FUNCTION after function declaration.",
		)?;

		self.consume(
			TokenType::Function,
			"Expected END FUNCTION after function declaration.",
		)?;

		Some(FunctionDefinition::new(
			name.lexeme,
			arity,
			name.line,
			FunctionType::User,
		))
	}

	fn consume(&mut self, token_type: TokenType, msg: &str) -> Option<Token> {
		if self.peek().token_type == token_type {
			self.advance();
			Some(self.tokens[self.current - 1].clone())
		} else {
			self.error_handler
				.error_token(&self.tokens[self.current], msg);
			None
		}
	}

	fn match_type(&mut self, token_type: TokenType) -> bool {
		if self.peek().token_type == token_type {
			self.advance();
			true
		} else {
			false
		}
	}

	fn is_at_end(&self) -> bool {
		self.peek().token_type == TokenType::Eof
	}

	fn peek(&self) -> &Token {
		&self.tokens[self.current]
	}

	fn peek_next(&self) -> &Token {
		&self.tokens[self.current + 1]
	}

	fn previous(&self) -> &Token {
		&self.tokens[self.current - 1]
	}

	fn advance(&mut self) {
		if !self.is_at_end() {
			self.current += 1;
		}
	}
}