diff --git a/src/expr.rs b/src/expr.rs index ccfac83..2ba712e 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -22,6 +22,8 @@ use std::collections::HashMap; use std::ops::{AddAssign,BitAndAssign,BitOrAssign,BitXorAssign,DivAssign,MulAssign,RemAssign,ShlAssign,ShrAssign,SubAssign}; use std::num::Wrapping; +use std::mem::size_of; +use std::os::raw::*; use literal::{self,CChar}; use token::{Token,Kind as TokenKind}; @@ -65,7 +67,7 @@ impl EvalResult { result_opt!(fn as_float: Float -> f64); result_opt!(fn as_char: Char -> CChar); result_opt!(fn as_str: Str -> Vec); - + fn as_numeric(self) -> Option { match self { EvalResult::Int(_) | EvalResult::Float(_) => Some(self), @@ -130,6 +132,14 @@ macro_rules! p ( ($i:expr, $c:expr) => (exact_token!($i,Punctuation,$c.as_bytes())) ); +macro_rules! k ( + ($i:expr, $c:expr) => (exact_token!($i,Keyword,$c.as_bytes())) +); + +macro_rules! i ( + ($i:expr, $c:expr) => (exact_token!($i,Identifier,$c.as_bytes())) +); + macro_rules! one_of_punctuation ( ($i:expr, $c:expr) => ({ if $i.is_empty() { @@ -298,9 +308,57 @@ macro_rules! numeric ( ($i:expr, $f:expr ) => (map_opt!($i,call!($f),EvalResult::as_numeric)); ); +enum TypeCast { + SignedInt(usize), + UnsignedInt(usize), +} + +impl EvalResult { + fn cast(self, ctype: TypeCast) -> Self { + use self::TypeCast::*; + let round = |bits| 2i64.pow((bits) as u32); + let unsigned_mask = |i, b| (i+round(b*8)) % round(b*8); + let signed_mask = |i, b| { + let rounded = unsigned_mask(i, b); + let half = round(8*b-1); + if rounded >= half {rounded - half * 2} else { rounded } + }; + match self { + EvalResult::Float(f) => match ctype { + SignedInt(b) => EvalResult::Int(Wrapping(signed_mask(f as i64, b))), + UnsignedInt(b) => EvalResult::Int(Wrapping(unsigned_mask(f as i64, b))), + }, + EvalResult::Int(Wrapping(i)) => match ctype { + SignedInt(b) => EvalResult::Int(Wrapping(signed_mask(i, b))), + UnsignedInt(b) => EvalResult::Int(Wrapping(unsigned_mask(i, b))), + }, + _ => self + } + } +} + + impl<'a> PRef<'a> { + method!(cast,&[Token],TypeCast,::Error>, mut self, + delimited!(p!("("), alt!( + do_parse!(k!("unsigned") >> k!("int") >> (TypeCast::UnsignedInt(size_of::()))) | + do_parse!(k!("unsigned") >> k!("short") >> (TypeCast::UnsignedInt(size_of::()))) | + do_parse!(k!("unsigned") >> k!("char") >> (TypeCast::UnsignedInt(size_of::()))) | + do_parse!(opt!(k!("signed")) >> k!("int") >> (TypeCast::SignedInt(size_of::()))) | + do_parse!(opt!(k!("signed")) >> k!("short") >> (TypeCast::SignedInt(size_of::()))) | + do_parse!(opt!(k!("signed")) >> k!("char") >> (TypeCast::SignedInt(size_of::()))) | + do_parse!(i!("uint8_t") >> (TypeCast::UnsignedInt(1))) | + do_parse!(i!("uint16_t") >> (TypeCast::UnsignedInt(2))) | + do_parse!(i!("uint32_t") >> (TypeCast::UnsignedInt(4))) | + do_parse!(i!("int8_t") >> (TypeCast::SignedInt(1))) | + do_parse!(i!("int16_t") >> (TypeCast::SignedInt(2))) | + do_parse!(i!("int32_t") >> (TypeCast::SignedInt(4))) + ) ,p!(")")) + ); + method!(unary,&[Token],EvalResult,::Error>, mut self, alt!( + map!(pair!(call_m!(self.cast), call_m!(self.unary)), |(c, r)| r.cast(c) ) | delimited!(p!("("),call_m!(self.numeric_expr),p!(")")) | numeric!(call_m!(self.literal)) | numeric!(call_m!(self.identifier)) | @@ -489,7 +547,7 @@ 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. diff --git a/tests/clang.rs b/tests/clang.rs index 043077f..1fc2744 100644 --- a/tests/clang.rs +++ b/tests/clang.rs @@ -254,3 +254,4 @@ test_file!(strings); test_file!(int_signed); test_file!(int_unsigned); test_file!(fail); +test_file!(casts); diff --git a/tests/input/casts.h b/tests/input/casts.h new file mode 100644 index 0000000..3071e37 --- /dev/null +++ b/tests/input/casts.h @@ -0,0 +1,8 @@ +#define Int_n2 (int)-2 +#define Int_254 (uint8_t)-2 +#define Float_236p3 (int)233.3 + 3.3 +#define Int_255 (uint8_t)255.6 +#define Int_n128 (int8_t)128 +#define Int_n121 (int8_t)1234567 +#define Int_111 (unsigned int)111.111 +#define Int_4294967295 (unsigned int)-1 \ No newline at end of file diff --git a/tests/input/int_unsigned.h b/tests/input/int_unsigned.h index e42a975..9bef303 100644 --- a/tests/input/int_unsigned.h +++ b/tests/input/int_unsigned.h @@ -8,7 +8,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