mirror of
https://github.com/openharmony/third_party_rust_rust-cexpr.git
synced 2026-07-01 21:14:01 -04:00
e6cec0912e
Issue:https://gitee.com/openharmony/build/issues/I6UFTP Signed-off-by: lubinglun <lubinglun@huawei.com>
611 lines
20 KiB
Rust
611 lines
20 KiB
Rust
// (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.
|
|
//! Evaluating C expressions from tokens.
|
|
//!
|
|
//! Numerical operators are supported. All numerical values are treated as
|
|
//! `i64` or `f64`. Type casting is not supported. `i64` are converted to
|
|
//! `f64` when used in conjunction with a `f64`. Right shifts are always
|
|
//! arithmetic shifts.
|
|
//!
|
|
//! The `sizeof` operator is not supported.
|
|
//!
|
|
//! String concatenation is supported, but width prefixes are ignored; all
|
|
//! strings are treated as narrow strings.
|
|
//!
|
|
//! Use the `IdentifierParser` to substitute identifiers found in expressions.
|
|
|
|
use std::collections::HashMap;
|
|
use std::num::Wrapping;
|
|
use std::ops::{
|
|
AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, DivAssign, MulAssign, RemAssign, ShlAssign,
|
|
ShrAssign, SubAssign,
|
|
};
|
|
|
|
use crate::literal::{self, CChar};
|
|
use crate::token::{Kind as TokenKind, Token};
|
|
use crate::ToCexprResult;
|
|
use nom::branch::alt;
|
|
use nom::combinator::{complete, map, map_opt};
|
|
use nom::multi::{fold_many0, many0, separated_list0};
|
|
use nom::sequence::{delimited, pair, preceded};
|
|
use nom::*;
|
|
|
|
/// Expression parser/evaluator that supports identifiers.
|
|
#[derive(Debug)]
|
|
pub struct IdentifierParser<'ident> {
|
|
identifiers: &'ident HashMap<Vec<u8>, EvalResult>,
|
|
}
|
|
#[derive(Copy, Clone)]
|
|
struct PRef<'a>(&'a IdentifierParser<'a>);
|
|
|
|
/// A shorthand for the type of cexpr expression evaluation results.
|
|
pub type CResult<'a, R> = IResult<&'a [Token], R, crate::Error<&'a [Token]>>;
|
|
|
|
/// The result of parsing a literal or evaluating an expression.
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
#[allow(missing_docs)]
|
|
pub enum EvalResult {
|
|
Int(Wrapping<i64>),
|
|
Float(f64),
|
|
Char(CChar),
|
|
Str(Vec<u8>),
|
|
Invalid,
|
|
}
|
|
|
|
macro_rules! result_opt (
|
|
(fn $n:ident: $e:ident -> $t:ty) => (
|
|
#[allow(dead_code)]
|
|
#[allow(clippy::wrong_self_convention)]
|
|
fn $n(self) -> Option<$t> {
|
|
if let EvalResult::$e(v) = self {
|
|
Some(v)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
);
|
|
);
|
|
|
|
impl EvalResult {
|
|
result_opt!(fn as_int: Int -> Wrapping<i64>);
|
|
result_opt!(fn as_float: Float -> f64);
|
|
result_opt!(fn as_char: Char -> CChar);
|
|
result_opt!(fn as_str: Str -> Vec<u8>);
|
|
|
|
#[allow(clippy::wrong_self_convention)]
|
|
fn as_numeric(self) -> Option<EvalResult> {
|
|
match self {
|
|
EvalResult::Int(_) | EvalResult::Float(_) => Some(self),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Vec<u8>> for EvalResult {
|
|
fn from(s: Vec<u8>) -> EvalResult {
|
|
EvalResult::Str(s)
|
|
}
|
|
}
|
|
|
|
// ===========================================
|
|
// ============= Clang tokens ================
|
|
// ===========================================
|
|
|
|
macro_rules! exact_token (
|
|
($k:ident, $c:expr) => ({
|
|
move |input: &[Token]| {
|
|
if input.is_empty() {
|
|
let res: CResult<'_, &[u8]> = Err(crate::nom::Err::Incomplete(Needed::new($c.len())));
|
|
res
|
|
} else {
|
|
if input[0].kind==TokenKind::$k && &input[0].raw[..]==$c {
|
|
Ok((&input[1..], &input[0].raw[..]))
|
|
} else {
|
|
Err(crate::nom::Err::Error((input, crate::ErrorKind::ExactToken(TokenKind::$k,$c)).into()))
|
|
}
|
|
}
|
|
}
|
|
});
|
|
);
|
|
|
|
fn identifier_token(input: &[Token]) -> CResult<'_, &[u8]> {
|
|
if input.is_empty() {
|
|
let res: CResult<'_, &[u8]> = Err(nom::Err::Incomplete(Needed::new(1)));
|
|
res
|
|
} else {
|
|
if input[0].kind == TokenKind::Identifier {
|
|
Ok((&input[1..], &input[0].raw[..]))
|
|
} else {
|
|
Err(crate::nom::Err::Error((input, crate::ErrorKind::TypedToken(TokenKind::Identifier)).into()))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn p(c: &'static str) -> impl Fn(&[Token]) -> CResult<'_, &[u8]> {
|
|
exact_token!(Punctuation, c.as_bytes())
|
|
}
|
|
|
|
fn one_of_punctuation(c: &'static [&'static str]) -> impl Fn(&[Token]) -> CResult<'_, &[u8]> {
|
|
move |input| {
|
|
if input.is_empty() {
|
|
let min = c
|
|
.iter()
|
|
.map(|opt| opt.len())
|
|
.min()
|
|
.expect("at least one option");
|
|
Err(crate::nom::Err::Incomplete(Needed::new(min)))
|
|
} else if input[0].kind == TokenKind::Punctuation
|
|
&& c.iter().any(|opt| opt.as_bytes() == &input[0].raw[..])
|
|
{
|
|
Ok((&input[1..], &input[0].raw[..]))
|
|
} else {
|
|
Err(crate::nom::Err::Error(
|
|
(
|
|
input,
|
|
crate::ErrorKind::ExactTokens(TokenKind::Punctuation, c),
|
|
)
|
|
.into(),
|
|
))
|
|
}
|
|
}
|
|
}
|
|
|
|
// ==================================================
|
|
// ============= Numeric expressions ================
|
|
// ==================================================
|
|
|
|
impl<'a> AddAssign<&'a EvalResult> for EvalResult {
|
|
fn add_assign(&mut self, rhs: &'a EvalResult) {
|
|
use self::EvalResult::*;
|
|
*self = match (&*self, rhs) {
|
|
(&Int(a), &Int(b)) => Int(a + b),
|
|
(&Float(a), &Int(b)) => Float(a + (b.0 as f64)),
|
|
(&Int(a), &Float(b)) => Float(a.0 as f64 + b),
|
|
(&Float(a), &Float(b)) => Float(a + b),
|
|
_ => Invalid,
|
|
};
|
|
}
|
|
}
|
|
impl<'a> BitAndAssign<&'a EvalResult> for EvalResult {
|
|
fn bitand_assign(&mut self, rhs: &'a EvalResult) {
|
|
use self::EvalResult::*;
|
|
*self = match (&*self, rhs) {
|
|
(&Int(a), &Int(b)) => Int(a & b),
|
|
_ => Invalid,
|
|
};
|
|
}
|
|
}
|
|
impl<'a> BitOrAssign<&'a EvalResult> for EvalResult {
|
|
fn bitor_assign(&mut self, rhs: &'a EvalResult) {
|
|
use self::EvalResult::*;
|
|
*self = match (&*self, rhs) {
|
|
(&Int(a), &Int(b)) => Int(a | b),
|
|
_ => Invalid,
|
|
};
|
|
}
|
|
}
|
|
impl<'a> BitXorAssign<&'a EvalResult> for EvalResult {
|
|
fn bitxor_assign(&mut self, rhs: &'a EvalResult) {
|
|
use self::EvalResult::*;
|
|
*self = match (&*self, rhs) {
|
|
(&Int(a), &Int(b)) => Int(a ^ b),
|
|
_ => Invalid,
|
|
};
|
|
}
|
|
}
|
|
impl<'a> DivAssign<&'a EvalResult> for EvalResult {
|
|
fn div_assign(&mut self, rhs: &'a EvalResult) {
|
|
use self::EvalResult::*;
|
|
*self = match (&*self, rhs) {
|
|
(&Int(a), &Int(b)) => Int(a / b),
|
|
(&Float(a), &Int(b)) => Float(a / (b.0 as f64)),
|
|
(&Int(a), &Float(b)) => Float(a.0 as f64 / b),
|
|
(&Float(a), &Float(b)) => Float(a / b),
|
|
_ => Invalid,
|
|
};
|
|
}
|
|
}
|
|
impl<'a> MulAssign<&'a EvalResult> for EvalResult {
|
|
fn mul_assign(&mut self, rhs: &'a EvalResult) {
|
|
use self::EvalResult::*;
|
|
*self = match (&*self, rhs) {
|
|
(&Int(a), &Int(b)) => Int(a * b),
|
|
(&Float(a), &Int(b)) => Float(a * (b.0 as f64)),
|
|
(&Int(a), &Float(b)) => Float(a.0 as f64 * b),
|
|
(&Float(a), &Float(b)) => Float(a * b),
|
|
_ => Invalid,
|
|
};
|
|
}
|
|
}
|
|
impl<'a> RemAssign<&'a EvalResult> for EvalResult {
|
|
fn rem_assign(&mut self, rhs: &'a EvalResult) {
|
|
use self::EvalResult::*;
|
|
*self = match (&*self, rhs) {
|
|
(&Int(a), &Int(b)) => Int(a % b),
|
|
(&Float(a), &Int(b)) => Float(a % (b.0 as f64)),
|
|
(&Int(a), &Float(b)) => Float(a.0 as f64 % b),
|
|
(&Float(a), &Float(b)) => Float(a % b),
|
|
_ => Invalid,
|
|
};
|
|
}
|
|
}
|
|
impl<'a> ShlAssign<&'a EvalResult> for EvalResult {
|
|
fn shl_assign(&mut self, rhs: &'a EvalResult) {
|
|
use self::EvalResult::*;
|
|
*self = match (&*self, rhs) {
|
|
(&Int(a), &Int(b)) => Int(a << (b.0 as usize)),
|
|
_ => Invalid,
|
|
};
|
|
}
|
|
}
|
|
impl<'a> ShrAssign<&'a EvalResult> for EvalResult {
|
|
fn shr_assign(&mut self, rhs: &'a EvalResult) {
|
|
use self::EvalResult::*;
|
|
*self = match (&*self, rhs) {
|
|
(&Int(a), &Int(b)) => Int(a >> (b.0 as usize)),
|
|
_ => Invalid,
|
|
};
|
|
}
|
|
}
|
|
impl<'a> SubAssign<&'a EvalResult> for EvalResult {
|
|
fn sub_assign(&mut self, rhs: &'a EvalResult) {
|
|
use self::EvalResult::*;
|
|
*self = match (&*self, rhs) {
|
|
(&Int(a), &Int(b)) => Int(a - b),
|
|
(&Float(a), &Int(b)) => Float(a - (b.0 as f64)),
|
|
(&Int(a), &Float(b)) => Float(a.0 as f64 - b),
|
|
(&Float(a), &Float(b)) => Float(a - b),
|
|
_ => Invalid,
|
|
};
|
|
}
|
|
}
|
|
|
|
fn unary_op(input: (&[u8], EvalResult)) -> Option<EvalResult> {
|
|
use self::EvalResult::*;
|
|
assert_eq!(input.0.len(), 1);
|
|
match (input.0[0], input.1) {
|
|
(b'+', i) => Some(i),
|
|
(b'-', Int(i)) => Some(Int(Wrapping(i.0.wrapping_neg()))), // impl Neg for Wrapping not until rust 1.10...
|
|
(b'-', Float(i)) => Some(Float(-i)),
|
|
(b'-', _) => unreachable!("non-numeric unary op"),
|
|
(b'~', Int(i)) => Some(Int(!i)),
|
|
(b'~', Float(_)) => None,
|
|
(b'~', _) => unreachable!("non-numeric unary op"),
|
|
_ => unreachable!("invalid unary op"),
|
|
}
|
|
}
|
|
|
|
fn numeric<I: Clone, E: nom::error::ParseError<I>, F>(
|
|
f: F,
|
|
) -> impl FnMut(I) -> nom::IResult<I, EvalResult, E>
|
|
where
|
|
F: FnMut(I) -> nom::IResult<I, EvalResult, E>,
|
|
{
|
|
nom::combinator::map_opt(f, EvalResult::as_numeric)
|
|
}
|
|
|
|
impl<'a> PRef<'a> {
|
|
fn unary(self, input: &'_ [Token]) -> CResult<'_, EvalResult> {
|
|
alt((
|
|
delimited(p("("), |i| self.numeric_expr(i), p(")")),
|
|
numeric(|i| self.literal(i)),
|
|
numeric(|i| self.identifier(i)),
|
|
map_opt(
|
|
pair(one_of_punctuation(&["+", "-", "~"][..]), |i| self.unary(i)),
|
|
unary_op,
|
|
),
|
|
))(input)
|
|
}
|
|
|
|
fn mul_div_rem(self, input: &'_ [Token]) -> CResult<'_, EvalResult> {
|
|
let (input, acc) = self.unary(input)?;
|
|
fold_many0(
|
|
pair(complete(one_of_punctuation(&["*", "/", "%"][..])), |i| {
|
|
self.unary(i)
|
|
}),
|
|
move || acc.clone(),
|
|
|mut acc, (op, val): (&[u8], EvalResult)| {
|
|
match op[0] as char {
|
|
'*' => acc *= &val,
|
|
'/' => acc /= &val,
|
|
'%' => acc %= &val,
|
|
_ => unreachable!(),
|
|
};
|
|
acc
|
|
},
|
|
)(input)
|
|
}
|
|
|
|
fn add_sub(self, input: &'_ [Token]) -> CResult<'_, EvalResult> {
|
|
let (input, acc) = self.mul_div_rem(input)?;
|
|
fold_many0(
|
|
pair(complete(one_of_punctuation(&["+", "-"][..])), |i| {
|
|
self.mul_div_rem(i)
|
|
}),
|
|
move || acc.clone(),
|
|
|mut acc, (op, val): (&[u8], EvalResult)| {
|
|
match op[0] as char {
|
|
'+' => acc += &val,
|
|
'-' => acc -= &val,
|
|
_ => unreachable!(),
|
|
};
|
|
acc
|
|
},
|
|
)(input)
|
|
}
|
|
|
|
fn shl_shr(self, input: &'_ [Token]) -> CResult<'_, EvalResult> {
|
|
let (input, acc) = self.add_sub(input)?;
|
|
numeric(fold_many0(
|
|
pair(complete(one_of_punctuation(&["<<", ">>"][..])), |i| {
|
|
self.add_sub(i)
|
|
}),
|
|
move || acc.clone(),
|
|
|mut acc, (op, val): (&[u8], EvalResult)| {
|
|
match op {
|
|
b"<<" => acc <<= &val,
|
|
b">>" => acc >>= &val,
|
|
_ => unreachable!(),
|
|
};
|
|
acc
|
|
},
|
|
))(input)
|
|
}
|
|
|
|
fn and(self, input: &'_ [Token]) -> CResult<'_, EvalResult> {
|
|
let (input, acc) = self.shl_shr(input)?;
|
|
numeric(fold_many0(
|
|
preceded(complete(p("&")), |i| self.shl_shr(i)),
|
|
move || acc.clone(),
|
|
|mut acc, val: EvalResult| {
|
|
acc &= &val;
|
|
acc
|
|
},
|
|
))(input)
|
|
}
|
|
|
|
fn xor(self, input: &'_ [Token]) -> CResult<'_, EvalResult> {
|
|
let (input, acc) = self.and(input)?;
|
|
numeric(fold_many0(
|
|
preceded(complete(p("^")), |i| self.and(i)),
|
|
move || acc.clone(),
|
|
|mut acc, val: EvalResult| {
|
|
acc ^= &val;
|
|
acc
|
|
},
|
|
))(input)
|
|
}
|
|
|
|
fn or(self, input: &'_ [Token]) -> CResult<'_, EvalResult> {
|
|
let (input, acc) = self.xor(input)?;
|
|
numeric(fold_many0(
|
|
preceded(complete(p("|")), |i| self.xor(i)),
|
|
move || acc.clone(),
|
|
|mut acc, val: EvalResult| {
|
|
acc |= &val;
|
|
acc
|
|
},
|
|
))(input)
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn numeric_expr(self, input: &'_ [Token]) -> CResult<'_, EvalResult> {
|
|
self.or(input)
|
|
}
|
|
}
|
|
|
|
// =======================================================
|
|
// ============= Literals and identifiers ================
|
|
// =======================================================
|
|
|
|
impl<'a> PRef<'a> {
|
|
fn identifier(self, input: &'_ [Token]) -> CResult<'_, EvalResult> {
|
|
match input.split_first() {
|
|
None => Err(Err::Incomplete(Needed::new(1))),
|
|
Some((
|
|
&Token {
|
|
kind: TokenKind::Identifier,
|
|
ref raw,
|
|
},
|
|
rest,
|
|
)) => {
|
|
if let Some(r) = self.identifiers.get(&raw[..]) {
|
|
Ok((rest, r.clone()))
|
|
} else {
|
|
Err(Err::Error(
|
|
(input, crate::ErrorKind::UnknownIdentifier).into(),
|
|
))
|
|
}
|
|
}
|
|
Some(_) => Err(Err::Error(
|
|
(input, crate::ErrorKind::TypedToken(TokenKind::Identifier)).into(),
|
|
)),
|
|
}
|
|
}
|
|
|
|
fn literal(self, input: &'_ [Token]) -> CResult<'_, EvalResult> {
|
|
match input.split_first() {
|
|
None => Err(Err::Incomplete(Needed::new(1))),
|
|
Some((
|
|
&Token {
|
|
kind: TokenKind::Literal,
|
|
ref raw,
|
|
},
|
|
rest,
|
|
)) => match literal::parse(raw) {
|
|
Ok((_, result)) => Ok((rest, result)),
|
|
_ => Err(Err::Error((input, crate::ErrorKind::InvalidLiteral).into())),
|
|
},
|
|
Some(_) => Err(Err::Error(
|
|
(input, crate::ErrorKind::TypedToken(TokenKind::Literal)).into(),
|
|
)),
|
|
}
|
|
}
|
|
|
|
fn string(self, input: &'_ [Token]) -> CResult<'_, Vec<u8>> {
|
|
alt((
|
|
map_opt(|i| self.literal(i), EvalResult::as_str),
|
|
map_opt(|i| self.identifier(i), EvalResult::as_str),
|
|
))(input)
|
|
.to_cexpr_result()
|
|
}
|
|
|
|
// "string1" "string2" etc...
|
|
fn concat_str(self, input: &'_ [Token]) -> CResult<'_, EvalResult> {
|
|
map(
|
|
pair(|i| self.string(i), many0(complete(|i| self.string(i)))),
|
|
|(first, v)| {
|
|
Vec::into_iter(v)
|
|
.fold(first, |mut s, elem| {
|
|
Vec::extend_from_slice(&mut s, Vec::<u8>::as_slice(&elem));
|
|
s
|
|
})
|
|
.into()
|
|
},
|
|
)(input)
|
|
.to_cexpr_result()
|
|
}
|
|
|
|
fn expr(self, input: &'_ [Token]) -> CResult<'_, EvalResult> {
|
|
alt((
|
|
|i| self.numeric_expr(i),
|
|
delimited(p("("), |i| self.expr(i), p(")")),
|
|
|i| self.concat_str(i),
|
|
|i| self.literal(i),
|
|
|i| self.identifier(i),
|
|
))(input)
|
|
.to_cexpr_result()
|
|
}
|
|
|
|
fn macro_definition(self, input: &'_ [Token]) -> CResult<'_, (&'_ [u8], EvalResult)> {
|
|
pair(identifier_token, |i| self.expr(i))(input)
|
|
}
|
|
}
|
|
|
|
impl<'a> ::std::ops::Deref for PRef<'a> {
|
|
type Target = IdentifierParser<'a>;
|
|
fn deref(&self) -> &IdentifierParser<'a> {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
impl<'ident> IdentifierParser<'ident> {
|
|
fn as_ref(&self) -> PRef<'_> {
|
|
PRef(self)
|
|
}
|
|
|
|
/// Create a new `IdentifierParser` with a set of known identifiers. When
|
|
/// a known identifier is encountered during parsing, it is substituted
|
|
/// for the value specified.
|
|
pub fn new(identifiers: &HashMap<Vec<u8>, EvalResult>) -> IdentifierParser<'_> {
|
|
IdentifierParser { identifiers }
|
|
}
|
|
|
|
/// Parse and evaluate an expression of a list of tokens.
|
|
///
|
|
/// Returns an error if the input is not a valid expression or if the token
|
|
/// stream contains comments, keywords or unknown identifiers.
|
|
pub fn expr<'a>(&self, input: &'a [Token]) -> CResult<'a, EvalResult> {
|
|
self.as_ref().expr(input)
|
|
}
|
|
|
|
/// Parse and evaluate a macro definition from a list of tokens.
|
|
///
|
|
/// Returns the identifier for the macro and its replacement evaluated as an
|
|
/// expression. The input should not include `#define`.
|
|
///
|
|
/// Returns an error if the replacement is not a valid expression, if called
|
|
/// on most function-like macros, or if the token stream contains comments,
|
|
/// keywords or unknown identifiers.
|
|
///
|
|
/// N.B. This is intended to fail on function-like macros, but if it the
|
|
/// macro takes a single argument, the argument name is defined as an
|
|
/// identifier, and the macro otherwise parses as an expression, it will
|
|
/// return a result even on function-like macros.
|
|
///
|
|
/// ```c
|
|
/// // will evaluate into IDENTIFIER
|
|
/// #define DELETE(IDENTIFIER)
|
|
/// // will evaluate into IDENTIFIER-3
|
|
/// #define NEGATIVE_THREE(IDENTIFIER) -3
|
|
/// ```
|
|
pub fn macro_definition<'a>(&self, input: &'a [Token]) -> CResult<'a, (&'a [u8], EvalResult)> {
|
|
crate::assert_full_parse(self.as_ref().macro_definition(input))
|
|
}
|
|
}
|
|
|
|
/// Parse and evaluate an expression of a list of tokens.
|
|
///
|
|
/// Returns an error if the input is not a valid expression or if the token
|
|
/// stream contains comments, keywords or identifiers.
|
|
pub fn expr(input: &[Token]) -> CResult<'_, EvalResult> {
|
|
IdentifierParser::new(&HashMap::new()).expr(input)
|
|
}
|
|
|
|
/// Parse and evaluate a macro definition from a list of tokens.
|
|
///
|
|
/// Returns the identifier for the macro and its replacement evaluated as an
|
|
/// expression. The input should not include `#define`.
|
|
///
|
|
/// Returns an error if the replacement is not a valid expression, if called
|
|
/// on a function-like macro, or if the token stream contains comments,
|
|
/// keywords or identifiers.
|
|
pub fn macro_definition(input: &[Token]) -> CResult<'_, (&'_ [u8], EvalResult)> {
|
|
IdentifierParser::new(&HashMap::new()).macro_definition(input)
|
|
}
|
|
|
|
/// Parse a functional macro declaration from a list of tokens.
|
|
///
|
|
/// Returns the identifier for the macro and the argument list (in order). The
|
|
/// input should not include `#define`. The actual definition is not parsed and
|
|
/// may be obtained from the unparsed data returned.
|
|
///
|
|
/// Returns an error if the input is not a functional macro or if the token
|
|
/// stream contains comments.
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// use cexpr::expr::{IdentifierParser, EvalResult, fn_macro_declaration};
|
|
/// use cexpr::assert_full_parse;
|
|
/// use cexpr::token::Kind::*;
|
|
/// use cexpr::token::Token;
|
|
///
|
|
/// // #define SUFFIX(arg) arg "suffix"
|
|
/// let tokens = vec![
|
|
/// (Identifier, &b"SUFFIX"[..]).into(),
|
|
/// (Punctuation, &b"("[..]).into(),
|
|
/// (Identifier, &b"arg"[..]).into(),
|
|
/// (Punctuation, &b")"[..]).into(),
|
|
/// (Identifier, &b"arg"[..]).into(),
|
|
/// (Literal, &br#""suffix""#[..]).into(),
|
|
/// ];
|
|
///
|
|
/// // Try to parse the functional part
|
|
/// let (expr, (ident, args)) = fn_macro_declaration(&tokens).unwrap();
|
|
/// assert_eq!(ident, b"SUFFIX");
|
|
///
|
|
/// // Create dummy arguments
|
|
/// let idents = args.into_iter().map(|arg|
|
|
/// (arg.to_owned(), EvalResult::Str(b"test".to_vec()))
|
|
/// ).collect();
|
|
///
|
|
/// // Evaluate the macro
|
|
/// let (_, evaluated) = assert_full_parse(IdentifierParser::new(&idents).expr(expr)).unwrap();
|
|
/// assert_eq!(evaluated, EvalResult::Str(b"testsuffix".to_vec()));
|
|
/// ```
|
|
pub fn fn_macro_declaration(input: &[Token]) -> CResult<'_, (&[u8], Vec<&[u8]>)> {
|
|
pair(
|
|
identifier_token,
|
|
delimited(
|
|
p("("),
|
|
separated_list0(p(","), identifier_token),
|
|
p(")"),
|
|
),
|
|
)(input)
|
|
}
|