diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..5170f97 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,149 @@ +// (C) Copyright 2016 Jethro G. Beekman +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +//! A C expression parser and evaluator. +//! +//! This crate provides methods for parsing and evaluating simple C expressions. In general, the +//! crate can handle most arithmetic expressions that would appear in macros or the definition of +//! constants, as well as string and character constants. +//! +//! The main entry point for is [`token::parse`], which parses a byte string and returns its +//! evaluated value. +#![warn(rust_2018_idioms)] +#![warn(missing_docs)] +#![allow(deprecated)] + +pub mod nom { + //! nom's result types, re-exported. + pub use nom::{error::ErrorKind, error::Error, Err, IResult, Needed}; +} +pub mod expr; +pub mod literal; +pub mod token; + +/// Parsing errors specific to C parsing +#[derive(Debug)] +pub enum ErrorKind { + /// Expected the specified token + ExactToken(token::Kind, &'static [u8]), + /// Expected one of the specified tokens + ExactTokens(token::Kind, &'static [&'static str]), + /// Expected a token of the specified kind + TypedToken(token::Kind), + /// An unknown identifier was encountered + UnknownIdentifier, + /// An invalid literal was encountered. + /// + /// When encountered, this generally means a bug exists in the data that + /// was passed in or the parsing logic. + InvalidLiteral, + /// A full parse was requested, but data was left over after parsing finished. + Partial, + /// An error occurred in an underlying nom parser. + Parser(nom::ErrorKind), +} + +impl From<nom::ErrorKind> for ErrorKind { + fn from(k: nom::ErrorKind) -> Self { + ErrorKind::Parser(k) + } +} + +impl From<u32> for ErrorKind { + fn from(_: u32) -> Self { + ErrorKind::InvalidLiteral + } +} + +/// Parsing errors specific to C parsing. +/// +/// This is a superset of `(I, nom::ErrorKind)` that includes the additional errors specified by +/// [`ErrorKind`]. +#[derive(Debug)] +pub struct Error<I> { + /// The remainder of the input stream at the time of the error. + pub input: I, + /// The error that occurred. + pub error: ErrorKind, +} + +impl<I> From<(I, nom::ErrorKind)> for Error<I> { + fn from(e: (I, nom::ErrorKind)) -> Self { + Self::from((e.0, ErrorKind::from(e.1))) + } +} + +impl<I> From<(I, ErrorKind)> for Error<I> { + fn from(e: (I, ErrorKind)) -> Self { + Self { + input: e.0, + error: e.1, + } + } +} + +impl<I> From<::nom::error::Error<I>> for Error<I> { + fn from(e: ::nom::error::Error<I>) -> Self { + Self { + input: e.input, + error: e.code.into(), + } + } +} + +impl<I> ::nom::error::ParseError<I> for Error<I> { + fn from_error_kind(input: I, kind: nom::ErrorKind) -> Self { + Self { + input, + error: kind.into(), + } + } + + fn append(_: I, _: nom::ErrorKind, other: Self) -> Self { + other + } +} + +// in lieu of https://github.com/Geal/nom/issues/1010 +trait ToCexprResult<I, O> { + fn to_cexpr_result(self) -> nom::IResult<I, O, Error<I>>; +} +impl<I, O, E> ToCexprResult<I, O> for nom::IResult<I, O, E> +where + Error<I>: From<E>, +{ + fn to_cexpr_result(self) -> nom::IResult<I, O, Error<I>> { + match self { + Ok(v) => Ok(v), + Err(nom::Err::Incomplete(n)) => Err(nom::Err::Incomplete(n)), + Err(nom::Err::Error(e)) => Err(nom::Err::Error(e.into())), + Err(nom::Err::Failure(e)) => Err(nom::Err::Failure(e.into())), + } + } +} + +/// If the input result indicates a succesful parse, but there is data left, +/// return an `Error::Partial` instead. +pub fn assert_full_parse<'i, I: 'i, O, E>( + result: nom::IResult<&'i [I], O, E>, +) -> nom::IResult<&'i [I], O, Error<&'i [I]>> +where + Error<&'i [I]>: From<E>, +{ + match result.to_cexpr_result() { + Ok((rem, output)) => { + if rem.is_empty() { + Ok((rem, output)) + } else { + Err(nom::Err::Error((rem, ErrorKind::Partial).into())) + } + } + Err(nom::Err::Incomplete(n)) => Err(nom::Err::Incomplete(n)), + Err(nom::Err::Failure(e)) => Err(nom::Err::Failure(e)), + Err(nom::Err::Error(e)) => Err(nom::Err::Error(e)), + } +} |