Introduce Mismatch versus Error distinction.
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 15 Sep 2024 01:20:10 +0000 (18:20 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 15 Sep 2024 01:20:10 +0000 (18:20 -0700)
rust/pspp-derive/src/lib.rs
rust/pspp/src/command.rs

index 6fd7a8678d4afc077eeb3f0e6f812b79aea11656..da1a879a1431f81ae22bd172813f5d9f4e784dc9 100644 (file)
@@ -43,21 +43,21 @@ fn derive_enum(ast: &DeriveInput, e: &DataEnum) -> Result<TokenStream2, Error> {
         } else {
             quote! { cursor.match_keyword(#ident_string)}
         };
-        let construction = construct_fields(&variant.fields);
+        let construction = construct_fields(&variant.fields, true);
         let check_equals = if struct_attrs.required_equals && !variant.fields.is_empty() {
-            quote! { cursor.force(&Token::Punct(Punct::Equals))?; }
+            quote! { cursor.force(&Token::Punct(Punct::Equals)).map_err(ParseError::Error)?; }
         } else {
             quote!{}
         };
         body.extend(quote! { if #match_expr { #check_equals Self::#ident #construction } });
     }
-    body.extend(quote! { else { return Err(cursor.error("Syntax error.")); } });
+    body.extend(quote! { else { return Err(ParseError::Mismatch(cursor.error("Syntax error."))); } });
 
     let name = &ast.ident;
     let lifetime = struct_attrs.lifetime();
     let output = quote! {
         impl<'a> FromTokens<'a> for #name #lifetime {
-            fn from_tokens(cursor: &Cursor<'a>) -> Result<Self, Diagnostic> {
+            fn from_tokens(cursor: &Cursor<'a>) -> ParseResult<Self> {
                 Ok(#body)
             }
         }
@@ -66,10 +66,15 @@ fn derive_enum(ast: &DeriveInput, e: &DataEnum) -> Result<TokenStream2, Error> {
     Ok(output)
 }
 
-fn construct_fields(fields: &Fields) -> impl ToTokens {
+fn construct_fields(fields: &Fields, mismatch_to_error: bool) -> impl ToTokens {
     let mut construction = TokenStream2::new();
+    let convert = if mismatch_to_error {
+        quote! { .mismatch_to_error() }
+    } else {
+        quote! {}
+    };
     for field in fields {
-        let value = quote! { FromTokens::from_tokens(cursor)? };
+        let value = quote! { FromTokens::from_tokens(cursor) #convert ? };
         if let Some(name) = field.ident.as_ref() {
             construction.extend(quote! { #name: #value, });
         } else {
@@ -87,11 +92,11 @@ fn construct_fields(fields: &Fields) -> impl ToTokens {
 fn derive_struct(ast: &DeriveInput, s: &DataStruct) -> Result<TokenStream2, Error> {
     let struct_attrs = StructAttrs::parse(&ast.attrs)?;
     let name = &ast.ident;
-    let construction = construct_fields(&s.fields);
+    let construction = construct_fields(&s.fields, false);
     let lifetime = struct_attrs.lifetime();
     let output = quote! {
         impl<'a> FromTokens<'a> for #name #lifetime {
-            fn from_tokens(cursor: &Cursor<'a>) -> Result<Self, Diagnostic> {
+            fn from_tokens(cursor: &Cursor<'a>) -> ParseResult<Self> {
                 Ok(#name #construction)
             }
         }
index 6a20e85473018f35927b180c21eea1451aecb3ee..fd79e62b0ed4b7015a0c5838526004403f0eb68d 100644 (file)
@@ -106,8 +106,29 @@ struct Subcommand {
     name: &str,
 }*/
 
+#[derive(Debug)]
+enum ParseError {
+    Error(Diagnostic),
+    Mismatch(Diagnostic),
+}
+
+type ParseResult<T> = Result<T, ParseError>;
+
+trait MismatchToError {
+    fn mismatch_to_error(self) -> Self;
+}
+
+impl<T> MismatchToError for ParseResult<T> {
+    fn mismatch_to_error(self) -> Self {
+        match self {
+            Err(ParseError::Mismatch(diagnostic)) => Err(ParseError::Error(diagnostic)),
+            rest => rest
+        }
+    }
+}
+
 trait FromTokens<'a> {
-    fn from_tokens(cursor: &Cursor<'a>) -> Result<Self, Diagnostic>
+    fn from_tokens(cursor: &Cursor<'a>) -> ParseResult<Self>
     where
         Self: Sized;
 }
@@ -116,7 +137,7 @@ impl<'a, T> FromTokens<'a> for Option<T>
 where
     T: FromTokens<'a>,
 {
-    fn from_tokens(cursor: &Cursor<'a>) -> Result<Self, Diagnostic>
+    fn from_tokens(cursor: &Cursor<'a>) -> ParseResult<Self>
     where
         Self: Sized,
     {
@@ -135,7 +156,7 @@ impl<'a, T> FromTokens<'a> for Vec<T>
 where
     T: FromTokens<'a>,
 {
-    fn from_tokens(cursor: &Cursor<'a>) -> Result<Self, Diagnostic>
+    fn from_tokens(cursor: &Cursor<'a>) -> ParseResult<Self>
     where
         Self: Sized,
     {
@@ -148,7 +169,7 @@ where
 }
 
 impl<'a> FromTokens<'a> for TokenSlice<'a> {
-    fn from_tokens(cursor: &Cursor<'a>) -> Result<Self, Diagnostic>
+    fn from_tokens(cursor: &Cursor<'a>) -> ParseResult<Self>
     where
         Self: Sized,
     {
@@ -183,13 +204,15 @@ impl<'a, T> FromTokens<'a> for Subcommand<T>
 where
     T: FromTokens<'a>,
 {
-    fn from_tokens(cursor: &Cursor<'a>) -> Result<Self, Diagnostic>
+    fn from_tokens(cursor: &Cursor<'a>) -> ParseResult<Self>
     where
         Self: Sized,
     {
         cursor.advance_until(|token| token != &Token::Punct(Punct::Slash));
         if cursor.at_end() {
-            return Err(cursor.error("Syntax error at end of input."));
+            return Err(ParseError::Error(
+                cursor.error("Syntax error at end of input."),
+            ));
         }
         let start = cursor.get_pos();
         cursor.advance_until(|token| token == &Token::Punct(Punct::Slash));
@@ -204,10 +227,8 @@ where
     }
 }
 
-/*
 #[derive(FromTokens, Debug)]
-#[pspp(add_lifetime, required_equals)]*/
-#[derive(Debug)]
+#[pspp(add_lifetime, required_equals)]
 enum DescriptivesSubcommand<'a> {
     Variables(Vec<DescriptivesVarRange<'a>>),
     Missing(Vec<Missing>),
@@ -216,8 +237,9 @@ enum DescriptivesSubcommand<'a> {
     Sort(Sort),
 }
 
+/*
 impl<'a> FromTokens<'a> for DescriptivesSubcommand<'a> {
-    fn from_tokens(cursor: &Cursor<'a>) -> Result<Self, Diagnostic> {
+    fn from_tokens(cursor: &Cursor<'a>) -> ParseResult<Self> {
             println!("{}:{}", file!(), line!());
         Ok(if cursor.match_keyword("Variables") {
             println!("{}:{}", file!(), line!());
@@ -239,7 +261,7 @@ impl<'a> FromTokens<'a> for DescriptivesSubcommand<'a> {
             return Err(cursor.error("Syntax error."));
         })
     }
-}
+}*/
 
 #[derive(FromTokens, Debug)]
 enum Missing {
@@ -262,13 +284,17 @@ impl<'a, T> FromTokens<'a> for InParens<T>
 where
     T: FromTokens<'a>,
 {
-    fn from_tokens(cursor: &Cursor<'a>) -> Result<Self, Diagnostic>
+    fn from_tokens(cursor: &Cursor<'a>) -> ParseResult<Self>
     where
         Self: Sized,
     {
-        cursor.force(&Token::Punct(Punct::LParen))?;
+        cursor
+            .force(&Token::Punct(Punct::LParen))
+            .map_err(ParseError::Mismatch)?;
         let inner = T::from_tokens(cursor)?;
-        cursor.force(&Token::Punct(Punct::RParen))?;
+        cursor
+            .force(&Token::Punct(Punct::RParen))
+            .map_err(ParseError::Mismatch)?;
         Ok(Self(inner))
     }
 }
@@ -280,26 +306,26 @@ struct VarRange<'a> {
 }
 
 impl<'a> FromTokens<'a> for VarRange<'a> {
-    fn from_tokens(cursor: &Cursor<'a>) -> Result<Self, Diagnostic>
+    fn from_tokens(cursor: &Cursor<'a>) -> ParseResult<Self>
     where
         Self: Sized,
     {
         Ok(Self {
-            from: cursor.force_id()?,
+            from: cursor.force_id().map_err(ParseError::Mismatch)?,
             to: cursor
                 .match_(&Token::Punct(Punct::To))
-                .then(|| cursor.force_id())
+                .then(|| cursor.force_id().map_err(ParseError::Mismatch))
                 .transpose()?,
         })
     }
 }
 
 impl<'a> FromTokens<'a> for &'a Identifier {
-    fn from_tokens(cursor: &Cursor<'a>) -> Result<Self, Diagnostic>
+    fn from_tokens(cursor: &Cursor<'a>) -> ParseResult<Self>
     where
         Self: Sized,
     {
-        cursor.force_id()
+        cursor.force_id().map_err(ParseError::Mismatch)
     }
 }