tests pass again

This commit is contained in:
nora 2022-09-24 18:23:39 +02:00
parent 53e8bec4ec
commit d7844568ca
5 changed files with 294 additions and 56 deletions

View file

@ -1,29 +1,33 @@
//! a bunch of this code is adapted from [stylish](https://github.com/Nullus157/stylish-rs)
#![allow(dead_code, unreachable_code, unused_variables)]
use std::cell::Cell;
use format::Parse as _;
use proc_macro::TokenStream;
use quote::quote;
use quote::{ToTokens, quote};
use syn::{
parse::{Parse, ParseStream},
parse_macro_input, Expr, ExprAssign, ExprPath, Ident, LitStr, PathArguments, Result, Token,
};
use to_tokens::Scoped;
mod format;
mod to_tokens;
struct Input {
crate_ident: Ident,
format_str: String,
prefix: proc_macro2::TokenStream,
format_str: LitStr,
positional_args: Vec<Expr>,
named_args: Vec<(Ident, Expr)>,
}
impl Parse for Input {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let crate_ident = input.parse()?;
let crate_ident = input.parse::<syn::Path>()?;
let prefix = quote! { #crate_ident::_private };
let format_str = input.parse::<LitStr>()?.value();
let format_str = input.parse::<LitStr>()?;
let mut positional_args = Vec::new();
let mut named_args = Vec::new();
@ -59,7 +63,7 @@ impl Parse for Input {
}
}
Ok(Self {
crate_ident,
prefix,
format_str,
positional_args,
named_args,
@ -68,13 +72,12 @@ impl Parse for Input {
}
fn format_args_impl(input: Input) -> syn::Result<TokenStream> {
todo!();
let (_, fmt_parts) = format::Format::parse(&input.format_str).unwrap();
let str = input.format_str.value();
let (_, fmt_parts) = format::Format::parse(&str).unwrap();
Ok(quote! {
fmt_parts
}
.into())
let current_position = Cell::new(0);
Ok(Scoped::new(&input, &fmt_parts, &current_position).to_token_stream().into())
}
#[proc_macro]

View file

@ -1,16 +1,28 @@
use proc_macro2::TokenStream;
use std::cell::Cell;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens};
use crate::format::{Align, FormatTrait};
use crate::{
format::{
Align, Count, Format, FormatArg, FormatArgRef, FormatTrait, FormatterArgs, Piece, Sign,
},
Input,
};
pub struct Scoped<'a, T> {
export: &'a syn::Path,
pub(crate) struct Scoped<'a, T> {
input: &'a Input,
current_position: &'a Cell<usize>,
inner: &'a T,
}
impl<'a, T> Scoped<'a, T> {
pub fn new(export: &'a syn::Path, inner: &'a T) -> Self {
Self { export, inner }
pub fn new(input: &'a Input, inner: &'a T, current_position: &'a Cell<usize>) -> Self {
Self {
input,
inner,
current_position,
}
}
fn scope<'b, U>(&self, inner: &'b U) -> Scoped<'b, U>
@ -19,7 +31,8 @@ impl<'a, T> Scoped<'a, T> {
{
Scoped {
inner,
export: self.export,
input: self.input,
current_position: self.current_position,
}
}
@ -28,6 +41,164 @@ impl<'a, T> Scoped<'a, T> {
}
}
impl ToTokens for Scoped<'_, Format<'_>> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let parts = self.inner.pieces.iter().map(|piece| self.scope(piece));
tokens.extend(quote! {
(
#(#parts),*
)
})
}
}
impl ToTokens for Scoped<'_, Piece<'_>> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = &self.input.prefix;
match self.inner {
Piece::Lit(literal) => {
let lit = syn::LitStr::new(literal, self.input.format_str.span());
tokens.extend(quote! { #prefix::Str(#lit) });
}
Piece::Arg(arg) => self.scope(arg).to_tokens(tokens),
}
}
}
impl ToTokens for Scoped<'_, FormatArg<'_>> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = &self.input.prefix;
let base = self.inner.format_spec.format_trait;
let expr = match self.inner.arg {
None => {
let current_position = self.current_position.get();
self.current_position.set(current_position + 1);
self.input.positional_args[current_position].to_token_stream()
}
Some(FormatArgRef::Positional(idx)) => {
self.input.positional_args[idx].to_token_stream()
}
Some(FormatArgRef::Named(name)) => self
.input
.named_args
.iter()
.find(|(arg, _)| arg == name)
.map(|(_, expr)| expr.to_token_stream())
.unwrap_or_else(|| Ident::new(name, Span::call_site()).to_token_stream()),
};
let opt_ty = opt_ty_tokens(self.scope(&self.inner.format_spec.formatter_args));
let opt_values = opt_value_tokens(self.scope(&self.inner.format_spec.formatter_args));
tokens.extend(quote! { #prefix::#base::<_, #opt_ty>(#expr, #opt_values) })
}
}
fn opt_value_tokens(scope: Scoped<'_, FormatterArgs<'_>>) -> TokenStream {
let args = &scope.inner;
let prefix = &scope.input.prefix;
let mut opts = quote! { () };
if args.alternate {
opts = quote! { #prefix::WithAlternate(#opts) };
}
if let Some(width) = args.width {
let width = match width {
Count::Integer(int) => int,
Count::Parameter(_) => panic!("parameter counts are not supported right now"),
};
opts = quote! { #prefix::WithWidth(#opts) };
}
if let Some(align) = args.align {
opts = quote! { #prefix::WithAlign(#opts) };
}
if let Some(Sign::Plus) = args.sign {
opts = quote! { #prefix::WithSignPlus(#opts) };
}
if let Some(Sign::Minus) = args.sign {
opts = quote! { #prefix::WithMinus(#opts)};
}
if let Some(precision) = args.precision {
let precision = match precision {
Count::Integer(int) => int,
Count::Parameter(_) => panic!("parameter counts are not supported right now"),
};
opts = quote! { #prefix::WithPrecision(#opts) };
}
if let Some(Sign::Plus) = args.sign {
opts = quote! { #prefix::WithSignPlus(#opts) };
}
if args.zero {
opts = quote! { #prefix::WithSignAwareZeroPad(#opts) };
}
opts
}
fn opt_ty_tokens(scope: Scoped<'_, FormatterArgs<'_>>) -> TokenStream {
let args = &scope.inner;
let prefix = &scope.input.prefix;
let mut opts = quote! { () };
if args.alternate {
opts = quote! { #prefix::WithAlternate<#opts> };
}
if let Some(width) = args.width {
let width = match width {
Count::Integer(int) => int,
Count::Parameter(_) => panic!("parameter counts are not supported right now"),
};
opts = quote! { #prefix::WithWidth<#opts, #width> };
}
if let Some(align) = args.align {
opts = quote! { #prefix::WithAlign<#opts, #align> };
}
if let Some(Sign::Plus) = args.sign {
opts = quote! { #prefix::WithSignPlus<#opts> };
}
if let Some(Sign::Minus) = args.sign {
opts = quote! { #prefix::WithMinus<#opts> };
}
if let Some(precision) = args.precision {
let precision = match precision {
Count::Integer(int) => int,
Count::Parameter(_) => panic!("parameter counts are not supported right now"),
};
opts = quote! { #prefix::WithPrecision<#opts, #precision> };
}
if let Some(Sign::Plus) = args.sign {
opts = quote! { #prefix::WithSignPlus<#opts> };
}
if args.zero {
opts = quote! { #prefix::WithSignAwareZeroPad<#opts> };
}
opts
}
impl ToTokens for Align {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend(match self {
@ -41,15 +212,15 @@ impl ToTokens for Align {
impl ToTokens for FormatTrait {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
FormatTrait::Display => quote!(Display),
FormatTrait::Debug => quote!(Debug),
FormatTrait::Octal => quote!(Octal),
FormatTrait::LowerHex => quote!(LowerHex),
FormatTrait::UpperHex => quote!(UpperHex),
FormatTrait::Pointer => quote!(Pointer),
FormatTrait::Binary => quote!(Binary),
FormatTrait::LowerExp => quote!(LowerExp),
FormatTrait::UpperExp => quote!(UpperExp),
FormatTrait::Display => quote! { DisplayArg },
FormatTrait::Debug => quote! { DebugArg },
FormatTrait::Octal => quote! { OctalArg },
FormatTrait::LowerHex => quote! { LowerHexArg },
FormatTrait::UpperHex => quote! { UpperHexArg },
FormatTrait::Pointer => quote! { PointerArg },
FormatTrait::Binary => quote! { BinaryArg },
FormatTrait::LowerExp => quote! { LowerExpArg },
FormatTrait::UpperExp => quote! { UpperExpArg },
}
.to_tokens(tokens)
}