use std::ops::{Deref, DerefMut};
use gramatika::{
Lexer as _, Parse, ParseStreamer, PartialLexer, Span, Spanned, SpannedError, Substr,
Token as _, TokenStream,
};
use crate::{expr::Expr, parser::ErrorRecoveringParseStream, utils, Text, Token};
#[cfg(test)]
use crate::utils::WithErrors;
use super::{
chunk::{ChunkKind, Lexer as ChunkLexer},
directive::{DirectiveKind, DirectiveLexer},
Chunk, Directive,
};
#[derive(DebugLisp, Default)]
pub struct PreParsed {
pub sections: Vec<Section>,
}
#[derive(DebugLisp)]
pub enum Section {
Text(Text),
Branch(Box<Branch>),
Definition(Text),
}
#[derive(DebugLisp)]
pub struct Branch {
pub directive: Directive,
pub condition: Option<Expr>,
pub body: Vec<Section>,
pub else_: Option<Box<Branch>>,
}
impl Deref for PreParsed {
type Target = Vec<Section>;
fn deref(&self) -> &Self::Target {
&self.sections
}
}
impl DerefMut for PreParsed {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.sections
}
}
pub struct ParseStream {
inner: gramatika::ParseStream<Chunk, ChunkLexer>,
errors: Vec<SpannedError>,
}
impl ParseStream {
pub fn into_inner(self) -> (Substr, Vec<SpannedError>) {
(self.inner.source(), self.errors)
}
pub fn source(&self) -> Substr {
self.inner.source()
}
}
impl<S> From<S> for ParseStream
where S: Into<Substr>
{
fn from(input: S) -> Self {
let input = input.into();
Self {
inner: gramatika::ParseStream::from(input),
errors: vec![],
}
}
}
#[cfg(test)]
impl WithErrors for ParseStream {
type Output = SpannedError;
fn get(&self) -> &[Self::Output] {
&self.errors
}
}
impl ErrorRecoveringParseStream for ParseStream {
fn push_error(&mut self, error: SpannedError) {
self.errors.push(error);
}
}
impl ParseStreamer for ParseStream {
type Token = Chunk;
fn is_empty(&mut self) -> bool {
self.inner.is_empty()
}
fn peek(&mut self) -> Option<&Self::Token> {
self.inner.peek()
}
fn prev(&mut self) -> Option<&Self::Token> {
self.inner.prev()
}
fn check_kind(&mut self, kind: <Self::Token as gramatika::Token>::Kind) -> bool {
self.inner.check_kind(kind)
}
fn check(&mut self, compare: Self::Token) -> bool {
self.inner.check(compare)
}
fn consume(&mut self, compare: Self::Token) -> gramatika::Result<Self::Token> {
self.inner.consume(compare)
}
fn consume_kind(
&mut self,
kind: <Self::Token as gramatika::Token>::Kind,
) -> gramatika::Result<Self::Token> {
self.inner.consume_kind(kind)
}
fn consume_as(
&mut self,
kind: <Self::Token as gramatika::Token>::Kind,
convert: gramatika::TokenCtor<Self::Token>,
) -> gramatika::Result<Self::Token> {
self.inner.consume_as(kind, convert)
}
fn upgrade_last(
&mut self,
kind: <Self::Token as gramatika::Token>::Kind,
convert: gramatika::TokenCtor<Self::Token>,
) -> gramatika::Result<Self::Token> {
self.inner.upgrade_last(kind, convert)
}
fn upgrade(
&mut self,
token: Self::Token,
convert: gramatika::TokenCtor<Self::Token>,
) -> gramatika::Result<Self::Token> {
self.inner.upgrade(token, convert)
}
fn discard(&mut self) {
self.inner.discard()
}
}
impl Iterator for ParseStream {
type Item = Chunk;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl Parse for PreParsed {
type Stream = ParseStream;
fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
let sections = input.parse_seq(|input| !input.is_empty());
Ok(Self { sections })
}
}
impl Parse for Section {
type Stream = ParseStream;
fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
match input.peek().cloned() {
Some(Chunk::Definition(_, _)) => {
let (substr, span) = input.next().unwrap().as_inner();
Ok(Section::Definition(Text(substr, span)))
}
Some(Chunk::Line(_, _)) => Ok(Section::Text(parse_text(input)?)),
Some(Chunk::BranchStart(_, _)) => Ok(Section::Branch(Box::new(input.parse()?))),
Some(other) => {
let span = other.span();
Err(SpannedError {
message: "Unexpected input".into(),
source: input.source(),
span: Some(span),
})
}
None => Err(SpannedError {
message: "Unexpected end of input".into(),
source: input.source(),
span: input.prev().map(|token| token.span()),
}),
}
}
}
fn parse_text(input: &mut ParseStream) -> gramatika::Result<Text> {
let (mut text, mut span) = input.consume_kind(ChunkKind::Line)?.as_inner();
while let Some(Chunk::Line(next_text, next_span)) = input.peek() {
text = utils::join_substrs(&text, next_text);
span = span.through(*next_span);
input.next();
}
Ok(Text(text, span))
}
impl Parse for Branch {
type Stream = ParseStream;
fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
let (line, line_span) = match input.next() {
Some(Chunk::BranchStart(line, span) | Chunk::BranchFork(line, span)) => (line, span),
Some(other) => {
return Err(SpannedError {
message: "Expected `#if ...`, `#ifdef ...` `#ifndef ...` or `#else ...`".into(),
source: input.source(),
span: Some(other.span()),
});
}
None => {
return Err(SpannedError {
message: "Unexpected end of input".into(),
source: input.source(),
span: input.prev().map(|token| token.span()),
});
}
};
let mut lexer = DirectiveLexer::from_remaining(line.clone(), line_span.start);
let directive = lexer.scan_token().ok_or_else(|| SpannedError {
message: "Unexpected end of line".into(),
source: lexer.source(),
span: Some(Span {
start: line_span.end,
end: line_span.end,
}),
})?;
let (remaining, position) = lexer.stop();
let condition_lexer = TokenStream::<Token>::from_remaining(remaining, position);
let condition = crate::ParseStream::new(condition_lexer)
.parse::<Expr>()
.ok();
let body = input.parse_seq(|input| {
!input.check_kind(ChunkKind::BranchEnd) && !input.check_kind(ChunkKind::BranchFork)
});
let else_ = if input.check_kind(ChunkKind::BranchFork) {
Some(Box::new(input.parse()?))
} else {
None
};
if !matches!(
directive.kind(),
DirectiveKind::Else
| DirectiveKind::ElseIf
| DirectiveKind::ElseIfDef
| DirectiveKind::ElseIfNDef
) {
input.consume_kind(ChunkKind::BranchEnd)?;
}
Ok(Self {
directive,
condition,
body,
else_,
})
}
}
impl Spanned for Section {
fn span(&self) -> Span {
match self {
Section::Definition(inner) | Section::Text(inner) => inner.span(),
Section::Branch(inner) => inner.span(),
}
}
}
impl Spanned for Branch {
fn span(&self) -> Span {
let start = self.directive.span();
let end = self
.else_
.as_ref()
.map(|branch| branch.span())
.or_else(|| self.body.last().map(|last| last.span()))
.or_else(|| self.condition.as_ref().map(|cond| cond.span()));
match end {
Some(end) => start.through(end),
None => start,
}
}
}