From 7a6ace9dc3da25e433291eff58d003254962e7bf Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Mar 2024 19:17:29 +0700 Subject: [PATCH 1/6] Implement derive macro for Cinable --- hcpl_proc_macro/Cargo.toml | 15 +++ hcpl_proc_macro/src/gen.rs | 182 ++++++++++++++++++++++++++++++++++ hcpl_proc_macro/src/lib.rs | 115 +++++++++++++++++++++ hcpl_proc_macro/src/result.rs | 30 ++++++ 4 files changed, 342 insertions(+) create mode 100644 hcpl_proc_macro/Cargo.toml create mode 100644 hcpl_proc_macro/src/gen.rs create mode 100644 hcpl_proc_macro/src/lib.rs create mode 100644 hcpl_proc_macro/src/result.rs diff --git a/hcpl_proc_macro/Cargo.toml b/hcpl_proc_macro/Cargo.toml new file mode 100644 index 0000000..8c93bcd --- /dev/null +++ b/hcpl_proc_macro/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "hcpl_proc_macro" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +proc-macro-error = "1.0.4" +proc-macro2 = "1.0.78" +quote = "1.0.35" +syn = { version = "2.0.50", features = ["full", "extra-traits"] } + +[lib] +proc-macro = true \ No newline at end of file diff --git a/hcpl_proc_macro/src/gen.rs b/hcpl_proc_macro/src/gen.rs new file mode 100644 index 0000000..fc620a6 --- /dev/null +++ b/hcpl_proc_macro/src/gen.rs @@ -0,0 +1,182 @@ +use crate::result::{self, ErrorKind}; +use proc_macro2::TokenStream; +use proc_macro_error::abort; +use quote::quote; +use syn::{ + parse::Parser, punctuated::Punctuated, Attribute, Error, Expr, ExprLit, Fields, FieldsNamed, + FieldsUnnamed, Ident, Meta, MetaNameValue, Pat, Token, Type, Variant, +}; + +pub(crate) fn gen_read_from_fields(fields: &Fields, ident: &Ident) -> result::Result { + let ts = match fields { + Fields::Named(FieldsNamed { named, .. }) => { + let fields = named.iter().map(|field| { + let fident = field.ident.as_ref().unwrap(); + quote!(#fident: cin.get()) + }); + quote! ( #ident {#(#fields),*} ) + } + Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { + let fields = unnamed.iter().map(|_| quote!(cin.get())); + + quote! { #ident (#(#fields),*) } + } + Fields::Unit => quote!(#ident), + }; + Ok(ts) +} + +pub(crate) fn gen_read_from_variants( + variants: &Punctuated, + attrs: &Vec, +) -> result::Result { + let ty = get_tag_type(attrs)?; + + let mut match_arms = vec![]; + for variant in variants { + let pat = match get_tag_pat(variant) { + Ok(pat) => pat, + Err(ek) => return ek.span_now(variant).into(), + }; + let read_from_fields = gen_read_from_fields(&variant.fields, &variant.ident)?; + match_arms.push(quote!( #pat => Self::#read_from_fields )); + } + match_arms.push(quote!( _ => panic!("unexpected value: {:?}", tag) )); + + let ts = quote! { + let tag = cin.get::<#ty>(); + match tag { + #(#match_arms),* + } + }; + Ok(ts) +} + +pub(crate) fn gen_read_from_union( + fields_named: &FieldsNamed, + attrs: &Vec, +) -> result::Result { + let ty = get_tag_type(attrs)?; + let FieldsNamed { + named: fields_named, + .. + } = fields_named; + + let mut match_arms = vec![]; + for field_named in fields_named { + let pat = match get_tag_pat(field_named) { + Ok(pat) => pat, + Err(ek) => return ek.span_now(field_named).into(), + }; + let fident = field_named.ident.as_ref().unwrap(); + match_arms.push(quote!( #pat => Self { #fident: cin.get() } )); + } + match_arms.push(quote!( _ => panic!("unexpected value: {:?}", tag) )); + + let ts = quote! { + let tag = cin.get::<#ty>(); + match tag { + #(#match_arms),* + } + }; + Ok(ts) +} + +fn get_tag_type(attrs: &Vec) -> result::Result { + let tag_type_attrs: Vec<_> = attrs + .iter() + .filter(|attr| attr.path().is_ident("tag_type")) + .collect(); + + let tag_type_attr = if tag_type_attrs.len() == 1 { + tag_type_attrs[0] + } else { + const MESSAGE: &str = + "there must be exactly one tag_type attribute when deriving Cinable for enum"; + return if tag_type_attrs.is_empty() { + ErrorKind::SpanLater(MESSAGE.to_owned()).into() + } else { + Err(Error::new_spanned(tag_type_attrs[1], MESSAGE).into()) + }; + }; + + let meta_list = match tag_type_attr.meta.require_list() { + Ok(meta_list) => meta_list, + Err(_) => abort!( + tag_type_attr, + "expected attribute list (e.g. #[tag_type(bool)])" + ), + }; + + let ty = match meta_list.parse_args::() { + Ok(ty) => ty, + Err(_) => abort!(meta_list.delimiter.span().join(), "expected a single type"), + }; + Ok(ty) +} + +trait Attrs { + fn attrs(&self) -> &Vec; +} + +impl Attrs for syn::Field { + fn attrs(&self) -> &Vec { + &self.attrs + } +} + +impl Attrs for syn::Variant { + fn attrs(&self) -> &Vec { + &self.attrs + } +} + +fn get_tag_pat(item: &T) -> result::Result> { + let tag_attrs: Vec<_> = item + .attrs() + .iter() + .filter(|attr| attr.path().is_ident("tag")) + .collect(); + + if tag_attrs.is_empty() { + return ErrorKind::SpanLater( + "there must be at least one tag attribute for each enum variant".to_owned(), + ) + .into(); + } + + let mut or_pats = Punctuated::::new(); + for tag_attr in tag_attrs { + match &tag_attr.meta { + Meta::NameValue(MetaNameValue { value, .. }) => match value { + Expr::Lit(expr_lit) => or_pats.push(expr_lit.clone().into()), + _ => abort!(value, "attribute value must be a literal"), + }, + Meta::List(meta_list) => { + if meta_list.tokens.is_empty() { + abort!( + meta_list.delimiter.span().join(), + "expected at least one literal" + ); + } + + let expr_lits: Punctuated = + Parser::parse2(Punctuated::parse_terminated, meta_list.tokens.clone())?; + + or_pats.extend( + expr_lits + .iter() + .map(|expr_lit| -> Pat { expr_lit.clone().into() }), + ) + } + Meta::Path(path) => { + abort!( + path, + "path-only syntax unsupported.\n\ + please use name-value syntax (#[tag = '1']) or list-style syntax (#[tag(3, 5)])" + ) + } + }; + } + Ok(or_pats) +} diff --git a/hcpl_proc_macro/src/lib.rs b/hcpl_proc_macro/src/lib.rs new file mode 100644 index 0000000..6088e7d --- /dev/null +++ b/hcpl_proc_macro/src/lib.rs @@ -0,0 +1,115 @@ +/*! +This crate contains procedural macros for `hcpl`. +*/ + +mod gen; +mod result; + +use gen::{gen_read_from_fields, gen_read_from_union, gen_read_from_variants}; +use proc_macro2::TokenStream; +use proc_macro_error::{abort, proc_macro_error, set_dummy}; +use quote::quote; +use result::ErrorKind; +use syn::{ + parse_macro_input, punctuated::Punctuated, Attribute, Data, DataEnum, DataStruct, DataUnion, + DeriveInput, Fields, FieldsNamed, Generics, Ident, Token, Variant, +}; + +/** +Derive macro generating an impl of the trait +`Cinable`. +You can find documentation on how to use this +derive macro in +the documentation of `hcpl_io`. +*/ +#[proc_macro_derive(Cinable, attributes(tag_type, tag))] +#[proc_macro_error] +pub fn derive_cinable(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let item = parse_macro_input!(item as DeriveInput); + let item_name = &item.ident; + + set_dummy(quote! { + impl ::hcpl_io::Cinable for #item_name { + fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + unimplemented!(); + } + } + }); + + let ts = match &item.data { + Data::Struct(DataStruct { fields, .. }) => { + derive_cinable_for_struct(item_name, fields, &item.generics) + } + Data::Enum(DataEnum { variants, .. }) => { + derive_cinable_for_enum(item_name, &item.attrs, variants, &item.generics) + } + Data::Union(DataUnion { fields, .. }) => { + derive_cinable_for_union(item_name, &item.attrs, fields, &item.generics) + } + }; + + match ts { + Ok(ts) => ts.into(), + Err(ErrorKind::SpanLater(msg)) => abort!(item, msg), + Err(ErrorKind::Spanned(err)) => err.into_compile_error().into(), + } +} + +fn derive_cinable_for_struct( + name: &Ident, + fields: &Fields, + generics: &Generics, +) -> result::Result { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let read_from_fields = gen_read_from_fields(fields, name)?; + + let ts = quote! { + #[automatically_derived] + impl #impl_generics ::hcpl_io::Cinable for #name #ty_generics #where_clause { + fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + #read_from_fields + } + } + }; + Ok(ts) +} + +fn derive_cinable_for_enum( + name: &Ident, + attrs: &Vec, + variants: &Punctuated, + generics: &Generics, +) -> result::Result { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let read_from_variants = gen_read_from_variants(variants, attrs)?; + + let ts = quote! { + #[automatically_derived] + impl #impl_generics ::hcpl_io::Cinable for #name #ty_generics #where_clause { + fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + #read_from_variants + } + } + }; + Ok(ts) +} + +fn derive_cinable_for_union( + name: &Ident, + attrs: &Vec, + fields: &FieldsNamed, + generics: &Generics, +) -> result::Result { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let read_from_union = gen_read_from_union(fields, attrs)?; + + let ts = quote! { + #[automatically_derived] + impl #impl_generics ::hcpl_io::Cinable for #name #ty_generics #where_clause { + fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + #read_from_union + } + } + }; + Ok(ts) +} diff --git a/hcpl_proc_macro/src/result.rs b/hcpl_proc_macro/src/result.rs new file mode 100644 index 0000000..b879f85 --- /dev/null +++ b/hcpl_proc_macro/src/result.rs @@ -0,0 +1,30 @@ +use quote::ToTokens; +use syn::Error; + +pub(crate) enum ErrorKind { + SpanLater(String), + Spanned(Error), +} + +pub(crate) type Result = std::result::Result; + +impl From for ErrorKind { + fn from(value: Error) -> Self { + Self::Spanned(value) + } +} + +impl Into> for ErrorKind { + fn into(self) -> Result { + Err(self) + } +} + +impl ErrorKind { + pub(crate) fn span_now(self, token: T) -> Self { + match self { + Self::SpanLater(msg) => Self::Spanned(Error::new_spanned(token, msg)), + spanned => spanned, + } + } +} From 89234344c562ba4c9a949919b583e167b35350d0 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Mar 2024 19:18:41 +0700 Subject: [PATCH 2/6] Add Cinable derive macro to hcpl_io --- Cargo.toml | 1 + hcpl_io/Cargo.toml | 4 ++++ hcpl_io/src/cin.rs | 3 +++ 3 files changed, 8 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 7ea340f..674f344 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,5 @@ members = [ "hcpl_tensor", "hcpl_union_find", "hcpl_util", + "hcpl_proc_macro", ] diff --git a/hcpl_io/Cargo.toml b/hcpl_io/Cargo.toml index d44bc4d..53b3e56 100644 --- a/hcpl_io/Cargo.toml +++ b/hcpl_io/Cargo.toml @@ -7,3 +7,7 @@ license = "MIT" [dependencies] hcpl_integer = { path = "../hcpl_integer" } +hcpl_proc_macro = { path = "../hcpl_proc_macro" } + +[features] +derive = [] \ No newline at end of file diff --git a/hcpl_io/src/cin.rs b/hcpl_io/src/cin.rs index 0cf02a3..8214c5b 100644 --- a/hcpl_io/src/cin.rs +++ b/hcpl_io/src/cin.rs @@ -2,6 +2,9 @@ use std::io::BufRead; use hcpl_integer::Integer; +#[cfg(feature = "derive")] +pub use hcpl_proc_macro::Cinable; + pub struct Cin<'a> { stdin: std::io::StdinLock<'a>, } From afddcbd9cec09e7c9e2f70bebb109cfb136568f5 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Mar 2024 19:19:24 +0700 Subject: [PATCH 3/6] Remove leading :: Make it cargo-equip-friendly --- hcpl_io/README.md | 240 +++++++++++++++++++++++++++++++++++++ hcpl_io/src/lib.rs | 2 + hcpl_proc_macro/src/lib.rs | 12 +- 3 files changed, 248 insertions(+), 6 deletions(-) create mode 100644 hcpl_io/README.md diff --git a/hcpl_io/README.md b/hcpl_io/README.md new file mode 100644 index 0000000..d4f09db --- /dev/null +++ b/hcpl_io/README.md @@ -0,0 +1,240 @@ +This module contains various tools for standard I/O operations, +specifically used for competitive programming. + +# The `Cinable` derive macro +You can `derive(Cinable)` for all accepted `derive` inputs: +struct, enum, and union. + +## Using `derive(Cinable)` on a struct +Here is an example of `derive(Cinable)` usage on all of three kinds +of struct expressions: struct, tuple, and unit. + +```rust +use hcpl_proc_macro::Cinable; + +#[derive(Cinable)] +struct StructStruct { + x: char, + y: i32, +} + +#[derive(Cinable)] +struct TupleStruct(bool, u64); + +#[derive(Cinable)] +struct UnitStruct; +``` + +The macro for the code above will expand to something +like the following. +```rust +# use hcpl_io; +# #[allow(dead_code)] +# struct StructStruct { +# x: char, +# y: i32, +# } +#[automatically_derived] +impl ::hcpl_io::Cinable for StructStruct { + fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + StructStruct { + x: cin.get(), + y: cin.get(), + } + } +} + +# struct TupleStruct(bool, u64); +#[automatically_derived] +impl ::hcpl_io::Cinable for TupleStruct { + fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + TupleStruct(cin.get(), cin.get()) + } +} + +# struct UnitStruct; +#[automatically_derived] +impl ::hcpl_io::Cinable for UnitStruct { + fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + UnitStruct + } +} +``` + +## Using `derive(Cinable)` on an enum +To inform the program about which enum variant needs to be +constructed, you need to add tags to each variant. This is +achieved by using the `tag` attribute. Consequently, you need +to explicitly specify the type of tags before the enum's +definition using the `tag_type` attribute. For more information +about the tag_type and tag attributes, please refer to the +[The tag_type and tag attributes](#the-tag_type-and-tag-attributes) +section. + +Here is an example of `derive(Cinable)` usage on an enum. +```rust +use hcpl_proc_macro::Cinable; + +#[derive(Cinable)] +#[allow(dead_code)] +#[tag_type(char)] +enum UfdsOperations { + #[tag = 'u'] + Unite { x: usize, y: usize }, + #[tag = 'f'] + Find(usize), + #[tag = 's'] + Size, +} +``` + +The macro for the code above will expand to something +like the following. +```rust +# use hcpl_io; +# #[allow(dead_code)] +# enum UfdsOperations { +# Unite { x: usize, y: usize }, +# Find(usize), +# Size, +# } +#[automatically_derived] +impl ::hcpl_io::Cinable for UfdsOperations { + fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + let tag = cin.get::(); + match tag { + 'u' => { + Self::Unite { + x: cin.get(), + y: cin.get(), + } + } + 'f' => Self::Find(cin.get()), + 's' => Self::Size, + _ => panic!("unexpected value: {0:?}", tag), + } + } +} +``` + +## Deriving `Cinable` on a union +Although using an enum is sufficient for most use cases, you can +also `derive(Cinable)` for unions. Like enums, you must provide +`tag_type` and `tag` attributes. + +Here is an example of `derive(Cinable)` usage on an union. +```rust +use hcpl_proc_macro::Cinable; + +#[derive(Cinable)] +#[tag_type(u16)] +union Ascii { + #[tag = 2] + ch: char, + #[tag = 0] + byte: u8, + #[tag = 1] + another_byte: u8, +} +``` + +The macro for the code above will expand to something +like the following. +```rust +# use hcpl_io; +# union Ascii { +# ch: char, +# byte: u8, +# another_byte: u8, +# } +#[automatically_derived] +impl ::hcpl_io::Cinable for Ascii { + fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + let tag = cin.get::(); + match tag { + 2 => Self { ch: cin.get() }, + 0 => Self { byte: cin.get() }, + 1 => Self { another_byte: cin.get() }, + _ => panic!("unexpected value: {0:?}", tag), + } + } +} +``` + +## The `tag_type` and `tag` attributes +- The `tag_type` attribute defines the tag type for tagged +unions. It has the following syntax: + ```text + #[tag_type(T)] + ``` + Where `T` is a type and must implement [`Cinable`]. Each enum + and union that uses `derive(Cinable)` must have **exactly one** + `tag_type` attribute. + +- The `tag` attribute defines the tag for enum variants and union +fields. It has the following syntax for name-value pairs: + ```text + #[tag = lit] + ``` + or the following syntax for list-style attributes: + ```text + #[tag(expr_lit, ...)] + ``` + Where `lit` is a literal and `expr_lit` is a literal expression. +Each enum variant and union field must have **at least one** +tag` attribute. + + Please note that the value in name-value syntax is a literal. That +means you can't use `-1` as value because it consists of a minus +sign (`-`) and the literal `1`. Instead, use the list-style syntax +for that. + +Here is an example to demonstrate how to use the `tag_type` and +`tag` attributes. +```rust +use hcpl_proc_macro::Cinable; + +#[derive(Cinable)] +#[tag_type(i32)] +enum NumberType { + #[tag = 1] + One, + #[tag(2, 3, 5)] + Prime, + #[tag(-2, -5)] + #[tag(-3)] + NegativePrime, + #[tag(100,)] + #[tag = 727] + #[tag = 421] + ThreeDigits +} +``` + +An enum variant or union field that has multiple tags +will expand to a sequence of literal patterns separated +by vertical bars (`|`). The macro for the code above +will expand to something like the following. +```rust +# use hcpl_io; +# use hcpl_proc_macro::Cinable; +# enum NumberType { +# One, +# Prime, +# NegativePrime, +# ThreeDigits, +# } +#[automatically_derived] +impl ::hcpl_io::Cinable for NumberType { + fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + let tag = cin.get::(); + match tag { + 1 => Self::One, + 2 | 3 | 5 => Self::Prime, + -2 | -5 | -3 => Self::NegativePrime, + 100 | 727 | 421 => Self::ThreeDigits, + _ => panic!("unexpected value: {0:?}", tag), + } + } +} +``` \ No newline at end of file diff --git a/hcpl_io/src/lib.rs b/hcpl_io/src/lib.rs index afde2ea..a2b27cd 100644 --- a/hcpl_io/src/lib.rs +++ b/hcpl_io/src/lib.rs @@ -1,3 +1,5 @@ +#![doc = include_str!("../README.md")] + mod cin; mod cout; pub mod input_macro; diff --git a/hcpl_proc_macro/src/lib.rs b/hcpl_proc_macro/src/lib.rs index 6088e7d..8afc2cf 100644 --- a/hcpl_proc_macro/src/lib.rs +++ b/hcpl_proc_macro/src/lib.rs @@ -65,8 +65,8 @@ fn derive_cinable_for_struct( let ts = quote! { #[automatically_derived] - impl #impl_generics ::hcpl_io::Cinable for #name #ty_generics #where_clause { - fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + impl #impl_generics hcpl_io::Cinable for #name #ty_generics #where_clause { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { #read_from_fields } } @@ -85,8 +85,8 @@ fn derive_cinable_for_enum( let ts = quote! { #[automatically_derived] - impl #impl_generics ::hcpl_io::Cinable for #name #ty_generics #where_clause { - fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + impl #impl_generics hcpl_io::Cinable for #name #ty_generics #where_clause { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { #read_from_variants } } @@ -105,8 +105,8 @@ fn derive_cinable_for_union( let ts = quote! { #[automatically_derived] - impl #impl_generics ::hcpl_io::Cinable for #name #ty_generics #where_clause { - fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + impl #impl_generics hcpl_io::Cinable for #name #ty_generics #where_clause { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { #read_from_union } } From caffd19ee0f2cd008a8a2c9f6361cebe6e86b45c Mon Sep 17 00:00:00 2001 From: Dewangga Putra Sheradhien Date: Tue, 12 Mar 2024 19:15:25 +0700 Subject: [PATCH 4/6] Copy doc to code Unfortunately, cargo-equip doesn't know how to expand `include_str!`. --- hcpl_io/README.md | 24 ++--- hcpl_io/src/lib.rs | 243 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 254 insertions(+), 13 deletions(-) diff --git a/hcpl_io/README.md b/hcpl_io/README.md index d4f09db..9af8961 100644 --- a/hcpl_io/README.md +++ b/hcpl_io/README.md @@ -35,8 +35,8 @@ like the following. # y: i32, # } #[automatically_derived] -impl ::hcpl_io::Cinable for StructStruct { - fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { +impl hcpl_io::Cinable for StructStruct { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { StructStruct { x: cin.get(), y: cin.get(), @@ -46,16 +46,16 @@ impl ::hcpl_io::Cinable for StructStruct { # struct TupleStruct(bool, u64); #[automatically_derived] -impl ::hcpl_io::Cinable for TupleStruct { - fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { +impl hcpl_io::Cinable for TupleStruct { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { TupleStruct(cin.get(), cin.get()) } } # struct UnitStruct; #[automatically_derived] -impl ::hcpl_io::Cinable for UnitStruct { - fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { +impl hcpl_io::Cinable for UnitStruct { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { UnitStruct } } @@ -99,8 +99,8 @@ like the following. # Size, # } #[automatically_derived] -impl ::hcpl_io::Cinable for UfdsOperations { - fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { +impl hcpl_io::Cinable for UfdsOperations { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { let tag = cin.get::(); match tag { 'u' => { @@ -148,8 +148,8 @@ like the following. # another_byte: u8, # } #[automatically_derived] -impl ::hcpl_io::Cinable for Ascii { - fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { +impl hcpl_io::Cinable for Ascii { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { let tag = cin.get::(); match tag { 2 => Self { ch: cin.get() }, @@ -225,8 +225,8 @@ will expand to something like the following. # ThreeDigits, # } #[automatically_derived] -impl ::hcpl_io::Cinable for NumberType { - fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { +impl hcpl_io::Cinable for NumberType { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { let tag = cin.get::(); match tag { 1 => Self::One, diff --git a/hcpl_io/src/lib.rs b/hcpl_io/src/lib.rs index a2b27cd..23e5d9f 100644 --- a/hcpl_io/src/lib.rs +++ b/hcpl_io/src/lib.rs @@ -1,4 +1,245 @@ -#![doc = include_str!("../README.md")] +/*! +This module contains various tools for standard I/O operations, +specifically used for competitive programming. + +# The `Cinable` derive macro +You can `derive(Cinable)` for all accepted `derive` inputs: +struct, enum, and union. + +## Using `derive(Cinable)` on a struct +Here is an example of `derive(Cinable)` usage on all of three kinds +of struct expressions: struct, tuple, and unit. + +```rust +use hcpl_proc_macro::Cinable; + +#[derive(Cinable)] +struct StructStruct { + x: char, + y: i32, +} + +#[derive(Cinable)] +struct TupleStruct(bool, u64); + +#[derive(Cinable)] +struct UnitStruct; +``` + +The macro for the code above will expand to something +like the following. +```rust +# use hcpl_io; +# #[allow(dead_code)] +# struct StructStruct { +# x: char, +# y: i32, +# } +#[automatically_derived] +impl hcpl_io::Cinable for StructStruct { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { + StructStruct { + x: cin.get(), + y: cin.get(), + } + } +} + +# struct TupleStruct(bool, u64); +#[automatically_derived] +impl hcpl_io::Cinable for TupleStruct { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { + TupleStruct(cin.get(), cin.get()) + } +} + +# struct UnitStruct; +#[automatically_derived] +impl hcpl_io::Cinable for UnitStruct { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { + UnitStruct + } +} +``` + +## Using `derive(Cinable)` on an enum +To inform the program about which enum variant needs to be +constructed, you need to add tags to each variant. This is +achieved by using the `tag` attribute. Consequently, you need +to explicitly specify the type of tags before the enum's +definition using the `tag_type` attribute. For more information +about the tag_type and tag attributes, please refer to the +[The tag_type and tag attributes](#the-tag_type-and-tag-attributes) +section. + +Here is an example of `derive(Cinable)` usage on an enum. +```rust +use hcpl_proc_macro::Cinable; + +#[derive(Cinable)] +#[allow(dead_code)] +#[tag_type(char)] +enum UfdsOperations { + #[tag = 'u'] + Unite { x: usize, y: usize }, + #[tag = 'f'] + Find(usize), + #[tag = 's'] + Size, +} +``` + +The macro for the code above will expand to something +like the following. +```rust +# use hcpl_io; +# #[allow(dead_code)] +# enum UfdsOperations { +# Unite { x: usize, y: usize }, +# Find(usize), +# Size, +# } +#[automatically_derived] +impl hcpl_io::Cinable for UfdsOperations { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { + let tag = cin.get::(); + match tag { + 'u' => { + Self::Unite { + x: cin.get(), + y: cin.get(), + } + } + 'f' => Self::Find(cin.get()), + 's' => Self::Size, + _ => panic!("unexpected value: {0:?}", tag), + } + } +} +``` + +## Deriving `Cinable` on a union +Although using an enum is sufficient for most use cases, you can +also `derive(Cinable)` for unions. Like enums, you must provide +`tag_type` and `tag` attributes. + +Here is an example of `derive(Cinable)` usage on an union. +```rust +use hcpl_proc_macro::Cinable; + +#[derive(Cinable)] +#[tag_type(u16)] +union Ascii { + #[tag = 2] + ch: char, + #[tag = 0] + byte: u8, + #[tag = 1] + another_byte: u8, +} +``` + +The macro for the code above will expand to something +like the following. +```rust +# use hcpl_io; +# union Ascii { +# ch: char, +# byte: u8, +# another_byte: u8, +# } +#[automatically_derived] +impl hcpl_io::Cinable for Ascii { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { + let tag = cin.get::(); + match tag { + 2 => Self { ch: cin.get() }, + 0 => Self { byte: cin.get() }, + 1 => Self { another_byte: cin.get() }, + _ => panic!("unexpected value: {0:?}", tag), + } + } +} +``` + +## The `tag_type` and `tag` attributes +- The `tag_type` attribute defines the tag type for tagged +unions. It has the following syntax: + ```text + #[tag_type(T)] + ``` + Where `T` is a type and must implement [`Cinable`]. Each enum + and union that uses `derive(Cinable)` must have **exactly one** + `tag_type` attribute. + +- The `tag` attribute defines the tag for enum variants and union +fields. It has the following syntax for name-value pairs: + ```text + #[tag = lit] + ``` + or the following syntax for list-style attributes: + ```text + #[tag(expr_lit, ...)] + ``` + Where `lit` is a literal and `expr_lit` is a literal expression. +Each enum variant and union field must have **at least one** +tag` attribute. + + Please note that the value in name-value syntax is a literal. That +means you can't use `-1` as value because it consists of a minus +sign (`-`) and the literal `1`. Instead, use the list-style syntax +for that. + +Here is an example to demonstrate how to use the `tag_type` and +`tag` attributes. +```rust +use hcpl_proc_macro::Cinable; + +#[derive(Cinable)] +#[tag_type(i32)] +enum NumberType { + #[tag = 1] + One, + #[tag(2, 3, 5)] + Prime, + #[tag(-2, -5)] + #[tag(-3)] + NegativePrime, + #[tag(100,)] + #[tag = 727] + #[tag = 421] + ThreeDigits +} +``` + +An enum variant or union field that has multiple tags +will expand to a sequence of literal patterns separated +by vertical bars (`|`). The macro for the code above +will expand to something like the following. +```rust +# use hcpl_io; +# use hcpl_proc_macro::Cinable; +# enum NumberType { +# One, +# Prime, +# NegativePrime, +# ThreeDigits, +# } +#[automatically_derived] +impl hcpl_io::Cinable for NumberType { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { + let tag = cin.get::(); + match tag { + 1 => Self::One, + 2 | 3 | 5 => Self::Prime, + -2 | -5 | -3 => Self::NegativePrime, + 100 | 727 | 421 => Self::ThreeDigits, + _ => panic!("unexpected value: {0:?}", tag), + } + } +} +``` +*/ mod cin; mod cout; From 0ad5065f7f890e2b36d6cf1e29361f905f28bb6d Mon Sep 17 00:00:00 2001 From: Dewangga Putra Sheradhien Date: Wed, 13 Mar 2024 06:42:17 +0700 Subject: [PATCH 5/6] Add note about crate feature --- hcpl_io/README.md | 5 ++++- hcpl_io/src/lib.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hcpl_io/README.md b/hcpl_io/README.md index 9af8961..006805d 100644 --- a/hcpl_io/README.md +++ b/hcpl_io/README.md @@ -2,9 +2,12 @@ This module contains various tools for standard I/O operations, specifically used for competitive programming. # The `Cinable` derive macro -You can `derive(Cinable)` for all accepted `derive` inputs: +You can `derive(Cinable)` for all accepted derive inputs: struct, enum, and union. +Please note that this derive macro is available on **crate +feature** `derive` only. + ## Using `derive(Cinable)` on a struct Here is an example of `derive(Cinable)` usage on all of three kinds of struct expressions: struct, tuple, and unit. diff --git a/hcpl_io/src/lib.rs b/hcpl_io/src/lib.rs index 23e5d9f..83b1add 100644 --- a/hcpl_io/src/lib.rs +++ b/hcpl_io/src/lib.rs @@ -3,9 +3,12 @@ This module contains various tools for standard I/O operations, specifically used for competitive programming. # The `Cinable` derive macro -You can `derive(Cinable)` for all accepted `derive` inputs: +You can `derive(Cinable)` for all accepted derive inputs: struct, enum, and union. +Please note that this derive macro is available on **crate +feature** `derive` only. + ## Using `derive(Cinable)` on a struct Here is an example of `derive(Cinable)` usage on all of three kinds of struct expressions: struct, tuple, and unit. From 175b8024170f1fedd21a9acc35a0e91d83d58807 Mon Sep 17 00:00:00 2001 From: Dewangga Putra Sheradhien Date: Wed, 13 Mar 2024 06:49:37 +0700 Subject: [PATCH 6/6] Remove leading :: Whoops. Forgot to remove it from dummy expansion as well. --- hcpl_proc_macro/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hcpl_proc_macro/src/lib.rs b/hcpl_proc_macro/src/lib.rs index 8afc2cf..6e6b9cf 100644 --- a/hcpl_proc_macro/src/lib.rs +++ b/hcpl_proc_macro/src/lib.rs @@ -29,8 +29,8 @@ pub fn derive_cinable(item: proc_macro::TokenStream) -> proc_macro::TokenStream let item_name = &item.ident; set_dummy(quote! { - impl ::hcpl_io::Cinable for #item_name { - fn read_from(cin: &mut ::hcpl_io::Cin) -> Self { + impl hcpl_io::Cinable for #item_name { + fn read_from(cin: &mut hcpl_io::Cin) -> Self { unimplemented!(); } }