Skip to content

Commit faa3493

Browse files
committed
Modifications to support rustc 1.6.0 stable.
* Added compatibility information to crate docs and readme. * Added an extra rule to `scan!` for trailing commas. * Modified syntax of an internal macro, because `where` isn't in `ty`'s follow set yet. * Had to work around what appears to be an even worse case of rust-lang/rust#26448. This involved dropping the generic impls of some scanners and supporting *only* `&str` and `String`.
1 parent 7f9b828 commit faa3493

File tree

8 files changed

+276
-17
lines changed

8 files changed

+276
-17
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ readme = "README.md"
1010
keywords = ["input", "parse", "read", "stdin"]
1111
license = "MIT/Apache-2.0"
1212

13+
build = "build.rs"
1314
exclude = [
1415
"update-docs.py",
1516
]
@@ -23,3 +24,6 @@ itertools = "0.4.0"
2324
lazy_static = "0.1.14"
2425
regex = "0.1.41"
2526
strcursor = "0.2.1"
27+
28+
[build-dependencies]
29+
rustc_version = "0.1.4"

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ The available abstract scanners can be found in the `scanner` module.
1818
* [Latest Docs](https://danielkeep.github.io/rust-scan-rules/doc/scan_rules/index.html)
1919
* [Repository](https://github.com/DanielKeep/rust-scan-rules)
2020

21+
## Compatibility
22+
23+
v0.0.4 was tested against `rustc` versions 1.6.0, 1.7.0-beta.1, and nightly 2016-01-20.
24+
25+
* `rustc` versions prior to 1.7 will have only concrete implementations of `ScanFromStr` for the `Everything`, `Ident`, `Line`, `NonSpace`, `Number`, `Word`, and `Wordish` scanners for `&str` and `String` output types. 1.7 and higher will have generic implementations for all output types such that `&str: Into<Output>`.
26+
2127
## Quick Examples
2228

2329
Here is a simple CLI program that asks the user their name and age. You can run this using `cargo run --example ask_age`.

build.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
extern crate rustc_version;
2+
use rustc_version::{version_matches};
3+
4+
fn main() {
5+
println!("cargo:rerun-if-changed=build.rs");
6+
7+
/*
8+
See <https://github.com/rust-lang/rust/issues/26448#issuecomment-173794570>.
9+
*/
10+
if version_matches("< 1.7.0") {
11+
println!("cargo:rustc-cfg=str_into_output_extra_broken");
12+
}
13+
}

src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ The provided scanners can be found in the [`scanner`](scanner/index.html) module
4848
4949
<span></span></div>
5050
51+
## Compatibility
52+
53+
v0.0.4 was tested against `rustc` versions 1.6.0, 1.7.0-beta.1, and nightly 2016-01-20.
54+
55+
* `rustc` versions prior to 1.7 will have only concrete implementations of `ScanFromStr` for the `Everything`, `Ident`, `Line`, `NonSpace`, `Number`, `Word`, and `Wordish` scanners for `&str` and `String` output types. 1.7 and higher will have generic implementations for all output types such that `&str: Into<Output>`.
56+
5157
## Features
5258
5359
The following optional features are available:

src/macros.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,15 @@ See also: [Pattern Syntax](index.html#pattern-syntax).
112112
*/
113113
#[macro_export]
114114
macro_rules! scan {
115+
($input:expr;
116+
$(($($patterns:tt)*) => $bodies:expr),+
117+
) => {
118+
scan!($input; $(($($patterns)*) => $bodies,)+)
119+
};
120+
115121
($input:expr;
116122
($($head_pattern:tt)*) => $head_body:expr
117-
$(, ($($tail_patterns:tt)*) => $tail_bodies:expr)* $(,)*
123+
, $(($($tail_patterns:tt)*) => $tail_bodies:expr,)*
118124
) => {
119125
{
120126
let cur: $crate::input::Cursor = ::std::convert::Into::into($input);

src/scanner/macros.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,11 @@ macro_rules! scanner {
202202
(
203203
impl<$lt:tt $(, $ty_params:ident)*> ScanFromStr for $ty:ty { $($patterns:tt)* }
204204
) => {
205-
scanner! { impl<$lt $(, $ty_params)*> ScanFromStr for $ty where {} { $($patterns)* } }
205+
scanner! { impl<$lt $(, $ty_params)*> ScanFromStr for $ty, where {} { $($patterns)* } }
206206
};
207207

208208
(
209-
impl<$lt:tt $(, $ty_params:ident)*> ScanFromStr for $ty:ty where {$($clauses:tt)*} { $($patterns:tt)* }
209+
impl<$lt:tt $(, $ty_params:ident)*> ScanFromStr for $ty:ty, where {$($clauses:tt)*} { $($patterns:tt)* }
210210
) => {
211211
scanner! {
212212
@as_item

src/scanner/misc.rs

Lines changed: 233 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,25 @@ In most cases, you should use the `.. name` tail capture term to perform this ta
5858
*/
5959
pub struct Everything<'a, Output=&'a str>(PhantomData<(&'a (), Output)>);
6060

61-
impl<'a, Output> ScanFromStr<'a> for Everything<'a, Output> where &'a str: Into<Output> {
61+
#[cfg(str_into_output_extra_broken)]
62+
impl<'a> ScanFromStr<'a> for Everything<'a, &'a str> {
63+
type Output = &'a str;
64+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
65+
Ok((s.into(), s.len()))
66+
}
67+
}
68+
69+
#[cfg(str_into_output_extra_broken)]
70+
impl<'a> ScanFromStr<'a> for Everything<'a, String> {
71+
type Output = String;
72+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
73+
Ok((s.into(), s.len()))
74+
}
75+
}
76+
77+
#[cfg(not(str_into_output_extra_broken))]
78+
impl<'a, Output> ScanFromStr<'a> for Everything<'a, Output>
79+
where &'a str: Into<Output> {
6280
type Output = Output;
6381
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
6482
Ok((s.into(), s.len()))
@@ -69,9 +87,9 @@ impl<'a, Output> ScanFromStr<'a> for Everything<'a, Output> where &'a str: Into<
6987
#[test]
7088
fn test_everything() {
7189
// That's the scanner named `Everything`, not literally everything.
72-
assert_match!(Everything::scan_from(""), Ok(("", 0)));
73-
assert_match!(Everything::scan_from("で"), Ok(("で", 3)));
74-
assert_match!(Everything::scan_from("うまいー うまいー ぼうぼうぼうぼう"), Ok(("うまいー うまいー ぼうぼうぼうぼう", 54)));
90+
assert_match!(Everything::<&str>::scan_from(""), Ok(("", 0)));
91+
assert_match!(Everything::<&str>::scan_from("で"), Ok(("で", 3)));
92+
assert_match!(Everything::<&str>::scan_from("うまいー うまいー ぼうぼうぼうぼう"), Ok(("うまいー うまいー ぼうぼうぼうぼう", 54)));
7593
}
7694

7795
/**
@@ -103,6 +121,45 @@ Specifically, this will match a single `XID_Start` character (or underscore) fol
103121
*/
104122
pub struct Ident<'a, Output=&'a str>(PhantomData<(&'a (), Output)>);
105123

124+
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
125+
#[cfg(str_into_output_extra_broken)]
126+
impl<'a> ScanFromStr<'a> for Ident<'a, &'a str> {
127+
type Output = &'a str;
128+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
129+
match IDENT_RE.find(s) {
130+
Some((a, b)) => {
131+
let word = &s[a..b];
132+
let tail = &s[b..];
133+
Ok((word.into(), s.subslice_offset(tail).unwrap()))
134+
},
135+
None => {
136+
// Err(ScanErrorKind::Syntax(Some("expected identifier")))
137+
Err(ScanErrorKind::SyntaxNoMessage)
138+
},
139+
}
140+
}
141+
}
142+
143+
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
144+
#[cfg(str_into_output_extra_broken)]
145+
impl<'a> ScanFromStr<'a> for Ident<'a, String> {
146+
type Output = String;
147+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
148+
match IDENT_RE.find(s) {
149+
Some((a, b)) => {
150+
let word = &s[a..b];
151+
let tail = &s[b..];
152+
Ok((word.into(), s.subslice_offset(tail).unwrap()))
153+
},
154+
None => {
155+
// Err(ScanErrorKind::Syntax(Some("expected identifier")))
156+
Err(ScanErrorKind::SyntaxNoMessage)
157+
},
158+
}
159+
}
160+
}
161+
162+
#[cfg(not(str_into_output_extra_broken))]
106163
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
107164
impl<'a, Output> ScanFromStr<'a> for Ident<'a, Output>
108165
where &'a str: Into<Output> {
@@ -143,6 +200,31 @@ Note that this is effectively equivalent to the `Everything` matcher when used w
143200
*/
144201
pub struct Line<'a, Output=&'a str>(PhantomData<(&'a (), Output)>);
145202

203+
#[cfg(str_into_output_extra_broken)]
204+
impl<'a> ScanFromStr<'a> for Line<'a, &'a str> {
205+
type Output = &'a str;
206+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
207+
const EX_MSG: &'static str = "line scanning regex failed to match anything";
208+
let cap = LINE_RE.captures(s).expect(EX_MSG);
209+
let (_, b) = cap.pos(0).expect(EX_MSG);
210+
let (c, d) = cap.pos(1).expect(EX_MSG);
211+
Ok((s[c..d].into(), b))
212+
}
213+
}
214+
215+
#[cfg(str_into_output_extra_broken)]
216+
impl<'a> ScanFromStr<'a> for Line<'a, String> {
217+
type Output = String;
218+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
219+
const EX_MSG: &'static str = "line scanning regex failed to match anything";
220+
let cap = LINE_RE.captures(s).expect(EX_MSG);
221+
let (_, b) = cap.pos(0).expect(EX_MSG);
222+
let (c, d) = cap.pos(1).expect(EX_MSG);
223+
Ok((s[c..d].into(), b))
224+
}
225+
}
226+
227+
#[cfg(not(str_into_output_extra_broken))]
146228
impl<'a, Output> ScanFromStr<'a> for Line<'a, Output> where &'a str: Into<Output> {
147229
type Output = Output;
148230
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
@@ -157,11 +239,11 @@ impl<'a, Output> ScanFromStr<'a> for Line<'a, Output> where &'a str: Into<Output
157239
#[cfg(test)]
158240
#[test]
159241
fn test_line() {
160-
assert_match!(Line::scan_from(""), Ok(("", 0)));
161-
assert_match!(Line::scan_from("abc def"), Ok(("abc def", 7)));
162-
assert_match!(Line::scan_from("abc\ndef"), Ok(("abc", 4)));
163-
assert_match!(Line::scan_from("abc\r\ndef"), Ok(("abc", 5)));
164-
assert_match!(Line::scan_from("abc\rdef"), Ok(("abc", 4)));
242+
assert_match!(Line::<&str>::scan_from(""), Ok(("", 0)));
243+
assert_match!(Line::<&str>::scan_from("abc def"), Ok(("abc def", 7)));
244+
assert_match!(Line::<&str>::scan_from("abc\ndef"), Ok(("abc", 4)));
245+
assert_match!(Line::<&str>::scan_from("abc\r\ndef"), Ok(("abc", 5)));
246+
assert_match!(Line::<&str>::scan_from("abc\rdef"), Ok(("abc", 4)));
165247
}
166248

167249
/**
@@ -172,6 +254,41 @@ This *will not* match an empty sequence; there must be at least one non-space ch
172254
pub struct NonSpace<'a, Output=&'a str>(PhantomData<(&'a (), Output)>);
173255

174256
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
257+
#[cfg(str_into_output_extra_broken)]
258+
impl<'a> ScanFromStr<'a> for NonSpace<'a, &'a str> {
259+
type Output = &'a str;
260+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
261+
match NONSPACE_RE.find(s) {
262+
Some((a, b)) => {
263+
let word = &s[a..b];
264+
let tail = &s[b..];
265+
Ok((word.into(), s.subslice_offset(tail).unwrap()))
266+
},
267+
// None => Err(ScanErrorKind::Syntax(Some("expected at least one non-space character"))),
268+
None => Err(ScanErrorKind::SyntaxNoMessage)
269+
}
270+
}
271+
}
272+
273+
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
274+
#[cfg(str_into_output_extra_broken)]
275+
impl<'a> ScanFromStr<'a> for NonSpace<'a, String> {
276+
type Output = String;
277+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
278+
match NONSPACE_RE.find(s) {
279+
Some((a, b)) => {
280+
let word = &s[a..b];
281+
let tail = &s[b..];
282+
Ok((word.into(), s.subslice_offset(tail).unwrap()))
283+
},
284+
// None => Err(ScanErrorKind::Syntax(Some("expected at least one non-space character"))),
285+
None => Err(ScanErrorKind::SyntaxNoMessage)
286+
}
287+
}
288+
}
289+
290+
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
291+
#[cfg(not(str_into_output_extra_broken))]
175292
impl<'a, Output> ScanFromStr<'a> for NonSpace<'a, Output>
176293
where &'a str: Into<Output> {
177294
type Output = Output;
@@ -215,6 +332,41 @@ Note that this *includes* non-ASCII decimal characters, meaning it will scan num
215332
pub struct Number<'a, Output=&'a str>(PhantomData<(&'a (), Output)>);
216333

217334
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
335+
#[cfg(str_into_output_extra_broken)]
336+
impl<'a> ScanFromStr<'a> for Number<'a, &'a str> {
337+
type Output = &'a str;
338+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
339+
match NUMBER_RE.find(s) {
340+
Some((a, b)) => {
341+
let word = &s[a..b];
342+
let tail = &s[b..];
343+
Ok((word.into(), s.subslice_offset(tail).unwrap()))
344+
},
345+
// None => Err(ScanErrorKind::Syntax(Some("expected a number"))),
346+
None => Err(ScanErrorKind::SyntaxNoMessage),
347+
}
348+
}
349+
}
350+
351+
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
352+
#[cfg(str_into_output_extra_broken)]
353+
impl<'a> ScanFromStr<'a> for Number<'a, String> {
354+
type Output = String;
355+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
356+
match NUMBER_RE.find(s) {
357+
Some((a, b)) => {
358+
let word = &s[a..b];
359+
let tail = &s[b..];
360+
Ok((word.into(), s.subslice_offset(tail).unwrap()))
361+
},
362+
// None => Err(ScanErrorKind::Syntax(Some("expected a number"))),
363+
None => Err(ScanErrorKind::SyntaxNoMessage),
364+
}
365+
}
366+
}
367+
368+
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
369+
#[cfg(not(str_into_output_extra_broken))]
218370
impl<'a, Output> ScanFromStr<'a> for Number<'a, Output>
219371
where &'a str: Into<Output> {
220372
type Output = Output;
@@ -378,6 +530,41 @@ Specifically, this will match a continuous run of alphabetic, digit, punctuation
378530
pub struct Word<'a, Output=&'a str>(PhantomData<(&'a (), Output)>);
379531

380532
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
533+
#[cfg(str_into_output_extra_broken)]
534+
impl<'a> ScanFromStr<'a> for Word<'a, &'a str> {
535+
type Output = &'a str;
536+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
537+
match WORD_RE.find(s) {
538+
Some((a, b)) => {
539+
let word = &s[a..b];
540+
let tail = &s[b..];
541+
Ok((word.into(), s.subslice_offset(tail).unwrap()))
542+
},
543+
// None => Err(ScanErrorKind::Syntax(Some("expected a word"))),
544+
None => Err(ScanErrorKind::SyntaxNoMessage),
545+
}
546+
}
547+
}
548+
549+
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
550+
#[cfg(str_into_output_extra_broken)]
551+
impl<'a> ScanFromStr<'a> for Word<'a, String> {
552+
type Output = String;
553+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
554+
match WORD_RE.find(s) {
555+
Some((a, b)) => {
556+
let word = &s[a..b];
557+
let tail = &s[b..];
558+
Ok((word.into(), s.subslice_offset(tail).unwrap()))
559+
},
560+
// None => Err(ScanErrorKind::Syntax(Some("expected a word"))),
561+
None => Err(ScanErrorKind::SyntaxNoMessage),
562+
}
563+
}
564+
}
565+
566+
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
567+
#[cfg(not(str_into_output_extra_broken))]
381568
impl<'a, Output> ScanFromStr<'a> for Word<'a, Output>
382569
where &'a str: Into<Output> {
383570
type Output = Output;
@@ -420,6 +607,43 @@ Specifically, this will match a word (a continuous run of alphabetic, digit, pun
420607
pub struct Wordish<'a, Output=&'a str>(PhantomData<(&'a (), Output)>);
421608

422609
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
610+
#[cfg(str_into_output_extra_broken)]
611+
impl<'a> ScanFromStr<'a> for Wordish<'a, &'a str> {
612+
type Output = &'a str;
613+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
614+
// TODO: This should be modified to grab an entire *grapheme cluster* in the event it can't find a word or number.
615+
match WORDISH_RE.find(s) {
616+
Some((a, b)) => {
617+
let word = &s[a..b];
618+
let tail = &s[b..];
619+
Ok((word.into(), s.subslice_offset(tail).unwrap()))
620+
},
621+
// None => Err(ScanErrorKind::Syntax(Some("expected a word, number or some other character"))),
622+
None => Err(ScanErrorKind::SyntaxNoMessage),
623+
}
624+
}
625+
}
626+
627+
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
628+
#[cfg(str_into_output_extra_broken)]
629+
impl<'a> ScanFromStr<'a> for Wordish<'a, String> {
630+
type Output = String;
631+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
632+
// TODO: This should be modified to grab an entire *grapheme cluster* in the event it can't find a word or number.
633+
match WORDISH_RE.find(s) {
634+
Some((a, b)) => {
635+
let word = &s[a..b];
636+
let tail = &s[b..];
637+
Ok((word.into(), s.subslice_offset(tail).unwrap()))
638+
},
639+
// None => Err(ScanErrorKind::Syntax(Some("expected a word, number or some other character"))),
640+
None => Err(ScanErrorKind::SyntaxNoMessage),
641+
}
642+
}
643+
}
644+
645+
// FIXME: Error message omitted due to https://github.com/rust-lang/rust/issues/26448.
646+
#[cfg(not(str_into_output_extra_broken))]
423647
impl<'a, Output> ScanFromStr<'a> for Wordish<'a, Output>
424648
where &'a str: Into<Output> {
425649
type Output = Output;

0 commit comments

Comments
 (0)