diff --git a/src/expr.rs b/src/expr.rs index 7f7e458..57305ea 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -29,8 +29,8 @@ use std::ops::{ 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::branch::{alt, permutation}; +use nom::combinator::{complete, map, map_opt, opt}; use nom::multi::{fold_many0, many0, separated_list0}; use nom::sequence::{delimited, pair, preceded}; use nom::*; @@ -54,6 +54,7 @@ pub enum EvalResult { Float(f64), Char(CChar), Str(Vec), + Cast(Vec>, Box), Invalid, } @@ -126,6 +127,10 @@ fn identifier_token(input: &[Token]) -> CResult<'_, &[u8]> { } } +fn keyword(c: &'static str) -> impl Fn(&[Token]) -> CResult<'_, &[u8]> { + exact_token!(Keyword, c.as_bytes()) +} + fn p(c: &'static str) -> impl Fn(&[Token]) -> CResult<'_, &[u8]> { exact_token!(Punctuation, c.as_bytes()) } @@ -289,6 +294,10 @@ where nom::combinator::map_opt(f, EvalResult::as_numeric) } +fn expr_cast(input: (Vec>, EvalResult)) -> EvalResult { + EvalResult::Cast(input.0, Box::new(input.1)) +} + impl<'a> PRef<'a> { fn unary(self, input: &'_ [Token]) -> CResult<'_, EvalResult> { alt(( @@ -473,6 +482,10 @@ impl<'a> PRef<'a> { fn expr(self, input: &'_ [Token]) -> CResult<'_, EvalResult> { alt(( + map( + pair(|i| self.cast(i), |i| self.expr(i)), + expr_cast + ), |i| self.numeric_expr(i), delimited(p("("), |i| self.expr(i), p(")")), |i| self.concat_str(i), @@ -482,6 +495,88 @@ impl<'a> PRef<'a> { .to_cexpr_result() } + fn cast(self, input: &'_ [Token]) -> CResult<'_, Vec>> { + delimited(p("("), |i| self.ty(i), p(")"))(input) + } + + fn int_ty(input: &'_ [Token]) -> CResult<'_, Vec<&[u8]>> { + fn int_signedness(input: &'_ [Token]) -> CResult<'_, &[u8]> { + alt(( + keyword("unsigned"), + keyword("signed"), + ))(input) + } + + fn int_longness(input: &'_ [Token]) -> CResult<'_, &[u8]> { + alt(( + keyword("short"), + keyword("long"), + ))(input) + } + + alt(( + // [const] [(un)signed] long long [int] + map( + permutation((opt(keyword("const")), opt(int_signedness), keyword("long"), keyword("long"), opt(keyword("int")))), + |(_, s, i1, i2, _)| if let Some(s) = s { + if s == b"signed" { + vec![i1, i2] + } else { + vec![s, i1, i2] + } + } else { + vec![i1, i2] + }, + ), + // [const] [(un)signed] long/short [int] + map( + permutation((opt(keyword("const")), opt(int_signedness), int_longness, opt(keyword("int")))), + |(_, s, i, _)| if let Some(s) = s { + if s == b"signed" { + vec![i] + } else { + vec![s, i] + } + } else { + vec![i] + }, + ), + // [const] [(un)signed] char/int + map( + permutation((opt(keyword("const")), opt(int_signedness), alt((keyword("char"), keyword("int"))))), + |(_, s, i)| if let Some(s) = s { + if s == b"signed" && i == b"int" { + vec![i] + } else { + vec![s, i] + } + } else { + vec![i] + }, + ), + ))(input) + } + + fn ty(self, input: &'_ [Token]) -> CResult<'_, Vec>> { + map( + alt(( + // [const] + map( + permutation((opt(keyword("const")), identifier_token)), + |(_, id)| vec![id], + ), + // [const] bool + map( + permutation((opt(keyword("const")), keyword("bool"))), + |(_, b)| vec![b], + ), + Self::int_ty, + )), + |v| v.into_iter().map(|t| t.to_vec()).collect(), + )(input) + .to_cexpr_result() + } + fn macro_definition(self, input: &'_ [Token]) -> CResult<'_, (&'_ [u8], EvalResult)> { pair(identifier_token, |i| self.expr(i))(input) } diff --git a/tests/clang.rs b/tests/clang.rs index b2484f0..d8ae361 100644 --- a/tests/clang.rs +++ b/tests/clang.rs @@ -12,6 +12,7 @@ use std::collections::HashMap; use std::io::Write; use std::str::{self, FromStr}; use std::{char, ffi, mem, ptr, slice}; +use std::num::Wrapping; use cexpr::assert_full_parse; use cexpr::expr::{fn_macro_declaration, EvalResult, IdentifierParser}; @@ -25,18 +26,17 @@ fn test_definition( tokens: &[Token], idents: &mut HashMap, EvalResult>, ) -> bool { + use cexpr::expr::EvalResult::*; + fn bytes_to_int(value: &[u8]) -> Option { - str::from_utf8(value) - .ok() - .map(|s| s.replace("n", "-")) - .map(|s| s.replace("_", "")) - .and_then(|v| i64::from_str(&v).ok()) - .map(::std::num::Wrapping) + let s = str::from_utf8(value).ok()?; + let s = s.rsplit_once('_').map(|(_, s)| s).unwrap_or(s); + + i64::from_str(&s.replace("n", "-")).ok() + .map(Wrapping) .map(Int) } - use cexpr::expr::EvalResult::*; - let display_name = String::from_utf8_lossy(&ident).into_owned(); let functional; @@ -127,6 +127,7 @@ fn test_definition( return false; } } + assert_full_parse(IdentifierParser::new(&fnidents).expr(&expr_tokens)) } else { IdentifierParser::new(idents) diff --git a/tests/input/fail.h b/tests/input/fail.h index fd416bc..fb59128 100644 --- a/tests/input/fail.h +++ b/tests/input/fail.h @@ -1,4 +1,3 @@ -#define FAIL_function_like(x) 3 #define FAIL_empty #define FAIL_invalid_for_radix 0b2 #define FAIL_shift_by_float 3<<1f diff --git a/tests/input/int_signed.h b/tests/input/int_signed.h index 65854a6..1164861 100644 --- a/tests/input/int_signed.h +++ b/tests/input/int_signed.h @@ -1,3 +1,5 @@ #define Int_n3 -(-(-3)) #define Int_n5 -3-2 #define Int_n9223372036854775808 -9223372036854775808 + +#define Fn_Int_n9(_3) _3*-3 diff --git a/tests/input/int_unsigned.h b/tests/input/int_unsigned.h index 6663dda..7e5af0e 100644 --- a/tests/input/int_unsigned.h +++ b/tests/input/int_unsigned.h @@ -10,7 +10,7 @@ #define Int_124 124u #define Int_125 125uL #define Int_126 126LuL -#define Int_16 (((1)<<4ULL))/*comment*/ +#define Int_16 (((1)<<4ULL))/*comment*/ #define Int_13 1|8^6&2<<1 #define Int_47 32|15 @@ -27,3 +27,4 @@ #define Int_n9223372036854775808 9223372036854775808 #define Fn_Int_9(_3) _3*3 +#define Fn_Int_unused_arg_3(x_unused_0) 3