Skip to content
Snippets Groups Projects
Commit b33cc37d authored by Per Lindgren's avatar Per Lindgren
Browse files

lit and lite improved

parent 6564cc67
No related branches found
No related tags found
No related merge requests found
...@@ -33,7 +33,11 @@ fn main() { ...@@ -33,7 +33,11 @@ fn main() {
// should be rejected // should be rejected
let _v = lit!((99)); let _v = lit!((99));
// should be rejected
let _v = lit!(0);
} }
``` ```
The expected outcome for `lit!` is: The expected outcome for `lit!` is:
...@@ -115,7 +119,9 @@ The error message is presented as a `TokenStream` throught the `.emit()` functio ...@@ -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 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 ``` rust
#![feature(proc_macro)] #![feature(proc_macro)]
...@@ -126,13 +132,17 @@ use parsetest::lite; ...@@ -126,13 +132,17 @@ use parsetest::lite;
fn main() { fn main() {
// should be rejected // should be rejected
let _v = lite!((99)); 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 ``` shell
> Executing task: cargo run --example ex_lite < > 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 error: failed to parse integer literal: failed to parse
--> examples/ex_lite.rs:8:14 --> examples/ex_lite.rs:8:14
...@@ -140,11 +150,19 @@ error: failed to parse integer literal: failed to parse ...@@ -140,11 +150,19 @@ error: failed to parse integer literal: failed to parse
8 | let _v = lite!((99)); 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`. 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. The backing implementation now matches the parsed result and inserts a `compiler_error!`, instead of the previous unwrap.
``` rust ``` rust
...@@ -191,7 +209,9 @@ pub fn lite(input: TokenStream) -> TokenStream { ...@@ -191,7 +209,9 @@ pub fn lite(input: TokenStream) -> TokenStream {
The description here is the default error, provided by the built in `LitInt` parser. 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 /// MyLit
struct MyLit { struct MyLit {
......
...@@ -15,4 +15,7 @@ fn main() { ...@@ -15,4 +15,7 @@ fn main() {
// should be rejected // should be rejected
let _v = lit!((99)); let _v = lit!((99));
// should be rejected
let _v = lit!(0);
} }
...@@ -6,4 +6,7 @@ use parsetest::lite; ...@@ -6,4 +6,7 @@ use parsetest::lite;
fn main() { fn main() {
// should be rejected // should be rejected
let _v = lite!((99)); let _v = lite!((99));
// should be rejected
let _v = lite!(0);
} }
...@@ -16,7 +16,7 @@ use std::convert::From; ...@@ -16,7 +16,7 @@ use std::convert::From;
use std::error::Error; use std::error::Error;
/// Procedural macro `lit` /// Procedural macro `lit`
/// Parsing a LinInt with compile time range checking /// Parsing a `LitInt` with compile time range checking
#[proc_macro] #[proc_macro]
pub fn lit(input: TokenStream) -> TokenStream { pub fn lit(input: TokenStream) -> TokenStream {
let v: LitInt = syn::parse(input).unwrap(); let v: LitInt = syn::parse(input).unwrap();
...@@ -31,7 +31,7 @@ pub fn lit(input: TokenStream) -> TokenStream { ...@@ -31,7 +31,7 @@ pub fn lit(input: TokenStream) -> TokenStream {
} }
/// Procedural macro `lite` /// Procedural macro `lite`
/// Parsing a LinInt with compile time range checking /// Parsing a `LitInt` with compile time range checking
#[proc_macro] #[proc_macro]
pub fn lite(input: TokenStream) -> TokenStream { pub fn lite(input: TokenStream) -> TokenStream {
match syn::parse::<LitInt>(input) { match syn::parse::<LitInt>(input) {
...@@ -55,6 +55,36 @@ pub fn lite(input: TokenStream) -> TokenStream { ...@@ -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 /// MyLit
struct MyLit { struct MyLit {
val: LitInt, val: LitInt,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment