pear_codegen-0.2.9/.cargo_vcs_info.json0000644000000001450000000000100134570ustar { "git": { "sha1": "f803fa9daf1a022f1a216a16da2a0778474ecd43" }, "path_in_vcs": "codegen" }pear_codegen-0.2.9/Cargo.toml0000644000000017510000000000100114610ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "pear_codegen" version = "0.2.9" authors = ["Sergio Benitez "] description = "A (codegen) pear is a fruit." license = "MIT OR Apache-2.0" repository = "https://github.com/SergioBenitez/Pear" [lib] proc-macro = true [dependencies.proc-macro2] version = "1.0.50" [dependencies.proc-macro2-diagnostics] version = "0.10.1" [dependencies.quote] version = "1.0.30" [dependencies.syn] version = "2.0.30" features = [ "full", "extra-traits", "visit-mut", ] [dev-dependencies] pear_codegen-0.2.9/Cargo.toml.orig000064400000000000000000000007571046102023000151470ustar 00000000000000[package] name = "pear_codegen" version = "0.2.9" authors = ["Sergio Benitez "] description = "A (codegen) pear is a fruit." license = "MIT OR Apache-2.0" edition = "2018" repository = "https://github.com/SergioBenitez/Pear" [lib] proc-macro = true [dependencies] quote = "1.0.30" proc-macro2 = { version = "1.0.50" } syn = { version = "2.0.30", features = ["full", "extra-traits", "visit-mut"] } proc-macro2-diagnostics = "0.10.1" [dev-dependencies] pear = { path = "../lib" } pear_codegen-0.2.9/src/lib.rs000064400000000000000000000345321046102023000141610ustar 00000000000000#![recursion_limit="256"] extern crate proc_macro; extern crate proc_macro2; extern crate syn; #[macro_use] extern crate quote; mod parser; use syn::parse::Parser; use syn::visit_mut::{self, VisitMut}; use syn::spanned::Spanned; use proc_macro2::TokenStream; use proc_macro2_diagnostics::{Diagnostic, SpanDiagnosticExt}; use crate::parser::*; fn parse_marker_ident(span: proc_macro2::Span) -> syn::Ident { const PARSE_MARKER_IDENT: &str = "____parse_parse_marker"; syn::Ident::new(PARSE_MARKER_IDENT, span) } fn parser_info_ident(span: proc_macro2::Span) -> syn::Ident { const PARSE_INFO_IDENT: &str = "____parse_parser_info"; syn::Ident::new(PARSE_INFO_IDENT, span) } #[derive(Copy, Clone)] enum State { Start, InTry } #[derive(Clone)] struct ParserTransformer { input: syn::Expr, output: syn::Type, state: State, } impl ParserTransformer { fn new(input: syn::Expr, output: syn::Type) -> ParserTransformer { ParserTransformer { input, output, state: State::Start } } } impl VisitMut for ParserTransformer { fn visit_expr_try_mut(&mut self, v: &mut syn::ExprTry) { let last_state = self.state; self.state = State::InTry; visit_mut::visit_expr_try_mut(self, v); self.state = last_state; let expr = &v.expr; let new_expr = quote_spanned!(expr.span() => #expr.map_err(|e| e.into())); let method_call: syn::Expr = syn::parse2(new_expr).expect("okay"); v.expr = Box::new(method_call); } fn visit_expr_call_mut(&mut self, call: &mut syn::ExprCall) { if let State::InTry = self.state { // TODO: Should we keep recursing? call.args.insert(0, self.input.clone()); // Only insert into the _first_ call. self.state = State::Start; } else { visit_mut::visit_expr_call_mut(self, call); } } fn visit_macro_mut(&mut self, m: &mut syn::Macro) { if let Some(segment) = m.path.segments.last() { let name = segment.ident.to_string(); if name == "switch" || name.starts_with("parse_") { let (input, output) = (&self.input, &self.output); let tokens = match syn::parse2::(m.tokens.clone()) { Ok(mut expr) => { let mut transformer = self.clone(); transformer.state = State::Start; visit_mut::visit_expr_mut(&mut transformer, &mut expr); quote!(#expr) }, Err(_) => m.tokens.clone() }; let info = parser_info_ident(self.input.span()); let mark = parse_marker_ident(m.span()); let parser_info = quote!([#info; #input; #mark; #output]); m.tokens = quote_spanned!(m.span() => #parser_info #tokens); } } } } fn extract_input_ident_ty(f: &syn::ItemFn) -> PResult<(syn::Ident, syn::Type)> { use syn::{FnArg::Typed, PatType, Pat::Ident, Type::Reference}; let first = f.sig.inputs.first().ok_or_else(|| { let paren_span = f.sig.paren_token.span.join(); paren_span.error("parsing functions require at least one input") })?; let e = first.span().error("invalid type for parser input"); match first { Typed(PatType { pat, ty, .. }) => match **pat { Ident(ref p) => match **ty { Reference(ref r) => Ok((p.ident.clone(), *r.elem.clone())), _ => Err(e) } _ => Err(e) } _ => Err(first.span().error("invalid type for parser input")) } } fn wrapping_fn_block( function: &syn::ItemFn, scope: TokenStream, args: &AttrArgs, ret_ty: &syn::Type, ) -> PResult { let (input, input_ty) = extract_input_ident_ty(function)?; let fn_block = &function.block; let span = function.span(); let mark_ident = parse_marker_ident(input.span()); let info_ident = parser_info_ident(function.sig.ident.span()); let result_map = match args.raw.is_some() { true => quote_spanned!(span => ( |#info_ident: &#scope::input::ParserInfo, #mark_ident: &mut <#input_ty as #scope::input::Input>::Marker| { #fn_block }) ), false => quote_spanned!(span => ( |#info_ident: &#scope::input::ParserInfo, #mark_ident: &mut <#input_ty as #scope::input::Input>::Marker| { use #scope::result::IntoResult; IntoResult::into_result(#fn_block) } )) }; let rewind_expr = |span| quote_spanned! { span => <#input_ty as #scope::input::Rewind>::rewind_to(#input, ___mark); }; let (rewind, peek) = (args.rewind.map(rewind_expr), args.peek.map(rewind_expr)); let new_block_tokens = { let (name, raw) = (&function.sig.ident, args.raw.is_some()); let name_str = name.to_string(); quote_spanned!(span => { let ___info = #scope::input::ParserInfo { name: #name_str, raw: #raw }; if let Some(ref mut ___debugger) = #input.options.debugger { ___debugger.on_entry(&___info); } let mut ___mark = #scope::input::Input::mark(#input, &___info); let mut ___res: #ret_ty = #result_map(&___info, &mut ___mark); match ___res { Ok(_) => { #peek }, Err(ref mut ___e) if #input.options.stacked_context => { let ___ctxt = #scope::input::Input::context(#input, ___mark); ___e.push_info(___info, ___ctxt); #rewind }, Err(_) => { #rewind }, } if #input.options.debugger.is_some() { let ___ctxt = #scope::input::Input::context(#input, ___mark); if let Some(ref mut ___debugger) = #input.options.debugger { ___debugger.on_exit(&___info, ___res.is_ok(), ___ctxt); } } ___res }) }; syn::parse(new_block_tokens.into()) .map_err(|e| function.span().error(format!("bad function: {}", e))) } fn parser_attribute(input: proc_macro::TokenStream, args: &AttrArgs) -> PResult { let input: proc_macro2::TokenStream = input.into(); let span = input.span(); let mut function: syn::ItemFn = syn::parse2(input).map_err(|_| { span.error("`parser` attribute only supports functions") })?; let ret_ty: syn::Type = match &function.sig.output { syn::ReturnType::Default => { return Err(function.sig.span().error("parse function requires return type")); }, syn::ReturnType::Type(_, ty) => (**ty).clone(), }; let (input_ident, _) = extract_input_ident_ty(&function)?; let input_expr: syn::Expr = syn::parse2(quote!(#input_ident)).unwrap(); let mut transformer = ParserTransformer::new(input_expr, ret_ty.clone()); visit_mut::visit_item_fn_mut(&mut transformer, &mut function); let scope = args.raw.map(|_| quote!(crate)).unwrap_or_else(|| quote!(pear)); let inline = syn::Attribute::parse_outer.parse2(quote!(#[inline])).unwrap(); function.block = Box::new(wrapping_fn_block(&function, scope, args, &ret_ty)?); function.attrs.extend(inline); Ok(quote! { #[allow(clippy::all, clippy::pedantic, clippy::nursery)] #function }) } impl Case { fn to_tokens<'a, I>(context: &Context, mut cases: I) -> TokenStream where I: Iterator { let this = match cases.next() { None => return quote!(), Some(case) => case }; let (input, output) = (&context.input, &context.output); let mut transformer = ParserTransformer::new(input.clone(), output.clone()); let mut case_expr = this.expr.clone(); visit_mut::visit_expr_mut(&mut transformer, &mut case_expr); match this.pattern { Pattern::Wild(..) => match this.guard.as_ref() { Some(guard) => { let rest_tokens = Case::to_tokens(context, cases); quote!(if #guard { #case_expr } else { #rest_tokens }) } None => quote!(#case_expr), } Pattern::Calls(ref calls) => { let case_branch = calls.iter().enumerate().map(|(i, call)| { let prefix = match i { 0 => quote!(if), _ => quote!(else if) }; let name = call.name.clone() .unwrap_or_else(|| syn::Ident::new("___", call.span())); // FIXME: We're repeating ourselves, aren't we? We alrady do // this in input insertion in the visitor. let mut call_expr = call.expr.clone(); call_expr.args.insert(0, input.clone()); let call_expr = quote!({ let ___preserve_error = #input.emit_error; #input.emit_error = false; let ___call_result = #call_expr; #input.emit_error = ___preserve_error; ___call_result }); let guarded_call = this.guard.as_ref() .map(|guard| &guard.expr) .map(|guard| quote!({ match #call_expr { Ok(#name) if #guard => Some(#name), _ => None, } })) .unwrap_or_else(|| quote!(#call_expr.ok())); quote! { #prefix let Some(#name) = #guarded_call { #case_expr } } }); let rest_tokens = Case::to_tokens(context, cases); quote_spanned! { this.span => #(#case_branch)* else { #rest_tokens } } } } } } impl Switch { fn to_tokens(&self) -> TokenStream { Case::to_tokens(&self.context, self.cases.iter()) } } /// The core attribute macro. Can only be applied to free functions with at /// least one parameter and a return value. To typecheck, the free function must /// meet the following typing requirements: /// /// - The _first_ parameter's type must be a mutable reference to a [`Pear`] /// here `I` implements [`Input`]. This is the _input_ parameter. /// - The return type must be [`Result`] where `I` is the inner type /// of the input parameter and `O` can be any type. /// /// The following transformations are applied to the _contents_ of the /// attributed function: /// /// - The functions first parameter (of type `&mut Pear`) is passed as the /// first parameter to every function call in the function with a posfix /// `?`. That is, every function call of the form `foo(a, b, c, ...)?` is /// converted to `foo(input, a, b, c, ...)?` where `input` is the input /// parameter. /// - The inputs to every macro whose name starts with `parse_` are prefixed /// with `[PARSER_NAME, INPUT, MARKER, OUTPUT]` where `PARSER_NAME` is the /// raw string literal of the functon's name, `INPUT` is the input /// parameter expression, `MARKER` is the marker expression, and `OUTPUT` /// is the output type. Aditionally, if the input to the macro is a valid /// Rust expression, it is applied the same transformations as a function /// atributed with `#[parser]`. /// /// Declare a `parse_` macro as: /// /// ```rust,ignore /// macro_rules! parse_my_macro { /// ([$n:expr; $i:expr; $m:expr; $T:ty] ..) => { /// /* .. */ /// } /// } /// ``` /// /// The following transformations are applied _around_ the attributed /// function: /// /// - The [`Input::mark()`] method is called before the function executes. /// The returned mark, if any, is stored on the stack. /// - A return value of `O` is automatically converted (or "lifted") into a /// type of [`Result`] by wrapping it in `Ok`. /// - If the function returns an `Err`, [`Input::context()`] is called with /// the current mark, and the returned context, if any, is pushed into the /// error via [`ParseError::push_context()`]. /// - The [`Input::unmark()`] method is called after the function executes, /// passing in the current mark. /// /// # Example /// /// ```rust /// use pear::input::{Pear, Text, Result}; /// use pear::macros::{parser, parse}; /// use pear::parsers::*; /// # /// # use pear::macros::parse_declare; /// # parse_declare!(Input<'a>(Token = char, Slice = &'a str, Many = &'a str)); /// /// #[parser] /// fn ab_in_dots<'a, I: Input<'a>>(input: &mut Pear) -> Result<&'a str, I> { /// eat('.')?; /// let inside = take_while(|&c| c == 'a' || c == 'b')?; /// eat('.')?; /// /// inside /// } /// /// # /// let x = parse!(ab_in_dots: Text::from(".abba.")); /// assert_eq!(x.unwrap(), "abba"); /// /// let x = parse!(ab_in_dots: Text::from(".ba.")); /// assert_eq!(x.unwrap(), "ba"); /// /// let x = parse!(ab_in_dots: Text::from("...")); /// assert!(x.is_err()); /// ``` #[proc_macro_attribute] pub fn parser( args: proc_macro::TokenStream, input: proc_macro::TokenStream ) -> proc_macro::TokenStream { let args = match AttrArgs::syn_parse.parse(args) { Ok(args) => args, Err(e) => return Diagnostic::from(e).emit_as_item_tokens().into(), }; match parser_attribute(input, &args) { Ok(tokens) => tokens.into(), Err(diag) => diag.emit_as_item_tokens().into(), } } /// Invoked much like match, except each condition must be a parser, which is /// executed, and the corresponding arm is executed only if the parser succeeds. /// Once a condition succeeds, no other condition is executed. /// /// ```rust,ignore /// switch! { /// parser() => expr, /// x@parser1() | x@parser2(a, b, c) => expr(x), /// _ => last_expr /// } /// ``` #[proc_macro] pub fn switch(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // TODO: We lose diagnostic information by using syn's thing here. We need a // way to get a SynParseStream from a TokenStream to not do that. match Switch::syn_parse.parse(input) { Ok(switch) => switch.to_tokens().into(), Err(e) => Diagnostic::from(e).emit_as_expr_tokens().into(), } } pear_codegen-0.2.9/src/parser.rs000064400000000000000000000203001046102023000146730ustar 00000000000000use syn::spanned::Spanned; use syn::{punctuated::Punctuated, Token}; use syn::parse::{Parse as SynParse, ParseStream as SynParseStream}; use proc_macro2::{Span, Delimiter}; use proc_macro2_diagnostics::{Diagnostic, SpanDiagnosticExt}; pub type PResult = Result; pub trait Parse: Sized { fn parse(input: syn::parse::ParseStream) -> PResult; fn syn_parse(input: syn::parse::ParseStream) -> syn::parse::Result { Self::parse(input).map_err(|e| e.into()) } } trait ParseStreamExt { fn parse_group(self, delimiter: Delimiter, parser: F) -> syn::parse::Result where F: FnOnce(SynParseStream) -> syn::parse::Result; fn try_parse(self, parser: F) -> syn::parse::Result where F: Fn(SynParseStream) -> syn::parse::Result; } impl<'a> ParseStreamExt for SynParseStream<'a> { fn parse_group(self, delimiter: Delimiter, parser: F) -> syn::parse::Result where F: FnOnce(SynParseStream) -> syn::parse::Result { let content; match delimiter { Delimiter::Brace => { syn::braced!(content in self); }, Delimiter::Bracket => { syn::bracketed!(content in self); }, Delimiter::Parenthesis => { syn::parenthesized!(content in self); }, Delimiter::None => return parser(self), } parser(&content) } fn try_parse(self, parser: F) -> syn::parse::Result where F: Fn(SynParseStream) -> syn::parse::Result { let input = self.fork(); parser(&input)?; parser(self) } } #[derive(Debug)] pub struct CallPattern { pub name: Option, pub at: Option, pub expr: syn::ExprCall, } impl syn::parse::Parse for CallPattern { fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { Self::syn_parse(input) } } impl quote::ToTokens for CallPattern { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { let (expr, at) = (&self.expr, &self.at); match self.name { Some(ref name) => quote!(#name #at #expr).to_tokens(tokens), None => expr.to_tokens(tokens) } } } impl quote::ToTokens for Guard { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { self.expr.to_tokens(tokens) } } type CallPatterns = Punctuated; #[derive(Debug)] pub enum Pattern { Wild(Token![_]), Calls(CallPatterns), } #[derive(Debug)] pub struct Guard { pub _if: Token![if], pub expr: syn::Expr, } #[derive(Debug)] pub struct Case { pub pattern: Pattern, pub expr: syn::Expr, pub guard: Option, pub span: Span, } #[derive(Debug)] pub struct Switch { pub context: Context, pub cases: Punctuated } // FIXME(syn): Something like this should be in `syn` fn parse_expr_call(input: SynParseStream) -> syn::parse::Result { let path: syn::ExprPath = input.parse()?; let paren_span = input.cursor().span(); let args = input.parse_group(Delimiter::Parenthesis, |i| { i.parse_terminated(syn::Expr::parse, Token![,]) })?; Ok(syn::ExprCall { attrs: vec![], func: Box::new(syn::Expr::Path(path)), paren_token: syn::token::Paren(paren_span), args }) } impl Parse for CallPattern { fn parse(input: SynParseStream) -> PResult { let name_at = input.try_parse(|input| { let ident: syn::Ident = input.parse()?; let at = input.parse::()?; Ok((ident, at)) }).ok(); let (name, at) = match name_at { Some((name, at)) => (Some(name), Some(at)), None => (None, None) }; Ok(CallPattern { name, at, expr: parse_expr_call(input)? }) } } impl Parse for Guard { fn parse(input: SynParseStream) -> PResult { Ok(Guard { _if: input.parse()?, expr: input.parse()?, }) } } impl Parse for Pattern { fn parse(input: SynParseStream) -> PResult { type CallPatterns = Punctuated; // Parse the pattern. let pattern = match input.parse::() { Ok(wild) => Pattern::Wild(wild), Err(_) => Pattern::Calls(input.call(CallPatterns::parse_separated_nonempty)?) }; // Validate the pattern. if let Pattern::Calls(ref calls) = pattern { let first_name = calls.first().and_then(|call| call.name.clone()); for call in calls.iter() { if first_name != call.name { let mut err = if let Some(ref ident) = call.name { ident.span() .error("captured name differs from declaration") } else { call.expr.span() .error("expected capture name due to previous declaration") }; err = match first_name { Some(p) => err.span_note(p.span(), "declared here"), None => err }; return Err(err); } } } Ok(pattern) } } impl Parse for Case { fn parse(input: SynParseStream) -> PResult { let case_span_start = input.cursor().span(); let pattern = Pattern::parse(input)?; let guard = match input.peek(Token![if]) { true => Some(Guard::parse(input)?), false => None, }; input.parse::]>()?; let expr: syn::Expr = input.parse()?; let span = case_span_start .join(input.cursor().span()) .unwrap_or(case_span_start); Ok(Case { pattern, expr, guard, span, }) } } #[derive(Debug)] pub struct Context { pub info: syn::Ident, pub input: syn::Expr, pub marker: syn::Expr, pub output: syn::Type, } impl Parse for Context { fn parse(stream: SynParseStream) -> PResult { let (info, input, marker, output) = stream.parse_group(Delimiter::Bracket, |inner| { let info: syn::Ident = inner.parse()?; inner.parse::()?; let input: syn::Expr = inner.parse()?; inner.parse::()?; let marker: syn::Expr = inner.parse()?; inner.parse::()?; let output: syn::Type = inner.parse()?; Ok((info, input, marker, output)) })?; Ok(Context { info, input, marker, output }) } } impl Parse for Switch { fn parse(stream: SynParseStream) -> PResult { let context = stream.try_parse(Context::syn_parse)?; let cases = stream.parse_terminated(Case::syn_parse, Token![,])?; if !stream.is_empty() { Err(stream.error("trailing characters; expected eof"))?; } if cases.is_empty() { Err(stream.error("switch cannot be empty"))?; } for case in cases.iter().take(cases.len() - 1) { if let Pattern::Wild(..) = case.pattern { if case.guard.is_none() { Err(case.span.error("unguarded `_` can only appear as the last case"))?; } } } Ok(Switch { context, cases }) } } #[derive(Debug, Clone)] pub struct AttrArgs { pub raw: Option, pub rewind: Option, pub peek: Option, } impl Parse for AttrArgs { fn parse(input: SynParseStream) -> PResult { let args = input.call(>::parse_terminated)?; let (mut raw, mut rewind, mut peek) = Default::default(); for case in args.iter() { if case == "raw" { raw = Some(case.span()); } else if case == "rewind" { rewind = Some(case.span()); } else if case == "peek" { peek = Some(case.span()); } else { return Err(case.span() .error(format!("unknown attribute argument `{}`", case)) .help("supported arguments are: `rewind`, `peek`")); } } Ok(AttrArgs { raw, rewind, peek }) } }