Skip to content

Commit

Permalink
Clean up macro parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed Sep 25, 2023
1 parent 8123523 commit 765116d
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 120 deletions.
44 changes: 25 additions & 19 deletions genco-macros/src/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ use crate::cursor::Cursor;
use crate::fake::LineColumn;
use crate::requirements::Requirements;
use crate::static_buffer::StaticBuffer;
use crate::Ctxt;

use proc_macro2::{Span, TokenStream};
use syn::Result;

/// Struct to deal with emitting the necessary spacing.
pub(crate) struct Encoder<'a> {
/// The identifier that received the input.
receiver: &'a syn::Ident,
/// Context for encoding.
cx: &'a Ctxt,
/// Use to modify the initial line/column in case something was processed
/// before the input was handed off to the quote parser.
///
Expand Down Expand Up @@ -38,15 +39,15 @@ pub(crate) struct Encoder<'a> {

impl<'a> Encoder<'a> {
pub(crate) fn new(
receiver: &'a syn::Ident,
cx: &'a Ctxt,
span_start: Option<LineColumn>,
span_end: Option<LineColumn>,
) -> Self {
Self {
receiver,
cx,
span_start,
span_end,
item_buffer: StaticBuffer::new(receiver),
item_buffer: StaticBuffer::new(cx),
output: TokenStream::new(),
last: None,
last_start_column: None,
Expand Down Expand Up @@ -151,29 +152,32 @@ impl<'a> Encoder<'a> {
}

pub(crate) fn encode_string(&mut self, has_eval: bool, stream: TokenStream) {
let Ctxt { receiver, module } = self.cx;

self.item_buffer.flush(&mut self.output);
let receiver = self.receiver;

self.output.extend(q::quote! {
#receiver.append(genco::tokens::Item::OpenQuote(#has_eval));
#receiver.append(#module::tokens::Item::OpenQuote(#has_eval));
#stream
#receiver.append(genco::tokens::Item::CloseQuote);
#receiver.append(#module::tokens::Item::CloseQuote);
});
}

pub(crate) fn encode_quoted(&mut self, s: syn::LitStr) {
let receiver = self.receiver;
let Ctxt { receiver, module } = self.cx;

self.item_buffer.flush(&mut self.output);

self.output.extend(q::quote! {
#receiver.append(genco::tokens::Item::OpenQuote(false));
#receiver.append(genco::tokens::ItemStr::Static(#s));
#receiver.append(genco::tokens::Item::CloseQuote);
#receiver.append(#module::tokens::Item::OpenQuote(false));
#receiver.append(#module::tokens::ItemStr::Static(#s));
#receiver.append(#module::tokens::Item::CloseQuote);
});
}

pub(crate) fn encode_control(&mut self, control: Control) {
let receiver = self.receiver;
let Ctxt { receiver, .. } = self.cx;

self.item_buffer.flush(&mut self.output);

match control.kind {
Expand All @@ -193,7 +197,7 @@ impl<'a> Encoder<'a> {
}

pub(crate) fn encode_scope(&mut self, binding: Option<syn::Ident>, content: TokenStream) {
let receiver = self.receiver;
let Ctxt { receiver, .. } = self.cx;

if binding.is_some() {
self.item_buffer.flush(&mut self.output);
Expand All @@ -209,7 +213,8 @@ impl<'a> Encoder<'a> {

/// Encode an evaluation of the given expression.
pub(crate) fn encode_eval_ident(&mut self, ident: syn::Ident) {
let receiver = self.receiver;
let Ctxt { receiver, .. } = self.cx;

self.item_buffer.flush(&mut self.output);
self.output.extend(q::quote! {
#receiver.append(#ident);
Expand All @@ -218,7 +223,8 @@ impl<'a> Encoder<'a> {

/// Encode an evaluation of the given expression.
pub(crate) fn encode_eval(&mut self, expr: syn::Expr) {
let receiver = self.receiver;
let Ctxt { receiver, .. } = self.cx;

self.item_buffer.flush(&mut self.output);
self.output.extend(q::quote! {
#receiver.append(#expr);
Expand Down Expand Up @@ -335,6 +341,8 @@ impl<'a> Encoder<'a> {

/// Finalize the encoder.
fn finalize(&mut self) -> Result<()> {
let Ctxt { receiver, .. } = self.cx;

// evaluate whitespace in case we have an explicit end span.
while let Some(to) = self.span_end.take() {
if let Some(from) = self.from() {
Expand All @@ -345,8 +353,6 @@ impl<'a> Encoder<'a> {

self.item_buffer.flush(&mut self.output);

let receiver = self.receiver;

while self.indents.pop().is_some() {
self.output.extend(q::quote!(#receiver.unindent();));
}
Expand All @@ -362,7 +368,7 @@ impl<'a> Encoder<'a> {
to: LineColumn,
to_span: Option<Span>,
) -> Result<()> {
let r = self.receiver;
let Ctxt { receiver: r, .. } = self.cx;

// Do nothing if empty span.
if from == to {
Expand Down
34 changes: 29 additions & 5 deletions genco-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,29 @@ extern crate proc_macro;
use proc_macro2::Span;
use syn::parse::{ParseStream, Parser as _};

struct Ctxt {
receiver: syn::Ident,
module: syn::Path,
}

impl Default for Ctxt {
fn default() -> Self {
let mut module = syn::Path {
leading_colon: None,
segments: syn::punctuated::Punctuated::default(),
};

module
.segments
.push(syn::Ident::new("genco", Span::call_site()).into());

Self {
receiver: syn::Ident::new("__genco_macros_toks", Span::call_site()),
module,
}
}
}

mod ast;
mod cursor;
mod encoder;
Expand Down Expand Up @@ -659,9 +682,8 @@ mod token;
/// [escape]: #escape-sequences
#[proc_macro]
pub fn quote(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let receiver = &syn::Ident::new("__genco_macros_toks", Span::call_site());

let parser = crate::quote::Quote::new(receiver);
let cx = Ctxt::default();
let parser = crate::quote::Quote::new(&cx);

let parser = move |stream: ParseStream| parser.parse(stream);

Expand All @@ -670,10 +692,12 @@ pub fn quote(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
Err(e) => return proc_macro::TokenStream::from(e.to_compile_error()),
};

let check = req.into_check(receiver);
let check = req.into_check(&cx.receiver);

let Ctxt { receiver, module } = &cx;

let gen = q::quote! {{
let mut #receiver = genco::tokens::Tokens::new();
let mut #receiver = #module::tokens::Tokens::new();

{
let mut #receiver = &mut #receiver;
Expand Down
40 changes: 18 additions & 22 deletions genco-macros/src/quote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ use crate::fake::Buf;
use crate::fake::LineColumn;
use crate::requirements::Requirements;
use crate::string_parser::StringParser;
use crate::Ctxt;

pub(crate) struct Quote<'a> {
/// Used to set the receiver identifier which is being modified by this
/// macro.
receiver: &'a syn::Ident,
/// Context variables.
cx: &'a Ctxt,
/// Use to modify the initial line/column in case something was processed
/// before the input was handed off to the quote parser.
///
Expand All @@ -32,9 +32,9 @@ pub(crate) struct Quote<'a> {

impl<'a> Quote<'a> {
/// Construct a new quote parser.
pub(crate) fn new(receiver: &'a syn::Ident) -> Self {
pub(crate) fn new(cx: &'a Ctxt) -> Self {
Self {
receiver,
cx,
span_start: None,
span_end: None,
until_comma: false,
Expand All @@ -43,9 +43,9 @@ impl<'a> Quote<'a> {
}

/// Construct a new quote parser that will only parse until the given token.
pub(crate) fn new_until_comma(receiver: &'a syn::Ident) -> Self {
pub(crate) fn new_until_comma(cx: &'a Ctxt) -> Self {
Self {
receiver,
cx,
span_start: None,
span_end: None,
until_comma: true,
Expand Down Expand Up @@ -78,7 +78,7 @@ impl<'a> Quote<'a> {

/// Parse until end of stream.
pub(crate) fn parse(mut self, input: ParseStream) -> Result<(Requirements, TokenStream)> {
let mut encoder = Encoder::new(self.receiver, self.span_start, self.span_end);
let mut encoder = Encoder::new(self.cx, self.span_start, self.span_end);
self.parse_inner(&mut encoder, input, 0)?;
encoder.into_output()
}
Expand All @@ -90,7 +90,7 @@ impl<'a> Quote<'a> {

if input.peek(Token![=>]) {
input.parse::<Token![=>]>()?;
let (req, then_branch) = Quote::new(self.receiver).parse(input)?;
let (req, then_branch) = Quote::new(self.cx).parse(input)?;

return Ok((
req,
Expand All @@ -107,7 +107,7 @@ impl<'a> Quote<'a> {
let content;
syn::braced!(content in input);

let (r, then_branch) = Quote::new(self.receiver).parse(&content)?;
let (r, then_branch) = Quote::new(self.cx).parse(&content)?;
req.merge_with(r);

let else_branch = if input.peek(Token![else]) {
Expand All @@ -116,7 +116,7 @@ impl<'a> Quote<'a> {
let content;
syn::braced!(content in input);

let (r, else_branch) = Quote::new(self.receiver).parse(&content)?;
let (r, else_branch) = Quote::new(self.cx).parse(&content)?;
req.merge_with(r);

Some(else_branch)
Expand Down Expand Up @@ -151,9 +151,7 @@ impl<'a> Quote<'a> {
let content;
let paren = syn::parenthesized!(content in input);

let (r, join) = Quote::new(self.receiver)
.with_span(paren.span)?
.parse(&content)?;
let (r, join) = Quote::new(self.cx).with_span(paren.span)?.parse(&content)?;
req.merge_with(r);

Some(join)
Expand All @@ -171,7 +169,7 @@ impl<'a> Quote<'a> {
&content
};

let parser = Quote::new(self.receiver);
let parser = Quote::new(self.cx);
let (r, stream) = parser.parse(input)?;
req.merge_with(r);

Expand Down Expand Up @@ -213,17 +211,15 @@ impl<'a> Quote<'a> {
let block;
syn::braced!(block in body);

let parser = Quote::new(self.receiver);
let parser = Quote::new(self.cx);
parser.parse(&block)?
} else if body.peek(token::Paren) {
let block;
let paren = syn::parenthesized!(block in body);

Quote::new(self.receiver)
.with_span(paren.span)?
.parse(&block)?
Quote::new(self.cx).with_span(paren.span)?.parse(&block)?
} else {
let parser = Quote::new_until_comma(self.receiver);
let parser = Quote::new_until_comma(self.cx);
parser.parse(&body)?
};

Expand Down Expand Up @@ -348,7 +344,7 @@ impl<'a> Quote<'a> {
));
}
(LiteralName::Ident("str"), Some(content)) => {
let parser = StringParser::new(self.receiver, &self.buf, end)?;
let parser = StringParser::new(self.cx, &self.buf, end)?;

let (options, r, stream) = parser.parse(&content)?;
encoder.requirements.merge_with(r);
Expand All @@ -358,7 +354,7 @@ impl<'a> Quote<'a> {
encoder.encode(
cursor,
Ast::String {
has_eval: options.has_eval,
has_eval: options.has_eval.get(),
stream,
},
)?;
Expand Down
13 changes: 9 additions & 4 deletions genco-macros/src/quote_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@ use proc_macro2::TokenStream;
use syn::parse::{Parse, ParseStream};
use syn::Result;

use crate::Ctxt;

pub(crate) struct QuoteFn {
pub(crate) stream: TokenStream,
}

impl Parse for QuoteFn {
fn parse(input: ParseStream) -> Result<Self> {
let receiver = &syn::Ident::new("__genco_macros_toks", input.span());
let parser = crate::quote::Quote::new(receiver);
let cx = Ctxt::default();

let parser = crate::quote::Quote::new(&cx);
let (req, output) = parser.parse(input)?;

let check = req.into_check(receiver);
let check = req.into_check(&cx.receiver);

let Ctxt { receiver, module } = &cx;

let stream = q::quote! {
genco::tokens::from_fn(move |#receiver| {
#module::tokens::from_fn(move |#receiver| {
#output
#check
})
Expand Down
13 changes: 9 additions & 4 deletions genco-macros/src/quote_in.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned as _;
use syn::{Result, Token};

use crate::Ctxt;

pub(crate) struct QuoteIn {
pub(crate) stream: TokenStream,
}
Expand All @@ -13,15 +15,18 @@ impl Parse for QuoteIn {
let expr = input.parse::<syn::Expr>()?;
input.parse::<Token![=>]>()?;

let receiver = &syn::Ident::new("__genco_macros_toks", expr.span());
let parser = crate::quote::Quote::new(receiver);
let cx = Ctxt::default();

let parser = crate::quote::Quote::new(&cx);
let (req, output) = parser.parse(input)?;

let check = req.into_check(receiver);
let check = req.into_check(&cx.receiver);

let Ctxt { receiver, module } = &cx;

// Give the assignment its own span to improve diagnostics.
let assign_mut = q::quote_spanned! { expr.span() =>
let #receiver: &mut genco::tokens::Tokens<_> = &mut #expr;
let #receiver: &mut #module::tokens::Tokens<_> = &mut #expr;
};

let stream = q::quote! {{
Expand Down
Loading

0 comments on commit 765116d

Please sign in to comment.