@@ -4,67 +4,71 @@ Disclaimer, I'm NOT the author of the `syn` and `nom` parsing frameworks, so no
...
@@ -4,67 +4,71 @@ Disclaimer, I'm NOT the author of the `syn` and `nom` parsing frameworks, so no
The same goes for all references to supporting crates and references to other Rust libraries, crates and the language itself, so use material at own risk :)
The same goes for all references to supporting crates and references to other Rust libraries, crates and the language itself, so use material at own risk :)
## Parsing a `LitInt` and rejecting invalid range
## Built in parsers
As a simple example let us start out with `mylit!(LitInt)`, where
The `syn::parse` function allows to parse any struct that implements the `syn::synom::Synom` trait. The `syn` crate provides a large set of structs which satisfies this requirement, which we can use directly or for building parser combinators.
We can invoke custom parsers through procedural macros in Rust.
As a simple example let us start out with simple macro `lit!` parsing a `LitInt`, where
`LitInt` should be an intereger literal `x` in the range `10 <= x < 100`.
`LitInt` should be an intereger literal `x` in the range `10 <= x < 100`.
=help: the suffix must be one of the integral types (`u32`, `isize`, etc)
=help: the suffix must be one of the integral types (`u32`, `isize`, etc)
error: proc macro panicked
error: proc macro panicked
--> examples/ex1.rs:17:14
--> examples/ex_lit.rs:17:14
|
|
17 | let _v =mylit!((99));
17 | let _v = lit!((99));
| ^^^^^^^^^^^^
| ^^^^^^^^^^
|
|
=help: message: called `Result::unwrap()` on an `Err` value: ParseError(None)
=help: message: called `Result::unwrap()` on an `Err` value: ParseError(Some("failed to parse integer literal: failed to parse"))
error: Could not compile `parsetest`.
error: Could not compile `parsetest`.
```
```
...
@@ -87,6 +91,108 @@ use quote::ToTokens;
...
@@ -87,6 +91,108 @@ use quote::ToTokens;
usestd::convert::From;
usestd::convert::From;
/// Procedural macro `lit`
/// Parsing a LinInt with compile time range checking
#[proc_macro]
pubfnlit(input:TokenStream)->TokenStream{
letv:LitInt=syn::parse(input).unwrap();
letvalue=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())
}
```
The `syn::LitInt` type implement the `syn::synom::Synom` trait (required by the `syn::parse` function). The unwrapped result is of type `LitInt`, for which we can get the inner value representation (a `u64`).
In this example we use that to (at compile time) ensure that the value is within a given range, (between 10 and 100), and reject faulty assignment with a sensible error message.
The error message is presented as a `TokenStream` throught the `.emit()` function. This does NOT imply an immediate abort (unlike a `panic!`), thus rustc will continue compilation and catch all errors.
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 handle that as well as follows, for the example `ex_lite.rs`.
Besides just parsing an `IntLit` (that is a `u64`), we want at compile time to ensure that it is in a given range, (between 10 and 100 in this example), and report a sensible error message for illegal usage.
The error message is presented as a `TokenStream` throught the `.emit()` function.