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

small fixes

parent 0b808a4b
Branches
No related tags found
No related merge requests found
# Writing parsers using `syn` and `nom` parser combinators # Writing parsers using `syn` and `nom` parser combinators
## Parsing a LitInt and rejecting invalid range Disclaimer, I'm NOT the author of the `syn` and `nom` parsing frameworks, so no guarantee the text accurately conveys their idiomatic use.
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
As a simple example let us start out with `mylit!(LitInt)`, where As a simple example let us start out with `mylit!(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`.
...@@ -71,7 +75,6 @@ The procedural macro is defined in the `src/lib.rs` as follows. ...@@ -71,7 +75,6 @@ The procedural macro is defined in the `src/lib.rs` as follows.
#![feature(proc_macro)] #![feature(proc_macro)]
extern crate proc_macro; extern crate proc_macro;
#[macro_use]
extern crate quote; extern crate quote;
#[macro_use] #[macro_use]
extern crate syn; extern crate syn;
...@@ -114,12 +117,14 @@ pub trait Synom: Sized { ...@@ -114,12 +117,14 @@ pub trait Synom: Sized {
The `do_parse!` processes a combination of parsers (in this case just a single one), The `do_parse!` processes a combination of parsers (in this case just a single one),
`val: syn!(LitInt) >> (MyLit { val })`, where `val` will be assigned the result of `val: syn!(LitInt) >> (MyLit { val })`, where `val` will be assigned the result of
`syn!(LitInt)`, and used in the right hand `MyLit {val}`. (As usal in Rust a struct can be contructed from varibles with matching field names, to make it more succint.) `syn!(LitInt)`, and used in the right hand `MyLit { val }`. (As usal in Rust a struct can be constructed from varibles with matching field names, to make it more succint.)
The `syn!` macro simly applies the parser for `LitInt` (requres a type itself implementing `Synom`). The `syn!` macro simly applies the parser for `LitInt` (requires a type that itself implementing `Synom`).
So now we have broken down the trait implmentation, we can see the expanded code. (As we see, the use of macros greately facilitates writing parsers.) So now we have broken down the trait implmentation, we can see the expanded code. (As we see, the use of macros greately facilitates writing parsers.)
(On a side note, we did not implment `description` for `MyLit`, here we can rely on a default implemntation given by the `syn` crate.)
``` rust ``` rust
fn parse(i: ::buffer::Cursor) -> ::synom::PResult<Self> { fn parse(i: ::buffer::Cursor) -> ::synom::PResult<Self> {
match <LitInt as ::synom::Synom>::parse(i) { match <LitInt as ::synom::Synom>::parse(i) {
...@@ -132,15 +137,13 @@ fn parse(i: ::buffer::Cursor) -> ::synom::PResult<Self> { ...@@ -132,15 +137,13 @@ fn parse(i: ::buffer::Cursor) -> ::synom::PResult<Self> {
} }
``` ```
(On a side note, we did not implment `description` for `MyLit`, here we can rely on a default implemntation given by the `syn` crate.)
The parser is made accissible to the user by a procedural macro as follows. The parser is made accissible to the user by a procedural macro as follows.
``` rust ``` rust
#[proc_macro] #[proc_macro]
pub fn mylit(input: TokenStream) -> TokenStream { pub fn mylit(input: TokenStream) -> TokenStream {
let v: MyLit = syn::parse(input).unwrap(); let v: MyLit = syn::parse(input).unwrap();
let value: u32 = v.val.value() as u32; let value = v.val.value();
if !(10 <= value && value < 100) { if !(10 <= value && value < 100) {
v.val v.val
.span() .span()
...@@ -152,4 +155,6 @@ pub fn mylit(input: TokenStream) -> TokenStream { ...@@ -152,4 +155,6 @@ pub fn mylit(input: TokenStream) -> TokenStream {
} }
``` ```
Besides just parsing an `IntLit` (that is a `u64`), we want at compile time to ensure that it is in a given range, say between 10 and 100, and report a sensible error message for illegal usage. The 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.
\ No newline at end of file
The error message is presented as a `TokenStream` throught the `.emit()` function.
\ No newline at end of file
#![feature(proc_macro)] #![feature(proc_macro)]
extern crate proc_macro; extern crate proc_macro;
#[macro_use] // #[macro_use]
extern crate quote; extern crate quote;
#[macro_use] #[macro_use]
extern crate syn; extern crate syn;
...@@ -13,7 +13,7 @@ use syn::LitInt; ...@@ -13,7 +13,7 @@ use syn::LitInt;
use quote::ToTokens; use quote::ToTokens;
use std::convert::From; use std::convert::From;
use std::error::Error; // use std::error::Error;
/// MyLit /// MyLit
struct MyLit { struct MyLit {
...@@ -29,7 +29,7 @@ impl Synom for MyLit { ...@@ -29,7 +29,7 @@ impl Synom for MyLit {
#[proc_macro] #[proc_macro]
pub fn mylit(input: TokenStream) -> TokenStream { pub fn mylit(input: TokenStream) -> TokenStream {
let v: MyLit = syn::parse(input).unwrap(); let v: MyLit = syn::parse(input).unwrap();
let value: u32 = v.val.value() as u32; let value = v.val.value();
if !(10 <= value && value < 100) { if !(10 <= value && value < 100) {
v.val v.val
.span() .span()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment