Parse based on proc-macro2
diff --git a/Cargo.toml b/Cargo.toml
index b3d0b53..a37a992 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "syn"
-version = "0.11.11" # don't forget to update version in readme for breaking changes
+version = "0.12.0" # don't forget to update version in readme for breaking changes
authors = ["David Tolnay <dtolnay@gmail.com>"]
license = "MIT/Apache-2.0"
description = "Nom parser for Rust source code"
@@ -13,15 +13,15 @@
default = ["parsing", "printing"]
aster = []
full = []
-parsing = ["unicode-xid", "synom"]
+parsing = ["synom", "relex"]
printing = ["quote"]
visit = []
fold = []
[dependencies]
quote = { version = "0.3.7", optional = true }
-unicode-xid = { version = "0.0.4", optional = true }
-synom = { version = "0.11", path = "synom", optional = true }
+synom = { version = "0.12", path = "synom", optional = true }
+relex = { git = "https://github.com/mystor/relex.git", optional = true }
[dev-dependencies]
syntex_pos = "0.58"
diff --git a/README.md b/README.md
index 6273b44..5a0551a 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@
```toml
[dependencies]
-syn = "0.11"
+syn = "0.12"
quote = "0.3"
[lib]
diff --git a/src/attr.rs b/src/attr.rs
index 0a2b016..fa550dd 100644
--- a/src/attr.rs
+++ b/src/attr.rs
@@ -210,19 +210,43 @@
use lit::{Lit, StrStyle};
use mac::{Token, TokenTree};
use mac::parsing::token_trees;
- use synom::space::{block_comment, whitespace};
use ty::parsing::mod_style_path;
+ use synom::{TokenKind, IResult};
+
+ pub fn inner_doc_comment(i: &[synom::TokenTree]) -> IResult<&[synom::TokenTree], String> {
+ match i.first() {
+ Some(&synom::TokenTree { kind: TokenKind::Literal(ref lit), .. }) => {
+ let s = lit.to_string();
+ if s.starts_with("//!") || s.starts_with("/*!") {
+ IResult::Done(&i[1..], s)
+ } else {
+ IResult::Error
+ }
+ }
+ _ => IResult::Error,
+ }
+ }
+
+ pub fn outer_doc_comment(i: &[synom::TokenTree]) -> IResult<&[synom::TokenTree], String> {
+ match i.first() {
+ Some(&synom::TokenTree { kind: TokenKind::Literal(ref lit), .. }) => {
+ let s = lit.to_string();
+ if s.starts_with("///") || s.starts_with("/**") {
+ IResult::Done(&i[1..], s)
+ } else {
+ IResult::Error
+ }
+ }
+ _ => IResult::Error,
+ }
+ }
#[cfg(feature = "full")]
named!(pub inner_attr -> Attribute, alt!(
do_parse!(
punct!("#") >>
punct!("!") >>
- path_and_tts: delimited!(
- punct!("["),
- tuple!(mod_style_path, token_trees),
- punct!("]")
- ) >>
+ path_and_tts: delim!(Bracket, tuple!(mod_style_path, token_trees)) >>
({
let (path, tts) = path_and_tts;
@@ -236,29 +260,13 @@
)
|
do_parse!(
- punct!("//!") >>
- content: take_until!("\n") >>
+ comment: inner_doc_comment >>
(Attribute {
style: AttrStyle::Inner,
path: "doc".into(),
tts: vec![
TokenTree::Token(Token::Eq),
- TokenTree::Token(Token::Literal(Lit::Str(format!("//!{}", content).into(), StrStyle::Cooked))),
- ],
- is_sugared_doc: true,
- })
- )
- |
- do_parse!(
- option!(whitespace) >>
- peek!(tag!("/*!")) >>
- com: block_comment >>
- (Attribute {
- style: AttrStyle::Inner,
- path: "doc".into(),
- tts: vec![
- TokenTree::Token(Token::Eq),
- TokenTree::Token(Token::Literal(Lit::Str(com.into(), StrStyle::Cooked))),
+ TokenTree::Token(Token::Literal(Lit::Str(comment.into(), StrStyle::Cooked))),
],
is_sugared_doc: true,
})
@@ -268,11 +276,7 @@
named!(pub outer_attr -> Attribute, alt!(
do_parse!(
punct!("#") >>
- path_and_tts: delimited!(
- punct!("["),
- tuple!(mod_style_path, token_trees),
- punct!("]")
- ) >>
+ path_and_tts: delim!(Bracket, tuple!(mod_style_path, token_trees)) >>
({
let (path, tts) = path_and_tts;
@@ -286,30 +290,13 @@
)
|
do_parse!(
- punct!("///") >>
- not!(tag!("/")) >>
- content: take_until!("\n") >>
+ comment: outer_doc_comment >>
(Attribute {
style: AttrStyle::Outer,
path: "doc".into(),
tts: vec![
TokenTree::Token(Token::Eq),
- TokenTree::Token(Token::Literal(Lit::Str(format!("///{}", content).into(), StrStyle::Cooked))),
- ],
- is_sugared_doc: true,
- })
- )
- |
- do_parse!(
- option!(whitespace) >>
- peek!(tuple!(tag!("/**"), not!(tag!("*")))) >>
- com: block_comment >>
- (Attribute {
- style: AttrStyle::Outer,
- path: "doc".into(),
- tts: vec![
- TokenTree::Token(Token::Eq),
- TokenTree::Token(Token::Literal(Lit::Str(com.into(), StrStyle::Cooked))),
+ TokenTree::Token(Token::Literal(Lit::Str(comment.into(), StrStyle::Cooked))),
],
is_sugared_doc: true,
})
diff --git a/src/constant.rs b/src/constant.rs
index ad0d325..037b524 100644
--- a/src/constant.rs
+++ b/src/constant.rs
@@ -89,9 +89,7 @@
));
named!(and_call -> Vec<ConstExpr>, do_parse!(
- punct!("(") >>
- args: terminated_list!(punct!(","), const_expr) >>
- punct!(")") >>
+ args: delim!(Parenthesis, terminated_list!(punct!(","), const_expr)) >>
(args)
));
@@ -107,12 +105,10 @@
named!(expr_path -> ConstExpr, map!(path, ConstExpr::Path));
- named!(and_index -> ConstExpr, delimited!(punct!("["), const_expr, punct!("]")));
+ named!(and_index -> ConstExpr, delim!(Brace, const_expr));
named!(expr_paren -> ConstExpr, do_parse!(
- punct!("(") >>
- e: const_expr >>
- punct!(")") >>
+ e: delim!(Parenthesis, const_expr) >>
(ConstExpr::Paren(Box::new(e)))
));
diff --git a/src/data.rs b/src/data.rs
index 69df303..2174617 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -122,9 +122,7 @@
named!(pub enum_body -> (WhereClause, Vec<Variant>), do_parse!(
wh: where_clause >>
- punct!("{") >>
- variants: terminated_list!(punct!(","), variant) >>
- punct!("}") >>
+ variants: delim!(Brace, terminated_list!(punct!(","), variant)) >>
(wh, variants)
));
@@ -157,20 +155,26 @@
terminated!(expr, after_discriminant) => { ConstExpr::Other }
));
+ // XXX: HACKY
#[cfg(feature = "full")]
- named!(after_discriminant -> &str, peek!(alt!(punct!(",") | punct!("}"))));
+ pub fn eof(input: &[synom::TokenTree]) -> synom::IResult<&[synom::TokenTree], &'static str> {
+ if input.is_empty() {
+ synom::IResult::Done(&[], "")
+ } else {
+ synom::IResult::Error
+ }
+ }
+
+ #[cfg(feature = "full")]
+ named!(after_discriminant -> &str, peek!(alt!(punct!(",") | input_end!())));
named!(pub struct_like_body -> Vec<Field>, do_parse!(
- punct!("{") >>
- fields: terminated_list!(punct!(","), struct_field) >>
- punct!("}") >>
+ fields: delim!(Brace, terminated_list!(punct!(","), struct_field)) >>
(fields)
));
named!(tuple_like_body -> Vec<Field>, do_parse!(
- punct!("(") >>
- fields: terminated_list!(punct!(","), tuple_field) >>
- punct!(")") >>
+ fields: delim!(Parenthesis, terminated_list!(punct!(","), tuple_field)) >>
(fields)
));
@@ -203,34 +207,25 @@
named!(pub visibility -> Visibility, alt!(
do_parse!(
keyword!("pub") >>
- punct!("(") >>
- keyword!("crate") >>
- punct!(")") >>
+ delim!(Parenthesis, keyword!("crate")) >>
(Visibility::Crate)
)
|
do_parse!(
keyword!("pub") >>
- punct!("(") >>
- keyword!("self") >>
- punct!(")") >>
+ delim!(Parenthesis, keyword!("self")) >>
(Visibility::Restricted(Box::new("self".into())))
)
|
do_parse!(
keyword!("pub") >>
- punct!("(") >>
- keyword!("super") >>
- punct!(")") >>
+ delim!(Parenthesis, keyword!("super")) >>
(Visibility::Restricted(Box::new("super".into())))
)
|
do_parse!(
keyword!("pub") >>
- punct!("(") >>
- keyword!("in") >>
- restricted: mod_style_path >>
- punct!(")") >>
+ restricted: delim!(Parenthesis, preceded!(keyword!("in"), mod_style_path)) >>
(Visibility::Restricted(Box::new(restricted)))
)
|
diff --git a/src/escape.rs b/src/escape.rs
deleted file mode 100644
index c17a158..0000000
--- a/src/escape.rs
+++ /dev/null
@@ -1,294 +0,0 @@
-use std::{char, str};
-use std::num::ParseIntError;
-use synom::IResult;
-
-pub fn cooked_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);
- }
- '\r' => {
- if let Some((_, '\n')) = chars.next() {
- s.push('\n');
- } else {
- break;
- }
- }
- '\\' => {
- match chars.next() {
- Some((_, 'x')) => {
- match backslash_x_char(&mut chars) {
- Some(ch) => s.push(ch),
- None => break,
- }
- }
- Some((_, 'n')) => s.push('\n'),
- Some((_, 'r')) => s.push('\r'),
- Some((_, 't')) => s.push('\t'),
- Some((_, '\\')) => s.push('\\'),
- Some((_, '0')) => s.push('\0'),
- Some((_, 'u')) => {
- match backslash_u(&mut chars) {
- Some(ch) => s.push(ch),
- None => break,
- }
- }
- Some((_, '\'')) => s.push('\''),
- Some((_, '"')) => s.push('"'),
- Some((_, '\n')) | Some((_, '\r')) => {
- while let Some(&(_, ch)) = chars.peek() {
- if ch.is_whitespace() {
- chars.next();
- } else {
- break;
- }
- }
- }
- _ => break,
- }
- }
- ch => {
- s.push(ch);
- }
- }
- }
- IResult::Error
-}
-
-pub fn cooked_byte_string(mut input: &str) -> IResult<&str, Vec<u8>> {
- let mut vec = Vec::new();
- let mut bytes = input.bytes().enumerate();
- 'outer: while let Some((offset, b)) = bytes.next() {
- match b {
- b'"' => {
- return IResult::Done(&input[offset..], vec);
- }
- b'\r' => {
- if let Some((_, b'\n')) = bytes.next() {
- vec.push(b'\n');
- } else {
- break;
- }
- }
- b'\\' => {
- match bytes.next() {
- Some((_, b'x')) => {
- match backslash_x_byte(&mut bytes) {
- Some(b) => vec.push(b),
- None => break,
- }
- }
- Some((_, b'n')) => vec.push(b'\n'),
- Some((_, b'r')) => vec.push(b'\r'),
- Some((_, b't')) => vec.push(b'\t'),
- Some((_, b'\\')) => vec.push(b'\\'),
- Some((_, b'0')) => vec.push(b'\0'),
- Some((_, b'\'')) => vec.push(b'\''),
- Some((_, b'"')) => vec.push(b'"'),
- Some((newline, b'\n')) |
- Some((newline, b'\r')) => {
- let rest = &input[newline + 1..];
- for (offset, ch) in rest.char_indices() {
- if !ch.is_whitespace() {
- input = &rest[offset..];
- bytes = input.bytes().enumerate();
- continue 'outer;
- }
- }
- break;
- }
- _ => break,
- }
- }
- b if b < 0x80 => {
- vec.push(b);
- }
- _ => break,
- }
- }
- IResult::Error
-}
-
-pub fn cooked_char(input: &str) -> IResult<&str, char> {
- let mut chars = input.char_indices();
- let ch = match chars.next().map(|(_, ch)| ch) {
- Some('\\') => {
- match chars.next().map(|(_, ch)| ch) {
- Some('x') => backslash_x_char(&mut chars),
- Some('n') => Some('\n'),
- Some('r') => Some('\r'),
- Some('t') => Some('\t'),
- Some('\\') => Some('\\'),
- Some('0') => Some('\0'),
- Some('u') => backslash_u(&mut chars),
- Some('\'') => Some('\''),
- Some('"') => Some('"'),
- _ => None,
- }
- }
- ch => ch,
- };
- match ch {
- Some(ch) => IResult::Done(chars.as_str(), ch),
- None => IResult::Error,
- }
-}
-
-pub fn cooked_byte(input: &str) -> IResult<&str, u8> {
- let mut bytes = input.bytes().enumerate();
- let b = match bytes.next().map(|(_, b)| b) {
- Some(b'\\') => {
- match bytes.next().map(|(_, b)| b) {
- Some(b'x') => backslash_x_byte(&mut bytes),
- Some(b'n') => Some(b'\n'),
- Some(b'r') => Some(b'\r'),
- Some(b't') => Some(b'\t'),
- Some(b'\\') => Some(b'\\'),
- Some(b'0') => Some(b'\0'),
- Some(b'\'') => Some(b'\''),
- Some(b'"') => Some(b'"'),
- _ => None,
- }
- }
- b => b,
- };
- match b {
- Some(b) => {
- match bytes.next() {
- Some((offset, _)) => IResult::Done(&input[offset..], b),
- None => IResult::Done("", b),
- }
- }
- None => IResult::Error,
- }
-}
-
-pub fn raw_string(input: &str) -> IResult<&str, (String, usize)> {
- let mut chars = input.char_indices();
- let mut n = 0;
- while let Some((byte_offset, ch)) = chars.next() {
- match ch {
- '"' => {
- n = byte_offset;
- break;
- }
- '#' => {}
- _ => return IResult::Error,
- }
- }
- let mut s = String::new();
- for (byte_offset, ch) in chars {
- match ch {
- '"' if input[byte_offset + 1..].starts_with(&input[..n]) => {
- let rest = &input[byte_offset + 1 + n..];
- return IResult::Done(rest, (s, n));
- }
- '\r' => {}
- _ => s.push(ch),
- }
- }
- IResult::Error
-}
-
-macro_rules! next_ch {
- ($chars:ident @ $pat:pat $(| $rest:pat)*) => {
- match $chars.next() {
- Some((_, ch)) => match ch {
- $pat $(| $rest)* => ch,
- _ => return None,
- },
- None => return None,
- }
- };
-}
-
-trait FromStrRadix: Sized {
- fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError>;
-}
-
-impl FromStrRadix for u8 {
- fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
- u8::from_str_radix(src, radix)
- }
-}
-
-impl FromStrRadix for u32 {
- fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
- u32::from_str_radix(src, radix)
- }
-}
-
-macro_rules! from_hex {
- ($($ch:ident)+) => {{
- let hex_bytes = &[$($ch as u8),*];
- let hex_str = str::from_utf8(hex_bytes).unwrap();
- FromStrRadix::from_str_radix(hex_str, 16).unwrap()
- }};
-}
-
-#[cfg_attr(feature = "cargo-clippy", allow(diverging_sub_expression))]
-fn backslash_x_char<I>(chars: &mut I) -> Option<char>
- where I: Iterator<Item = (usize, char)>
-{
- let a = next_ch!(chars @ '0'...'7');
- let b = next_ch!(chars @ '0'...'9' | 'a'...'f' | 'A'...'F');
- char::from_u32(from_hex!(a b))
-}
-
-#[cfg_attr(feature = "cargo-clippy", allow(diverging_sub_expression))]
-fn backslash_x_byte<I>(chars: &mut I) -> Option<u8>
- where I: Iterator<Item = (usize, u8)>
-{
- let a = next_ch!(chars @ b'0'...b'9' | b'a'...b'f' | b'A'...b'F');
- let b = next_ch!(chars @ b'0'...b'9' | b'a'...b'f' | b'A'...b'F');
- Some(from_hex!(a b))
-}
-
-#[cfg_attr(feature = "cargo-clippy", allow(diverging_sub_expression, many_single_char_names))]
-fn backslash_u<I>(chars: &mut I) -> Option<char>
- where I: Iterator<Item = (usize, char)>
-{
- next_ch!(chars @ '{');
- let a = next_ch!(chars @ '0'...'9' | 'a'...'f' | 'A'...'F');
- let b = next_ch!(chars @ '0'...'9' | 'a'...'f' | 'A'...'F' | '}');
- if b == '}' {
- return char::from_u32(from_hex!(a));
- }
- let c = next_ch!(chars @ '0'...'9' | 'a'...'f' | 'A'...'F' | '}');
- if c == '}' {
- return char::from_u32(from_hex!(a b));
- }
- let d = next_ch!(chars @ '0'...'9' | 'a'...'f' | 'A'...'F' | '}');
- if d == '}' {
- return char::from_u32(from_hex!(a b c));
- }
- let e = next_ch!(chars @ '0'...'9' | 'a'...'f' | 'A'...'F' | '}');
- if e == '}' {
- return char::from_u32(from_hex!(a b c d));
- }
- let f = next_ch!(chars @ '0'...'9' | 'a'...'f' | 'A'...'F' | '}');
- if f == '}' {
- return char::from_u32(from_hex!(a b c d e));
- }
- next_ch!(chars @ '}');
- char::from_u32(from_hex!(a b c d e f))
-}
-
-#[test]
-fn test_cooked_string() {
- let input = "\\x62 \\\n \\u{7} \\u{64} \\u{bf5} \\u{12ba} \\u{1F395} \\u{102345}\"";
- let expected = "\x62 \u{7} \u{64} \u{bf5} \u{12ba} \u{1F395} \u{102345}";
- assert_eq!(cooked_string(input),
- IResult::Done("\"", expected.to_string()));
-}
-
-#[test]
-fn test_cooked_byte_string() {
- let input = "\\x62 \\\n \\xEF\"";
- let expected = b"\x62 \xEF";
- assert_eq!(cooked_byte_string(input),
- IResult::Done("\"", expected.to_vec()));
-}
diff --git a/src/expr.rs b/src/expr.rs
index fae3e65..90b6538 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -369,15 +369,16 @@
use item::parsing::item;
use lit::parsing::{digits, lit};
use mac::parsing::{mac, token_trees};
- use synom::IResult::{self, Error};
use op::parsing::{assign_op, binop, unop};
use ty::parsing::{mutability, path, qpath, ty, unsafety};
+ use synom::{self, IResult};
// Struct literals are ambiguous in certain positions
// https://github.com/rust-lang/rfcs/pull/92
macro_rules! named_ambiguous_expr {
($name:ident -> $o:ty, $allow_struct:ident, $submac:ident!( $($args:tt)* )) => {
- fn $name(i: &str, $allow_struct: bool) -> $crate::synom::IResult<&str, $o> {
+ fn $name(i: &[$crate::synom::TokenTree], $allow_struct: bool)
+ -> $crate::synom::IResult<&[$crate::synom::TokenTree], $o> {
$submac!(i, $($args)*)
}
};
@@ -394,7 +395,10 @@
named!(expr_no_struct -> Expr, ambiguous_expr!(false));
#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
- fn ambiguous_expr(i: &str, allow_struct: bool, allow_block: bool) -> IResult<&str, Expr> {
+ fn ambiguous_expr(i: &[synom::TokenTree],
+ allow_struct: bool,
+ allow_block: bool)
+ -> IResult<&[synom::TokenTree], Expr> {
do_parse!(
i,
mut e: alt!(
@@ -507,9 +511,7 @@
named!(expr_mac -> ExprKind, map!(mac, ExprKind::Mac));
named!(expr_paren -> ExprKind, do_parse!(
- punct!("(") >>
- e: expr >>
- punct!(")") >>
+ e: delim!(Parenthesis, expr) >>
(ExprKind::Paren(Box::new(e)))
));
@@ -522,9 +524,7 @@
named!(expr_in_place -> ExprKind, do_parse!(
keyword!("in") >>
place: expr_no_struct >>
- punct!("{") >>
- value: within_block >>
- punct!("}") >>
+ value: delim!(Brace, within_block) >>
(ExprKind::InPlace(
Box::new(place),
Box::new(ExprKind::Block(Unsafety::Normal, Block {
@@ -533,17 +533,13 @@
))
));
- named!(expr_array -> ExprKind, do_parse!(
- punct!("[") >>
+ named!(expr_array -> ExprKind, delim!(Bracket, do_parse!(
elems: terminated_list!(punct!(","), expr) >>
- punct!("]") >>
(ExprKind::Array(elems))
- ));
+ )));
named!(and_call -> Vec<Expr>, do_parse!(
- punct!("(") >>
- args: terminated_list!(punct!(","), expr) >>
- punct!(")") >>
+ args: delim!(Parenthesis, terminated_list!(punct!(","), expr)) >>
(args)
));
@@ -558,16 +554,12 @@
punct!(">")
)
)) >>
- punct!("(") >>
- args: terminated_list!(punct!(","), expr) >>
- punct!(")") >>
+ args: delim!(Parenthesis, terminated_list!(punct!(","), expr)) >>
(method, ascript, args)
));
named!(expr_tup -> ExprKind, do_parse!(
- punct!("(") >>
- elems: terminated_list!(punct!(","), expr) >>
- punct!(")") >>
+ elems: delim!(Parenthesis, terminated_list!(punct!(","), expr)) >>
(ExprKind::Tup(elems))
));
@@ -612,18 +604,14 @@
named!(expr_if -> ExprKind, do_parse!(
keyword!("if") >>
cond: cond >>
- punct!("{") >>
- then_block: within_block >>
- punct!("}") >>
+ then_block: delim!(Brace, within_block) >>
else_block: option!(preceded!(
keyword!("else"),
alt!(
expr_if
|
do_parse!(
- punct!("{") >>
- else_block: within_block >>
- punct!("}") >>
+ else_block: delim!(Brace, within_block) >>
(ExprKind::Block(Unsafety::Normal, Block {
stmts: else_block,
}).into())
@@ -669,19 +657,20 @@
named!(expr_match -> ExprKind, do_parse!(
keyword!("match") >>
obj: expr_no_struct >>
- punct!("{") >>
- mut arms: many0!(do_parse!(
- arm: match_arm >>
- cond!(arm_requires_comma(&arm), punct!(",")) >>
- cond!(!arm_requires_comma(&arm), option!(punct!(","))) >>
- (arm)
+ res: delim!(Brace, do_parse!(
+ mut arms: many0!(do_parse!(
+ arm: match_arm >>
+ cond!(arm_requires_comma(&arm), punct!(",")) >>
+ cond!(!arm_requires_comma(&arm), option!(punct!(","))) >>
+ (arm)
+ )) >>
+ last_arm: option!(match_arm) >>
+ (ExprKind::Match(Box::new(obj), {
+ arms.extend(last_arm);
+ arms
+ }))
)) >>
- last_arm: option!(match_arm) >>
- punct!("}") >>
- (ExprKind::Match(Box::new(obj), {
- arms.extend(last_arm);
- arms
- }))
+ (res)
));
named!(expr_catch -> ExprKind, do_parse!(
@@ -790,17 +779,18 @@
named!(expr_struct -> ExprKind, do_parse!(
path: path >>
- punct!("{") >>
- fields: separated_list!(punct!(","), field_value) >>
- base: option!(do_parse!(
- cond!(!fields.is_empty(), punct!(",")) >>
- punct!("..") >>
- base: expr >>
- (base)
+ res: delim!(Brace, do_parse!(
+ fields: separated_list!(punct!(","), field_value) >>
+ base: option!(do_parse!(
+ cond!(!fields.is_empty(), punct!(",")) >>
+ punct!("..") >>
+ base: expr >>
+ (base)
+ )) >>
+ cond!(!fields.is_empty() && base.is_none(), option!(punct!(","))) >>
+ (ExprKind::Struct(path, fields, base.map(Box::new)))
)) >>
- cond!(!fields.is_empty() && base.is_none(), option!(punct!(","))) >>
- punct!("}") >>
- (ExprKind::Struct(path, fields, base.map(Box::new)))
+ (res)
));
named!(field_value -> FieldValue, alt!(
@@ -824,14 +814,12 @@
})
));
- named!(expr_repeat -> ExprKind, do_parse!(
- punct!("[") >>
+ named!(expr_repeat -> ExprKind, delim!(Bracket, do_parse!(
value: expr >>
punct!(";") >>
times: expr >>
- punct!("]") >>
(ExprKind::Repeat(Box::new(value), Box::new(times)))
- ));
+ )));
named!(expr_block -> ExprKind, do_parse!(
rules: unsafety >>
@@ -876,7 +864,7 @@
named!(and_tup_field -> u64, preceded!(punct!("."), digits));
- named!(and_index -> Expr, delimited!(punct!("["), expr, punct!("]")));
+ named!(and_index -> Expr, delim!(Bracket, expr));
named_ambiguous_expr!(and_range -> (RangeLimits, Option<Expr>), allow_struct, tuple!(
range_limits,
@@ -884,9 +872,7 @@
));
named!(pub block -> Block, do_parse!(
- punct!("{") >>
- stmts: within_block >>
- punct!("}") >>
+ stmts: delim!(Brace, within_block) >>
(Block {
stmts: stmts,
})
@@ -921,9 +907,7 @@
punct!("!") >>
// Only parse braces here; paren and bracket will get parsed as
// expression statements
- punct!("{") >>
- tts: token_trees >>
- punct!("}") >>
+ tts: delim!(Brace, token_trees) >>
semi: option!(punct!(";")) >>
(Stmt::Mac(Box::new((
Mac {
@@ -983,7 +967,7 @@
if semi.is_some() {
Stmt::Semi(Box::new(e))
} else if requires_semi(&e) {
- return Error;
+ return IResult::Error;
} else {
Stmt::Expr(Box::new(e))
}
@@ -1056,15 +1040,16 @@
named!(pat_struct -> Pat, do_parse!(
path: path >>
- punct!("{") >>
- fields: separated_list!(punct!(","), field_pat) >>
- more: option!(preceded!(
- cond!(!fields.is_empty(), punct!(",")),
- punct!("..")
+ res: delim!(Brace, do_parse!(
+ fields: separated_list!(punct!(","), field_pat) >>
+ more: option!(preceded!(
+ cond!(!fields.is_empty(), punct!(",")),
+ punct!("..")
+ )) >>
+ cond!(!fields.is_empty() && more.is_none(), option!(punct!(","))) >>
+ (Pat::Struct(path, fields, more.is_some()))
)) >>
- cond!(!fields.is_empty() && more.is_none(), option!(punct!(","))) >>
- punct!("}") >>
- (Pat::Struct(path, fields, more.is_some()))
+ (res)
));
named!(field_pat -> FieldPat, alt!(
@@ -1115,8 +1100,7 @@
|(pats, dotdot)| Pat::Tuple(pats, dotdot)
));
- named!(pat_tuple_helper -> (Vec<Pat>, Option<usize>), do_parse!(
- punct!("(") >>
+ named!(pat_tuple_helper -> (Vec<Pat>, Option<usize>), delim!(Parenthesis, do_parse!(
mut elems: separated_list!(punct!(","), pat) >>
dotdot: option!(do_parse!(
cond!(!elems.is_empty(), punct!(",")) >>
@@ -1126,7 +1110,6 @@
(rest)
)) >>
cond!(!elems.is_empty() && dotdot.is_none(), option!(punct!(","))) >>
- punct!(")") >>
(match dotdot {
Some(rest) => {
let pos = elems.len();
@@ -1135,7 +1118,7 @@
}
None => (elems, None),
})
- ));
+ )));
named!(pat_ref -> Pat, do_parse!(
punct!("&") >>
@@ -1174,8 +1157,7 @@
})
));
- named!(pat_slice -> Pat, do_parse!(
- punct!("[") >>
+ named!(pat_slice -> Pat, delim!(Bracket, do_parse!(
mut before: separated_list!(punct!(","), pat) >>
after: option!(do_parse!(
comma_before_dots: option!(cond_reduce!(!before.is_empty(), punct!(","))) >>
@@ -1185,7 +1167,6 @@
(comma_before_dots.is_some(), after)
)) >>
cond!(after.is_none(), option!(punct!(","))) >>
- punct!("]") >>
(match after {
None => Pat::Slice(before, None, Vec::new()),
Some((true, after)) => {
@@ -1199,7 +1180,7 @@
Pat::Slice(before, Some(Box::new(rest)), after)
}
})
- ));
+ )));
named!(capture_by -> CaptureBy, alt!(
keyword!("move") => { |_| CaptureBy::Value }
diff --git a/src/generics.rs b/src/generics.rs
index fbb89b1..2e3b7b9 100644
--- a/src/generics.rs
+++ b/src/generics.rs
@@ -191,6 +191,7 @@
use attr::parsing::outer_attr;
use ident::parsing::ident;
use ty::parsing::{ty, poly_trait_ref};
+ use synom::{TokenTree, IResult};
named!(pub generics -> Generics, map!(
alt!(
@@ -215,18 +216,21 @@
}
));
- named!(pub lifetime -> Lifetime, preceded!(
- punct!("'"),
- alt!(
- map!(ident, |id| Lifetime {
- ident: format!("'{}", id).into(),
- })
- |
- map!(keyword!("static"), |_| Lifetime {
- ident: "'static".into(),
- })
- )
- ));
+ pub fn lifetime(input: &[TokenTree]) -> IResult<&[TokenTree], Lifetime> {
+ use synom::*;
+ if let Some(&TokenTree { kind: TokenKind::Word(ref id), .. }) = input.first() {
+ // Check if this word is _actually_ a lifetime, and treat that differently
+ if id.chars().next().unwrap() == '\'' {
+ IResult::Done(&input[1..], Lifetime {
+ ident: id.to_string().into()
+ })
+ } else {
+ IResult::Error
+ }
+ } else {
+ IResult::Error
+ }
+ }
named!(pub lifetime_def -> LifetimeDef, do_parse!(
attrs: many0!(outer_attr) >>
diff --git a/src/ident.rs b/src/ident.rs
index 8f698be..fbdce4f 100644
--- a/src/ident.rs
+++ b/src/ident.rs
@@ -57,63 +57,47 @@
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
- use synom::IResult;
- use synom::space::skip_whitespace;
- use unicode_xid::UnicodeXID;
+ use synom::{TokenTree, TokenKind, IResult};
+ #[cfg(feature = "full")]
+ use lit::parsing::int;
- pub fn ident(input: &str) -> IResult<&str, Ident> {
- let (rest, id) = match word(input) {
- IResult::Done(rest, id) => (rest, id),
- IResult::Error => return IResult::Error,
- };
-
- match id.as_ref() {
- // From https://doc.rust-lang.org/grammar.html#keywords
- "abstract" | "alignof" | "as" | "become" | "box" | "break" | "const" | "continue" |
- "crate" | "do" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" |
- "if" | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" |
- "mut" | "offsetof" | "override" | "priv" | "proc" | "pub" | "pure" | "ref" |
- "return" | "Self" | "self" | "sizeof" | "static" | "struct" | "super" | "trait" |
- "true" | "type" | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" |
- "while" | "yield" => IResult::Error,
- _ => IResult::Done(rest, id),
+ pub fn ident(input: &[TokenTree]) -> IResult<&[TokenTree], Ident> {
+ if let IResult::Done(rest, id) = word(input) {
+ match id.as_ref() {
+ // From https://doc.rust-lang.org/grammar.html#keywords
+ "abstract" | "alignof" | "as" | "become" | "box" | "break" | "const" | "continue" |
+ "crate" | "do" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" |
+ "if" | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" |
+ "mut" | "offsetof" | "override" | "priv" | "proc" | "pub" | "pure" | "ref" |
+ "return" | "Self" | "self" | "sizeof" | "static" | "struct" | "super" | "trait" |
+ "true" | "type" | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" |
+ "while" | "yield" => IResult::Error,
+ _ => IResult::Done(rest, id),
+ }
+ } else {
+ IResult::Error
}
}
- pub fn word(mut input: &str) -> IResult<&str, Ident> {
- input = skip_whitespace(input);
-
- let mut chars = input.char_indices();
- match chars.next() {
- Some((_, ch)) if UnicodeXID::is_xid_start(ch) || ch == '_' => {}
- _ => return IResult::Error,
- }
-
- for (i, ch) in chars {
- if !UnicodeXID::is_xid_continue(ch) {
- return IResult::Done(&input[i..], input[..i].into());
+ pub fn word(input: &[TokenTree]) -> IResult<&[TokenTree], Ident> {
+ if let Some(&TokenTree { kind: TokenKind::Word(ref id), .. }) = input.first() {
+ // Check if this word is _actually_ a lifetime, and treat that differently
+ if id.chars().next().unwrap() == '\'' {
+ IResult::Error
+ } else {
+ IResult::Done(&input[1..], Ident(id.to_string()))
}
+ } else {
+ IResult::Error
}
-
- IResult::Done("", input.into())
}
#[cfg(feature = "full")]
- pub fn wordlike(mut input: &str) -> IResult<&str, Ident> {
- input = skip_whitespace(input);
-
- for (i, ch) in input.char_indices() {
- if !UnicodeXID::is_xid_start(ch) && !UnicodeXID::is_xid_continue(ch) {
- return if i == 0 {
- IResult::Error
- } else {
- IResult::Done(&input[i..], input[..i].into())
- };
- }
- }
-
- IResult::Done("", input.into())
- }
+ named!(pub wordlike -> Ident, alt!(
+ word
+ |
+ int => { |d| format!("{}", d).into() }
+ ));
}
#[cfg(feature = "printing")]
diff --git a/src/item.rs b/src/item.rs
index 4cbac33..8d55f5c 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -365,17 +365,13 @@
named!(view_path_list -> ViewPath, do_parse!(
path: path >>
punct!("::") >>
- punct!("{") >>
- items: terminated_list!(punct!(","), path_list_item) >>
- punct!("}") >>
+ items: delim!(Brace, terminated_list!(punct!(","), path_list_item)) >>
(ViewPath::List(path, items))
));
named!(view_path_list_root -> ViewPath, do_parse!(
global: option!(punct!("::")) >>
- punct!("{") >>
- items: terminated_list!(punct!(","), path_list_item) >>
- punct!("}") >>
+ items: delim!(Brace, terminated_list!(punct!(","), path_list_item)) >>
(ViewPath::List(Path {
global: global.is_some(),
segments: Vec::new(),
@@ -441,21 +437,18 @@
keyword!("fn") >>
name: ident >>
generics: generics >>
- punct!("(") >>
- inputs: terminated_list!(punct!(","), fn_arg) >>
- punct!(")") >>
+ inputs: delim!(Parenthesis, terminated_list!(punct!(","), fn_arg)) >>
ret: option!(preceded!(punct!("->"), ty)) >>
where_clause: where_clause >>
- punct!("{") >>
- inner_attrs: many0!(inner_attr) >>
- stmts: within_block >>
- punct!("}") >>
+ inner_attrs_stmts: delim!(Brace, tuple!(
+ many0!(inner_attr), within_block
+ )) >>
(Item {
ident: name,
vis: vis,
attrs: {
let mut attrs = outer_attrs;
- attrs.extend(inner_attrs);
+ attrs.extend(inner_attrs_stmts.0);
attrs
},
node: ItemKind::Fn(
@@ -472,7 +465,7 @@
.. generics
},
Box::new(Block {
- stmts: stmts,
+ stmts: inner_attrs_stmts.1,
}),
),
})
@@ -513,13 +506,12 @@
content: alt!(
punct!(";") => { |_| None }
|
- delimited!(
- punct!("{"),
+ delim!(
+ Brace,
tuple!(
many0!(inner_attr),
items
- ),
- punct!("}")
+ )
) => { Some }
) >>
(match content {
@@ -545,9 +537,7 @@
named!(item_foreign_mod -> Item, do_parse!(
attrs: many0!(outer_attr) >>
abi: abi >>
- punct!("{") >>
- items: many0!(foreign_item) >>
- punct!("}") >>
+ items: delim!(Brace, many0!(foreign_item)) >>
(Item {
ident: "".into(),
vis: Visibility::Inherited,
@@ -571,11 +561,12 @@
keyword!("fn") >>
name: ident >>
generics: generics >>
- punct!("(") >>
- inputs: separated_list!(punct!(","), fn_arg) >>
- trailing_comma: option!(punct!(",")) >>
- variadic: option!(cond_reduce!(trailing_comma.is_some(), punct!("..."))) >>
- punct!(")") >>
+ inputs_and_variadic: delim!(Parenthesis, do_parse!(
+ inputs: separated_list!(punct!(","), fn_arg) >>
+ trailing_comma: option!(punct!(",")) >>
+ variadic: option!(cond_reduce!(trailing_comma.is_some(), punct!("..."))) >>
+ ((inputs, variadic.is_some()))
+ )) >>
ret: option!(preceded!(punct!("->"), ty)) >>
where_clause: where_clause >>
punct!(";") >>
@@ -584,9 +575,9 @@
attrs: attrs,
node: ForeignItemKind::Fn(
Box::new(FnDecl {
- inputs: inputs,
+ inputs: inputs_and_variadic.0,
output: ret.map(FunctionRetTy::Ty).unwrap_or(FunctionRetTy::Default),
- variadic: variadic.is_some(),
+ variadic: inputs_and_variadic.1,
}),
Generics {
where_clause: where_clause,
@@ -689,9 +680,7 @@
separated_nonempty_list!(punct!("+"), ty_param_bound)
)) >>
where_clause: where_clause >>
- punct!("{") >>
- body: many0!(trait_item) >>
- punct!("}") >>
+ body: delim!(Brace, many0!(trait_item)) >>
(Item {
ident: id,
vis: vis,
@@ -715,8 +704,7 @@
path: path >>
keyword!("for") >>
punct!("..") >>
- punct!("{") >>
- punct!("}") >>
+ delim!(Brace, epsilon!()) >>
(Item {
ident: "".into(),
vis: Visibility::Inherited,
@@ -758,15 +746,12 @@
keyword!("fn") >>
name: ident >>
generics: generics >>
- punct!("(") >>
- inputs: terminated_list!(punct!(","), fn_arg) >>
- punct!(")") >>
+ inputs: delim!(Parenthesis, terminated_list!(punct!(","), fn_arg)) >>
ret: option!(preceded!(punct!("->"), ty)) >>
where_clause: where_clause >>
- body: option!(delimited!(
- punct!("{"),
- tuple!(many0!(inner_attr), within_block),
- punct!("}")
+ body: option!(delim!(
+ Brace,
+ tuple!(many0!(inner_attr), within_block)
)) >>
cond!(body.is_none(), punct!(";")) >>
({
@@ -855,9 +840,7 @@
) >>
self_ty: ty >>
where_clause: where_clause >>
- punct!("{") >>
- body: many0!(impl_item) >>
- punct!("}") >>
+ body: delim!(Brace, many0!(impl_item)) >>
(Item {
ident: "".into(),
vis: Visibility::Inherited,
@@ -916,22 +899,19 @@
keyword!("fn") >>
name: ident >>
generics: generics >>
- punct!("(") >>
- inputs: terminated_list!(punct!(","), fn_arg) >>
- punct!(")") >>
+ inputs: delim!(Parenthesis, terminated_list!(punct!(","), fn_arg)) >>
ret: option!(preceded!(punct!("->"), ty)) >>
where_clause: where_clause >>
- punct!("{") >>
- inner_attrs: many0!(inner_attr) >>
- stmts: within_block >>
- punct!("}") >>
+ inner_attrs_stmts: delim!(Brace, tuple!(
+ many0!(inner_attr), within_block
+ )) >>
(ImplItem {
ident: name,
vis: vis,
defaultness: defaultness,
attrs: {
let mut attrs = outer_attrs;
- attrs.extend(inner_attrs);
+ attrs.extend(inner_attrs_stmts.0);
attrs
},
node: ImplItemKind::Method(
@@ -950,7 +930,7 @@
},
},
Block {
- stmts: stmts,
+ stmts: inner_attrs_stmts.1,
},
),
})
diff --git a/src/krate.rs b/src/krate.rs
index 077a36d..9bef3df 100644
--- a/src/krate.rs
+++ b/src/krate.rs
@@ -14,25 +14,28 @@
use item::parsing::items;
named!(pub krate -> Crate, do_parse!(
- option!(byte_order_mark) >>
- shebang: option!(shebang) >>
+ // NOTE: The byte order mark and shebang are not tokens which can appear
+ // in a TokenStream, so we can't parse them anymore.
+
+ //option!(byte_order_mark) >>
+ //shebang: option!(shebang) >>
attrs: many0!(inner_attr) >>
items: items >>
(Crate {
- shebang: shebang,
+ shebang: None,
attrs: attrs,
items: items,
})
));
- named!(byte_order_mark -> &str, tag!("\u{feff}"));
+ // named!(byte_order_mark -> &str, tag!("\u{feff}"));
- named!(shebang -> String, do_parse!(
- tag!("#!") >>
- not!(tag!("[")) >>
- content: take_until!("\n") >>
- (format!("#!{}", content))
- ));
+ // named!(shebang -> String, do_parse!(
+ // tag!("#!") >>
+ // not!(tag!("[")) >>
+ // content: take_until!("\n") >>
+ // (format!("#!{}", content))
+ // ));
}
#[cfg(feature = "printing")]
diff --git a/src/lib.rs b/src/lib.rs
index 3f7f0df..7e9533c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,7 +6,7 @@
extern crate quote;
#[cfg(feature = "parsing")]
-extern crate unicode_xid;
+extern crate relex;
#[cfg(feature = "parsing")]
#[macro_use]
@@ -24,9 +24,6 @@
mod data;
pub use data::{Field, Variant, VariantData, Visibility};
-#[cfg(feature = "parsing")]
-mod escape;
-
#[cfg(feature = "full")]
mod expr;
#[cfg(feature = "full")]
@@ -92,59 +89,108 @@
use super::*;
use {derive, generics, ident, mac, ty, attr};
- use synom::{space, IResult};
+ use synom::{IResult, TokenStream};
+
+ use std::convert::From;
+ use std::error::Error;
+ use std::fmt;
#[cfg(feature = "full")]
use {expr, item, krate};
+ #[derive(Debug)]
+ pub struct ParseError(String);
+
+ impl Error for ParseError {
+ fn description(&self) -> &str {
+ &self.0
+ }
+ }
+
+ impl fmt::Display for ParseError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ <String as fmt::Display>::fmt(&self.0, f)
+ }
+ }
+
+ impl From<synom::LexError> for ParseError {
+ fn from(_: synom::LexError) -> ParseError {
+ ParseError("error while lexing input string".to_owned())
+ }
+ }
+
/// Parse the stringified representation of a struct or enum passed
/// to a `proc_macro_derive` function.
- pub fn parse_derive_input(input: &str) -> Result<DeriveInput, String> {
+ pub fn parse_derive_input(input: TokenStream) -> Result<DeriveInput, ParseError> {
unwrap("derive input", derive::parsing::derive_input, input)
}
+ /// Parse an entire crate into an AST. This function takes a string as input
+ /// instead of a TokenStream, as we need to handle parsing the BOM and
+ /// shebang from the string.
#[cfg(feature = "full")]
- pub fn parse_crate(input: &str) -> Result<Crate, String> {
- unwrap("crate", krate::parsing::krate, input)
+ pub fn parse_crate(mut input: &str) -> Result<Crate, ParseError> {
+ // Strip the BOM if it is present
+ const BOM: &str = "\u{feff}";
+ if input.starts_with(BOM) {
+ input = &input[BOM.len()..];
+ }
+
+ let mut shebang = None;
+ if input.starts_with("#!") && !input.starts_with("#![") {
+ if let Some(idx) = input.find('\n') {
+ shebang = Some(input[..idx].to_string());
+ input = &input[idx..];
+ } else {
+ shebang = Some(input.to_string());
+ input = "";
+ }
+ }
+
+ let mut krate = unwrap("crate", krate::parsing::krate,
+ input.parse()?)?;
+ krate.shebang = shebang;
+ Ok(krate)
+
}
#[cfg(feature = "full")]
- pub fn parse_item(input: &str) -> Result<Item, String> {
+ pub fn parse_item(input: TokenStream) -> Result<Item, ParseError> {
unwrap("item", item::parsing::item, input)
}
#[cfg(feature = "full")]
- pub fn parse_items(input: &str) -> Result<Vec<Item>, String> {
+ pub fn parse_items(input: TokenStream) -> Result<Vec<Item>, ParseError> {
unwrap("items", item::parsing::items, input)
}
#[cfg(feature = "full")]
- pub fn parse_expr(input: &str) -> Result<Expr, String> {
+ pub fn parse_expr(input: TokenStream) -> Result<Expr, ParseError> {
unwrap("expression", expr::parsing::expr, input)
}
- pub fn parse_type(input: &str) -> Result<Ty, String> {
+ pub fn parse_type(input: TokenStream) -> Result<Ty, ParseError> {
unwrap("type", ty::parsing::ty, input)
}
/// Parse a path, such as `std::str::FromStr` or `::syn::parse_path`.
- pub fn parse_path(input: &str) -> Result<Path, String> {
+ pub fn parse_path(input: TokenStream) -> Result<Path, ParseError> {
unwrap("path", ty::parsing::path, input)
}
- pub fn parse_where_clause(input: &str) -> Result<WhereClause, String> {
+ pub fn parse_where_clause(input: TokenStream) -> Result<WhereClause, ParseError> {
unwrap("where clause", generics::parsing::where_clause, input)
}
- pub fn parse_token_trees(input: &str) -> Result<Vec<TokenTree>, String> {
+ pub fn parse_token_trees(input: TokenStream) -> Result<Vec<TokenTree>, ParseError> {
unwrap("token trees", mac::parsing::token_trees, input)
}
- pub fn parse_ident(input: &str) -> Result<Ident, String> {
+ pub fn parse_ident(input: TokenStream) -> Result<Ident, ParseError> {
unwrap("identifier", ident::parsing::ident, input)
}
- pub fn parse_ty_param_bound(input: &str) -> Result<TyParamBound, String> {
+ pub fn parse_ty_param_bound(input: TokenStream) -> Result<TyParamBound, ParseError> {
unwrap("type parameter bound",
generics::parsing::ty_param_bound,
input)
@@ -152,7 +198,7 @@
/// Parse an attribute declared outside the item it annotates, such as
/// a struct annotation. They are written as `#[...]`.
- pub fn parse_outer_attr(input: &str) -> Result<Attribute, String> {
+ pub fn parse_outer_attr(input: TokenStream) -> Result<Attribute, ParseError> {
unwrap("outer attribute", attr::parsing::outer_attr, input)
}
@@ -160,30 +206,30 @@
/// for crate annotations or for mod-level declarations when modules are in
/// their own files. They are written as `#![...]`.
#[cfg(feature = "full")]
- pub fn parse_inner_attr(input: &str) -> Result<Attribute, String> {
+ pub fn parse_inner_attr(input: TokenStream) -> Result<Attribute, ParseError> {
unwrap("inner attribute", attr::parsing::inner_attr, input)
}
/// Deprecated: Use `parse_derive_input` instead.
#[doc(hidden)]
#[deprecated(since="0.11.0", note = "Use `parse_derive_input` instead")]
- pub fn parse_macro_input(input: &str) -> Result<MacroInput, String> {
+ pub fn parse_macro_input(input: TokenStream) -> Result<MacroInput, ParseError> {
parse_derive_input(input)
}
/// Alias for `syn::parse_derive_input`.
impl FromStr for DeriveInput {
- type Err = String;
+ type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
- parse_derive_input(s)
+ parse_derive_input(s.parse()?)
}
}
/// Alias for `syn::parse_crate`.
#[cfg(feature = "full")]
impl FromStr for Crate {
- type Err = String;
+ type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_crate(s)
@@ -193,85 +239,85 @@
/// Alias for `syn::parse_item`.
#[cfg(feature = "full")]
impl FromStr for Item {
- type Err = String;
+ type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
- parse_item(s)
+ parse_item(s.parse()?)
}
}
/// Alias for `syn::parse_expr`.
#[cfg(feature = "full")]
impl FromStr for Expr {
- type Err = String;
+ type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
- parse_expr(s)
+ parse_expr(s.parse()?)
}
}
/// Alias for `syn::parse_type`.
impl FromStr for Ty {
- type Err = String;
+ type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
- parse_type(s)
+ parse_type(s.parse()?)
}
}
/// Alias for `syn::parse_path`.
impl FromStr for Path {
- type Err = String;
+ type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
- parse_path(s)
+ parse_path(s.parse()?)
}
}
/// Alias for `syn::parse_where_clause`.
impl FromStr for WhereClause {
- type Err = String;
+ type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
- parse_where_clause(s)
+ parse_where_clause(s.parse()?)
}
}
/// Alias for `syn::parse_ident`.
impl FromStr for Ident {
- type Err = String;
+ type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
- parse_ident(s)
+ parse_ident(s.parse()?)
}
}
/// Alias for `syn::parse_ty_param_bound`.
impl FromStr for TyParamBound {
- type Err = String;
+ type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
- parse_ty_param_bound(s)
+ parse_ty_param_bound(s.parse()?)
}
}
fn unwrap<T>(name: &'static str,
- f: fn(&str) -> IResult<&str, T>,
- input: &str)
- -> Result<T, String> {
- match f(input) {
- IResult::Done(mut rest, t) => {
- rest = space::skip_whitespace(rest);
+ f: fn(&[synom::TokenTree]) -> IResult<&[synom::TokenTree], T>,
+ input: TokenStream)
+ -> Result<T, ParseError> {
+ let input = synom::InputBuf::new(input);
+ match f(&input) {
+ IResult::Done(rest, t) => {
if rest.is_empty() {
Ok(t)
} else if rest.len() == input.len() {
// parsed nothing
- Err(format!("failed to parse {}: {:?}", name, rest))
+ Err(ParseError(format!("failed to parse {}", name)))
} else {
- Err(format!("unparsed tokens after {}: {:?}", name, rest))
+ Err(ParseError(format!("unparsed tokens after {}", name)))
}
}
- IResult::Error => Err(format!("failed to parse {}: {:?}", name, input)),
+ IResult::Error => Err(ParseError(format!("failed to parse {}", name))),
}
}
}
diff --git a/src/lit.rs b/src/lit.rs
index 81be285..4aeec28 100644
--- a/src/lit.rs
+++ b/src/lit.rs
@@ -157,244 +157,159 @@
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
- use escape::{cooked_byte, cooked_byte_string, cooked_char, cooked_string, raw_string};
- use synom::space::skip_whitespace;
- use synom::IResult;
- use unicode_xid::UnicodeXID;
+ use synom::{IResult, TokenTree, TokenKind};
+ use relex;
- named!(pub lit -> Lit, alt!(
- string => { |StrLit { value, style }| Lit::Str(value, style) }
- |
- byte_string => { |ByteStrLit { value, style }| Lit::ByteStr(value, style) }
- |
- byte => { |b| Lit::Byte(b) }
- |
- character => { |ch| Lit::Char(ch) }
- |
- float => { |FloatLit { value, suffix }| Lit::Float(value, suffix) } // must be before int
- |
- int => { |IntLit { value, suffix }| Lit::Int(value, suffix) }
- |
- boolean => { |value| Lit::Bool(value) }
- ));
-
- named!(pub string -> StrLit, alt!(
- quoted_string => { |s| StrLit { value: s, style: StrStyle::Cooked } }
- |
- preceded!(
- punct!("r"),
- raw_string
- ) => { |(s, n)| StrLit { value: s, style: StrStyle::Raw(n) }}
- ));
-
- named!(pub quoted_string -> String, delimited!(
- punct!("\""),
- cooked_string,
- tag!("\"")
- ));
-
- named!(pub byte_string -> ByteStrLit, alt!(
- delimited!(
- punct!("b\""),
- cooked_byte_string,
- tag!("\"")
- ) => { |vec| ByteStrLit { value: vec, style: StrStyle::Cooked } }
- |
- preceded!(
- punct!("br"),
- raw_string
- ) => { |(s, n): (String, _)| ByteStrLit { value: s.into_bytes(), style: StrStyle::Raw(n) } }
- ));
-
- named!(pub byte -> u8, do_parse!(
- punct!("b") >>
- tag!("'") >>
- b: cooked_byte >>
- tag!("'") >>
- (b)
- ));
-
- named!(pub character -> char, do_parse!(
- punct!("'") >>
- ch: cooked_char >>
- tag!("'") >>
- (ch)
- ));
-
- named!(pub float -> FloatLit, do_parse!(
- value: float_string >>
- suffix: alt!(
- tag!("f32") => { |_| FloatTy::F32 }
- |
- tag!("f64") => { |_| FloatTy::F64 }
- |
- epsilon!() => { |_| FloatTy::Unsuffixed }
- ) >>
- (FloatLit { value: value, suffix: suffix })
- ));
-
- named!(pub int -> IntLit, do_parse!(
- value: digits >>
- suffix: alt!(
- tag!("isize") => { |_| IntTy::Isize }
- |
- tag!("i8") => { |_| IntTy::I8 }
- |
- tag!("i16") => { |_| IntTy::I16 }
- |
- tag!("i32") => { |_| IntTy::I32 }
- |
- tag!("i64") => { |_| IntTy::I64 }
- |
- tag!("usize") => { |_| IntTy::Usize }
- |
- tag!("u8") => { |_| IntTy::U8 }
- |
- tag!("u16") => { |_| IntTy::U16 }
- |
- tag!("u32") => { |_| IntTy::U32 }
- |
- tag!("u64") => { |_| IntTy::U64 }
- |
- epsilon!() => { |_| IntTy::Unsuffixed }
- ) >>
- (IntLit { value: value, suffix: suffix })
- ));
-
- named!(pub boolean -> bool, alt!(
- keyword!("true") => { |_| true }
- |
- keyword!("false") => { |_| false }
- ));
-
- fn float_string(mut input: &str) -> IResult<&str, String> {
- input = skip_whitespace(input);
-
- let mut chars = input.chars().peekable();
- match chars.next() {
- Some(ch) if ch >= '0' && ch <= '9' => {}
- _ => return IResult::Error,
- }
-
- let mut len = 1;
- let mut has_dot = false;
- let mut has_exp = false;
- while let Some(&ch) = chars.peek() {
- match ch {
- '0'...'9' | '_' => {
- chars.next();
- len += 1;
- }
- '.' => {
- if has_dot {
- break;
+ pub fn lit(i: &[TokenTree]) -> IResult<&[TokenTree], Lit> {
+ match i.first() {
+ Some(&TokenTree{ kind: TokenKind::Literal(ref l), .. }) => {
+ // XXX: I'm using my lexer for this temporarially, as it makes
+ // my life easier. A final version shouldn't be this hacky,
+ // though we'll probably want `proc_macro::Literal` -> actual
+ // literal value conversions to be in a separate crate, rather
+ // than requiring syn (which seems a bit heavyweight for that).
+ let tok = if let Ok(tok) = relex::relex_literal(&l.to_string()) {
+ tok
+ } else {
+ return IResult::Error
+ };
+ let lit = match tok {
+ relex::LToken::Lit(relex::Lit::Byte(b)) => {
+ Lit::Byte(b)
}
- chars.next();
- if chars.peek()
- .map(|&ch| ch == '.' || UnicodeXID::is_xid_start(ch))
- .unwrap_or(false) {
- return IResult::Error;
+ relex::LToken::Lit(relex::Lit::Char(c)) => {
+ Lit::Char(c)
}
- len += 1;
- has_dot = true;
- }
- 'e' | 'E' => {
- chars.next();
- len += 1;
- has_exp = true;
- break;
- }
- _ => break,
+ relex::LToken::Lit(relex::Lit::Integer(v, suffix)) => {
+ let suffix = match suffix {
+ relex::IntSuffix::Unsuffixed =>
+ IntTy::Unsuffixed,
+ relex::IntSuffix::Isize =>
+ IntTy::Isize,
+ relex::IntSuffix::Usize =>
+ IntTy::Usize,
+ relex::IntSuffix::I8 =>
+ IntTy::I8,
+ relex::IntSuffix::U8 =>
+ IntTy::U8,
+ relex::IntSuffix::I16 =>
+ IntTy::I16,
+ relex::IntSuffix::U16 =>
+ IntTy::U16,
+ relex::IntSuffix::I32 =>
+ IntTy::I32,
+ relex::IntSuffix::U32 =>
+ IntTy::U32,
+ relex::IntSuffix::I64 =>
+ IntTy::I64,
+ relex::IntSuffix::U64 =>
+ IntTy::U64,
+ };
+ Lit::Int(v, suffix)
+ }
+ relex::LToken::Lit(relex::Lit::Float(v, suffix)) => {
+ let suffix = match suffix {
+ relex::FloatSuffix::Unsuffixed =>
+ FloatTy::Unsuffixed,
+ relex::FloatSuffix::F32 =>
+ FloatTy::F32,
+ relex::FloatSuffix::F64 =>
+ FloatTy::F64,
+ };
+ Lit::Float(v, suffix)
+ }
+ relex::LToken::Lit(relex::Lit::Str(s, relex::StrStyle::Cooked)) => {
+ Lit::Str(s, StrStyle::Cooked)
+ }
+ relex::LToken::Lit(relex::Lit::Str(s, relex::StrStyle::Raw(n))) => {
+ Lit::Str(s, StrStyle::Raw(n))
+ }
+ relex::LToken::Lit(relex::Lit::ByteStr(s, relex::StrStyle::Cooked)) => {
+ Lit::ByteStr(s, StrStyle::Cooked)
+ }
+ relex::LToken::Lit(relex::Lit::ByteStr(s, relex::StrStyle::Raw(n))) => {
+ Lit::ByteStr(s, StrStyle::Raw(n))
+ }
+ _ => return IResult::Error
+ };
+
+ IResult::Done(&i[1..], lit)
}
- }
-
- let rest = &input[len..];
- if !(has_dot || has_exp || rest.starts_with("f32") || rest.starts_with("f64")) {
- return IResult::Error;
- }
-
- if has_exp {
- let mut has_exp_value = false;
- while let Some(&ch) = chars.peek() {
- match ch {
- '+' | '-' => {
- if has_exp_value {
- break;
- }
- chars.next();
- len += 1;
- }
- '0'...'9' => {
- chars.next();
- len += 1;
- has_exp_value = true;
- }
- '_' => {
- chars.next();
- len += 1;
- }
- _ => break,
+ Some(&TokenTree{ kind: TokenKind::Word(ref w), .. }) => {
+ if &**w == "true" {
+ IResult::Done(&i[1..], Lit::Bool(true))
+ } else if &**w == "false" {
+ IResult::Done(&i[1..], Lit::Bool(false))
+ } else {
+ IResult::Error
}
}
- if !has_exp_value {
- return IResult::Error;
- }
+ _ => IResult::Error
}
-
- IResult::Done(&input[len..], input[..len].replace("_", ""))
}
- pub fn digits(mut input: &str) -> IResult<&str, u64> {
- input = skip_whitespace(input);
-
- let base = if input.starts_with("0x") {
- input = &input[2..];
- 16
- } else if input.starts_with("0o") {
- input = &input[2..];
- 8
- } else if input.starts_with("0b") {
- input = &input[2..];
- 2
+ #[cfg(feature = "full")]
+ pub fn digits(i: &[TokenTree]) -> IResult<&[TokenTree], u64> {
+ if let IResult::Done(r, Lit::Int(v, IntTy::Unsuffixed)) = lit(i) {
+ IResult::Done(r, v)
} else {
- 10
- };
-
- let mut value = 0u64;
- let mut len = 0;
- let mut empty = true;
- for b in input.bytes() {
- let digit = match b {
- b'0'...b'9' => (b - b'0') as u64,
- b'a'...b'f' => 10 + (b - b'a') as u64,
- b'A'...b'F' => 10 + (b - b'A') as u64,
- b'_' => {
- if empty && base == 10 {
- return IResult::Error;
- }
- len += 1;
- continue;
- }
- _ => break,
- };
- if digit >= base {
- return IResult::Error;
- }
- value = match value.checked_mul(base) {
- Some(value) => value,
- None => return IResult::Error,
- };
- value = match value.checked_add(digit) {
- Some(value) => value,
- None => return IResult::Error,
- };
- len += 1;
- empty = false;
- }
- if empty {
IResult::Error
+ }
+ }
+
+ pub fn string(i: &[TokenTree]) -> IResult<&[TokenTree], String> {
+ if let IResult::Done(r, Lit::Str(v, _)) = lit(i) {
+ IResult::Done(r, v)
} else {
- IResult::Done(&input[len..], value)
+ IResult::Error
+ }
+ }
+
+ pub fn byte_string(i: &[TokenTree]) -> IResult<&[TokenTree], Vec<u8>> {
+ if let IResult::Done(r, Lit::ByteStr(v, _)) = lit(i) {
+ IResult::Done(r, v)
+ } else {
+ IResult::Error
+ }
+ }
+
+ pub fn byte(i: &[TokenTree]) -> IResult<&[TokenTree], u8> {
+ if let IResult::Done(r, Lit::Byte(b)) = lit(i) {
+ IResult::Done(r, b)
+ } else {
+ IResult::Error
+ }
+ }
+
+ pub fn character(i: &[TokenTree]) -> IResult<&[TokenTree], char> {
+ if let IResult::Done(r, Lit::Char(c)) = lit(i) {
+ IResult::Done(r, c)
+ } else {
+ IResult::Error
+ }
+ }
+
+ pub fn float(i: &[TokenTree]) -> IResult<&[TokenTree], String> {
+ if let IResult::Done(r, Lit::Float(f, _)) = lit(i) {
+ IResult::Done(r, f)
+ } else {
+ IResult::Error
+ }
+ }
+
+ pub fn int(i: &[TokenTree]) -> IResult<&[TokenTree], u64> {
+ if let IResult::Done(r, Lit::Int(v, _)) = lit(i) {
+ IResult::Done(r, v)
+ } else {
+ IResult::Error
+ }
+ }
+
+ pub fn boolean(i: &[TokenTree]) -> IResult<&[TokenTree], bool> {
+ if let IResult::Done(r, Lit::Bool(b)) = lit(i) {
+ IResult::Done(r, b)
+ } else {
+ IResult::Error
}
}
}
diff --git a/src/mac.rs b/src/mac.rs
index 26b3c61..a5d781f 100644
--- a/src/mac.rs
+++ b/src/mac.rs
@@ -116,8 +116,8 @@
use generics::parsing::lifetime;
use ident::parsing::word;
use lit::parsing::lit;
- use synom::space::{block_comment, whitespace};
use ty::parsing::path;
+ use attr::parsing::{inner_doc_comment, outer_doc_comment};
named!(pub mac -> Mac, do_parse!(
what: path >>
@@ -132,22 +132,19 @@
named!(pub token_trees -> Vec<TokenTree>, many0!(token_tree));
named!(pub delimited -> Delimited, alt!(
- delimited!(
- punct!("("),
- token_trees,
- punct!(")")
+ delim!(
+ Parenthesis,
+ token_trees
) => { |tts| Delimited { delim: DelimToken::Paren, tts: tts } }
|
- delimited!(
- punct!("["),
- token_trees,
- punct!("]")
+ delim!(
+ Bracket,
+ token_trees
) => { |tts| Delimited { delim: DelimToken::Bracket, tts: tts } }
|
- delimited!(
- punct!("{"),
- token_trees,
- punct!("}")
+ delim!(
+ Brace,
+ token_trees
) => { |tts| Delimited { delim: DelimToken::Brace, tts: tts } }
));
@@ -268,32 +265,9 @@
));
named!(doc_comment -> String, alt!(
- do_parse!(
- punct!("//!") >>
- content: take_until!("\n") >>
- (format!("//!{}", content))
- )
+ inner_doc_comment
|
- do_parse!(
- option!(whitespace) >>
- peek!(tag!("/*!")) >>
- com: block_comment >>
- (com.to_owned())
- )
- |
- do_parse!(
- punct!("///") >>
- not!(tag!("/")) >>
- content: take_until!("\n") >>
- (format!("///{}", content))
- )
- |
- do_parse!(
- option!(whitespace) >>
- peek!(tuple!(tag!("/**"), not!(tag!("*")))) >>
- com: block_comment >>
- (com.to_owned())
- )
+ outer_doc_comment
));
}
diff --git a/src/ty.rs b/src/ty.rs
index 7b7d3af..2440a28 100644
--- a/src/ty.rs
+++ b/src/ty.rs
@@ -240,9 +240,10 @@
use expr::parsing::expr;
use generics::parsing::{lifetime, lifetime_def, ty_param_bound, bound_lifetimes};
use ident::parsing::ident;
- use lit::parsing::quoted_string;
+ use lit::parsing::string;
use mac::parsing::mac;
- use std::str;
+ #[cfg(feature = "full")]
+ use synom::{IResult, TokenTree};
named!(pub ty -> Ty, alt!(
ty_paren // must be before ty_tup
@@ -272,35 +273,28 @@
named!(ty_mac -> Ty, map!(mac, Ty::Mac));
- named!(ty_vec -> Ty, do_parse!(
- punct!("[") >>
+ named!(ty_vec -> Ty, delim!(Bracket, do_parse!(
elem: ty >>
- punct!("]") >>
(Ty::Slice(Box::new(elem)))
- ));
+ )));
- named!(ty_array -> Ty, do_parse!(
- punct!("[") >>
+ named!(ty_array -> Ty, delim!(Bracket, do_parse!(
elem: ty >>
punct!(";") >>
len: array_len >>
- punct!("]") >>
(Ty::Array(Box::new(elem), len))
- ));
+ )));
#[cfg(not(feature = "full"))]
use constant::parsing::const_expr as array_len;
#[cfg(feature = "full")]
named!(array_len -> ConstExpr, alt!(
- terminated!(const_expr, after_array_len)
+ terminated!(const_expr, input_end!())
|
- terminated!(expr, after_array_len) => { ConstExpr::Other }
+ terminated!(expr, input_end!()) => { ConstExpr::Other }
));
- #[cfg(feature = "full")]
- named!(after_array_len -> &str, peek!(punct!("]")));
-
named!(ty_ptr -> Ty, do_parse!(
punct!("*") >>
mutability: alt!(
@@ -337,11 +331,12 @@
unsafety: unsafety >>
abi: option!(abi) >>
keyword!("fn") >>
- punct!("(") >>
- inputs: separated_list!(punct!(","), fn_arg) >>
- trailing_comma: option!(punct!(",")) >>
- variadic: option!(cond_reduce!(trailing_comma.is_some(), punct!("..."))) >>
- punct!(")") >>
+ inputs_and_variadic: delim!(Parenthesis, do_parse!(
+ inputs: separated_list!(punct!(","), fn_arg) >>
+ trailing_comma: option!(punct!(",")) >>
+ variadic: option!(cond_reduce!(trailing_comma.is_some(), punct!("..."))) >>
+ ((inputs, variadic.is_some()))
+ )) >>
output: option!(preceded!(
punct!("->"),
ty
@@ -350,21 +345,19 @@
unsafety: unsafety,
abi: abi,
lifetimes: lifetimes,
- inputs: inputs,
+ inputs: inputs_and_variadic.0,
output: match output {
Some(ty) => FunctionRetTy::Ty(ty),
None => FunctionRetTy::Default,
},
- variadic: variadic.is_some(),
+ variadic: inputs_and_variadic.1,
})))
));
named!(ty_never -> Ty, map!(punct!("!"), |_| Ty::Never));
named!(ty_tup -> Ty, do_parse!(
- punct!("(") >>
- elems: terminated_list!(punct!(","), ty) >>
- punct!(")") >>
+ elems: delim!(Parenthesis, terminated_list!(punct!(","), ty)) >>
(Ty::Tup(elems))
));
@@ -397,9 +390,7 @@
));
named!(parenthesized_parameter_data -> PathParameters, do_parse!(
- punct!("(") >>
- inputs: terminated_list!(punct!(","), ty) >>
- punct!(")") >>
+ inputs: delim!(Parenthesis, terminated_list!(punct!(","), ty)) >>
output: option!(preceded!(
punct!("->"),
ty
@@ -457,9 +448,7 @@
));
named!(ty_paren -> Ty, do_parse!(
- punct!("(") >>
- elem: ty >>
- punct!(")") >>
+ elem: delim!(Parenthesis, ty) >>
(Ty::Paren(Box::new(elem)))
));
@@ -572,8 +561,8 @@
named!(pub fn_arg -> BareFnArg, do_parse!(
name: option!(do_parse!(
name: ident >>
+ not!(punct!("::")) >>
punct!(":") >>
- not!(tag!(":")) >> // not ::
(name)
)) >>
ty: ty >>
@@ -591,7 +580,7 @@
named!(pub abi -> Abi, do_parse!(
keyword!("extern") >>
- name: option!(quoted_string) >>
+ name: option!(string) >>
(match name {
Some(name) => Abi::Named(name),
None => Abi::Rust,
diff --git a/synom/Cargo.toml b/synom/Cargo.toml
index fde5ed4..5ef7c0f 100644
--- a/synom/Cargo.toml
+++ b/synom/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "synom"
-version = "0.11.3"
+version = "0.12.0"
authors = ["David Tolnay <dtolnay@gmail.com>"]
license = "MIT/Apache-2.0"
description = "Stripped-down Nom parser used by Syn"
@@ -11,10 +11,10 @@
include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
[dependencies]
-unicode-xid = "0.0.4"
+proc-macro2 = { git = "https://github.com/mystor/proc-macro2", branch = "byte_literal" }
[dev-dependencies.syn]
-version = "0.11"
+version = "0.12"
path = ".."
features = ["parsing", "full"]
default-features = false
diff --git a/synom/src/helper.rs b/synom/src/helper.rs
index a488359..800874a 100644
--- a/synom/src/helper.rs
+++ b/synom/src/helper.rs
@@ -1,5 +1,4 @@
-use IResult;
-use space::{skip_whitespace, word_break};
+use {IResult, TokenTree, TokenKind, OpKind, Delimiter, InputBuf};
/// Parse a piece of punctuation like "+" or "+=".
///
@@ -33,13 +32,33 @@
// Not public API.
#[doc(hidden)]
-pub fn punct<'a>(input: &'a str, token: &'static str) -> IResult<&'a str, &'a str> {
- let input = skip_whitespace(input);
- if input.starts_with(token) {
- IResult::Done(&input[token.len()..], token)
- } else {
- IResult::Error
+pub fn punct<'a>(input: &'a [TokenTree], token: &'static str) -> IResult<&'a [TokenTree], &'a str> {
+ // Extract the chars from token, so we know how many tokens to expect, check
+ // if we are running past EOF, then confirm that the tokens exist as
+ // requested.
+ let expected = token.chars().collect::<Vec<_>>();
+ if input.len() < expected.len() {
+ return IResult::Error;
}
+ for i in 0..expected.len() {
+ if let TokenKind::Op(c, ok) = input[i].kind {
+ if c != expected[i] {
+ return IResult::Error;
+ }
+
+ // The last token in the sequence does not have to be marked as
+ // OpKind::Joint. Unfortunately OpKind doesn't implement
+ // Eq/PartialEq right now.
+ match ok {
+ OpKind::Alone if i != expected.len() - 1 => return IResult::Error,
+ _ => {}
+ }
+ } else {
+ return IResult::Error;
+ }
+ }
+
+ IResult::Done(&input[expected.len()..], token)
}
/// Parse a keyword like "fn" or "struct".
@@ -83,15 +102,11 @@
// Not public API.
#[doc(hidden)]
-pub fn keyword<'a>(input: &'a str, token: &'static str) -> IResult<&'a str, &'a str> {
- match punct(input, token) {
- IResult::Done(rest, _) => {
- match word_break(rest) {
- IResult::Done(_, _) => IResult::Done(rest, token),
- IResult::Error => IResult::Error,
- }
- }
- IResult::Error => IResult::Error,
+pub fn keyword<'a>(input: &'a [TokenTree], token: &'static str) -> IResult<&'a [TokenTree], &'static str> {
+ match input.first() {
+ Some(&TokenTree{ kind: TokenKind::Word(ref symbol), .. }) if &**symbol == token =>
+ IResult::Done(&input[1..], token),
+ _ => IResult::Error,
}
}
@@ -497,11 +512,11 @@
// Not public API.
#[doc(hidden)]
-pub fn separated_list<'a, T>(mut input: &'a str,
+pub fn separated_list<'a, T>(mut input: &'a [TokenTree],
sep: &'static str,
- f: fn(&'a str) -> IResult<&'a str, T>,
+ f: fn(&'a [TokenTree]) -> IResult<&'a [TokenTree], T>,
terminated: bool)
- -> IResult<&'a str, Vec<T>> {
+ -> IResult<&'a [TokenTree], Vec<T>> {
let mut res = Vec::new();
// get the first element
@@ -541,3 +556,45 @@
}
}
}
+
+#[macro_export]
+macro_rules! delim {
+ ($i:expr, $delim:ident, $fmac:ident!( $($fargs:tt)* )) => {
+ match $crate::helper::delim_impl($i, $crate::Delimiter::$delim) {
+ Some((i, ib)) => {
+ match $fmac!(&*ib, $($fargs)*) {
+ $crate::IResult::Done(rest, val) => {
+ if rest.is_empty() {
+ $crate::IResult::Done(i, val)
+ } else {
+ $crate::IResult::Error
+ }
+ }
+ _ => $crate::IResult::Error,
+ }
+ }
+ _ => $crate::IResult::Error,
+ }
+ };
+ ($i:expr, $delim:ident, $f:expr) => {
+ delim!($i, $delim, call!($f))
+ };
+}
+
+// Not a public API
+#[doc(hidden)]
+pub fn delim_impl(input: &[TokenTree],
+ expected_delim: Delimiter)
+ -> Option<(&[TokenTree], InputBuf)> {
+ // NOTE: The `as u32` hack is being used as `Delimiter` doesn't implement
+ // `PartialEq` or `Eq` despite being a simple c-style enum.
+ match input.first() {
+ Some(&TokenTree {
+ kind: TokenKind::Sequence(delim, ref stream),
+ ..
+ }) if delim as u32 == expected_delim as u32 => {
+ Some((&input[1..], InputBuf::new(stream.clone())))
+ }
+ _ => None
+ }
+}
diff --git a/synom/src/lib.rs b/synom/src/lib.rs
index 97c7833..ae9aebd 100644
--- a/synom/src/lib.rs
+++ b/synom/src/lib.rs
@@ -21,14 +21,51 @@
//! For our use case, this strategy is a huge improvement in usability,
//! correctness, and compile time over nom's `ws!` strategy.
-extern crate unicode_xid;
-
-#[doc(hidden)]
-pub mod space;
+extern crate proc_macro2;
#[doc(hidden)]
pub mod helper;
+// re-export TokenStream et. al. from proc_macro2, so that parsers which want to
+// use us don't have to manually import the type.
+pub use proc_macro2::{TokenStream, TokenTree, TokenKind, Delimiter, OpKind, LexError};
+
+use std::ops::Deref;
+
+/// A `TokenStream` does not provide a data format which is usable as a `synom`
+/// parser input. This type extracts `TokenTrees` from a `TokenStream` into a
+/// buffer, which can be iterated over as the `synom` input type.
+pub struct InputBuf {
+ data: Vec<TokenTree>,
+}
+
+impl InputBuf {
+ /// Transform the input `TokenStream` into a buffer which can be iterated
+ /// over as a `synom` parser input. Use the `Deref` implementation on this
+ /// type to extract the actual buffer type.
+ pub fn new(ts: TokenStream) -> Self {
+ fn flatten_stream(tt: TokenTree) -> Vec<TokenTree> {
+ match tt.kind {
+ TokenKind::Sequence(Delimiter::None, ts) => {
+ ts.into_iter().flat_map(flatten_stream).collect()
+ }
+ _ => vec![tt]
+ }
+ }
+
+ InputBuf {
+ data: ts.into_iter().flat_map(flatten_stream).collect()
+ }
+ }
+}
+
+impl Deref for InputBuf {
+ type Target = [TokenTree];
+ fn deref(&self) -> &[TokenTree] {
+ &self.data
+ }
+}
+
/// The result of a parser.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum IResult<I, O> {
@@ -39,7 +76,7 @@
Error,
}
-impl<'a, O> IResult<&'a str, O> {
+impl<'a, O> IResult<&'a [TokenTree], O> {
/// Unwraps the result, asserting the the parse is complete. Panics with a
/// message based on the given string if the parse failed or is incomplete.
///
@@ -66,12 +103,11 @@
/// ```
pub fn expect(self, name: &str) -> O {
match self {
- IResult::Done(mut rest, o) => {
- rest = space::skip_whitespace(rest);
+ IResult::Done(rest, o) => {
if rest.is_empty() {
o
} else {
- panic!("unparsed tokens after {}: {:?}", name, rest)
+ panic!("unparsed tokens after {}: {:?}", name, /* rest */ ())
}
}
IResult::Error => panic!("failed to parse {}", name),
@@ -97,13 +133,13 @@
#[macro_export]
macro_rules! named {
($name:ident -> $o:ty, $submac:ident!( $($args:tt)* )) => {
- fn $name(i: &str) -> $crate::IResult<&str, $o> {
+ fn $name(i: &[$crate::TokenTree]) -> $crate::IResult<&[$crate::TokenTree], $o> {
$submac!(i, $($args)*)
}
};
(pub $name:ident -> $o:ty, $submac:ident!( $($args:tt)* )) => {
- pub fn $name(i: &str) -> $crate::IResult<&str, $o> {
+ pub fn $name(i: &[$crate::TokenTree]) -> $crate::IResult<&[$crate::TokenTree], $o> {
$submac!(i, $($args)*)
}
};
@@ -564,9 +600,9 @@
//
// Not public API.
#[doc(hidden)]
-pub fn many0<'a, T>(mut input: &'a str,
- f: fn(&'a str) -> IResult<&'a str, T>)
- -> IResult<&'a str, Vec<T>> {
+pub fn many0<'a, T>(mut input: &'a [TokenTree],
+ f: fn(&'a [TokenTree]) -> IResult<&'a [TokenTree], T>)
+ -> IResult<&'a [TokenTree], Vec<T>> {
let mut res = Vec::new();
loop {
@@ -691,54 +727,6 @@
}};
}
-/// Parse the given string from exactly the current position in the input. You
-/// almost always want `punct!` or `keyword!` instead of this.
-///
-/// The `tag!` parser is equivalent to `punct!` but does not ignore leading
-/// whitespace. Both `punct!` and `keyword!` skip over leading whitespace. See
-/// an explanation of synom's whitespace handling strategy in the top-level
-/// crate documentation.
-///
-/// - **Syntax:** `tag!("...")`
-/// - **Output:** `"..."`
-///
-/// ```rust
-/// extern crate syn;
-/// #[macro_use] extern crate synom;
-///
-/// use syn::StrLit;
-/// use syn::parse::string;
-/// use synom::IResult;
-///
-/// // Parse a proposed syntax for an owned string literal: "abc"s
-/// named!(owned_string -> String,
-/// map!(
-/// terminated!(string, tag!("s")),
-/// |lit: StrLit| lit.value
-/// )
-/// );
-///
-/// fn main() {
-/// let input = r#" "abc"s "#;
-/// let parsed = owned_string(input).expect("owned string literal");
-/// println!("{:?}", parsed);
-///
-/// let input = r#" "abc" s "#;
-/// let err = owned_string(input);
-/// assert_eq!(err, IResult::Error);
-/// }
-/// ```
-#[macro_export]
-macro_rules! tag {
- ($i:expr, $tag:expr) => {
- if $i.starts_with($tag) {
- $crate::IResult::Done(&$i[$tag.len()..], &$i[..$tag.len()])
- } else {
- $crate::IResult::Error
- }
- };
-}
-
/// Pattern-match the result of a parser to select which other parser to run.
///
/// - **Syntax:** `switch!(TARGET, PAT1 => THEN1 | PAT2 => THEN2 | ...)`
@@ -1223,3 +1211,20 @@
}
};
}
+
+#[macro_export]
+macro_rules! input_end {
+ ($i:expr,) => {
+ $crate::input_end($i)
+ };
+}
+
+// Not a public API
+#[doc(hidden)]
+pub fn input_end(input: &[TokenTree]) -> IResult<&'static [TokenTree], &'static str> {
+ if input.is_empty() {
+ IResult::Done(&[], "")
+ } else {
+ IResult::Error
+ }
+}
diff --git a/synom/src/space.rs b/synom/src/space.rs
deleted file mode 100644
index 5237522..0000000
--- a/synom/src/space.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-use IResult;
-use unicode_xid::UnicodeXID;
-
-pub fn whitespace(input: &str) -> IResult<&str, ()> {
- if input.is_empty() {
- return IResult::Error;
- }
-
- let bytes = input.as_bytes();
- let mut i = 0;
- while i < bytes.len() {
- let s = &input[i..];
- if bytes[i] == b'/' {
- if s.starts_with("//") && (!s.starts_with("///") || s.starts_with("////")) &&
- !s.starts_with("//!") {
- if let Some(len) = s.find('\n') {
- i += len + 1;
- continue;
- }
- break;
- } else if s.starts_with("/*") && (!s.starts_with("/**") || s.starts_with("/***")) &&
- !s.starts_with("/*!") {
- match block_comment(s) {
- IResult::Done(_, com) => {
- i += com.len();
- continue;
- }
- IResult::Error => {
- return IResult::Error;
- }
- }
- }
- }
- match bytes[i] {
- b' ' | 0x09...0x0d => {
- i += 1;
- continue;
- }
- b if b <= 0x7f => {}
- _ => {
- let ch = s.chars().next().unwrap();
- if is_whitespace(ch) {
- i += ch.len_utf8();
- continue;
- }
- }
- }
- return if i > 0 {
- IResult::Done(s, ())
- } else {
- IResult::Error
- };
- }
- IResult::Done("", ())
-}
-
-pub fn block_comment(input: &str) -> IResult<&str, &str> {
- if !input.starts_with("/*") {
- return IResult::Error;
- }
-
- let mut depth = 0;
- let bytes = input.as_bytes();
- let mut i = 0;
- let upper = bytes.len() - 1;
- while i < upper {
- if bytes[i] == b'/' && bytes[i + 1] == b'*' {
- depth += 1;
- i += 1; // eat '*'
- } else if bytes[i] == b'*' && bytes[i + 1] == b'/' {
- depth -= 1;
- if depth == 0 {
- return IResult::Done(&input[i + 2..], &input[..i + 2]);
- }
- i += 1; // eat '/'
- }
- i += 1;
- }
- IResult::Error
-}
-
-pub fn word_break(input: &str) -> IResult<&str, ()> {
- match input.chars().next() {
- Some(ch) if UnicodeXID::is_xid_continue(ch) => IResult::Error,
- Some(_) | None => IResult::Done(input, ()),
- }
-}
-
-pub fn skip_whitespace(input: &str) -> &str {
- match whitespace(input) {
- IResult::Done(rest, _) => rest,
- IResult::Error => input,
- }
-}
-
-fn is_whitespace(ch: char) -> bool {
- // Rust treats left-to-right mark and right-to-left mark as whitespace
- ch.is_whitespace() || ch == '\u{200e}' || ch == '\u{200f}'
-}
diff --git a/tests/test_generics.rs b/tests/test_generics.rs
index 800daa7..4d6305e 100644
--- a/tests/test_generics.rs
+++ b/tests/test_generics.rs
@@ -68,7 +68,7 @@
fn test_ty_param_bound() {
let tokens = quote!('a);
let expected = TyParamBound::Region(Lifetime::new("'a"));
- assert_eq!(expected, parse_ty_param_bound(tokens.as_str()).unwrap());
+ assert_eq!(expected, parse_ty_param_bound(tokens.as_str().parse().unwrap()).unwrap());
let tokens = quote!(Debug);
let expected = TyParamBound::Trait(PolyTraitRef {
@@ -76,7 +76,7 @@
trait_ref: "Debug".into(),
},
TraitBoundModifier::None);
- assert_eq!(expected, parse_ty_param_bound(tokens.as_str()).unwrap());
+ assert_eq!(expected, parse_ty_param_bound(tokens.as_str().parse().unwrap()).unwrap());
let tokens = quote!(?Sized);
let expected = TyParamBound::Trait(PolyTraitRef {
@@ -84,5 +84,5 @@
trait_ref: "Sized".into(),
},
TraitBoundModifier::Maybe);
- assert_eq!(expected, parse_ty_param_bound(tokens.as_str()).unwrap());
+ assert_eq!(expected, parse_ty_param_bound(tokens.as_str().parse().unwrap()).unwrap());
}
diff --git a/tests/test_macro_input.rs b/tests/test_macro_input.rs
index d45ead3..963eb15 100644
--- a/tests/test_macro_input.rs
+++ b/tests/test_macro_input.rs
@@ -18,7 +18,7 @@
body: Body::Struct(VariantData::Unit),
};
- assert_eq!(expected, parse_macro_input(raw).unwrap());
+ assert_eq!(expected, parse_macro_input(raw.parse().unwrap()).unwrap());
}
#[test]
@@ -76,7 +76,7 @@
}])),
};
- let actual = parse_macro_input(raw).unwrap();
+ let actual = parse_macro_input(raw.parse().unwrap()).unwrap();
assert_eq!(expected, actual);
@@ -204,7 +204,7 @@
]),
};
- let actual = parse_macro_input(raw).unwrap();
+ let actual = parse_macro_input(raw.parse().unwrap()).unwrap();
assert_eq!(expected, actual);
@@ -257,7 +257,7 @@
body: Body::Struct(VariantData::Unit),
};
- let actual = parse_macro_input(raw).unwrap();
+ let actual = parse_macro_input(raw.parse().unwrap()).unwrap();
assert_eq!(expected, actual);
@@ -288,7 +288,7 @@
body: Body::Struct(VariantData::Unit),
};
- let actual = parse_macro_input(raw).unwrap();
+ let actual = parse_macro_input(raw.parse().unwrap()).unwrap();
assert_eq!(expected, actual);
@@ -315,7 +315,7 @@
body: Body::Struct(VariantData::Unit),
};
- let actual = parse_macro_input(raw).unwrap();
+ let actual = parse_macro_input(raw.parse().unwrap()).unwrap();
assert_eq!(expected, actual);
@@ -342,7 +342,7 @@
}])),
};
- let actual = parse_macro_input(raw).unwrap();
+ let actual = parse_macro_input(raw.parse().unwrap()).unwrap();
assert_eq!(expected, actual);
}
@@ -361,7 +361,7 @@
body: Body::Struct(VariantData::Unit),
};
- let actual = parse_macro_input(raw).unwrap();
+ let actual = parse_macro_input(raw.parse().unwrap()).unwrap();
assert_eq!(expected, actual);
}
@@ -380,7 +380,7 @@
body: Body::Struct(VariantData::Unit),
};
- let actual = parse_macro_input(raw).unwrap();
+ let actual = parse_macro_input(raw.parse().unwrap()).unwrap();
assert_eq!(expected, actual);
}
@@ -399,7 +399,7 @@
body: Body::Struct(VariantData::Unit),
};
- let actual = parse_macro_input(raw).unwrap();
+ let actual = parse_macro_input(raw.parse().unwrap()).unwrap();
assert_eq!(expected, actual);
}
diff --git a/tests/test_meta_item.rs b/tests/test_meta_item.rs
index 58a8eff..02b38a4 100644
--- a/tests/test_meta_item.rs
+++ b/tests/test_meta_item.rs
@@ -49,6 +49,6 @@
}
fn run_test(input: &str, expected: &MetaItem) {
- let attr = parse_outer_attr(input).unwrap();
+ let attr = parse_outer_attr(input.parse().unwrap()).unwrap();
assert_eq!(expected, &attr.meta_item().unwrap());
}
diff --git a/tests/test_round_trip.rs b/tests/test_round_trip.rs
index d77bf06..9db4f69 100644
--- a/tests/test_round_trip.rs
+++ b/tests/test_round_trip.rs
@@ -3,6 +3,7 @@
#[macro_use]
extern crate quote;
extern crate syn;
+extern crate synom;
extern crate syntex_pos;
extern crate syntex_syntax;
extern crate walkdir;
@@ -124,7 +125,7 @@
let (krate, elapsed) = match syn::parse_crate(&content) {
Ok(krate) => (krate, start.elapsed()),
Err(msg) => {
- errorf!("syn failed to parse\n{}\n", msg);
+ errorf!("syn failed to parse\n{:?}\n", msg);
failed += 1;
continue;
}
diff --git a/tests/test_token_trees.rs b/tests/test_token_trees.rs
index 8351f54..8292237 100644
--- a/tests/test_token_trees.rs
+++ b/tests/test_token_trees.rs
@@ -37,7 +37,7 @@
Token(Gt),
Token(Comma)])];
- let result = syn::parse_token_trees(raw).unwrap();
+ let result = syn::parse_token_trees(raw.parse().unwrap()).unwrap();
if result != expected {
panic!("{:#?}\n!=\n{:#?}", result, expected);
}