diff --git a/README.md b/README.md index 13c5e689f07413a3e95c6969b15bc1f899e5f87c..27fc72ca3e8384cba751b24614dec0910b7437d8 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,11 @@ fn main() { // should be rejected let _v = lit!((99)); + + // should be rejected + let _v = lit!(0); } + ``` The expected outcome for `lit!` is: @@ -115,7 +119,9 @@ The error message is presented as a `TokenStream` throught the `.emit()` functio For the first case (`lit!(99)`) the parsing succeeded and the returned token stream would amount to the literate integer 99. As our `v` is a `LitInt` we need to convert it into a `TokenStream`, a two stage process by first turning `v` into tokens, from wich we get a token stream. (You may comment out the failing parts and print the value of `_v` to convince yourself.) -For the second and third cases, we see that appropriate errors and spans are reported by the built in parser. However the last case is somewhat unsatisfying, due to the `syn::parse` failing (unwrap on an `Err`). We can improve that as follows, `ex_lite.rs`. +For the second and third cases, we see that appropriate errors and spans are reported by the built in parser. However the last case is somewhat unsatisfying, due to the `syn::parse` failing (unwrap on an `Err`) the procedural macro fails with a panic. Hence the `lit!(0)` is never parsed and the error reporting hempered. + +We can improve that as follows, `ex_lite.rs`. ``` rust #![feature(proc_macro)] @@ -126,25 +132,37 @@ use parsetest::lite; fn main() { // should be rejected let _v = lite!((99)); + + // should be rejected + let _v = lite!(0); } ``` -With the improved error reporting, we can get. +With the improved error reporting, we get. ``` shell > Executing task: cargo run --example ex_lite < -Compiling parsetest v0.1.0 (file:///home/pln/rtfm/parsetest) + + Compiling parsetest v0.1.0 (file:///home/pln/rtfm/parsetest) error: failed to parse integer literal: failed to parse --> examples/ex_lite.rs:8:14 | 8 | let _v = lite!((99)); | ^^^^^^^^^^^ -error: aborting due to previous error +error: expected literal 10 <= x < 100, got 0 + --> examples/ex_lite.rs:11:20 + | +11 | let _v = lite!(0); + | ^ + +error: aborting due to 2 previous errors error: Could not compile `parsetest`. ``` +Here we obtain a sensible error message for the first case, as well as for the second case (no more panicking behavior in the procedural macro). + The backing implementation now matches the parsed result and inserts a `compiler_error!`, instead of the previous unwrap. ``` rust @@ -191,7 +209,9 @@ pub fn lite(input: TokenStream) -> TokenStream { The description here is the default error, provided by the built in `LitInt` parser. -## Direct approach +## Combining parsers + +The real strenght of the `syn` and `nom` frameworks comes from the ability to combine parsers. We will start by looking at the last error, assuming we want to parse something like `(LitInt)` into a plain `LitInt`. /// MyLit struct MyLit { diff --git a/examples/ex_lit.rs b/examples/ex_lit.rs index ce02ca9f3da0891673359131980daf22728b39bd..7d554f6b15ca93be28fd648462bf549d5d08a2bc 100644 --- a/examples/ex_lit.rs +++ b/examples/ex_lit.rs @@ -15,4 +15,7 @@ fn main() { // should be rejected let _v = lit!((99)); + + // should be rejected + let _v = lit!(0); } diff --git a/examples/ex_lite.rs b/examples/ex_lite.rs index 3718a49dcb81dedcd35690fe21d97f2f9b3b5b5b..93aa7cf9e55d4715f1fce9e08bc5b686cd117966 100644 --- a/examples/ex_lite.rs +++ b/examples/ex_lite.rs @@ -6,4 +6,7 @@ use parsetest::lite; fn main() { // should be rejected let _v = lite!((99)); + + // should be rejected + let _v = lite!(0); } diff --git a/src/lib.rs b/src/lib.rs index c889c5530edbcb35299df83c7daff563510958eb..e59aaf4ccc80cceb5ff6a7e9a5d8efe69b877326 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ use std::convert::From; use std::error::Error; /// Procedural macro `lit` -/// Parsing a LinInt with compile time range checking +/// Parsing a `LitInt` with compile time range checking #[proc_macro] pub fn lit(input: TokenStream) -> TokenStream { let v: LitInt = syn::parse(input).unwrap(); @@ -31,7 +31,7 @@ pub fn lit(input: TokenStream) -> TokenStream { } /// Procedural macro `lite` -/// Parsing a LinInt with compile time range checking +/// Parsing a `LitInt` with compile time range checking #[proc_macro] pub fn lite(input: TokenStream) -> TokenStream { match syn::parse::<LitInt>(input) { @@ -55,6 +55,36 @@ pub fn lite(input: TokenStream) -> TokenStream { } } +// named!(parse -> LitInt, do_parse!( +// val: syn!(LitInt) >> (val) +// )); + +// /// Procedural macro `litep` +// /// Parsing a `(LitInt)` into a `LitInt` with compile time range checking +// #[proc_macro] +// pub fn litep(input: TokenStream) -> TokenStream { +// let v = do_parse!(val: parens!(call!(LitInt::parse)) >> (MyLitP { val: val.1 })); +// match v(input) { +// Ok(v) => { +// let value = v.value(); +// if !(10 <= value && value < 100) { +// v.span() +// .unstable() +// .error(format!("expected literal 10 <= x < 100, got {}", value,)) +// .emit(); +// } +// From::from(v.into_tokens()) +// } +// Err(err) => { +// let desc = err.description(); +// let tokens = quote! { +// compile_error!(#desc) +// }; +// return tokens.into(); +// } +// } +// } + /// MyLit struct MyLit { val: LitInt,