Nom nom
diff --git a/src/lib.rs b/src/lib.rs
index c9e6e52..1d36389 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,7 +2,7 @@
extern crate nom;
use nom::IResult;
-use nom::multispace as space;
+use nom::{digit, multispace as space};
use std::str;
@@ -255,7 +255,7 @@
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct MutTy {
pub ty: Ty,
- pub mutbl: Mutability,
+ pub mutability: Mutability,
}
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -320,6 +320,10 @@
Ty(Ty),
}
+pub fn epsilon<T>(input: T) -> IResult<T, ()> {
+ IResult::Done(input, ())
+}
+
pub fn escaped_string(input: &str) -> IResult<&str, String> {
let mut s = String::new();
let mut chars = input.char_indices().peekable();
@@ -357,81 +361,119 @@
IResult::Error(nom::Err::Position(nom::ErrorKind::Escaped, input))
}
+macro_rules! punct {
+ ($i:expr, $ch:expr) => {
+ tuple!($i, opt!(space), tag_s!($ch))
+ };
+}
+
named!(quoted<&str, String>, delimited!(
- tag_s!("\""),
+ punct!("\""),
escaped_string,
tag_s!("\"")
));
-named!(meta_item<&str, MetaItem>, chain!(
- space? ~
- meta_item: alt!(
- chain!(
- ident: word ~
- space? ~
- tag_s!("(") ~
- inner: separated_list!(tag_s!(","), meta_item) ~
- space? ~
- tag_s!(")"),
- move || MetaItem::List(ident, inner)
- )
- |
- chain!(
- ident: word ~
- space? ~
- tag_s!("=") ~
- space? ~
- string: quoted,
- move || MetaItem::NameValue(ident, string)
- )
- |
- map!(word, MetaItem::Word)
- ),
- move || meta_item
+named!(meta_item<&str, MetaItem>, alt!(
+ chain!(
+ ident: word ~
+ punct!("(") ~
+ inner: separated_list!(punct!(","), meta_item) ~
+ punct!(")"),
+ move || MetaItem::List(ident, inner)
+ )
+ |
+ chain!(
+ ident: word ~
+ punct!("=") ~
+ string: quoted,
+ move || MetaItem::NameValue(ident, string)
+ )
+ |
+ map!(word, MetaItem::Word)
));
named!(attribute<&str, Attribute>, chain!(
- space? ~
- tag_s!("#") ~
- space? ~
- tag_s!("[") ~
+ punct!("#") ~
+ punct!("[") ~
meta_item: meta_item ~
- space? ~
- tag_s!("]"),
+ punct!("]"),
move || Attribute {
value: meta_item,
is_sugared_doc: false,
}
));
-named!(visibility<&str, Visibility>,
- map!(opt!(terminated!(tag_s!("pub"), space)), |tag| match tag {
- Some(_) => Visibility::Public,
- None => Visibility::Inherited,
- })
-);
+named!(mutability<&str, Mutability>, preceded!(
+ opt!(space),
+ alt!(
+ terminated!(tag_s!("mut"), space) => { |_| Mutability::Mutable }
+ |
+ epsilon => { |_| Mutability::Immutable }
+ )
+));
+
+named!(visibility<&str, Visibility>, preceded!(
+ opt!(space),
+ alt!(
+ terminated!(tag_s!("pub"), space) => { |_| Visibility::Public }
+ |
+ epsilon => { |_| Visibility::Inherited }
+ )
+));
fn ident_ch(ch: char) -> bool {
ch.is_alphanumeric() || ch == '_'
}
-named!(word<&str, Ident>, map!(take_while1_s!(ident_ch), String::from));
+named!(word<&str, Ident>, preceded!(
+ opt!(space),
+ map!(take_while1_s!(ident_ch), String::from)
+));
+
+macro_rules! opt_vec (
+ ($i:expr, $submac:ident!( $($args:tt)* )) => ({
+ match $submac!($i, $($args)*) {
+ IResult::Done(i, o) => IResult::Done(i, o),
+ IResult::Error(_) => IResult::Done($i, Vec::new()),
+ IResult::Incomplete(i) => IResult::Incomplete(i)
+ }
+ });
+);
+
+named!(type_binding<&str, TypeBinding>, chain!(
+ ident: word ~
+ punct!("=") ~
+ ty: ty,
+ move || TypeBinding {
+ ident: ident,
+ ty: ty,
+ }
+));
named!(path_segment<&str, PathSegment>, alt!(
chain!(
ident: word ~
- space? ~
- tag_s!("<") ~
- types: many0!(ty) ~
- space? ~
- tag_s!(">"),
+ punct!("<") ~
+ lifetimes: separated_list!(punct!(","), lifetime) ~
+ types: opt_vec!(preceded!(
+ cond!(!lifetimes.is_empty(), punct!(",")),
+ separated_nonempty_list!(
+ punct!(","),
+ terminated!(ty, not!(peek!(punct!("="))))
+ )
+ )) ~
+ bindings: opt_vec!(preceded!(
+ cond!(!lifetimes.is_empty() || !types.is_empty(), punct!(",")),
+ separated_nonempty_list!(punct!(","), type_binding)
+ )) ~
+ punct!(">"),
move || PathSegment {
ident: ident,
parameters: PathParameters::AngleBracketed(
AngleBracketedParameterData {
- lifetimes: Vec::new(),
+ lifetimes: lifetimes,
types: types,
- bindings: Vec::new(),
+ bindings: bindings,
}
),
}
@@ -440,56 +482,146 @@
map!(word, PathSegment::ident)
));
-named!(ty<&str, Ty>, chain!(
- global: tag_s!("::")? ~
- segments: separated_nonempty_list!(tag_s!("::"), path_segment),
- move || Ty::Path(None, Path {
+named!(path<&str, Path>, chain!(
+ global: punct!("::")? ~
+ segments: separated_nonempty_list!(punct!("::"), path_segment),
+ move || Path {
global: global.is_some(),
segments: segments,
- })
+ }
));
-/*
- /// A variable-length array (`[T]`)
- Vec(Box<Ty>),
- /// A fixed length array (`[T; n]`)
- FixedLengthVec(Box<Ty>, usize),
- /// A raw pointer (`*const T` or `*mut T`)
- Ptr(Box<MutTy>),
- /// A reference (`&'a T` or `&'a mut T`)
- Rptr(Option<Lifetime>, Box<MutTy>),
- /// A bare function (e.g. `fn(usize) -> bool`)
- BareFn(Box<BareFnTy>),
- /// The never type (`!`)
- Never,
- /// A tuple (`(A, B, C, D, ...)`)
- Tup(Vec<Ty>),
- /// A path (`module::module::...::Type`), optionally
- /// "qualified", e.g. `<Vec<T> as SomeTrait>::SomeType`.
- ///
- /// Type parameters are stored in the Path itself
- Path(Option<QSelf>, Path),
- /// Something like `A+B`. Note that `B` must always be a path.
- ObjectSum(Box<Ty>, Vec<TyParamBound>),
- /// A type like `for<'a> Foo<&'a Bar>`
- PolyTraitRef(Vec<TyParamBound>),
- /// An `impl TraitA+TraitB` type.
- ImplTrait(Vec<TyParamBound>),
- /// No-op; kept solely so that we can pretty-print faithfully
- Paren(Box<Ty>),
- /// TyKind::Infer means the type should be inferred instead of it having been
- /// specified. This can appear anywhere in a type.
- Infer,
-*/
+named!(fn_arg<&str, Arg>, chain!(
+ pat: opt!(terminated!(word, punct!(":"))) ~
+ ty: ty,
+ move || Arg {
+ pat: pat,
+ ty: ty,
+ }
+));
+
+named!(ty<&str, Ty>, alt!(
+ delimited!(
+ punct!("["),
+ ty,
+ punct!("]")
+ ) => { |elem| Ty::Vec(Box::new(elem)) }
+ |
+ chain!(
+ punct!("[") ~
+ elem: ty ~
+ punct!(";") ~
+ space? ~
+ size: map_res!(digit, str::parse),
+ move || Ty::FixedLengthVec(Box::new(elem), size)
+ )
+ |
+ chain!(
+ punct!("*") ~
+ mutability: alt!(
+ punct!("const") => { |_| Mutability::Immutable }
+ |
+ punct!("mut") => { |_| Mutability::Mutable }
+ ) ~
+ target: ty,
+ move || Ty::Ptr(Box::new(MutTy {
+ ty: target,
+ mutability: mutability,
+ }))
+ )
+ |
+ chain!(
+ punct!("&") ~
+ life: lifetime? ~
+ mutability: mutability ~
+ target: ty,
+ move || Ty::Rptr(life, Box::new(MutTy {
+ ty: target,
+ mutability: mutability,
+ }))
+ )
+ |
+ chain!(
+ punct!("fn") ~
+ space ~
+ lifetimes: opt_vec!(delimited!(
+ punct!("<"),
+ separated_list!(punct!(","), lifetime_def),
+ punct!(">")
+ )) ~
+ punct!("(") ~
+ inputs: separated_list!(punct!(","), fn_arg) ~
+ punct!(")") ~
+ output: opt!(preceded!(
+ punct!("->"),
+ ty
+ )),
+ move || Ty::BareFn(Box::new(BareFnTy {
+ lifetimes: lifetimes,
+ decl: FnDecl {
+ inputs: inputs,
+ output: match output {
+ Some(ty) => FunctionRetTy::Ty(ty),
+ None => FunctionRetTy::Default,
+ },
+ },
+ }))
+ )
+ |
+ punct!("!") => { |_| Ty::Never }
+ |
+ delimited!(
+ punct!("("),
+ separated_list!(punct!(","), ty),
+ punct!(")")
+ ) => { Ty::Tup }
+ |
+ path => { |p| Ty::Path(None, p) }
+ |
+ chain!(
+ punct!("<") ~
+ this: map!(ty, Box::new) ~
+ path: opt!(preceded!(
+ tuple!(punct!("as"), space),
+ path
+ )) ~
+ punct!(">") ~
+ punct!("::") ~
+ rest: separated_nonempty_list!(punct!("::"), path_segment),
+ move || {
+ match path {
+ Some(mut path) => {
+ let pos = path.segments.len();
+ path.segments.extend(rest);
+ Ty::Path(Some(QSelf { ty: this, position: pos }), path)
+ }
+ None => {
+ Ty::Path(Some(QSelf { ty: this, position: 0 }), Path {
+ global: false,
+ segments: rest,
+ })
+ }
+ }
+ }
+ )
+ |
+ preceded!(
+ tuple!(punct!("impl"), space),
+ separated_nonempty_list!(punct!("+"), ty_param_bound)
+ ) => { Ty::ImplTrait }
+ |
+ delimited!(
+ punct!("("),
+ ty,
+ punct!(")")
+ ) => { |inner| Ty::Paren(Box::new(inner)) }
+));
named!(struct_field<&str, Field>, chain!(
attrs: many0!(attribute) ~
- space? ~
vis: visibility ~
ident: word ~
- space? ~
- tag_s!(":") ~
- space? ~
+ punct!(":") ~
ty: ty,
move || Field {
ident: Some(ident),
@@ -501,7 +633,6 @@
named!(tuple_field<&str, Field>, chain!(
attrs: many0!(attribute) ~
- space? ~
vis: visibility ~
ty: ty,
move || Field {
@@ -512,36 +643,40 @@
}
));
+named!(struct_like_body<&str, Vec<Field> >, chain!(
+ punct!("{") ~
+ fields: separated_list!(punct!(","), struct_field) ~
+ punct!(",")? ~
+ punct!("}"),
+ move || fields
+));
+
+named!(tuple_like_body<&str, Vec<Field> >, chain!(
+ punct!("(") ~
+ fields: separated_list!(punct!(","), tuple_field) ~
+ punct!(",")? ~
+ punct!(")"),
+ move || fields
+));
+
named!(struct_body<&str, (Style, Vec<Field>)>, alt!(
- chain!(
- tag_s!("{") ~
- fields: separated_list!(tag_s!(","), struct_field) ~
- space? ~
- tag_s!(",")? ~
- space? ~
- tag_s!("}"),
- move || (Style::Struct, fields)
- )
+ struct_like_body => { |fields| (Style::Struct, fields) }
|
- chain!(
- tag_s!("(") ~
- fields: separated_list!(tag_s!(","), tuple_field) ~
- space? ~
- tag_s!(",")? ~
- space? ~
- tag_s!(")"),
- move || (Style::Tuple, fields)
- )
+ terminated!(tuple_like_body, punct!(";")) => { |fields| (Style::Tuple, fields) }
|
- map!(tag_s!(";"), |_| (Style::Unit, Vec::new()))
+ punct!(";") => { |_| (Style::Unit, Vec::new()) }
));
named!(variant<&str, Variant>, chain!(
attrs: many0!(attribute) ~
- space? ~
ident: word ~
- space? ~
- body: struct_body,
+ body: alt!(
+ struct_like_body => { |fields| (Style::Struct, fields) }
+ |
+ tuple_like_body => { |fields| (Style::Tuple, fields) }
+ |
+ epsilon => { |_| (Style::Unit, Vec::new()) }
+ ),
move || Variant {
ident: ident,
attrs: attrs,
@@ -551,90 +686,131 @@
));
named!(enum_body<&str, Body>, chain!(
- tag_s!("{") ~
- variants: separated_list!(tag_s!(","), variant) ~
- space? ~
- tag_s!(",")? ~
- space? ~
- tag_s!("}"),
+ punct!("{") ~
+ variants: separated_list!(punct!(","), variant) ~
+ punct!(",")? ~
+ punct!("}"),
move || Body::Enum(variants)
));
named!(lifetime<&str, Lifetime>, preceded!(
- tag_s!("'"),
- map!(word, |n| Lifetime { ident: n })
+ punct!("'"),
+ map!(word, |ident| Lifetime { ident: ident })
));
-named!(where_predicate<&str, WherePredicate>, preceded!(
- opt!(space),
- alt!(
- map!(lifetime, |_| unimplemented!())
- |
- map!(word, |_| unimplemented!())
+named!(bound_lifetimes<&str, Vec<LifetimeDef> >, opt_vec!(chain!(
+ punct!("for") ~
+ punct!("<") ~
+ lifetimes: separated_list!(punct!(","), lifetime_def) ~
+ punct!(">"),
+ move || lifetimes
+)));
+
+named!(poly_trait_ref<&str, PolyTraitRef>, chain!(
+ bound_lifetimes: bound_lifetimes ~
+ trait_ref: path,
+ move || PolyTraitRef {
+ bound_lifetimes: bound_lifetimes,
+ trait_ref: trait_ref,
+ }
+));
+
+named!(ty_param_bound<&str, TyParamBound>, alt!(
+ tuple!(punct!("?"), punct!("Sized")) => { |_| TyParamBound::MaybeSized }
+ |
+ lifetime => { TyParamBound::Region }
+ |
+ poly_trait_ref => { TyParamBound::Trait }
+));
+
+named!(where_predicate<&str, WherePredicate>, alt!(
+ chain!(
+ ident: lifetime ~
+ punct!(":") ~
+ bounds: separated_nonempty_list!(punct!("+"), lifetime),
+ move || WherePredicate::RegionPredicate(WhereRegionPredicate {
+ lifetime: ident,
+ bounds: bounds,
+ })
+ )
+ |
+ chain!(
+ bound_lifetimes: bound_lifetimes ~
+ bounded_ty: ty ~
+ punct!(":") ~
+ bounds: separated_nonempty_list!(punct!("+"), ty_param_bound),
+ move || WherePredicate::BoundPredicate(WhereBoundPredicate {
+ bound_lifetimes: bound_lifetimes,
+ bounded_ty: bounded_ty,
+ bounds: bounds,
+ })
)
));
named!(lifetime_def<&str, LifetimeDef>, chain!(
- lifetime: lifetime,
+ life: lifetime ~
+ bounds: opt_vec!(preceded!(
+ punct!(":"),
+ separated_nonempty_list!(punct!(","), lifetime)
+ )),
move || LifetimeDef {
- lifetime: lifetime,
- bounds: Vec::new(),
+ lifetime: life,
+ bounds: bounds,
}
));
named!(ty_param<&str, TyParam>, chain!(
- space? ~
- ident: word,
+ ident: word ~
+ bounds: opt_vec!(preceded!(
+ punct!(":"),
+ separated_nonempty_list!(punct!("+"), ty_param_bound)
+ )) ~
+ default: opt!(preceded!(
+ punct!("="),
+ ty
+ )) ~
move || TyParam {
ident: ident,
- bounds: Vec::new(),
- default: None,
+ bounds: bounds,
+ default: default,
}
));
named!(generics<&str, Generics>, chain!(
- bracketed: map!(
- opt!(chain!(
- space? ~
- tag_s!("<") ~
- lifetimes: separated_list!(tag_s!(","), lifetime_def) ~
- ty_params: opt!(chain!(
- space? ~
- cond!(!lifetimes.is_empty(), tag_s!(",")) ~
- ty_params: separated_nonempty_list!(tag_s!(","), ty_param),
- move || ty_params
+ bracketed: alt!(
+ chain!(
+ punct!("<") ~
+ lifetimes: separated_list!(punct!(","), lifetime_def) ~
+ ty_params: opt_vec!(preceded!(
+ cond!(!lifetimes.is_empty(), punct!(",")),
+ separated_nonempty_list!(punct!(","), ty_param)
)) ~
- space? ~
- tag_s!(">"),
- move || (lifetimes, ty_params.unwrap_or_else(Vec::new))
- )),
- |opt: Option<_>| opt.unwrap_or_else(|| (Vec::new(), Vec::new()))
+ punct!(">"),
+ move || (lifetimes, ty_params)
+ )
+ |
+ epsilon => { |_| (Vec::new(), Vec::new()) }
) ~
- where_clause: opt!(chain!(
- tag_s!("where") ~
+ where_clause: opt_vec!(chain!(
+ punct!("where") ~
space ~
- predicates: separated_nonempty_list!(tag_s!(","), where_predicate) ~
- space? ~
- tag_s!(",")?,
+ predicates: separated_nonempty_list!(punct!(","), where_predicate) ~
+ punct!(",")? ~
move || predicates
)),
move || Generics {
lifetimes: bracketed.0,
ty_params: bracketed.1,
- where_clause: where_clause.unwrap_or_else(Vec::new),
+ where_clause: where_clause,
}
));
named!(item<&str, Item>, chain!(
attrs: many0!(attribute) ~
- space? ~
vis: visibility ~
which: alt!(tag_s!("struct") | tag_s!("enum")) ~
- space ~
ident: word ~
- space? ~
generics: generics ~
- space? ~
item: switch!(value!(which),
"struct" => map!(struct_body, move |(style, fields)| Item {
ident: ident,