Skip to content

Commit

Permalink
Removed error_callback attr, added to error
Browse files Browse the repository at this point in the history
`#[logos(error = SomeType)]`
`#[logos(error_callback = callback)]`

is now expressed as

`#[logos(error(SomeType, callback))]`
  • Loading branch information
mysteriouslyseeing committed Dec 3, 2024
1 parent a0a358e commit 582cfcc
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 21 deletions.
3 changes: 1 addition & 2 deletions examples/custom_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]+")]
Expand Down
4 changes: 2 additions & 2 deletions logos-codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
71 changes: 71 additions & 0 deletions logos-codegen/src/parser/error_type.rs
Original file line number Diff line number Diff line change
@@ -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<Callback>,
}

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<Self>) -> (MaybeVoid, Option<Callback>) {
if let Some(Self { ty, callback }) = opt {
(MaybeVoid::Some(ty), callback)
} else {
(MaybeVoid::Void, None)
}
}

pub fn span(&self) -> Span {
self.ty.span()
}
}
70 changes: 55 additions & 15 deletions logos-codegen/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,8 +30,7 @@ pub struct Parser {
pub source: Option<TokenStream>,
pub skips: Vec<Literal>,
pub extras: MaybeVoid,
pub error_type: MaybeVoid,
pub error_callback: Option<Callback>,
pub error_type: Option<ErrorType>,
pub subpatterns: Subpatterns,
pub logos_path: Option<TokenStream>,
types: TypeParams,
Expand Down Expand Up @@ -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::<Type>() {
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 {
Expand Down
3 changes: 1 addition & 2 deletions tests/tests/custom_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ fn parse_number(input: &str) -> Result<u32, LexingError> {
}

#[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),
Expand Down

0 comments on commit 582cfcc

Please sign in to comment.