use std::fmt::Display;
use crate::{
errors::{ModuleError, ParserContext},
io::{Comparator, SourceFile},
lexer::tokenize,
parsers::{cst::serialize_cst, cst::ConcreteSyntaxTree, ParseResult, SyntaxKind},
};
use serde::Serialize;
#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
pub struct ModuleFile {
pub name: String, #[serde(serialize_with = "serialize_cst")]
pub cst: ConcreteSyntaxTree,
}
impl Display for ModuleFile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "VB6 Module File: {}", self.name)
}
}
impl ModuleFile {
#[must_use]
pub fn parse(source_file: &SourceFile) -> ParseResult<'_, ModuleFile> {
let mut input = source_file.source_stream();
let mut ctx = ParserContext::new(input.file_name(), input.contents);
let _ = input.take_ascii_whitespaces();
if input
.take("Attribute", Comparator::CaseInsensitive)
.is_none()
{
ctx.error(input.span_here(), ModuleError::AttributeKeywordMissing);
}
if input.take_ascii_whitespaces().is_none() {
ctx.error(input.span_here(), ModuleError::MissingWhitespaceInHeader);
}
if input.take("VB_Name", Comparator::CaseInsensitive).is_none() {
ctx.error(input.span_here(), ModuleError::VBNameAttributeMissing);
}
let _ = input.take_ascii_whitespaces();
if input.take("=", Comparator::CaseInsensitive).is_none() {
ctx.error(input.span_here(), ModuleError::EqualMissing);
}
let _ = input.take_ascii_whitespaces();
if input.take("\"", Comparator::CaseInsensitive).is_none() {
ctx.error(input.span_here(), ModuleError::VBNameAttributeValueUnquoted);
}
match input.take_until("\"", Comparator::CaseInsensitive) {
None => {
let Some((vb_name_value, _)) = input.take_until_newline() else {
ctx.error(input.span_here(), ModuleError::VBNameAttributeValueUnquoted);
return ParseResult::new(None, ctx.into_errors());
};
let mut stream = source_file.source_stream();
let token_result = tokenize(&mut stream);
let (token_stream_opt, token_failures) = token_result.unpack();
ctx.extend_errors(token_failures);
if let Some(tokens) = token_stream_opt {
let cst = crate::parsers::cst::parse(tokens);
let filtered_cst = cst.without_kinds(&[SyntaxKind::AttributeStatement]);
ParseResult::new(
Some(ModuleFile {
name: vb_name_value.to_string(),
cst: filtered_cst,
}),
ctx.into_errors(),
)
} else {
ParseResult::new(None, ctx.into_errors())
}
}
Some((vb_name_value, _)) => {
let _ = input.take_count(1);
let _ = input.take_ascii_whitespaces();
let _ = input.take_newline();
let token_result = tokenize(&mut input);
let (token_stream_opt, token_failures) = token_result.unpack();
ctx.extend_errors(token_failures);
if let Some(tokens) = token_stream_opt {
let cst = crate::parsers::cst::parse(tokens);
let filtered_cst = cst.without_kinds(&[SyntaxKind::AttributeStatement]);
ParseResult::new(
Some(ModuleFile {
name: vb_name_value.to_string(),
cst: filtered_cst,
}),
ctx.into_errors(),
)
} else {
ParseResult::new(None, ctx.into_errors())
}
}
}
}
}