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

wip

parent f33a77e2
Branches
No related tags found
No related merge requests found
README.md 0 → 100644
# Writing parsers using `syn` and `nom` parser combinators
## Parsing a LitInt and rejecting invalid range
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`.
Consider the following program (`ex1.rs`):
``` rust
#![feature(proc_macro)]
extern crate parsetest;
use parsetest::mylit;
fn main() {
// should pass
let _v = mylit!(99);
// should be rejected
let _v = mylit!(102);
// should be rejected
let _v = mylit!(9o9);
// should be rejected
let _v = mylit!((99));
}
```
The expected outcome for `mylit!` is:
``` shell
> Executing task: cargo run --example ex1 <
Compiling parsetest v0.1.0 (file:///home/pln/course/parsetest)
error: expected literal 10 <= x < 100, got 102
--> examples/ex1.rs:11:21
|
11 | let _v = mylit!(102);
| ^^^
error: expected literal 10 <= x < 100, got 9
--> examples/ex1.rs:14:21
|
14 | let _v = mylit!(9o9);
| ^^^
error: invalid suffix `o9` for numeric literal
--> examples/ex1.rs:14:21
|
14 | let _v = mylit!(9o9);
| ^^^
|
= help: the suffix must be one of the integral types (`u32`, `isize`, etc)
error: proc macro panicked
--> examples/ex1.rs:17:14
|
17 | let _v = mylit!((99));
| ^^^^^^^^^^^^
|
= help: message: called `Result::unwrap()` on an `Err` value: ParseError(None)
error: Could not compile `parsetest`.
```
The procedural macro is defined in the `src/lib.rs` as follows.
``` rust
#![feature(proc_macro)]
extern crate proc_macro;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
use proc_macro::TokenStream;
use syn::spanned::Spanned;
use syn::synom::Synom;
use syn::LitInt;
use quote::ToTokens;
use std::convert::From;
/// MyLit
struct MyLit {
val: LitInt,
}
impl Synom for MyLit {
named!(parse -> Self, do_parse!(
val: syn!(LitInt) >> (MyLit { val })
));
}
#[proc_macro]
pub fn mylit(input: TokenStream) -> TokenStream {
let v: MyLit = syn::parse(input).unwrap();
let value: u32 = v.val.value() as u32;
if !(10 <= value && value < 100) {
v.val
.span()
.unstable()
.error(format!("expected literal 10 <= x < 100, got {}", value,))
.emit();
}
From::from(v.val.into_tokens())
}
```
So `MyLit` here is a structure for holding the parsed token.
We implement `Synom` for `MyLit`, to exactly parse a `LitInt`.
The `named!` macro turns the argument (a parser combinator) into a function, in this case
a function with the identifier `parser` (mathing the `Synom` trait `parser`).
``` rust
pub trait Synom: Sized {
fn parse(input: Cursor) -> PResult<Self>;
fn description() -> Option<&'static str> { ... }
}
```
In order for `syn::parse` to be able to parse a `TokenStream` for
#![feature(prelude_import)]
#![no_std]
#![feature(proc_macro)]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std as std;
extern crate proc_macro;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
use proc_macro::TokenStream;
use syn::spanned::Spanned;
use syn::synom::Synom;
use syn::LitInt;
use quote::ToTokens;
use std::convert::From;
use std::error::Error;
/// MyLit
struct MyLit {
val: LitInt,
}
impl Synom for MyLit {
// /// MyLitP
// struct MyLitP {
// val: LitInt,
// }
// impl Synom for MyLitP {
// named!(parse -> Self, do_parse!(
// val: parens!(call!(LitInt::parse)) >> (MyLitP { val: val.1 })
// ));
// }
// #[proc_macro]
// pub fn mylitp(input: TokenStream) -> TokenStream {
// match syn::parse::<MyLitP>(input) {
// Ok(v) => {
// let value: u32 = v.val.value() as u32;
// if !(10 <= value && value < 100) {
// v.val
// .span()
// .unstable()
// .error(format!(
// "expected literal 10 <= x < 100, got {}",
// value,
// ))
// .emit();
// }
// From::from(v.val.into_tokens())
// }
// Err(err) => {
// let desc = err.description();
// let tokens = quote! {
// compile_error!(#desc)
// };
// return tokens.into();
// }
// }
// }
// // /// MyLits
// // struct MyLits {
// // val: LitInt,
// // valp: LitInt,
// // }
// // impl Synom for MyLits {
// // named!(parse -> Self, do_parse!(
// // val: syn!
// // valp: parens!(call!(LitInt::parse)) >> (MyLitP { val: val.1 })
// // ));
// // }
// // #[proc_macro]
// // pub fn mylitp(input: TokenStream) -> TokenStream {
// // match syn::parse::<MyLitP>(input) {
// // Ok(v) => {
// // let value: u32 = v.val.value() as u32;
// // if !(10 <= value && value < 100) {
// // v.val
// // .span()
// // .unstable()
// // .error(format!(
// // "expected literal 10 <= x < 100, got {}",
// // value,
// // ))
// // .emit();
// // }
// // From::from(v.val.into_tokens())
// // }
// // Err(err) => {
// // //let desc = format!("could not parse {:?}", err);
// // let desc = err.description();
// // // println!(
// // // "here -----------------------------------------desc {:?}",
// // // desc
// // // );
// // let tokens = quote! {
// // compile_error!(#desc)
// // };
// // return tokens.into();
// // }
// // }
// // }
fn parse(i: ::buffer::Cursor) -> ::synom::PResult<Self> {
match <LitInt as ::synom::Synom>::parse(i) {
::std::result::Result::Err(err) => ::std::result::Result::Err(err),
::std::result::Result::Ok((o, i)) => {
let val = o;
::std::result::Result::Ok(((MyLit { val }), i))
}
}
}
}
#[proc_macro]
pub fn mylit(input: TokenStream) -> TokenStream {
let v: MyLit = syn::parse(input).unwrap();
let value: u32 = v.val.value() as u32;
if !(10 <= value && value < 100) {
v.val
.span()
.unstable()
.error(::fmt::format(::std::fmt::Arguments::new_v1_formatted(
&["expected literal 10 <= x < 100, got "],
&match (&value,) {
(__arg0,) => [
::std::fmt::ArgumentV1::new(
__arg0,
::std::fmt::Display::fmt,
),
],
},
&[
::std::fmt::rt::v1::Argument {
position: ::std::fmt::rt::v1::Position::At(0usize),
format: ::std::fmt::rt::v1::FormatSpec {
fill: ' ',
align: ::std::fmt::rt::v1::Alignment::Unknown,
flags: 0u32,
precision: ::std::fmt::rt::v1::Count::Implied,
width: ::std::fmt::rt::v1::Count::Implied,
},
},
],
)))
.emit();
}
From::from(v.val.into_tokens())
}
pub mod registrar {
extern crate proc_macro;
#[rustc_derive_registrar]
pub fn _registrar(_registrar: &mut proc_macro::__internal::Registry) -> () {
proc_macro::__internal::Registry::register_bang_proc_macro(
_registrar,
"mylit",
::mylit,
)
}
}
#![feature(proc_macro)] #![feature(proc_macro)]
extern crate parsetest; extern crate parsetest;
use parsetest::{mylit, mylitp}; use parsetest::mylit;
fn main() { fn main() {
println!("here"); // should pass
let _v = mylit!(99);
// let v = mylit!(99); // should be rejected
// println!("v {}", v); let _v = mylit!(102);
// let v = mylit!(102); // should be rejected
// println!("v {}", v); let _v = mylit!(9o9);
let v = mylitp!((99)); // should be rejected
println!("v {}", v); let _v = mylit!((99));
let v = mylitp!(99);
println!("v {}", v);
let v = mylitp!((9o9));
println!("v {}", v);
} }
#![feature(proc_macro)]
extern crate parsetest;
use parsetest::{mylit, mylitp};
fn main() {
println!("here");
// should pass
let v = mylit!(99);
println!("v {}", v);
// be rejected
let v = mylit!(102);
println!("v {}", v);
// should be rejected
let v = mylitp!((99));
println!("v {}", v);
// should pass
let v = mylitp!((99));
println!("v {}", v);
// should be rejected
let v = mylitp!(99);
println!("v {}", v);
// should be rejected
let v = mylitp!((9o9));
println!("v {}", v);
}
...@@ -7,7 +7,6 @@ extern crate quote; ...@@ -7,7 +7,6 @@ extern crate quote;
extern crate syn; extern crate syn;
use proc_macro::TokenStream; use proc_macro::TokenStream;
// use syn::punctuated::Punctuated;
use syn::spanned::Spanned; use syn::spanned::Spanned;
use syn::synom::Synom; use syn::synom::Synom;
use syn::LitInt; use syn::LitInt;
...@@ -23,7 +22,7 @@ struct MyLit { ...@@ -23,7 +22,7 @@ struct MyLit {
impl Synom for MyLit { impl Synom for MyLit {
named!(parse -> Self, do_parse!( named!(parse -> Self, do_parse!(
val: call!(LitInt::parse) >> (MyLit { val }) val: syn!(LitInt) >> (MyLit { val })
)); ));
} }
...@@ -41,52 +40,87 @@ pub fn mylit(input: TokenStream) -> TokenStream { ...@@ -41,52 +40,87 @@ pub fn mylit(input: TokenStream) -> TokenStream {
From::from(v.val.into_tokens()) From::from(v.val.into_tokens())
} }
/// MyLit // /// MyLitP
struct MyLitP { // struct MyLitP {
val: LitInt, // val: LitInt,
} // }
impl Synom for MyLitP { // impl Synom for MyLitP {
// named!(parse -> Self, do_parse!( // named!(parse -> Self, do_parse!(
// val: call!(LitInt::parse) >> (MyLitP { val }) // val: parens!(call!(LitInt::parse)) >> (MyLitP { val: val.1 })
// )); // ));
named!(parse -> Self, do_parse!( // }
val: parens!(call!(LitInt::parse)) >> (MyLitP { val: val.1 })
));
}
#[proc_macro] // #[proc_macro]
pub fn mylitp(input: TokenStream) -> TokenStream { // pub fn mylitp(input: TokenStream) -> TokenStream {
match syn::parse::<MyLitP>(input) { // match syn::parse::<MyLitP>(input) {
Ok(v) => { // Ok(v) => {
let value: u32 = v.val.value() as u32; // let value: u32 = v.val.value() as u32;
if !(10 <= value && value < 100) { // if !(10 <= value && value < 100) {
v.val // v.val
.span() // .span()
.unstable() // .unstable()
.error(format!( // .error(format!(
"expected literal 10 <= x < 100, got {}", // "expected literal 10 <= x < 100, got {}",
value, // value,
)) // ))
.emit(); // .emit();
} // }
From::from(v.val.into_tokens()) // From::from(v.val.into_tokens())
} // }
Err(err) => { // Err(err) => {
//let desc = format!("could not parse {:?}", err); // let desc = err.description();
let desc = err.description(); // let tokens = quote! {
// println!( // compile_error!(#desc)
// "here -----------------------------------------desc {:?}", // };
// desc // return tokens.into();
// ); // }
let tokens = quote! { // }
compile_error!(#desc) // }
};
return tokens.into(); // // /// MyLits
} // // struct MyLits {
} // // val: LitInt,
} // // valp: LitInt,
// // }
// // impl Synom for MyLits {
// // named!(parse -> Self, do_parse!(
// // val: syn!
// // valp: parens!(call!(LitInt::parse)) >> (MyLitP { val: val.1 })
// // ));
// // }
// // #[proc_macro]
// // pub fn mylitp(input: TokenStream) -> TokenStream {
// // match syn::parse::<MyLitP>(input) {
// // Ok(v) => {
// // let value: u32 = v.val.value() as u32;
// // if !(10 <= value && value < 100) {
// // v.val
// // .span()
// // .unstable()
// // .error(format!(
// // "expected literal 10 <= x < 100, got {}",
// // value,
// // ))
// // .emit();
// // }
// // From::from(v.val.into_tokens())
// // }
// // Err(err) => {
// // //let desc = format!("could not parse {:?}", err);
// // let desc = err.description();
// // // println!(
// // // "here -----------------------------------------desc {:?}",
// // // desc
// // // );
// // let tokens = quote! {
// // compile_error!(#desc)
// // };
//panic!("here {}", err), // // return tokens.into();
//let desc = syn::synom::Synom::description(err); // // }
// // }
// // }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment