start to work on parsing structs
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 8 Sep 2024 18:24:44 +0000 (11:24 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 8 Sep 2024 18:24:44 +0000 (11:24 -0700)
rust/pspp-derive/src/lib.rs
rust/pspp/src/command.rs

index 17ba7e6189dd8ed9b766bd69f764a9901fdf9627..58bcbd2c60f09f895720a30c879c8da4ff7ce4f8 100644 (file)
@@ -1,7 +1,7 @@
 use proc_macro::TokenStream;
 use proc_macro2::{Literal, TokenStream as TokenStream2};
-use quote::quote;
-use syn::{spanned::Spanned, Attribute, DeriveInput, Error, Token};
+use quote::{format_ident, quote};
+use syn::{spanned::Spanned, Attribute, DataEnum, DataStruct, DeriveInput, Error, Token};
 
 #[proc_macro_derive(FromTokens, attributes(pspp))]
 pub fn from_tokens_derive(input: TokenStream) -> TokenStream {
@@ -9,20 +9,27 @@ pub fn from_tokens_derive(input: TokenStream) -> TokenStream {
     // that we can manipulate
     let ast: DeriveInput = syn::parse(input).unwrap();
 
-    match  parse_derive_input(ast) {
+    match parse_derive_input(ast) {
         Ok(output) => output.into(),
-        Err(error) => error.to_compile_error().into()
+        Err(error) => error.to_compile_error().into(),
     }
 }
 
 fn parse_derive_input(ast: DeriveInput) -> Result<TokenStream2, Error> {
-    let syn::Data::Enum(e) = &ast.data else {
-        return Err(Error::new(ast.span(), "Only enums may currently be derived"));
-    };
+    match &ast.data {
+        syn::Data::Enum(e) => derive_enum(&ast, e),
+        syn::Data::Struct(s) => derive_struct(&ast, s),
+        syn::Data::Union(_) => Err(Error::new(
+            ast.span(),
+            "Only struct and enums may currently be derived",
+        )),
+    }
+}
 
+fn derive_enum(ast: &DeriveInput, e: &DataEnum) -> Result<TokenStream2, Error> {
     let mut body = TokenStream2::new();
     for (index, variant) in e.variants.iter().enumerate() {
-        let field_attrs =  parse_attributes(&variant.attrs)?;
+        let field_attrs = parse_attributes(&variant.attrs)?;
         if index > 0 {
             body.extend(quote! { else }.into_iter());
         }
@@ -38,15 +45,39 @@ fn parse_derive_input(ast: DeriveInput) -> Result<TokenStream2, Error> {
             };
         }
     }
-    body.extend(quote! { else { return Err(tokens.error("Syntax error.")); } });
+    body.extend(quote! { else { return Err(cursor.error("Syntax error.")); } });
+
+    let name = &ast.ident;
+    let output = quote! {
+        impl FromTokens for #name {
+            fn from_tokens<'a>(cursor: &Cursor<'a>) -> Result<Self, Diagnostic> {
+                Ok(#body)
+            }
+        }
+    };
+    println!("{output}");
+    Ok(output)
+}
+
+fn derive_struct(ast: &DeriveInput, s: &DataStruct) -> Result<TokenStream2, Error> {
+    let mut construction = TokenStream2::new();
+    let mut body = TokenStream2::new();
+    for (index, field) in s.fields.iter().enumerate() {
+        let varname = format_ident!("field{}", index);
+        let ty = &field.ty;
+        body.extend(quote! { let #varname = <#ty>::from_tokens(cursor)?; });
+        let name = field.ident.as_ref().unwrap();
+        if index > 0 {
+            construction.extend(quote! { , });
+        }
+        construction.extend(quote! { #name: #varname });
+    }
 
     let name = &ast.ident;
     let output = quote! {
         impl FromTokens for #name {
-            fn from_tokens<'a>(tokens: &TokenSlice<'a>) -> Result<Self, Diagnostic> {
-                let cursor = Cursor::new(&tokens);
-                let value = #body;
-                Ok(value)
+            fn from_tokens<'a>(cursor: &Cursor<'a>) -> Result<Self, Diagnostic> {
+                #body Ok(#name { #construction })
             }
         }
     };
index 8d9cae5fb36b98e093759d9a8d3fc7dc4fe30ae5..2f43b1e93a389182510bd2c6eecf11d2cf3347be 100644 (file)
@@ -116,11 +116,27 @@ struct Subcommand {
 }*/
 
 trait FromTokens {
-    fn from_tokens<'a>(tokens: &TokenSlice<'a>) -> Result<Self, Diagnostic>
+    fn from_tokens<'a>(tokens: &Cursor<'a>) -> Result<Self, Diagnostic>
     where
         Self: Sized;
 }
 
+impl<T> FromTokens for Option<T>
+where
+    T: FromTokens,
+{
+    fn from_tokens<'a>(tokens: &Cursor<'a>) -> Result<Self, Diagnostic>
+    where
+        Self: Sized,
+    {
+        match T::from_tokens(tokens) {
+            Ok(result) => Ok(Some(result)),
+            Err(_error) => Ok(None)
+        }
+    }
+}
+
+#[derive(FromTokens)]
 struct Sort {
     key: SortKey,
     direction: Option<Direction>,