use nom::branch::alt;
use nom::combinator::{map, opt};
use nom::multi::many1;
use nom::sequence::{delimited, pair};
use nom::IResult;
use crate::preprocessor::token::{Token, TokenType};
use crate::preprocessor::FilePreProcessor;
use crate::{Number, NumberUnsigned, SleighError, Span};
pub trait TokenHelper {
fn location(&self) -> Span;
fn ident(&self) -> Result<&str, Box<SleighError>>;
fn string(&self) -> Result<&str, Box<SleighError>>;
fn number(&self) -> Result<NumberUnsigned, Box<SleighError>>;
}
pub trait Parser {
fn parse_until(
&mut self,
token_stream: &mut Vec<Token>,
find: fn(&Token) -> bool,
) -> Result<(), Box<SleighError>>;
}
impl<'a> nom::error::ParseError<&'a [Token]> for SleighError {
fn from_error_kind(input: &'a [Token], kind: nom::error::ErrorKind) -> Self {
if let Some(first) = input.first() {
Self::UnableToParse(first.location.clone(), kind)
} else {
Self::UnexpectedEof
}
}
fn append(_input: &'a [Token], _kind: nom::error::ErrorKind, other: Self) -> Self {
other
}
}
impl From<Box<SleighError>> for nom::Err<Box<SleighError>> {
fn from(value: Box<SleighError>) -> Self {
nom::Err::Error(value)
}
}
impl From<nom::Err<Box<SleighError>>> for Box<SleighError> {
fn from(value: nom::Err<Box<SleighError>>) -> Self {
match value {
nom::Err::Incomplete(_) => unreachable!(),
nom::Err::Error(e) | nom::Err::Failure(e) => e,
}
}
}
impl nom::error::FromExternalError<&[Token], Box<SleighError>> for Box<SleighError> {
fn from_external_error(
_input: &[Token],
_kind: nom::error::ErrorKind,
e: Box<SleighError>,
) -> Self {
e
}
}
impl TokenHelper for Token {
fn location(&self) -> Span {
self.location.clone()
}
fn ident(&self) -> Result<&str, Box<SleighError>> {
match &self.token_type {
TokenType::Ident(value) => Ok(value),
_ => Err(Box::new(SleighError::StatementInvalid(self.location()))),
}
}
fn string(&self) -> Result<&str, Box<SleighError>> {
match &self.token_type {
TokenType::String(value) => Ok(value),
_ => Err(Box::new(SleighError::StatementInvalid(self.location()))),
}
}
fn number(&self) -> Result<NumberUnsigned, Box<SleighError>> {
match &self.token_type {
TokenType::Number(value) => Ok(*value),
_ => Err(Box::new(SleighError::StatementInvalid(self.location()))),
}
}
}
impl Parser for FilePreProcessor {
fn parse_until(
&mut self,
token_stream: &mut Vec<Token>,
find: fn(&Token) -> bool,
) -> Result<(), Box<SleighError>> {
while let Some(token) = self.parse()? {
let found_end = find(&token);
token_stream.push(token);
if found_end {
return Ok(());
}
}
Err(Box::new(SleighError::UnexpectedEof))
}
}
#[allow(clippy::type_complexity)]
pub fn this_ident<'a, 'b>(
value: &'b str,
) -> impl FnMut(&'a [Token]) -> IResult<&'a [Token], &'a Span, Box<SleighError>> + 'b {
move |input: &'a [Token]| {
let (first, rest) = input
.split_first()
.ok_or_else(|| Box::new(SleighError::UnexpectedEof))?;
if first.ident()? != value {
return Err(nom::Err::Error(Box::new(SleighError::StatementInvalid(
first.location.clone(),
))));
}
Ok((rest, &first.location))
}
}
#[rustfmt::skip]
macro_rules! token_type {
("$and") => { crate::preprocessor::token::TokenType::OpBoolAnd };
("$xor") => { crate::preprocessor::token::TokenType::OpBoolXor };
("$or") => { crate::preprocessor::token::TokenType::OpBoolOr };
("f!=") => { crate::preprocessor::token::TokenType::OpFnotequal };
("f<=") => { crate::preprocessor::token::TokenType::OpFlessequal };
("f==") => { crate::preprocessor::token::TokenType::OpFequal };
("f>=") => { crate::preprocessor::token::TokenType::OpFgreatequal };
("f*") => { crate::preprocessor::token::TokenType::OpFmult };
("f+") => { crate::preprocessor::token::TokenType::OpFadd };
("f-") => { crate::preprocessor::token::TokenType::OpFsub };
("f/") => { crate::preprocessor::token::TokenType::OpFdiv };
("f<") => { crate::preprocessor::token::TokenType::OpFless };
("f>") => { crate::preprocessor::token::TokenType::OpFgreat };
("s<=") => { crate::preprocessor::token::TokenType::OpSlessequal };
("s>=") => { crate::preprocessor::token::TokenType::OpSgreatequal };
("s>>") => { crate::preprocessor::token::TokenType::OpSright };
("s%") => { crate::preprocessor::token::TokenType::OpSrem };
("s/") => { crate::preprocessor::token::TokenType::OpSdiv };
("s<") => { crate::preprocessor::token::TokenType::OpSless };
("s>") => { crate::preprocessor::token::TokenType::OpSgreat };
("!=") => { crate::preprocessor::token::TokenType::OpNotequal };
("<<") => { crate::preprocessor::token::TokenType::OpLeft };
("<=") => { crate::preprocessor::token::TokenType::OpLessequal };
("==") => { crate::preprocessor::token::TokenType::OpEqual };
(">=") => { crate::preprocessor::token::TokenType::OpGreatequal };
(">>") => { crate::preprocessor::token::TokenType::OpRight };
("&&") => { crate::preprocessor::token::TokenType::OpBoolAnd };
("^^") => { crate::preprocessor::token::TokenType::OpBoolXor };
("||") => { crate::preprocessor::token::TokenType::OpBoolOr };
(">") => { crate::preprocessor::token::TokenType::OpGreat };
("<") => { crate::preprocessor::token::TokenType::OpLess };
("^") => { crate::preprocessor::token::TokenType::OpXor };
("&") => { crate::preprocessor::token::TokenType::OpAnd };
("|") => { crate::preprocessor::token::TokenType::OpOr };
("~") => { crate::preprocessor::token::TokenType::OpNeg };
("!") => { crate::preprocessor::token::TokenType::OpBitNeg };
("+") => { crate::preprocessor::token::TokenType::OpAdd };
("-") => { crate::preprocessor::token::TokenType::OpSub };
("*") => { crate::preprocessor::token::TokenType::OpMul };
("/") => { crate::preprocessor::token::TokenType::OpDiv };
("%") => { crate::preprocessor::token::TokenType::OpRem };
("=") => { crate::preprocessor::token::TokenType::OpAssign };
("(") => { crate::preprocessor::token::TokenType::DeliOpenParenteses };
(")") => { crate::preprocessor::token::TokenType::DeliCloseParenteses };
("[") => { crate::preprocessor::token::TokenType::DeliOpenBrackets };
("]") => { crate::preprocessor::token::TokenType::DeliCloseBrackets };
("{") => { crate::preprocessor::token::TokenType::DeliOpenCurly };
("}") => { crate::preprocessor::token::TokenType::DeliCloseCurly };
(",") => { crate::preprocessor::token::TokenType::Comma };
(":") => { crate::preprocessor::token::TokenType::DubleDot };
(";") => { crate::preprocessor::token::TokenType::StatementEnd };
("_") => { crate::preprocessor::token::TokenType::Underline };
("if") => { crate::preprocessor::token::TokenType::IfKey };
("...") => { crate::preprocessor::token::TokenType::Ellipsis };
("abs") => { crate::preprocessor::token::TokenType::OpAbs };
("nan") => { crate::preprocessor::token::TokenType::OpNan };
("call") => { crate::preprocessor::token::TokenType::CallKey };
("ceil") => { crate::preprocessor::token::TokenType::OpCeil };
("goto") => { crate::preprocessor::token::TokenType::GotoKey };
("sqrt") => { crate::preprocessor::token::TokenType::OpFloatSqrt };
("sext") => { crate::preprocessor::token::TokenType::OpSext };
("zext") => { crate::preprocessor::token::TokenType::OpZext };
("const") => { crate::preprocessor::token::TokenType::SpaceConst };
("trunc") => { crate::preprocessor::token::TokenType::OpTrunc };
("carry") => { crate::preprocessor::token::TokenType::OpCarry };
("floor") => { crate::preprocessor::token::TokenType::OpFloor };
("local") => { crate::preprocessor::token::TokenType::LocalKey };
("round") => { crate::preprocessor::token::TokenType::OpRound };
("unimpl") => { crate::preprocessor::token::TokenType::Unimpl };
("unique") => { crate::preprocessor::token::TokenType::SpaceUnique };
("return") => { crate::preprocessor::token::TokenType::ReturnKey };
("scarry") => { crate::preprocessor::token::TokenType::OpScarry };
("borrow") => { crate::preprocessor::token::TokenType::OpBorrow };
("sborrow") => { crate::preprocessor::token::TokenType::OpSborrow };
("epsilon") => { crate::preprocessor::token::TokenType::Epsilon };
("popcount") => { crate::preprocessor::token::TokenType::OpPopcount };
("lzcount") => { crate::preprocessor::token::TokenType::OpLzcount };
("int2float") => { crate::preprocessor::token::TokenType::OpInt2float };
("float2float") => { crate::preprocessor::token::TokenType::OpFloat2float };
}
#[rustfmt::skip]
macro_rules! tag {
($x:tt) => { crate::syntax::parser::tag_token_type(token_type!($x)) };
}
#[allow(clippy::type_complexity)]
pub fn tag_token_type(
value: TokenType,
) -> impl FnMut(&[Token]) -> IResult<&[Token], &Span, Box<SleighError>> {
move |input| {
let (first, rest) = input
.split_first()
.ok_or_else(|| Box::new(SleighError::UnexpectedEof))?;
if first.token_type != value {
return Err(nom::Err::Error(Box::new(SleighError::StatementInvalid(
first.location.clone(),
))));
}
Ok((rest, &first.location))
}
}
#[allow(clippy::type_complexity)]
pub fn option<'a, O, F>(
mut parser: F,
) -> impl FnMut(&'a [Token]) -> IResult<&'a [Token], (Option<O>, &'a Span), Box<SleighError>>
where
F: FnMut(&'a [Token]) -> IResult<&'a [Token], (O, &'a Span), Box<SleighError>>,
{
#[allow(clippy::redundant_closure)]
move |input| {
alt((
map(tag!("_"), |span| (None, span)),
map(|x| parser(x), |(value, span)| (Some(value), span)),
))(input)
}
}
#[allow(clippy::type_complexity)]
pub fn ident_ref(input: &[Token]) -> IResult<&[Token], (&str, &Span), Box<SleighError>> {
let (first, rest) = input
.split_first()
.ok_or_else(|| Box::new(SleighError::UnexpectedEof))?;
let name = first.ident()?;
Ok((rest, (name, &first.location)))
}
#[allow(clippy::type_complexity)]
pub fn ident(input: &[Token]) -> IResult<&[Token], (String, &Span), Box<SleighError>> {
let (rest, (ident, span)) = ident_ref(input)?;
Ok((rest, (ident.to_owned(), span)))
}
#[allow(clippy::type_complexity)]
pub fn string(input: &[Token]) -> IResult<&[Token], (String, &Span), Box<SleighError>> {
let (first, rest) = input
.split_first()
.ok_or_else(|| Box::new(SleighError::UnexpectedEof))?;
let name = first.string()?;
Ok((rest, (name.to_owned(), &first.location)))
}
#[allow(clippy::type_complexity)]
pub fn number(input: &[Token]) -> IResult<&[Token], (NumberUnsigned, &Span), Box<SleighError>> {
let (first, rest) = input
.split_first()
.ok_or_else(|| Box::new(SleighError::UnexpectedEof))?;
let number = first.number()?;
Ok((rest, (number, &first.location)))
}
#[allow(clippy::type_complexity)]
pub fn number_unsigned(input: &[Token]) -> IResult<&[Token], (Number, &Span), Box<SleighError>> {
map(number, |(number, span)| (Number::Positive(number), span))(input)
}
#[allow(clippy::type_complexity)]
pub fn number_signed(input: &[Token]) -> IResult<&[Token], (Number, &Span), Box<SleighError>> {
let (rest, (is_neg, (number, location))) = pair(opt(tag!("-")), number)(input)?;
let number = if is_neg.is_some() {
Number::Negative(number)
} else {
Number::Positive(number)
};
Ok((rest, (number, location)))
}
#[allow(clippy::type_complexity)]
fn string_inline(input: &[Token]) -> IResult<&[Token], (String, &Span), Box<SleighError>> {
alt((string, ident))(input)
}
#[allow(clippy::type_complexity)]
pub fn fieldlist(input: &[Token]) -> IResult<&[Token], Vec<(String, Span)>, Box<SleighError>> {
alt((
map(ident, |(x, span)| vec![(x, span.clone())]),
delimited(
tag!("["),
many1(map(ident, |(x, span)| (x, span.clone()))),
tag!("]"),
),
))(input)
}
#[allow(clippy::type_complexity)]
pub fn registerlist(
input: &[Token],
) -> IResult<&[Token], Vec<(Option<String>, Span)>, Box<SleighError>> {
alt((
map(ident, |(x, span)| vec![(Some(x), span.clone())]),
delimited(
tag!("["),
many1(map(option(ident), |(x, span)| (x, span.clone()))),
tag!("]"),
),
))(input)
}
#[allow(clippy::type_complexity)]
pub fn numberlist(
input: &[Token],
) -> IResult<&[Token], Vec<(Option<Number>, Span)>, Box<SleighError>> {
alt((
map(number_signed, |(x, span)| vec![(Some(x), span.clone())]),
delimited(
tag!("["),
many1(map(option(number_signed), |(x, span)| (x, span.clone()))),
tag!("]"),
),
))(input)
}
#[allow(clippy::type_complexity)]
pub fn stringlist(
input: &[Token],
) -> IResult<&[Token], Vec<(Option<String>, Span)>, Box<SleighError>> {
alt((
map(string_inline, |(x, span)| vec![(Some(x), span.clone())]),
delimited(
tag!("["),
many1(map(option(string_inline), |(x, span)| (x, span.clone()))),
tag!("]"),
),
))(input)
}