From 582cfcc8fa3ee2fb5177df28541036d1ef36c99c Mon Sep 17 00:00:00 2001 From: mysteriouslyseeing <61419567+mysteriouslyseeing@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:17:35 +1100 Subject: [PATCH] Removed error_callback attr, added to error `#[logos(error = SomeType)]` `#[logos(error_callback = callback)]` is now expressed as `#[logos(error(SomeType, callback))]` --- examples/custom_error.rs | 3 +- logos-codegen/src/lib.rs | 4 +- logos-codegen/src/parser/error_type.rs | 71 ++++++++++++++++++++++++++ logos-codegen/src/parser/mod.rs | 70 +++++++++++++++++++------ tests/tests/custom_error.rs | 3 +- 5 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 logos-codegen/src/parser/error_type.rs diff --git a/examples/custom_error.rs b/examples/custom_error.rs index 7aa936b4..a36a2bfe 100644 --- a/examples/custom_error.rs +++ b/examples/custom_error.rs @@ -37,8 +37,7 @@ impl LexingError { } #[derive(Debug, Logos, PartialEq)] -#[logos(error = LexingError)] -#[logos(error_callback = LexingError::from_lexer)] +#[logos(error(LexingError, LexingError::from_lexer))] #[logos(skip r"[ \t]+")] enum Token { #[regex(r"[a-zA-Z]+")] diff --git a/logos-codegen/src/lib.rs b/logos-codegen/src/lib.rs index 70700867..b6af8ac2 100644 --- a/logos-codegen/src/lib.rs +++ b/logos-codegen/src/lib.rs @@ -216,7 +216,7 @@ pub fn generate(input: TokenStream) -> TokenStream { debug!("Parsing additional options (extras, source, ...)"); - let error_type = parser.error_type.take(); + let (error_type, error_callback) = parser::ErrorType::unwrap(parser.error_type.take()); let extras = parser.extras.take(); let source = parser .source @@ -231,7 +231,7 @@ pub fn generate(input: TokenStream) -> TokenStream { .take() .unwrap_or_else(|| parse_quote!(::logos)); - let make_error_impl = match parser.error_callback.take() { + let make_error_impl = match error_callback { Some(leaf::Callback::Label(label)) => Some(quote! { fn make_error(lex: &mut #logos_path::Lexer<'s, Self>) -> #error_type { #label(lex) diff --git a/logos-codegen/src/parser/error_type.rs b/logos-codegen/src/parser/error_type.rs new file mode 100644 index 00000000..e5ccaa0f --- /dev/null +++ b/logos-codegen/src/parser/error_type.rs @@ -0,0 +1,71 @@ +use proc_macro2::{Span, TokenStream}; +use syn::spanned::Spanned; +use syn::Ident; + +use crate::leaf::Callback; +use crate::parser::nested::NestedValue; +use crate::parser::Parser; +use crate::util::MaybeVoid; + +pub struct ErrorType { + pub ty: TokenStream, + pub callback: Option, +} + +impl ErrorType { + pub fn new(ty: TokenStream) -> Self { + Self { ty, callback: None } + } + + pub fn named_attr(&mut self, name: Ident, value: NestedValue, parser: &mut Parser) { + match (name.to_string().as_str(), value) { + ("callback", NestedValue::Assign(tokens)) => { + let span = tokens.span(); + let callback = match parser.parse_callback(tokens) { + Some(callback) => callback, + None => { + parser.err("Not a valid callback", span); + return; + } + }; + + if let Some(previous) = self.callback.replace(callback) { + parser + .err( + "Callback has been already set", + span.join(name.span()).unwrap(), + ) + .err("Previous callback set here", previous.span()); + } + } + ("callback", _) => { + parser.err("Expected: callback = ...", name.span()); + } + (unknown, _) => { + parser.err( + format!( + "\ + Unknown nested attribute: {}\n\ + \n\ + Expected one of: callback\ + ", + unknown + ), + name.span(), + ); + } + } + } + + pub fn unwrap(opt: Option) -> (MaybeVoid, Option) { + if let Some(Self { ty, callback }) = opt { + (MaybeVoid::Some(ty), callback) + } else { + (MaybeVoid::Void, None) + } + } + + pub fn span(&self) -> Span { + self.ty.span() + } +} diff --git a/logos-codegen/src/parser/mod.rs b/logos-codegen/src/parser/mod.rs index 85832a6f..9fb60cd3 100644 --- a/logos-codegen/src/parser/mod.rs +++ b/logos-codegen/src/parser/mod.rs @@ -10,12 +10,14 @@ use crate::util::{expect_punct, MaybeVoid}; use crate::LOGOS_ATTR; mod definition; +mod error_type; mod ignore_flags; mod nested; mod subpattern; mod type_params; pub use self::definition::{Definition, Literal}; +pub use self::error_type::ErrorType; pub use self::ignore_flags::IgnoreFlags; use self::nested::{AttributeParser, Nested, NestedValue}; pub use self::subpattern::Subpatterns; @@ -28,8 +30,7 @@ pub struct Parser { pub source: Option, pub skips: Vec, pub extras: MaybeVoid, - pub error_type: MaybeVoid, - pub error_callback: Option, + pub error_type: Option, pub subpatterns: Subpatterns, pub logos_path: Option, types: TypeParams, @@ -109,33 +110,72 @@ impl Parser { NestedValue::Assign(value) => { let span = value.span(); - if let MaybeVoid::Some(previous) = parser.error_type.replace(value) { + let error_ty = ErrorType::new(value); + + if let Some(previous) = parser.error_type.replace(error_ty) { parser .err("Error type can be defined only once", span) .err("Previous definition here", previous.span()); } } - _ => { - parser.err("Expected: #[logos(error = SomeType)]", span); - } - }), - ("error_callback", |parser, span, value| match value { - NestedValue::Assign(value) => { - let callback = match parser.parse_callback(value) { - Some(callback) => callback, + NestedValue::Group(value) => { + let span = value.span(); + let mut nested = AttributeParser::new(value); + let ty = match nested.parsed::() { + Some(Ok(ty)) => ty, + Some(Err(e)) => { + parser.err(e.to_string(), e.span()); + return; + } None => { - parser.err("Not a valid callback", span); + parser.err("Expected #[logos(error(SomeType))]", span); return; } }; - if let Some(previous) = parser.error_callback.replace(callback) { + + let mut error_type = { + use quote::ToTokens; + ErrorType::new(ty.into_token_stream()) + }; + + for (position, next) in nested.enumerate() { + match next { + Nested::Unexpected(tokens) => { + parser.err("Unexpected token in attribute", tokens.span()); + } + Nested::Unnamed(tokens) => match position { + 0 => error_type.callback = parser.parse_callback(tokens), + _ => { + parser.err( + "\ + Expected a named argument at this position\n\ + \n\ + hint: If you are trying to define a callback here use: callback = ...\ + ", + tokens.span(), + ); + } + }, + Nested::Named(name, value) => { + error_type.named_attr(name, value, parser); + } + } + } + + if let Some(previous) = parser.error_type.replace(error_type) { parser - .err("Error callback can be defined only once", span) + .err("Error type can be defined only once", span) .err("Previous definition here", previous.span()); } } _ => { - parser.err("Expected #[logos(error_callback = ...)]", span); + parser.err( + concat!( + "Expected: #[logos(error = SomeType)] or ", + "#[logos(error(SomeType[, callback))]" + ), + span, + ); } }), ("extras", |parser, span, value| match value { diff --git a/tests/tests/custom_error.rs b/tests/tests/custom_error.rs index c6f15da9..a2b93fce 100644 --- a/tests/tests/custom_error.rs +++ b/tests/tests/custom_error.rs @@ -36,8 +36,7 @@ fn parse_number(input: &str) -> Result { } #[derive(Logos, Debug, Clone, Copy, PartialEq)] -#[logos(error = LexingError)] -#[logos(error_callback = LexingError::unrecognised_character)] +#[logos(error(LexingError, LexingError::unrecognised_character))] enum Token<'a> { #[regex(r"[0-9]+", |lex| parse_number(lex.slice()))] Number(u32),