binator 0.3.0

Parser Combinator
Documentation
use std::{
  env,
  fs::OpenOptions,
  io::{
    self,
    BufWriter,
    Write,
  },
  num::NonZeroUsize,
  path::{
    Path,
    PathBuf,
  },
};

use quote::{
  format_ident,
  quote,
};
use syn::Index;

fn tuple<W: Write>(mut w: W, i: NonZeroUsize) -> Result<(), io::Error> {
  let i = i.get();
  let parsers = (0usize..i).map(|i| format_ident!("P{}", i));
  let tuple = parsers.clone();
  let generics = parsers.clone();
  let generics_where = parsers;
  let tuple = quote! {
    #(#tuple,)*
  };
  let generics = quote! {
    #(#generics),*
  };
  let tokens = (0usize..i).map(|i| format_ident!("O{}", i));
  let generics_where = quote! {
    #(#generics_where: Parse<Stream, Context, Token = #tokens>),*
  };

  let tokens2 = (0usize..i).map(|i| format_ident!("O{}", i));
  let tokens3 = (0usize..i).map(|i| format_ident!("O{}", i));
  let tokens4 = (0usize..i).map(|i| format_ident!("token_{}", i));

  let matches = (0usize..i).map(Index::from).map(|i| {
    let token = format_ident!("token_{}", i);
    quote! {
      let Success { token: #token, stream } = self.#i.parse(stream)?;
    }
  });

  let codegen = quote! {
    impl<#(#tokens2: Debug,)* #generics, Stream, Context> Parse<Stream, Context> for (#tuple)
    where
      Stream: Streaming,
      #generics_where
    {
      type Token = (#(#tokens3,)*);

      fn parse(&mut self, stream: Stream) -> Parsed<Self::Token, Stream, Context> {
        #(#matches)*

        Parsed::new_success((#(#tokens4,)*), stream)
      }
    }
  };

  write!(w, "{}", rustfmt_wrapper::rustfmt(codegen).unwrap())
}

fn tuples(path: &Path) -> Result<(), io::Error> {
  let dest_path = Path::new(path).join("parse_tuple.rs");
  let file = OpenOptions::new()
    .create(true)
    .write(true)
    .truncate(true)
    .open(&dest_path)?;
  let mut buf = BufWriter::new(file);

  for i in 1..12 {
    tuple(&mut buf, i.try_into().unwrap())?;
  }

  Ok(())
}

fn main() -> Result<(), io::Error> {
  println!("cargo:rerun-if-changed=build.rs");

  let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
  tuples(&out_dir)?;

  Ok(())
}