use alloc::{
collections::btree_map::BTreeMap,
string::{String, ToString},
};
use crate::{
compilation::Lexer,
execution::{Chunk, op_code::OpCode},
};
use super::{
error::CompilationError,
parselets::{
AnyInfixParselet, AnyPrefixParselet, AssignmentParselet, BinaryKind, BinaryParselet, GroupingParselet,
InfixParselet, LiteralParselet, LogicKind, LogicParselet, PrefixParselet, UnaryParselet, ValueParselet,
},
precedence::Precedence,
token::{Token, TokenKind},
};
pub struct Parser {
prefix_parselets: BTreeMap<TokenKind, AnyPrefixParselet>,
infix_parselets: BTreeMap<TokenKind, AnyInfixParselet>,
current: Token,
next: Token,
}
impl core::fmt::Debug for Parser {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Parser")
.field("current", &self.current)
.field("next", &self.next)
.finish_non_exhaustive()
}
}
impl Default for Parser {
fn default() -> Self {
Self::new()
}
}
impl Parser {
#[must_use]
#[allow(clippy::too_many_lines)]
pub fn new() -> Self {
let mut parser = Self {
prefix_parselets: BTreeMap::default(),
infix_parselets: BTreeMap::default(),
current: Token::none(),
next: Token::none(),
};
parser.infix_parselets.insert(
TokenKind::Ampersand,
AnyInfixParselet::Logic(LogicParselet::new(LogicKind::BitAnd)),
);
parser
.infix_parselets
.insert(TokenKind::And, AnyInfixParselet::Logic(LogicParselet::new(LogicKind::And)));
parser
.prefix_parselets
.insert(TokenKind::Bang, AnyPrefixParselet::Unary(UnaryParselet));
parser.infix_parselets.insert(
TokenKind::BangEqual,
AnyInfixParselet::Binary(BinaryParselet::new(BinaryKind::BangEqual)),
);
parser.infix_parselets.insert(
TokenKind::Caret,
AnyInfixParselet::Logic(LogicParselet::new(LogicKind::BitXor)),
);
parser
.prefix_parselets
.insert(TokenKind::Enum, AnyPrefixParselet::Value(ValueParselet));
parser.infix_parselets.insert(
TokenKind::EqualEqual,
AnyInfixParselet::Binary(BinaryParselet::new(BinaryKind::EqualEqual)),
);
parser
.prefix_parselets
.insert(TokenKind::False, AnyPrefixParselet::Literal(LiteralParselet));
parser.infix_parselets.insert(
TokenKind::Greater,
AnyInfixParselet::Binary(BinaryParselet::new(BinaryKind::Greater)),
);
parser.infix_parselets.insert(
TokenKind::GreaterEqual,
AnyInfixParselet::Binary(BinaryParselet::new(BinaryKind::GreaterEqual)),
);
parser
.prefix_parselets
.insert(TokenKind::HexNumber, AnyPrefixParselet::Value(ValueParselet));
parser
.prefix_parselets
.insert(TokenKind::IntNumber, AnyPrefixParselet::Value(ValueParselet));
parser
.prefix_parselets
.insert(TokenKind::Ident, AnyPrefixParselet::Assignment(AssignmentParselet));
parser
.prefix_parselets
.insert(TokenKind::LeftParen, AnyPrefixParselet::Grouping(GroupingParselet));
parser.infix_parselets.insert(
TokenKind::Less,
AnyInfixParselet::Binary(BinaryParselet::new(BinaryKind::Less)),
);
parser.infix_parselets.insert(
TokenKind::LessEqual,
AnyInfixParselet::Binary(BinaryParselet::new(BinaryKind::LessEqual)),
);
parser
.prefix_parselets
.insert(TokenKind::Minus, AnyPrefixParselet::Unary(UnaryParselet));
parser.infix_parselets.insert(
TokenKind::Minus,
AnyInfixParselet::Binary(BinaryParselet::new(BinaryKind::Minus)),
);
parser
.prefix_parselets
.insert(TokenKind::Nil, AnyPrefixParselet::Literal(LiteralParselet));
parser
.prefix_parselets
.insert(TokenKind::FloatNumber, AnyPrefixParselet::Value(ValueParselet));
parser
.infix_parselets
.insert(TokenKind::Or, AnyInfixParselet::Logic(LogicParselet::new(LogicKind::Or)));
parser
.infix_parselets
.insert(TokenKind::Pipe, AnyInfixParselet::Logic(LogicParselet::new(LogicKind::BitOr)));
parser
.prefix_parselets
.insert(TokenKind::Plus, AnyPrefixParselet::Unary(UnaryParselet));
parser.infix_parselets.insert(
TokenKind::Plus,
AnyInfixParselet::Binary(BinaryParselet::new(BinaryKind::Plus)),
);
parser.infix_parselets.insert(
TokenKind::QMark,
AnyInfixParselet::Logic(LogicParselet::new(LogicKind::Ternary)),
);
parser.infix_parselets.insert(
TokenKind::Slash,
AnyInfixParselet::Binary(BinaryParselet::new(BinaryKind::Slash)),
);
parser.infix_parselets.insert(
TokenKind::Star,
AnyInfixParselet::Binary(BinaryParselet::new(BinaryKind::Star)),
);
parser
.prefix_parselets
.insert(TokenKind::String, AnyPrefixParselet::Value(ValueParselet));
parser
.prefix_parselets
.insert(TokenKind::Tilde, AnyPrefixParselet::Unary(UnaryParselet));
parser
.prefix_parselets
.insert(TokenKind::True, AnyPrefixParselet::Literal(LiteralParselet));
parser
}
pub fn parse(&mut self, enums: &BTreeMap<String, i8>, source_code: &str) -> Result<Chunk, CompilationError> {
let mut chunk = Chunk::default();
let mut lexer = Lexer::new(enums, source_code);
self.advance(&mut lexer)?;
while !self.check_next(TokenKind::None) {
if let Err(_error) = self.statement(&mut lexer, &mut chunk) {
while !(self.check_next(TokenKind::Semicolon)
|| self.check_next(TokenKind::Print)
|| self.check_next(TokenKind::None))
{
self.advance(&mut lexer)?;
}
}
}
self.emit_byte(OpCode::Return as u8, &mut chunk);
chunk.finalize();
Ok(chunk)
}
pub(super) fn current(&self) -> Token {
self.current.clone()
}
pub(super) fn next(&self) -> Token {
self.next.clone()
}
pub(super) fn advance(&mut self, lexer: &mut Lexer) -> Result<(), CompilationError> {
self.current = self.next.clone();
let tmp = lexer.next();
if let Some(token) = tmp {
self.next = token?;
} else {
self.next = Token::none();
}
Ok(())
}
pub(super) fn consume(&mut self, lexer: &mut Lexer, expected: TokenKind) -> Result<(), CompilationError> {
if self.next.kind == expected {
self.advance(lexer)
} else {
Err(CompilationError::TokenExpected {
expected: expected.to_string().into(),
found: self.next.kind.to_string().into(),
pos: self.next.line,
})
}
}
pub(super) fn check_next(&self, kind: TokenKind) -> bool {
self.next.kind == kind
}
pub(super) fn emit_byte(&self, byte: u8, chunk: &mut Chunk) {
chunk.write(byte, self.current.line);
}
pub(super) fn emit_bytes(&self, byte1: u8, byte2: u8, chunk: &mut Chunk) {
chunk.write(byte1, self.current.line);
chunk.write(byte2, self.current.line);
}
pub(super) fn emit_jump(&self, instruction: u8, chunk: &mut Chunk) -> usize {
chunk.write(instruction, self.current.line);
let target_pos = chunk.code().len();
chunk.write(0xFF, self.current.line);
chunk.write(0xFF, self.current.line);
target_pos
}
#[allow(clippy::cast_possible_truncation)]
pub(super) fn patch_jump(patch_pos: usize, chunk: &mut Chunk) {
let target = chunk.code().len();
let byte1 = (target >> 8) as u8;
let byte2 = target as u8;
chunk.patch(byte1, patch_pos);
chunk.patch(byte2, patch_pos + 1);
}
pub(super) fn statement(&mut self, lexer: &mut Lexer, chunk: &mut Chunk) -> Result<(), CompilationError> {
if self.next.kind == TokenKind::Print {
self.advance(lexer)?;
self.expression(lexer, chunk)?;
if !self.check_next(TokenKind::None) {
self.consume(lexer, TokenKind::Semicolon)?;
}
#[cfg(feature = "std")]
self.emit_byte(OpCode::Print as u8, chunk);
#[cfg(not(feature = "std"))]
self.emit_byte(OpCode::Pop as u8, chunk);
} else {
self.expression(lexer, chunk)?;
if !self.check_next(TokenKind::None) {
self.consume(lexer, TokenKind::Semicolon)?;
}
}
Ok(())
}
pub(super) fn expression(&mut self, lexer: &mut Lexer, chunk: &mut Chunk) -> Result<(), CompilationError> {
self.with_precedence(lexer, Precedence::Assignment, chunk)
}
#[allow(clippy::expect_used)]
pub(super) fn with_precedence(
&mut self,
lexer: &mut Lexer,
precedence: Precedence,
chunk: &mut Chunk,
) -> Result<(), CompilationError> {
self.advance(lexer)?;
let token = self.current();
let Some(prefix_parselet) = self.prefix_parselets.get(&token.kind).cloned() else {
return Err(CompilationError::ExpressionExpected {
token: token.kind.to_string().into(),
pos: token.line,
});
};
prefix_parselet.parse(lexer, self, chunk, token)?;
while precedence <= self.get_precedence() {
self.advance(lexer)?;
let token = self.current();
if let Some(infix) = self.infix_parselets.get(&token.kind).cloned() {
infix.parse(lexer, self, chunk, token)?;
} else {
break;
}
}
Ok(())
}
fn get_precedence(&self) -> Precedence {
let token = self.next();
if let Some(parselet) = self.infix_parselets.get(&token.kind) {
return parselet.get_precedence();
}
Precedence::None
}
}
#[cfg(test)]
mod tests {
use super::*;
const fn is_normal<T: Sized + Send + Sync>() {}
#[test]
const fn normal_types() {
is_normal::<&Parser>();
is_normal::<Parser>();
}
}