cexpr/
lib.rs

1// (C) Copyright 2016 Jethro G. Beekman
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8//! A C expression parser and evaluator.
9//!
10//! This crate provides methods for parsing and evaluating simple C expressions. In general, the
11//! crate can handle most arithmetic expressions that would appear in macros or the definition of
12//! constants, as well as string and character constants.
13//!
14//! The main entry point for is [`token::parse`], which parses a byte string and returns its
15//! evaluated value.
16#![warn(rust_2018_idioms)]
17#![warn(missing_docs)]
18#![allow(deprecated)]
19
20pub mod nom {
21    //! nom's result types, re-exported.
22    pub use nom::{error::ErrorKind, error::Error, Err, IResult, Needed};
23}
24pub mod expr;
25pub mod literal;
26pub mod token;
27
28/// Parsing errors specific to C parsing
29#[derive(Debug)]
30pub enum ErrorKind {
31    /// Expected the specified token
32    ExactToken(token::Kind, &'static [u8]),
33    /// Expected one of the specified tokens
34    ExactTokens(token::Kind, &'static [&'static str]),
35    /// Expected a token of the specified kind
36    TypedToken(token::Kind),
37    /// An unknown identifier was encountered
38    UnknownIdentifier,
39    /// An invalid literal was encountered.
40    ///
41    /// When encountered, this generally means a bug exists in the data that
42    /// was passed in or the parsing logic.
43    InvalidLiteral,
44    /// A full parse was requested, but data was left over after parsing finished.
45    Partial,
46    /// An error occurred in an underlying nom parser.
47    Parser(nom::ErrorKind),
48}
49
50impl From<nom::ErrorKind> for ErrorKind {
51    fn from(k: nom::ErrorKind) -> Self {
52        ErrorKind::Parser(k)
53    }
54}
55
56impl From<u32> for ErrorKind {
57    fn from(_: u32) -> Self {
58        ErrorKind::InvalidLiteral
59    }
60}
61
62/// Parsing errors specific to C parsing.
63///
64/// This is a superset of `(I, nom::ErrorKind)` that includes the additional errors specified by
65/// [`ErrorKind`].
66#[derive(Debug)]
67pub struct Error<I> {
68    /// The remainder of the input stream at the time of the error.
69    pub input: I,
70    /// The error that occurred.
71    pub error: ErrorKind,
72}
73
74impl<I> From<(I, nom::ErrorKind)> for Error<I> {
75    fn from(e: (I, nom::ErrorKind)) -> Self {
76        Self::from((e.0, ErrorKind::from(e.1)))
77    }
78}
79
80impl<I> From<(I, ErrorKind)> for Error<I> {
81    fn from(e: (I, ErrorKind)) -> Self {
82        Self {
83            input: e.0,
84            error: e.1,
85        }
86    }
87}
88
89impl<I> From<::nom::error::Error<I>> for Error<I> {
90    fn from(e: ::nom::error::Error<I>) -> Self {
91        Self {
92            input: e.input,
93            error: e.code.into(),
94        }
95    }
96}
97
98impl<I> ::nom::error::ParseError<I> for Error<I> {
99    fn from_error_kind(input: I, kind: nom::ErrorKind) -> Self {
100        Self {
101            input,
102            error: kind.into(),
103        }
104    }
105
106    fn append(_: I, _: nom::ErrorKind, other: Self) -> Self {
107        other
108    }
109}
110
111// in lieu of https://github.com/Geal/nom/issues/1010
112trait ToCexprResult<I, O> {
113    fn to_cexpr_result(self) -> nom::IResult<I, O, Error<I>>;
114}
115impl<I, O, E> ToCexprResult<I, O> for nom::IResult<I, O, E>
116where
117    Error<I>: From<E>,
118{
119    fn to_cexpr_result(self) -> nom::IResult<I, O, Error<I>> {
120        match self {
121            Ok(v) => Ok(v),
122            Err(nom::Err::Incomplete(n)) => Err(nom::Err::Incomplete(n)),
123            Err(nom::Err::Error(e)) => Err(nom::Err::Error(e.into())),
124            Err(nom::Err::Failure(e)) => Err(nom::Err::Failure(e.into())),
125        }
126    }
127}
128
129/// If the input result indicates a succesful parse, but there is data left,
130/// return an `Error::Partial` instead.
131pub fn assert_full_parse<'i, I: 'i, O, E>(
132    result: nom::IResult<&'i [I], O, E>,
133) -> nom::IResult<&'i [I], O, Error<&'i [I]>>
134where
135    Error<&'i [I]>: From<E>,
136{
137    match result.to_cexpr_result() {
138        Ok((rem, output)) => {
139            if rem.is_empty() {
140                Ok((rem, output))
141            } else {
142                Err(nom::Err::Error((rem, ErrorKind::Partial).into()))
143            }
144        }
145        Err(nom::Err::Incomplete(n)) => Err(nom::Err::Incomplete(n)),
146        Err(nom::Err::Failure(e)) => Err(nom::Err::Failure(e)),
147        Err(nom::Err::Error(e)) => Err(nom::Err::Error(e)),
148    }
149}