use nom::{Err, ErrorKind, IResult, Needed, is_digit, is_space, not_line_ending};
use nom::IResult::*;
use std::str;
use super::{Decl, Defn, EnumDefn, Type, UnionCase, Value};
use super::{CLONE, COPY, DEBUG, EQ, PARTIALEQ};
#[inline]
fn ignore<T>(_: T) -> () {
()
}
fn ctag<T: AsRef<[u8]>>(input: &[u8], tag: T) -> IResult<&[u8], &[u8]> {
complete!(input, tag!(tag.as_ref()))
}
fn eof(input: &[u8]) -> IResult<&[u8], ()> {
if input.len() == 0 {
IResult::Done(input, ())
} else {
IResult::Error(Err::Position(ErrorKind::Eof, input))
}
}
pub fn specification(input: &str) -> Result<Vec<Defn>, String> {
match spec(input.as_bytes()) {
Done(_, spec) => Ok(spec),
Error(Err::Position(kind, input)) => {
Err(format!(
"{:?}: {}",
kind,
String::from(str::from_utf8(input).unwrap())
))
}
Error(err) => Err(format!("Error: {:?}", err)),
Incomplete(need) => Err(format!("Incomplete {:?}", need)),
}
}
named!(spec< Vec<Defn> >,
do_parse!(
opt!(directive) >>
defns: many0!(definition) >>
spaces >> eof >>
(defns))
);
#[test]
fn test_spec() {
assert_eq!(spec(&b"#include <foo>"[..]),
Done(&b""[..], vec!()));
assert_eq!(spec(&b"// hello\n#include <foo>"[..]),
Done(&b""[..], vec!()));
assert_eq!(spec(&b"#include <foo>\ntypedef int foo;"[..]),
Done(&b""[..], vec!(Defn::typesyn("foo", Type::Int))));
assert_eq!(spec(&br#"
/* test file */
#define foo bar
const mip = 123;
% passthrough
typedef int foo;
struct bar {
int a;
int b;
};
#include "other"
enum bop { a = 2, b = 1 };
"#[..]),
Done(&b""[..],
vec!(Defn::constant("mip", 123),
Defn::typesyn("foo", Type::Int),
Defn::typespec("bar", Type::Struct(vec!(Decl::named("a", Type::Int),
Decl::named("b", Type::Int)))),
Defn::typespec("bop", Type::Enum(vec!(EnumDefn::new("a", Some(Value::Const(2))),
EnumDefn::new("b", Some(Value::Const(1)))))))));
}
named!(definition<Defn>,
alt!(type_def => { |t| t } |
const_def => { |c| c }));
fn is_hexdigit(ch: u8) -> bool {
match ch as char {
'0'...'9' | 'A'...'F' | 'a'...'f' => true,
_ => false,
}
}
fn is_octdigit(ch: u8) -> bool {
match ch as char {
'0'...'7' => true,
_ => false,
}
}
fn digit<F: Fn(u8) -> bool>(input: &[u8], isdigit: F) -> IResult<&[u8], &[u8]> {
for (idx, item) in input.iter().enumerate() {
if !isdigit(*item) {
if idx == 0 {
return Error(Err::Position(ErrorKind::Digit, input));
} else {
return Done(&input[idx..], &input[0..idx]);
}
}
}
Incomplete(Needed::Unknown)
}
named!(lbrace, preceded!(spaces, apply!(ctag, "{")));
named!(rbrace, preceded!(spaces, apply!(ctag, "}")));
named!(lbrack, preceded!(spaces, apply!(ctag, "[")));
named!(rbrack, preceded!(spaces, apply!(ctag, "]")));
named!(lparen, preceded!(spaces, apply!(ctag, "(")));
named!(rparen, preceded!(spaces, apply!(ctag, ")")));
named!(lt, preceded!(spaces, apply!(ctag, "<")));
named!(gt, preceded!(spaces, apply!(ctag, ">")));
named!(colon, preceded!(spaces, apply!(ctag, ":")));
named!(semi, preceded!(spaces, apply!(ctag, ";")));
named!(comma, preceded!(spaces, apply!(ctag, ",")));
named!(eq, preceded!(spaces, apply!(ctag, "=")));
named!(star, preceded!(spaces, apply!(ctag, "*")));
named!(hexnumber<i64>,
do_parse!(
apply!(ctag, "0x") >>
val: map_res!(apply!(digit, is_hexdigit), str::from_utf8) >>
(i64::from_str_radix(val, 16).unwrap())
)
);
named!(octnumber<i64>,
do_parse!(
sign: opt!(apply!(ctag, "-")) >>
apply!(ctag, "0") >>
val: opt!(map_res!(apply!(digit, is_octdigit), str::from_utf8)) >>
(i64::from_str_radix(val.unwrap_or("0"), 8).unwrap() * (if sign.is_some() { -1 } else { 1 }))
)
);
named!(decnumber<i64>,
do_parse!(
sign: opt!(apply!(ctag, "-")) >>
val: map_res!(apply!(digit, is_digit), str::from_utf8) >>
(i64::from_str_radix(val, 10).unwrap() * (if sign.is_some() { -1 } else { 1 }))
)
);
named!(number<i64>, preceded!(spaces, alt!(hexnumber | octnumber | decnumber)));
#[test]
fn test_nums() {
assert_eq!(number(&b"0x12344+"[..]), Done(&b"+"[..], 0x12344));
assert_eq!(number(&b"012344+"[..]), Done(&b"+"[..], 0o12344));
assert_eq!(number(&b"-012344+"[..]), Done(&b"+"[..], -0o12344));
assert_eq!(number(&b"12344+"[..]), Done(&b"+"[..], 12344));
assert_eq!(number(&b"-12344+"[..]), Done(&b"+"[..], -12344));
assert_eq!(number(&b"0+"[..]), Done(&b"+"[..], 0));
assert_eq!(number(&b"-0+"[..]), Done(&b"+"[..], 0));
assert_eq!(number(&b" 0x12344+"[..]), Done(&b"+"[..], 0x12344));
assert_eq!(number(&b" 012344+"[..]), Done(&b"+"[..], 0o12344));
assert_eq!(number(&b" -012344+"[..]), Done(&b"+"[..], -0o12344));
assert_eq!(number(&b" 12344+"[..]), Done(&b"+"[..], 12344));
assert_eq!(number(&b" -12344+"[..]), Done(&b"+"[..], -12344));
assert_eq!(number(&b" 0+"[..]), Done(&b"+"[..], 0));
assert_eq!(number(&b" -0+"[..]), Done(&b"+"[..], 0));
assert_eq!(number(&b"0x12344"[..]), Incomplete(Needed::Unknown));
assert_eq!(number(&b"012344"[..]), Incomplete(Needed::Unknown));
assert_eq!(number(&b"-012344"[..]), Incomplete(Needed::Unknown));
assert_eq!(number(&b"12344"[..]), Incomplete(Needed::Unknown));
assert_eq!(number(&b"-12344"[..]), Incomplete(Needed::Unknown));
assert_eq!(number(&b"0"[..]), Incomplete(Needed::Unknown));
assert_eq!(number(&b"-0"[..]), Incomplete(Needed::Unknown));
}
fn token(input: &[u8]) -> IResult<&[u8], &[u8]> {
let input = ws(input);
for (idx, item) in input.iter().enumerate() {
match *item as char {
'a'...'z' | 'A'...'Z' | '_' => continue,
'0'...'9' if idx > 0 => continue,
_ => {
if idx > 0 {
return Done(&input[idx..], &input[0..idx]);
} else {
return Error(Err::Position(ErrorKind::AlphaNumeric, input));
}
}
}
}
Incomplete(Needed::Unknown)
}
macro_rules! kw {
($fnname:ident, $kw:expr) => (
fn $fnname(input: &[u8]) -> IResult<&[u8], ()> {
match token(input) {
Done(rest, val) =>
if val == $kw {
Done(rest, ())
} else {
Error(Err::Position(ErrorKind::Custom(0), input))
},
Error(e) => Error(e),
Incomplete(_) => {
if input.len() > $kw.len() || input != &$kw[..input.len()] {
Error(Err::Position(ErrorKind::Custom(0), input))
} else {
Incomplete(Needed::Size($kw.len() - input.len()))
}
},
}
});
}
kw!(kw_bool, b"bool");
kw!(kw_case, b"case");
kw!(kw_char, b"char"); kw!(kw_const, b"const");
kw!(kw_default, b"default");
kw!(kw_double, b"double");
kw!(kw_enum, b"enum");
kw!(kw_float, b"float");
kw!(kw_hyper, b"hyper");
kw!(kw_int, b"int");
kw!(kw_long, b"long"); kw!(kw_opaque, b"opaque");
kw!(kw_quadruple, b"quadruple");
kw!(kw_short, b"short"); kw!(kw_string, b"string");
kw!(kw_struct, b"struct");
kw!(kw_switch, b"switch");
kw!(kw_typedef, b"typedef");
kw!(kw_union, b"union");
kw!(kw_unsigned, b"unsigned");
kw!(kw_void, b"void");
named!(keyword<()>,
alt!(kw_bool |
kw_case |
kw_const |
kw_default |
kw_double |
kw_enum |
kw_float |
kw_hyper |
kw_int |
kw_opaque |
kw_quadruple |
kw_string |
kw_struct |
kw_switch |
kw_typedef |
kw_union |
kw_unsigned |
kw_void));
#[test]
fn test_kw() {
let kws = vec!("bool", "case", "const", "default",
"double", "enum", "float", "hyper", "int",
"opaque", "quadruple", "string", "struct",
"switch", "typedef", "union", "unsigned", "void");
for k in &kws {
println!("testing \"{}\"", k);
match keyword((*k).as_bytes()) {
Incomplete(_) => (),
err => panic!("failed \"{}\": {:?}", k, err),
}
}
for k in &kws {
println!("testing \"{} \"", k);
match keyword((String::from(*k) + " ").as_bytes()) {
Done(rest, ()) if rest == &b" "[..] => (),
err => panic!("failed \"{} \": {:?}", k, err),
}
}
for k in &kws {
println!("testing \"{}x \"", k);
match keyword((String::from(*k) + "x ").as_bytes()) {
Error(_) => (),
err => panic!("failed \"{}x \": {:?}", k, err),
}
}
for k in &kws {
println!("testing \"{}x \"", k);
match keyword((String::from(" ") + *k + " ").as_bytes()) {
Done(rest, ()) if rest == &b" "[..] => (),
err => panic!("failed \" {} \": {:?}", k, err),
}
}
for nk in &vec!("boo", "in", "inx", "booll") {
match keyword((*nk).as_bytes()) {
e @ Done(..) => panic!("{:?} => {:?}", nk, e),
e => println!("{:?} => {:?}", nk, e),
}
}
}
fn ident(input: &[u8]) -> IResult<&[u8], &str> {
match token(input) {
Done(rest, val) => {
match keyword(input) {
Done(..) => Error(Err::Position(ErrorKind::Custom(1), val)),
Error(..) | Incomplete(..) => Done(rest, str::from_utf8(val).unwrap()),
}
}
Error(e) => Error(e),
Incomplete(need) => Incomplete(need),
}
}
#[test]
fn test_ident() {
assert_eq!(ident(&b"foo "[..]), Done(&b" "[..], "foo"));
assert_eq!(ident(&b" foo "[..]), Done(&b" "[..], "foo"));
assert_eq!(ident(&b" bool "[..]), Error(Err::Position(ErrorKind::Custom(1), &b"bool"[..])));
}
named!(blockcomment<()>,
do_parse!(apply!(ctag, "/*") >> take_until_and_consume!(&b"*/"[..]) >> (())));
named!(linecomment<()>,
do_parse!(
apply!(ctag, "//") >> opt!(not_line_ending) >> peek!(alt!(eol | eof)) >> (())
)
);
named!(directive<()>,
do_parse!(
opt!(whitespace) >>
alt!(
apply!(ctag, "#") |
apply!(ctag, "%")) >>
opt!(not_line_ending) >>
peek!(alt!(eol | eof)) >> (())
)
);
#[test]
fn test_comments() {
assert_eq!(blockcomment(&b"/* foo */bar"[..]), Done(&b"bar"[..], ()));
assert_eq!(blockcomment(&b"/* blip /* foo */bar"[..]), Done(&b"bar"[..], ()));
assert_eq!(blockcomment(&b"x"[..]), Error(Err::Position(ErrorKind::Tag, &b"x"[..])));
assert_eq!(linecomment(&b"// foo\nbar"[..]), Done(&b"\nbar"[..], ()));
assert_eq!(linecomment(&b"// foo bar\n "[..]), Done(&b"\n "[..], ()));
assert_eq!(linecomment(&b"x"[..]), Error(Err::Position(ErrorKind::Tag, &b"x"[..])));
assert_eq!(directive(&b"#define foo bar\n "[..]), Done(&b"\n "[..], ()));
assert_eq!(directive(&b"%#define foo bar\n "[..]), Done(&b"\n "[..], ()));
assert_eq!(directive(&b"x"[..]), Error(Err::Position(ErrorKind::Alt, &b"x"[..])));
assert_eq!(preceded!(&b"\n#define x\n"[..], eol, directive),
Done(&b"\n"[..], ()));
}
named!(eol<()>, map!(alt!(apply!(ctag, "\n") |
apply!(ctag, "\r\n") |
apply!(ctag, "\u{2028}") |
apply!(ctag, "\u{2029}")),
ignore)
);
named!(whitespace<()>,
map!(take_while1!(is_space), ignore));
named!(spaces<()>,
map!(
many0!(
alt!( do_parse!(eol >> opt!(complete!(directive)) >> (()))
| whitespace
| blockcomment
| linecomment
)
),
ignore
)
);
fn ws(input: &[u8]) -> &[u8] {
match spaces(input) {
Done(rest, _) => rest,
_ => input,
}
}
#[test]
fn test_spaces() {
assert_eq!(eol(&b"\nx"[..]), Done(&b"x"[..], ()));
assert_eq!(eol(&b"\r\nx"[..]), Done(&b"x"[..], ()));
assert_eq!(eol(&b"\nx"[..]), Done(&b"x"[..], ()));
assert_eq!(whitespace(&b"x"[..]), Error(Err::Position(ErrorKind::TakeWhile1, &b"x"[..])));
assert_eq!(whitespace(&b" x"[..]), Done(&b"x"[..], ()));
assert_eq!(whitespace(&b" x"[..]), Done(&b"x"[..], ()));
assert_eq!(whitespace(&b"\tx"[..]), Done(&b"x"[..], ()));
assert_eq!(whitespace(&b" \tx"[..]), Done(&b"x"[..], ()));
assert_eq!(whitespace(&b"\t x"[..]), Done(&b"x"[..], ()));
assert_eq!(spaces(&b"x"[..]), Done(&b"x"[..], ()));
assert_eq!(spaces(&b"\nx"[..]), Done(&b"x"[..], ()));
assert_eq!(spaces(&b" x"[..]), Done(&b"x"[..], ()));
assert_eq!(spaces(&b" x"[..]), Done(&b"x"[..], ()));
assert_eq!(spaces(&b"\n\n x"[..]), Done(&b"x"[..], ()));
assert_eq!(spaces(&b"\r\n x"[..]), Done(&b"x"[..], ()));
assert_eq!(spaces(&b"//foo\n x"[..]), Done(&b"x"[..], ()));
assert_eq!(spaces(&b"/*\n*/ x"[..]), Done(&b"x"[..], ()));
assert_eq!(spaces(&b"\n#define a b\n x"[..]), Done(&b"x"[..], ()));
assert_eq!(spaces(&b"\n%foo a b\n x"[..]), Done(&b"x"[..], ()));
}
named!(enum_type_spec< Vec<EnumDefn> >,
preceded!(kw_enum, enum_body));
named!(enum_body< Vec<EnumDefn> >,
do_parse!(
lbrace >>
b: separated_nonempty_list!(comma, enum_assign) >>
rbrace >>
(b)
)
);
named!(enum_assign<EnumDefn>,
do_parse!(
id: ident >>
v: opt!(preceded!(eq, value)) >>
(EnumDefn::new(id, v))
)
);
named!(value<Value>,
alt!(number => { |c| Value::Const(c) } |
ident => { |id| Value::ident(id) }
)
);
named!(struct_type_spec< Vec<Decl> >,
preceded!(kw_struct, struct_body));
named!(struct_body< Vec<Decl> >,
do_parse!(
lbrace >>
decls: many1!(terminated!(declaration, semi)) >>
rbrace >>
(decls)
)
);
named!(union_type_spec<(Decl, Vec<UnionCase>, Option<Decl>)>,
do_parse!(kw_union >> body:union_body >> (body)));
named!(union_body<(Decl, Vec<UnionCase>, Option<Decl>)>,
do_parse!(
kw_switch >> lparen >> decl:declaration >> rparen >>
lbrace >>
ucss: many1!(union_case) >>
dfl: opt!(union_default) >>
rbrace >>
(decl, ucss.into_iter().flat_map(|v| v).collect(), dfl)
)
);
named!(union_case< Vec<UnionCase> >,
do_parse!(
vs: many1!(do_parse!(kw_case >> v:value >> colon >> (v))) >>
decl: declaration >> semi >>
(vs.into_iter().map(|v| UnionCase(v, decl.clone())).collect())
)
);
named!(union_default<Decl>,
do_parse!(
kw_default >> colon >>
decl: declaration >> semi >>
(decl)
)
);
named!(declaration<Decl>,
alt!(kw_void => { |_| Decl::Void } |
nonvoid_declaration));
named!(nonvoid_declaration<Decl>,
alt!(
do_parse!(ty: array_type_spec >> id: ident >> lbrack >> sz:value >> rbrack >>
(Decl::named(id, Type::array(ty, sz))))
| do_parse!(ty: array_type_spec >> id: ident >> lt >> sz:opt!(value) >> gt >>
(Decl::named(id, Type::flex(ty, sz))))
| do_parse!(ty: type_spec >> star >> id: ident >>
(Decl::named(id, Type::option(ty))))
| do_parse!(ty: type_spec >> id: ident >>
(Decl::named(id, ty)))
)
);
named!(array_type_spec<Type>,
alt!(kw_opaque => { |_| Type::Opaque } |
kw_string => { |_| Type::String } |
type_spec
)
);
#[test]
fn test_decls() {
assert_eq!(declaration(&b"void "[..]), Done(&b" "[..], Decl::Void));
assert_eq!(declaration(&b"int foo;"[..]), Done(&b";"[..], Decl::named("foo", Type::Int)));
assert_eq!(declaration(&b"int foo[123] "[..]),
Done(&b" "[..], Decl::named("foo",
Type::Array(Box::new(Type::Int), Value::Const(123)))));
assert_eq!(declaration(&b"int foo<123> "[..]),
Done(&b" "[..], Decl::named("foo",
Type::Flex(Box::new(Type::Int), Some(Value::Const(123))))));
assert_eq!(declaration(&b"int foo<> "[..]),
Done(&b" "[..], Decl::named("foo",
Type::Flex(Box::new(Type::Int), None))));
assert_eq!(declaration(&b"int *foo "[..]),
Done(&b" "[..], Decl::named("foo",
Type::Option(Box::new(Type::Int)))));
assert_eq!(declaration(&b"opaque foo[123] "[..]),
Done(&b" "[..], Decl::named("foo",
Type::Array(Box::new(Type::Opaque), Value::Const(123)))));
assert_eq!(declaration(&b"opaque foo<123> "[..]),
Done(&b" "[..], Decl::named("foo",
Type::Flex(Box::new(Type::Opaque), Some(Value::Const(123))))));
assert_eq!(declaration(&b"opaque foo<> "[..]),
Done(&b" "[..], Decl::named("foo",
Type::Flex(Box::new(Type::Opaque), None))));
assert_eq!(declaration(&b"string foo<123> "[..]),
Done(&b" "[..], Decl::named("foo",
Type::Flex(Box::new(Type::String), Some(Value::Const(123))))));
assert_eq!(declaration(&b"string foo<> "[..]),
Done(&b" "[..], Decl::named("foo",
Type::Flex(Box::new(Type::String), None))));
}
named!(type_spec<Type>,
preceded!(spaces,
alt!(
do_parse!(kw_unsigned >> kw_int >> (Type::UInt)) |
do_parse!(kw_unsigned >> kw_long >> (Type::UInt)) | do_parse!(kw_unsigned >> kw_char >> (Type::ident_with_derives("u8", COPY | CLONE | EQ | PARTIALEQ | DEBUG))) |
do_parse!(kw_unsigned >> kw_short >> (Type::UInt)) | do_parse!(kw_unsigned >> kw_hyper >> (Type::UHyper)) |
kw_unsigned => { |_| Type::UInt } | kw_long => { |_| Type::Int } | kw_char => { |_| Type::ident_with_derives("i8", COPY | CLONE | EQ | PARTIALEQ | DEBUG)
} |
kw_short => { |_| Type::Int } | kw_int => { |_| Type::Int } |
kw_hyper => { |_| Type::Hyper } |
kw_float => { |_| Type::Float } |
kw_double => { |_| Type::Double } |
kw_quadruple => { |_| Type::Quadruple } |
kw_bool => { |_| Type::Bool } |
enum_type_spec => { |defns| Type::Enum(defns) } |
struct_type_spec => { |defns| Type::Struct(defns) } |
do_parse!(kw_struct >> id:ident >> (Type::ident(id))) | union_type_spec => { |u| Type::union(u) } |
ident => { |id| Type::ident(id) }
)
)
);
#[test]
fn test_type() {
assert_eq!(type_spec(&b"int "[..]), Done(&b" "[..], Type::Int));
assert_eq!(type_spec(&b"unsigned int "[..]), Done(&b" "[..], Type::UInt));
assert_eq!(type_spec(&b"unsigned\nint "[..]), Done(&b" "[..], Type::UInt));
assert_eq!(type_spec(&b"unsigned/* foo */int "[..]), Done(&b" "[..], Type::UInt));
assert_eq!(type_spec(&b"unsigned//\nint "[..]), Done(&b" "[..], Type::UInt));
assert_eq!(type_spec(&b"unsigned hyper "[..]), Done(&b" "[..], Type::UHyper));
assert_eq!(type_spec(&b"unsigned char "[..]), Done(&b" "[..],
Type::Ident("u8".into(), Some(COPY | CLONE | EQ | PARTIALEQ | DEBUG))));
assert_eq!(type_spec(&b"unsigned short "[..]), Done(&b" "[..], Type::UInt));
assert_eq!(type_spec(&b" hyper "[..]), Done(&b" "[..], Type::Hyper));
assert_eq!(type_spec(&b" double "[..]), Done(&b" "[..], Type::Double));
assert_eq!(type_spec(&b"// thing\nquadruple "[..]), Done(&b" "[..], Type::Quadruple));
assert_eq!(type_spec(&b"// thing\n bool "[..]), Done(&b" "[..], Type::Bool));
assert_eq!(type_spec(&b"char "[..]), Done(&b" "[..],
Type::Ident("i8".into(), Some(COPY | CLONE | EQ | PARTIALEQ | DEBUG))));
assert_eq!(type_spec(&b"short "[..]), Done(&b" "[..], Type::Int));
assert_eq!(type_spec(&b"struct { int a; int b; } "[..]),
Done(&b" "[..],
Type::Struct(vec!(Decl::named("a", Type::Int),
Decl::named("b", Type::Int)))));
assert_eq!(type_spec(&b"union switch (int a) { case 1: void; case 2: int a; default: void; } "[..]),
Done(&b" "[..],
Type::Union(Box::new(Decl::named("a", Type::Int)),
vec!(UnionCase(Value::Const(1), Decl::Void),
UnionCase(Value::Const(2), Decl::named("a", Type::Int))),
Some(Box::new(Decl::Void)))));
}
#[test]
fn test_enum() {
assert_eq!(type_spec(&b"enum { a, b, c } "[..]),
Done(&b" "[..],
Type::Enum(vec!(EnumDefn::new("a", None),
EnumDefn::new("b", None),
EnumDefn::new("c", None)))));
assert_eq!(type_spec(&b"enum { a = 1, b, c } "[..]),
Done(&b" "[..],
Type::Enum(vec!(EnumDefn::new("a", Some(Value::Const(1))),
EnumDefn::new("b", None),
EnumDefn::new("c", None)))));
assert_eq!(type_spec(&b"enum { a = Bar, b, c } "[..]),
Done(&b" "[..],
Type::Enum(vec!(EnumDefn::new("a", Some(Value::ident("Bar"))),
EnumDefn::new("b", None),
EnumDefn::new("c", None)))));
assert_eq!(type_spec(&b"enum { } "[..]),
Error(Err::Position(ErrorKind::Alt, &b"enum { } "[..])));
}
named!(const_def<Defn>,
do_parse!(
kw_const >> id:ident >> eq >> v:number >> semi >>
(Defn::constant(id, v)))
);
#[test]
fn test_const() {
assert_eq!(const_def(&b"const foo = 123;"[..]), Done(&b""[..], Defn::constant("foo", 123)));
}
named!(type_def<Defn>,
alt!(
do_parse!(kw_typedef >> decl: nonvoid_declaration >> semi >>
({
match decl.clone() {
Decl::Named(name, ty) => {
if ty.is_syn() {
Defn::typesyn(name, ty)
} else {
Defn::typespec(name, ty)
}
},
Decl::Void => panic!("void non-void declaration?"),
}
})
)
| do_parse!(kw_enum >> id:ident >> e:enum_body >> semi >> (Defn::typespec(id, Type::Enum(e))))
| do_parse!(kw_struct >> id:ident >> s:struct_body >> semi >> (Defn::typespec(id, Type::Struct(s))))
| do_parse!(kw_union >> id:ident >> u:union_body >> semi >> (Defn::typespec(id, Type::union(u))))
)
);
#[test]
fn test_typedef() {
assert_eq!(type_def(&b"typedef int foo;"[..]),
Done(&b""[..], Defn::typesyn("foo", Type::Int)));
assert_eq!(type_def(&b"typedef unsigned int foo;"[..]),
Done(&b""[..], Defn::typesyn("foo", Type::UInt)));
assert_eq!(type_def(&b"typedef int foo<>;"[..]),
Done(&b""[..], Defn::typespec("foo", Type::Flex(Box::new(Type::Int), None))));
assert_eq!(type_def(&b"enum foo { a };"[..]),
Done(&b""[..], Defn::typespec("foo", Type::Enum(vec!(EnumDefn::new("a", None))))));
assert_eq!(type_def(&b"struct foo { int a; };"[..]),
Done(&b""[..], Defn::typespec("foo", Type::Struct(vec!(Decl::named("a", Type::Int))))));
assert_eq!(type_def(&b"union foo switch(int a) { case 1: int a; };"[..]),
Done(&b""[..], Defn::typespec("foo",
Type::Union(Box::new(Decl::named("a", Type::Int)),
vec!(UnionCase(Value::Const(1), Decl::named("a", Type::Int))),
None))));
}