docs and line/offset numbers in paths
This commit is contained in:
parent
af09bcd403
commit
2d9a029130
9 changed files with 366 additions and 116 deletions
|
|
@ -97,8 +97,25 @@ impl Separator {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> FileLocation<'a> {
|
||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{ascii::dec_uint, combinator::*, prelude::*};
|
||||
|
||||
let colon_number = || {
|
||||
(":".value(()), dec_uint::<_, u64, _>.take())
|
||||
.map(|(_, number): (_, &str)| Cow::Borrowed(number))
|
||||
};
|
||||
let line_offset = (colon_number(), opt(colon_number()));
|
||||
|
||||
trace(
|
||||
"file location",
|
||||
line_offset.map(|(line, offset)| FileLocation { line, offset }),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FileName<'a> {
|
||||
fn parse(segment: PathSegment<'a>) -> Self {
|
||||
fn parse(segment: PathSegment<'a>, location: Option<FileLocation<'a>>) -> Self {
|
||||
fn rsplit<'a>(
|
||||
input: Cow<'a, str>,
|
||||
delimiter: char,
|
||||
|
|
@ -113,34 +130,11 @@ impl<'a> FileName<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
let (rest, location) = if let Some((before, offset)) = rsplit(segment.segment.clone(), ':')
|
||||
{
|
||||
if let Some((before, line)) = rsplit(before.clone(), ':') {
|
||||
(
|
||||
before,
|
||||
Some(FileLocation {
|
||||
line,
|
||||
offset: Some(offset),
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
before,
|
||||
Some(FileLocation {
|
||||
line: offset,
|
||||
offset: None,
|
||||
}),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
(segment.segment, None)
|
||||
};
|
||||
|
||||
let (new_segment, ext_excluding_dot) =
|
||||
if let Some((segment, ext_excluding_dot)) = rsplit(rest.clone(), '.') {
|
||||
if let Some((segment, ext_excluding_dot)) = rsplit(segment.segment.clone(), '.') {
|
||||
(segment, Some(ext_excluding_dot))
|
||||
} else {
|
||||
(rest, None)
|
||||
(segment.segment, None)
|
||||
};
|
||||
|
||||
Self {
|
||||
|
|
@ -202,12 +196,13 @@ impl<'a> Path<'a> {
|
|||
opt_sep_and_next,
|
||||
repeat_till(0.., sep_and_next, peek(terminator()))
|
||||
.map(|(segments, _): (Vec<PathSegment>, _)| segments),
|
||||
opt(FileLocation::parse()),
|
||||
);
|
||||
|
||||
trace(
|
||||
"path",
|
||||
drive_and_segments
|
||||
.map(|(drive, segment, segments)| {
|
||||
.map(|(drive, segment, segments, location)| {
|
||||
let (segments, last) = {
|
||||
let mut segments = segments;
|
||||
segments.insert(0, segment);
|
||||
|
|
@ -215,7 +210,7 @@ impl<'a> Path<'a> {
|
|||
(segments, last)
|
||||
};
|
||||
|
||||
let filename = FileName::parse(last);
|
||||
let filename = FileName::parse(last, location);
|
||||
|
||||
Self {
|
||||
drive_excluding_colon: drive,
|
||||
|
|
@ -225,7 +220,6 @@ impl<'a> Path<'a> {
|
|||
})
|
||||
.verify(|i| {
|
||||
!i.segments.is_empty()
|
||||
|| i.drive_excluding_colon.is_some()
|
||||
|| i.filename.ext_excluding_dot.is_some()
|
||||
|| !matches!(i.filename.leading_separator, PathSep::None)
|
||||
}),
|
||||
|
|
@ -266,8 +260,8 @@ impl<'a> Token<'a> {
|
|||
"true".value(Self::True),
|
||||
"false".value(Self::False),
|
||||
"None".value(Self::None),
|
||||
Path::parse().map(Self::Path),
|
||||
Number::parse().map(Self::Number),
|
||||
Path::parse().map(Self::Path),
|
||||
AnyString::parse().map(Self::String),
|
||||
delimited,
|
||||
Atom::parse(alt((Separator::parse().value(""), ")", "]", "}"))).map(Self::Atom),
|
||||
|
|
@ -318,12 +312,10 @@ impl<'a> Delimited<'a> {
|
|||
trace(
|
||||
"delimited",
|
||||
(
|
||||
opt(Atom::parse(alt((
|
||||
"(",
|
||||
"[",
|
||||
"{",
|
||||
Separator::parse().value(""),
|
||||
)))),
|
||||
opt((
|
||||
Atom::parse(alt(("(", "[", "{", Separator::parse().value("")))),
|
||||
Space::parse(),
|
||||
)),
|
||||
alt((
|
||||
literal('(').map(|_| literal(')').value(Delimiter::Paren)),
|
||||
literal('[').map(|_| literal(']').value(Delimiter::Bracket)),
|
||||
|
|
@ -392,6 +384,12 @@ impl<'a> Segment<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parses an input string (a log line) into an ast.
|
||||
///
|
||||
/// This *should* never error.
|
||||
/// Many tests ensure that arbitrary input can be parsed.
|
||||
/// Even if non-structured or completely random.
|
||||
/// The parser will gracefully accept such strings anyway, and just categorize them suboptimally.
|
||||
pub fn parse_input<'a>(i: &'a str) -> Result<Segments<'a>, String> {
|
||||
use winnow::combinator::eof;
|
||||
Segments::parse(eof::<&str, winnow::error::EmptyError>)
|
||||
|
|
@ -405,7 +403,7 @@ mod tests {
|
|||
use insta::assert_debug_snapshot;
|
||||
use winnow::Parser;
|
||||
|
||||
use crate::format_debug_output::{
|
||||
use crate::{
|
||||
ast::{Path, Segments},
|
||||
parse_input,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue