qbe-parser 0.1.0

A parser for QBE IR
Documentation
#![allow(clippy::unused_unit, reason = "part of macros")]
use crate::ast::types::*;
use crate::lexer::{Token, TokenParser, keyword, operator};
use crate::parse::{Parse, impl_fromstr_via_parse, maybe_newline, spanned};

use chumsky::prelude::*;

impl Parse for AlignSpec {
    const DESC: &'static str = "alignment spec";
    fn parser<'a>() -> impl TokenParser<'a, AlignSpec> {
        keyword!(align)
            .parser()
            .then_ignore(maybe_newline())
            .ignore_then(Token::number())
            .map_with(|value, extra| AlignSpec {
                value,
                span: extra.span(),
            })
            .labelled(Self::DESC)
    }
}

impl Parse for TypeDef {
    const DESC: &'static str = "type definition";

    fn parser<'a>() -> impl TokenParser<'a, TypeDef> {
        keyword!(type)
            .parser()
            .ignore_then(TypeName::parser())
            .then_ignore(operator!(=).parser().padded_by(maybe_newline()))
            .then(AlignSpec::parser().padded_by(maybe_newline()).or_not())
            .then(TypeDefBody::parser().padded_by(maybe_newline()))
            .map_with(|((name, align), body), extra| TypeDef {
                span: extra.span(),
                body,
                align,
                name,
            })
            .validate(|td, extra, emitter| {
                if let Err(errors) = td.validate() {
                    for e in errors {
                        emitter.emit(Rich::custom(extra.span(), e));
                    }
                }
                td
            })
            .labelled(Self::DESC)
    }
}
impl_fromstr_via_parse!(TypeDef);

impl Parse for TypeDefBody {
    const DESC: &'static str = "type body";

    fn parser<'a>() -> impl TokenParser<'a, TypeDefBody> {
        choice((
            OpaqueBody::parser().map(TypeDefBody::Opaque),
            StructBody::parser().map(TypeDefBody::Struct),
            UnionBody::parser().map(TypeDefBody::Union),
        ))
        .labelled("typedef body")
    }
}
impl Parse for OpaqueBody {
    const DESC: &'static str = "opaque type body";
    fn parser<'a>() -> impl TokenParser<'a, Self> {
        Token::number()
            .padded_by(maybe_newline())
            .delimited_by(just(Token::OpenBrace), just(Token::CloseBrace))
            .map_with(spanned)
            .map(|Spanned { value: size, span }| OpaqueBody { span, size })
            .labelled(Self::DESC)
    }
}
impl Parse for StructBody {
    const DESC: &'static str = "struct body";
    fn parser<'a>() -> impl TokenParser<'a, Self> {
        FieldDef::parser()
            .padded_by(maybe_newline())
            .separated_by(operator!(,).parser())
            .allow_trailing()
            .collect::<Vec<_>>()
            .delimited_by(just(Token::OpenBrace), just(Token::CloseBrace))
            .map_with(|fields, extra| StructBody {
                span: extra.span(),
                fields,
            })
            .labelled(Self::DESC)
    }
}
impl Parse for UnionBody {
    const DESC: &'static str = "union body";
    fn parser<'a>() -> impl TokenParser<'a, Self> {
        StructBody::parser()
            .padded_by(maybe_newline())
            .repeated()
            .at_least(1)
            .collect::<Vec<_>>()
            .delimited_by(just(Token::OpenBrace), just(Token::CloseBrace))
            .map_with(|variants, extra| UnionBody {
                span: extra.span(),
                variants,
            })
            .labelled("struct body")
    }
}
impl Parse for FieldDef {
    const DESC: &'static str = "field definition";
    fn parser<'a>() -> impl TokenParser<'a, Self> {
        FieldType::parser()
            .padded_by(maybe_newline())
            .then(Token::number().or_not())
            .map_with(|(ty, repeated), extra| FieldDef {
                span: extra.span(),
                ty,
                repeated,
            })
            .labelled(Self::DESC)
    }
}
macro_rules! parse_maybe_named_type {
    (impl Parse for $target:ident {
        const DESC = $desc:literal;
        variants {
            Named(TypeName),
            $($variant:ident($inner:ty, Span)),+ $(,)?
        }
    }) => {
        impl Parse for $target {
            const DESC: &'static str = $desc;
            fn parser<'a>() -> impl TokenParser<'a, Self> {
                fn _check_variants(t: $target) {
                    match t {
                        $target::Named(tp) => {
                            let _: TypeName = tp;
                        },
                        $($target::$variant(inner, s) => {
                            let _: $inner = inner;
                            let _: Span = s;
                        },)*
                    }
                }
                let simple = choice((
                    $(<$inner>::parser().map_with(|tp, extra| {
                        $target::$variant(tp, extra.span().into())
                    }),)*
                ));
                TypeName::parser()
                    .map($target::Named)
                    .or(simple)
                    .labelled(Self::DESC)
            }
        }
        impl_fromstr_via_parse!($target);
    };
}
parse_maybe_named_type! {
    impl Parse for FieldType {
        const DESC = "field type";
        variants {
            Named(TypeName),
            Extended(ExtendedType, Span),
        }
    }
}
parse_maybe_named_type! {
    impl Parse for AbiType {
        const DESC = "abi type";
        variants {
            Named(TypeName),
            Base(BaseType, Span),
            SubWord(SubWordType, Span),
        }
    }
}

macro_rules! simple_type_parser {
    ($($target:ident { DESC = $desc:literal }),+ $(,)?) => {
        $(impl Parse for $target {
            const DESC: &'static str = $desc;
            fn parser<'a>() -> impl TokenParser<'a, Self> {
                select!(Token::ShortTypeSpec(spec) => spec)
                    .try_map(|spec, span| spec.try_into().map_err(|reason| {
                        Rich::custom(span, reason)
                    }))
                    .labelled(Self::DESC)
            }
        })*
    };
}
simple_type_parser!(
    BaseType { DESC = "base type" },
    ExtendedType { DESC = "extended type" },
    SubWordType { DESC = "sub-word type" },
);

#[cfg(test)]
mod test {}