More nom
diff --git a/src/lib.rs b/src/lib.rs
index 52eff8e..c9e6e52 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -320,23 +320,88 @@
Ty(Ty),
}
+pub fn escaped_string(input: &str) -> IResult<&str, String> {
+ let mut s = String::new();
+ let mut chars = input.char_indices().peekable();
+ while let Some((byte_offset, ch)) = chars.next() {
+ match ch {
+ '"' => {
+ return IResult::Done(&input[byte_offset..], s);
+ }
+ '\\' => {
+ match chars.next() {
+ Some((_, 'x')) => unimplemented!(),
+ Some((_, 'u')) => unimplemented!(),
+ Some((_, 'n')) => s.push('\n'),
+ Some((_, 'r')) => s.push('\r'),
+ Some((_, 't')) => s.push('\t'),
+ Some((_, '0')) => s.push('\0'),
+ Some((_, '\\')) => s.push('\\'),
+ Some((_, '\n')) => {
+ while let Some(&(_, ch)) = chars.peek() {
+ if ch.is_whitespace() {
+ chars.next();
+ } else {
+ break;
+ }
+ }
+ }
+ _ => break,
+ }
+ }
+ ch => {
+ s.push(ch);
+ }
+ }
+ }
+ IResult::Error(nom::Err::Position(nom::ErrorKind::Escaped, input))
+}
+
+named!(quoted<&str, String>, delimited!(
+ tag_s!("\""),
+ 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!(attribute<&str, Attribute>, chain!(
space? ~
- tag_s!("#[") ~
+ tag_s!("#") ~
space? ~
- first: word ~
+ tag_s!("[") ~
+ meta_item: meta_item ~
space? ~
- tag_s!("(") ~
- inner: separated_list!(tag_s!(", "), word) ~
- tag_s!(")") ~
- space? ~
- tag_s!("]") ,
- || { return Attribute {
- value: MetaItem::List(first.to_string(), inner.into_iter().map(|w|
- MetaItem::Word(w.to_string())
- ).collect()),
- is_sugared_doc: false,
- } }
+ tag_s!("]"),
+ move || Attribute {
+ value: meta_item,
+ is_sugared_doc: false,
+ }
));
named!(visibility<&str, Visibility>,
@@ -359,33 +424,29 @@
tag_s!("<") ~
types: many0!(ty) ~
space? ~
- tag_s!(">") ,
- || {
- PathSegment {
- ident: ident,
- parameters: PathParameters::AngleBracketed(
- AngleBracketedParameterData {
- lifetimes: Vec::new(),
- types: types,
- bindings: Vec::new(),
- }
- ),
- }
+ tag_s!(">"),
+ move || PathSegment {
+ ident: ident,
+ parameters: PathParameters::AngleBracketed(
+ AngleBracketedParameterData {
+ lifetimes: Vec::new(),
+ types: types,
+ bindings: Vec::new(),
+ }
+ ),
}
)
|
- map!(word, |n| PathSegment::ident(n))
+ map!(word, PathSegment::ident)
));
named!(ty<&str, Ty>, chain!(
global: tag_s!("::")? ~
- segments: separated_nonempty_list!(tag_s!("::"), path_segment) ,
- || {
- Ty::Path(None, Path {
- global: global.is_some(),
- segments: segments,
- })
- }
+ segments: separated_nonempty_list!(tag_s!("::"), path_segment),
+ move || Ty::Path(None, Path {
+ global: global.is_some(),
+ segments: segments,
+ })
));
/*
@@ -421,89 +482,207 @@
Infer,
*/
-named!(field<&str, Field>, chain!(
+named!(struct_field<&str, Field>, chain!(
+ attrs: many0!(attribute) ~
space? ~
vis: visibility ~
ident: word ~
space? ~
tag_s!(":") ~
space? ~
- ty: ty ,
- || {
- Field {
- ident: Some(ident),
- vis: vis,
- attrs: Vec::new(),
- ty: ty,
- }
+ ty: ty,
+ move || Field {
+ ident: Some(ident),
+ vis: vis,
+ attrs: attrs,
+ ty: ty,
}
));
-named!(struct_body<&str, Body>, alt!(
+named!(tuple_field<&str, Field>, chain!(
+ attrs: many0!(attribute) ~
+ space? ~
+ vis: visibility ~
+ ty: ty,
+ move || Field {
+ ident: None,
+ vis: vis,
+ attrs: attrs,
+ ty: ty,
+ }
+));
+
+named!(struct_body<&str, (Style, Vec<Field>)>, alt!(
chain!(
tag_s!("{") ~
- fields: separated_list!(tag_s!(","), field) ~
+ fields: separated_list!(tag_s!(","), struct_field) ~
space? ~
tag_s!(",")? ~
space? ~
- tag_s!("}") ,
- || { Body::Struct(Style::Struct, fields) }
+ tag_s!("}"),
+ move || (Style::Struct, fields)
)
|
chain!(
tag_s!("(") ~
- separated_list!(tag_s!(","), chain!(
- space? ~
- vis: visibility ~
- ty: ty ,
- || {}
- )) ~
+ fields: separated_list!(tag_s!(","), tuple_field) ~
space? ~
tag_s!(",")? ~
space? ~
- tag_s!(")") ,
- || { Body::Struct(Style::Tuple, Vec::new()) }
+ tag_s!(")"),
+ move || (Style::Tuple, fields)
)
|
- map!(tag_s!(";"), |_| Body::Struct(Style::Unit, Vec::new()))
+ map!(tag_s!(";"), |_| (Style::Unit, Vec::new()))
+));
+
+named!(variant<&str, Variant>, chain!(
+ attrs: many0!(attribute) ~
+ space? ~
+ ident: word ~
+ space? ~
+ body: struct_body,
+ move || Variant {
+ ident: ident,
+ attrs: attrs,
+ style: body.0,
+ fields: body.1,
+ }
+));
+
+named!(enum_body<&str, Body>, chain!(
+ tag_s!("{") ~
+ variants: separated_list!(tag_s!(","), variant) ~
+ space? ~
+ tag_s!(",")? ~
+ space? ~
+ tag_s!("}"),
+ move || Body::Enum(variants)
+));
+
+named!(lifetime<&str, Lifetime>, preceded!(
+ tag_s!("'"),
+ map!(word, |n| Lifetime { ident: n })
+));
+
+named!(where_predicate<&str, WherePredicate>, preceded!(
+ opt!(space),
+ alt!(
+ map!(lifetime, |_| unimplemented!())
+ |
+ map!(word, |_| unimplemented!())
+ )
+));
+
+named!(lifetime_def<&str, LifetimeDef>, chain!(
+ lifetime: lifetime,
+ move || LifetimeDef {
+ lifetime: lifetime,
+ bounds: Vec::new(),
+ }
+));
+
+named!(ty_param<&str, TyParam>, chain!(
+ space? ~
+ ident: word,
+ move || TyParam {
+ ident: ident,
+ bounds: Vec::new(),
+ default: None,
+ }
+));
+
+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
+ )) ~
+ space? ~
+ tag_s!(">"),
+ move || (lifetimes, ty_params.unwrap_or_else(Vec::new))
+ )),
+ |opt: Option<_>| opt.unwrap_or_else(|| (Vec::new(), Vec::new()))
+ ) ~
+ where_clause: opt!(chain!(
+ tag_s!("where") ~
+ space ~
+ predicates: separated_nonempty_list!(tag_s!(","), where_predicate) ~
+ space? ~
+ tag_s!(",")?,
+ move || predicates
+ )),
+ move || Generics {
+ lifetimes: bracketed.0,
+ ty_params: bracketed.1,
+ where_clause: where_clause.unwrap_or_else(Vec::new),
+ }
));
named!(item<&str, Item>, chain!(
attrs: many0!(attribute) ~
space? ~
vis: visibility ~
- tag_s!("struct") ~
+ which: alt!(tag_s!("struct") | tag_s!("enum")) ~
space ~
ident: word ~
space? ~
- body: struct_body ~
- space? ,
- || { return Item {
+ generics: generics ~
+ space? ~
+ item: switch!(value!(which),
+ "struct" => map!(struct_body, move |(style, fields)| Item {
ident: ident,
vis: vis,
attrs: attrs,
- generics: Generics::default(),
+ generics: generics,
+ body: Body::Struct(style, fields),
+ })
+ |
+ "enum" => map!(enum_body, move |body| Item {
+ ident: ident,
+ vis: vis,
+ attrs: attrs,
+ generics: generics,
body: body,
- } }
+ })
+ ) ~
+ space?,
+ move || item
));
-pub fn parse(input: &str) -> Result<Item, String> {
+pub fn parse(input: &str) -> Item {
match item(input) {
IResult::Done(rest, ast) => {
if rest.is_empty() {
- Ok(ast)
+ ast
} else {
- Err(format!("more than a single input item: {:?}", rest))
+ panic!("more than a single input item: {:?}", rest)
}
}
- IResult::Error(nom::Err::Code(kind)) |
- IResult::Error(nom::Err::Node(kind, _)) => {
- Err(format!("failed to parse {:?}", kind))
+ IResult::Error(err) => raise(err),
+ IResult::Incomplete(_) => panic!("incomplete input item"),
+ }
+}
+
+fn raise(mut err: nom::Err<&str>) -> ! {
+ loop {
+ match err {
+ nom::Err::Code(kind) => {
+ panic!("failed to parse {:?}", kind)
+ }
+ nom::Err::Position(kind, pos) => {
+ panic!("failed to parse {:?}: {:?}", kind, pos)
+ }
+ nom::Err::Node(_, next) |
+ nom::Err::NodePosition(_, _, next) => {
+ err = *next;
+ }
}
- IResult::Error(nom::Err::Position(kind, pos)) |
- IResult::Error(nom::Err::NodePosition(kind, pos, _)) => {
- Err(format!("failed to parse {:?}: {:?}", kind, pos))
- }
- IResult::Incomplete(_) => Err("incomplete input item".to_string()),
}
}