use std::{fmt::Debug, sync::Arc};
use derive_more::{Deref, From};
use enum_as_inner::EnumAsInner;
use crate::base::{
self,
source_file::{SourceElement, SourceFile, Span},
Handler,
};
use super::{
error::{self, UndelimitedDelimiter},
token::{Punctuation, Token, TokenizeError},
};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deref)]
pub struct TokenStream {
#[deref]
token_trees: Vec<TokenTree>,
}
impl Debug for TokenStream {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.token_trees.iter()).finish()
}
}
impl TokenStream {
#[must_use]
#[tracing::instrument(level = "debug", skip_all, fields(source_file = %source_file.path().display()))]
pub fn tokenize(source_file: &Arc<SourceFile>, handler: &impl Handler<base::Error>) -> Self {
let mut tokens = Vec::new();
let mut source_file_iterator = source_file.iter();
loop {
match Token::tokenize(&mut source_file_iterator, handler, tokens.last()) {
Ok(token) => tokens.push(token),
Err(TokenizeError::EndOfSourceCodeIteratorArgument) => {
break;
}
Err(TokenizeError::FatalLexicalError) => {
tracing::error!("Fatal lexical error encountered while tokenizing source code");
}
}
}
tokens.reverse();
tracing::debug!("Structuring tokens into trees");
let mut token_trees = Vec::new();
while let Some(token_tree) = Self::handle_token(&mut tokens, handler) {
token_trees.push(token_tree);
}
Self { token_trees }
}
fn handle_token(
tokens: &mut Vec<Token>,
handler: &impl Handler<base::Error>,
) -> Option<TokenTree> {
tokens
.pop()
.and_then(|token| Self::handle_popped_token(tokens, token, handler))
}
fn handle_popped_token(
tokens: &mut Vec<Token>,
popped_token: Token,
handler: &impl Handler<base::Error>,
) -> Option<TokenTree> {
match popped_token {
Token::Punctuation(punc) if punc.punctuation == '{' => {
Self::handle_delimited(tokens, punc, Delimiter::Brace, handler)
.map(TokenTree::Delimited)
}
Token::Punctuation(punc) if punc.punctuation == '[' => {
Self::handle_delimited(tokens, punc, Delimiter::Bracket, handler)
.map(TokenTree::Delimited)
}
Token::Punctuation(punc) if punc.punctuation == '(' => {
Self::handle_delimited(tokens, punc, Delimiter::Parenthesis, handler)
.map(TokenTree::Delimited)
}
token => Some(TokenTree::Token(token)),
}
}
fn handle_delimited(
tokens: &mut Vec<Token>,
open: Punctuation,
delimiter: Delimiter,
handler: &impl Handler<base::Error>,
) -> Option<Delimited> {
let mut token_trees = Vec::new();
while let Some(token) = tokens.pop() {
match (token, delimiter) {
(Token::Punctuation(p), Delimiter::Brace) if p.punctuation == '}' => {
return Some(Delimited {
open,
token_stream: Self { token_trees },
close: p,
delimiter,
});
}
(Token::Punctuation(punc), Delimiter::Bracket) if punc.punctuation == ']' => {
return Some(Delimited {
open,
token_stream: Self { token_trees },
close: punc,
delimiter,
})
}
(Token::Punctuation(punc), Delimiter::Parenthesis) if punc.punctuation == ')' => {
return Some(Delimited {
open,
token_stream: Self { token_trees },
close: punc,
delimiter,
})
}
(token, _) => {
let Some(token_tree) = Self::handle_popped_token(tokens, token, handler) else {
break;
};
token_trees.push(token_tree);
}
}
}
handler.receive(error::Error::UndelimitedDelimiter(UndelimitedDelimiter {
opening_span: open.span,
delimiter,
}));
None
}
#[must_use]
pub fn dissolve(self) -> Vec<TokenTree> {
self.token_trees
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, From, EnumAsInner)]
#[allow(missing_docs)]
pub enum TokenTree {
Token(Token),
Delimited(Delimited),
}
impl SourceElement for TokenTree {
fn span(&self) -> Span {
match self {
Self::Token(token) => token.span().to_owned(),
Self::Delimited(delimited) => delimited
.open
.span()
.join(&delimited.close.span)
.expect("Invalid delimited span"),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Delimiter {
Parenthesis,
Brace,
Bracket,
}
impl Delimiter {
#[must_use]
pub fn opening_char(&self) -> char {
match self {
Self::Parenthesis => '(',
Self::Brace => '{',
Self::Bracket => '[',
}
}
#[must_use]
pub fn closing_char(&self) -> char {
match self {
Self::Parenthesis => ')',
Self::Brace => '}',
Self::Bracket => ']',
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Delimited {
pub open: Punctuation,
pub token_stream: TokenStream,
pub close: Punctuation,
pub delimiter: Delimiter,
}