parse lifetimes

This commit is contained in:
Jana Dönszelmann 2026-04-03 17:24:45 +02:00
parent 53fc09c02f
commit 47f4c1e8c2
No known key found for this signature in database
6 changed files with 402 additions and 12 deletions

View file

@ -156,6 +156,28 @@ pub enum Token<'a> {
/// ``` /// ```
None, None,
/// A lifetime, a la rust. The rules for this are pretty restrictive.
/// A lifetime must be, a single quote, followed by no more than 3 ascii lowercase alphabetic characters,
/// followed by *not* a closing quote, and any nonalphabetic character. Like a comma,
/// whitespace, eof, etc.
///
/// ```
/// # logparse::generate_ast_recognizer!(is_lt, Token::Lifetime(_));
///
/// assert!(is_lt("'a"));
/// assert!(is_lt("'tcx"));
///
/// // some counterexamples
///
/// assert!(!is_lt("'verylong"));
/// assert!(!is_lt("'foo'"));
/// assert!(!is_lt("'a'"));
/// assert!(!is_lt("'a longer single-quoted string'"));
/// assert!(!is_lt("a"));
/// assert!(!is_lt("13"));
/// ``
Lifetime(Cow<'a, str>),
/// A path, anything that looks vaguely path-like. /// A path, anything that looks vaguely path-like.
/// For example: /// For example:
/// ///

View file

@ -165,6 +165,7 @@ impl<'a> Display for Token<'a> {
} => write!(f, "{before}{space_before}{separator}{after}"), } => write!(f, "{before}{space_before}{separator}{after}"),
Token::Delimited(delimited) => write!(f, "{delimited}"), Token::Delimited(delimited) => write!(f, "{delimited}"),
Token::String(s) => write!(f, "{s}"), Token::String(s) => write!(f, "{s}"),
Token::Lifetime(s) => write!(f, "'{s}"),
} }
} }
} }

View file

@ -263,11 +263,30 @@ impl<'a> Atom<'a> {
impl<'a> Token<'a> { impl<'a> Token<'a> {
fn parse_without_separator<E: ParserError<&'a str> + 'a>() -> impl Parser<&'a str, Self, E> { fn parse_without_separator<E: ParserError<&'a str> + 'a>() -> impl Parser<&'a str, Self, E> {
use winnow::{combinator::*, prelude::*}; use winnow::{combinator::*, prelude::*, token::*};
let delimited: Box<dyn Parser<&'a str, Self, E>> = let delimited: Box<dyn Parser<&'a str, Self, E>> =
Box::new(Delimited::parse().map(Self::Delimited)); Box::new(Delimited::parse().map(Self::Delimited));
let lifetime = (
"'",
alt((
repeat::<_, _, Cow<'a, str>, _, _>(
1..=3,
any.verify(|i: &char| i.is_ascii_lowercase() || *i == '_'),
)
.take(),
"{erased}",
)),
peek(alt((
any::<&str, _>
.verify(|i: &char| !i.is_alphabetic() && *i != '\'')
.value(()),
eof::<&str, _>.value(()),
))),
)
.map(|(_, lifetime, _)| Token::Lifetime(lifetime.into()));
trace( trace(
"token-without-sep", "token-without-sep",
alt(( alt((
@ -276,6 +295,7 @@ impl<'a> Token<'a> {
"None".value(Self::None), "None".value(Self::None),
Number::parse().map(Self::Number), Number::parse().map(Self::Number),
Path::parse().map(Self::Path), Path::parse().map(Self::Path),
lifetime,
AnyString::parse().map(Self::String), AnyString::parse().map(Self::String),
delimited, delimited,
Atom::parse(|| { Atom::parse(|| {
@ -1116,14 +1136,8 @@ mod tests {
leading_space: Space( leading_space: Space(
"", "",
), ),
token: String( token: Lifetime(
AnyString { "_",
prefix: "",
ty: Single,
contents: "_, ",
num_hashtags: 0,
suffix: "",
},
), ),
}, },
Segment { Segment {
@ -1132,10 +1146,18 @@ mod tests {
), ),
token: Atom( token: Atom(
Text( Text(
"_", ",",
), ),
), ),
}, },
Segment {
leading_space: Space(
" ",
),
token: Lifetime(
"_",
),
},
], ],
trailing_space: Space( trailing_space: Space(
"", "",
@ -1251,4 +1273,342 @@ mod tests {
} }
"#); "#);
} }
#[test]
fn parse_ex2() {
assert_debug_snapshot!(parse(r#"super_combine_consts::<rustc_type_ir::relate::solver_relating::SolverRelating<'_, rustc_infer::infer::InferCtxt<'_>, rustc_middle::ty::context::TyCtxt<'_>>>(?1c, ?2c)"#), @r#"
Segments {
segments: [
Segment {
leading_space: Space(
"",
),
token: Separated {
before: Atom(
Text(
"super_combine_consts",
),
),
space_before: Space(
"",
),
separator: DoubleColon,
after: Segment {
leading_space: Space(
"",
),
token: Delimited(
Delimited {
prefix: None,
delimiter: Angle,
contents: Segments {
segments: [
Segment {
leading_space: Space(
"",
),
token: Separated {
before: Atom(
Text(
"rustc_type_ir",
),
),
space_before: Space(
"",
),
separator: DoubleColon,
after: Segment {
leading_space: Space(
"",
),
token: Separated {
before: Atom(
Text(
"relate",
),
),
space_before: Space(
"",
),
separator: DoubleColon,
after: Segment {
leading_space: Space(
"",
),
token: Separated {
before: Atom(
Text(
"solver_relating",
),
),
space_before: Space(
"",
),
separator: DoubleColon,
after: Segment {
leading_space: Space(
"",
),
token: Delimited(
Delimited {
prefix: Some(
(
Text(
"SolverRelating",
),
Space(
"",
),
),
),
delimiter: Angle,
contents: Segments {
segments: [
Segment {
leading_space: Space(
"",
),
token: Lifetime(
"_",
),
},
Segment {
leading_space: Space(
"",
),
token: Atom(
Text(
",",
),
),
},
Segment {
leading_space: Space(
" ",
),
token: Separated {
before: Atom(
Text(
"rustc_infer",
),
),
space_before: Space(
"",
),
separator: DoubleColon,
after: Segment {
leading_space: Space(
"",
),
token: Separated {
before: Atom(
Text(
"infer",
),
),
space_before: Space(
"",
),
separator: DoubleColon,
after: Segment {
leading_space: Space(
"",
),
token: Delimited(
Delimited {
prefix: Some(
(
Text(
"InferCtxt",
),
Space(
"",
),
),
),
delimiter: Angle,
contents: Segments {
segments: [
Segment {
leading_space: Space(
"",
),
token: Lifetime(
"_",
),
},
],
trailing_space: Space(
"",
),
},
},
),
},
},
},
},
},
Segment {
leading_space: Space(
"",
),
token: Atom(
Text(
",",
),
),
},
Segment {
leading_space: Space(
" ",
),
token: Separated {
before: Atom(
Text(
"rustc_middle",
),
),
space_before: Space(
"",
),
separator: DoubleColon,
after: Segment {
leading_space: Space(
"",
),
token: Separated {
before: Atom(
Text(
"ty",
),
),
space_before: Space(
"",
),
separator: DoubleColon,
after: Segment {
leading_space: Space(
"",
),
token: Separated {
before: Atom(
Text(
"context",
),
),
space_before: Space(
"",
),
separator: DoubleColon,
after: Segment {
leading_space: Space(
"",
),
token: Delimited(
Delimited {
prefix: Some(
(
Text(
"TyCtxt",
),
Space(
"",
),
),
),
delimiter: Angle,
contents: Segments {
segments: [
Segment {
leading_space: Space(
"",
),
token: Lifetime(
"_",
),
},
],
trailing_space: Space(
"",
),
},
},
),
},
},
},
},
},
},
},
],
trailing_space: Space(
"",
),
},
},
),
},
},
},
},
},
},
},
],
trailing_space: Space(
"",
),
},
},
),
},
},
},
Segment {
leading_space: Space(
"",
),
token: Delimited(
Delimited {
prefix: None,
delimiter: Paren,
contents: Segments {
segments: [
Segment {
leading_space: Space(
"",
),
token: Atom(
Text(
"?1c,",
),
),
},
Segment {
leading_space: Space(
" ",
),
token: Atom(
Text(
"?2c",
),
),
},
],
trailing_space: Space(
"",
),
},
},
),
},
],
trailing_space: Space(
"",
),
}
"#)
}
} }

View file

@ -14,6 +14,8 @@ pub enum SpanKind {
Number, Number,
/// Known literals, like `true`, `false`, `None`, `Ok`, `Err` /// Known literals, like `true`, `false`, `None`, `Ok`, `Err`
Literal, Literal,
/// Lifetimes (`'foo`)
Lifetime,
/// Strings /// Strings
String, String,
/// Paths /// Paths
@ -193,6 +195,10 @@ mod private {
Token::Atom(atom) => { Token::Atom(atom) => {
atom.into_spans(cx); atom.into_spans(cx);
} }
Token::Lifetime(lifetime) => {
cx.push("'", SpanKind::Surroundings);
cx.push(lifetime, SpanKind::Lifetime);
}
} }
} }
} }

View file

@ -424,7 +424,7 @@ mod tests {
println!("undo"); println!("undo");
f.undo(); f.undo();
c.update_with_parents(&f); c.update_with_parents(&f);
assert_eq!(c.curr().message_or_name(), Some("nest")); assert_eq!(c.curr().message_or_name(), Some("enter"));
assert!(c.next(&f)); assert!(c.next(&f));
assert_eq!(c.curr().message_or_name(), Some("bar")); assert_eq!(c.curr().message_or_name(), Some("bar"));
assert!(!c.next(&f)); assert!(!c.next(&f));

View file

@ -165,12 +165,13 @@ pub fn style_span(kind: SpanKind, style: Style, styles: &Styles) -> Style {
SpanKind::Delimiter(_) => style.fg(styles.delimiter).bold(), SpanKind::Delimiter(_) => style.fg(styles.delimiter).bold(),
SpanKind::Separator => style.fg(styles.faded), SpanKind::Separator => style.fg(styles.faded),
SpanKind::Number => style.fg(styles.literal), SpanKind::Number => style.fg(styles.literal),
SpanKind::Literal => style.fg(styles.literal).dim(), SpanKind::Literal => style.fg(styles.literal),
SpanKind::String => style.fg(styles.string), SpanKind::String => style.fg(styles.string),
SpanKind::Path => style.fg(styles.literal).underlined(), SpanKind::Path => style.fg(styles.literal).underlined(),
SpanKind::Space(_) => style, SpanKind::Space(_) => style,
SpanKind::Constructor => style.fg(styles.literal), SpanKind::Constructor => style.fg(styles.literal),
SpanKind::Surroundings => style.fg(styles.faded), SpanKind::Surroundings => style.fg(styles.faded),
SpanKind::Lifetime => style.fg(styles.faded),
SpanKind::Text => style, SpanKind::Text => style,
} }
} }