diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..852134ce3b3a176060b8ff10449a835c6dd5d5ac --- /dev/null +++ b/README.md @@ -0,0 +1,129 @@ +# 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 diff --git a/epanded_ex1.rs b/epanded_ex1.rs new file mode 100644 index 0000000000000000000000000000000000000000..242e95e1d7b76f56f81b5f3c8eae76e92fbdfe69 --- /dev/null +++ b/epanded_ex1.rs @@ -0,0 +1,169 @@ +#![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, + ) + } +} diff --git a/examples/ex1.rs b/examples/ex1.rs index 77e285ff585ac8cca5d91dea549408d18797ed79..81f19737ec8fa2c01630b15a6d5d8e0882bad6a4 100644 --- a/examples/ex1.rs +++ b/examples/ex1.rs @@ -1,23 +1,18 @@ #![feature(proc_macro)] extern crate parsetest; -use parsetest::{mylit, mylitp}; +use parsetest::mylit; fn main() { - println!("here"); + // should pass + let _v = mylit!(99); - // let v = mylit!(99); - // println!("v {}", v); + // should be rejected + let _v = mylit!(102); - // let v = mylit!(102); - // println!("v {}", v); + // should be rejected + let _v = mylit!(9o9); - let v = mylitp!((99)); - println!("v {}", v); - - let v = mylitp!(99); - println!("v {}", v); - - let v = mylitp!((9o9)); - println!("v {}", v); + // should be rejected + let _v = mylit!((99)); } diff --git a/examples/ex2.rs b/examples/ex2.rs new file mode 100644 index 0000000000000000000000000000000000000000..0998c6818f1eebeee7a4d2cb6959af4f1b40f4fc --- /dev/null +++ b/examples/ex2.rs @@ -0,0 +1,32 @@ +#![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); +} diff --git a/src/lib.rs b/src/lib.rs index b858620df26d28851b1be98c03c83efdf1b35aa9..f42037dc9e142aea45df1b911c336efc0a43dd48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,6 @@ extern crate quote; extern crate syn; use proc_macro::TokenStream; -// use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::synom::Synom; use syn::LitInt; @@ -23,7 +22,7 @@ struct MyLit { impl Synom for MyLit { 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 { From::from(v.val.into_tokens()) } -/// MyLit -struct MyLitP { - val: LitInt, -} +// /// MyLitP +// struct MyLitP { +// val: LitInt, +// } -impl Synom for MyLitP { - // named!(parse -> Self, do_parse!( - // val: call!(LitInt::parse) >> (MyLitP { val }) - // )); - named!(parse -> Self, do_parse!( - val: parens!(call!(LitInt::parse)) >> (MyLitP { val: val.1 }) - )); -} +// 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 = format!("could not parse {:?}", err); - let desc = err.description(); - // println!( - // "here -----------------------------------------desc {:?}", - // desc - // ); - let tokens = quote! { - compile_error!(#desc) - }; +// #[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(); +// } +// } +// } - 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), -//let desc = syn::synom::Synom::description(err); +// // return tokens.into(); +// // } +// // } +// // }