-
-
Notifications
You must be signed in to change notification settings - Fork 358
[WIP] Config parsing #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
de6ba00
0d9495a
1bcbb74
f9492b8
0241e96
72f7c3e
97f6a17
1018342
832f8c7
ac2311b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
use crate::{file, spanned}; | ||
use dangerous::{Bytes, BytesReader, Error, Expected, Input}; | ||
|
||
fn config(bytes: &[u8]) -> Result<Vec<file::Token>, Expected<'_>> { | ||
fn config<'i, E>(input: Bytes<'i>, r: &mut BytesReader<'i, E>) -> Result<Vec<file::Token>, E> | ||
where | ||
E: Error<'i>, | ||
{ | ||
let mut tokens = Vec::new(); | ||
if let Some(section) = skip_whitespace_or_comment(r, ConsumeTo::NextToken) { | ||
tokens.push(spanned::Comment( | ||
dangerous::input(section) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In theory this conversion would have to be done for each token we parse here. Certainly this can be sugared up a bit more to feel less cluttered, but might also be a case for just using |
||
.span_of(&input) | ||
.expect("range contained") | ||
.into(), | ||
)) | ||
}; | ||
unimplemented!("sections and values"); | ||
} | ||
let input = dangerous::input(bytes); | ||
input.read_all(|r| config(dangerous::input(bytes), r)) | ||
} | ||
|
||
enum ConsumeTo { | ||
NextToken, | ||
EndOfLine, | ||
} | ||
|
||
#[must_use] | ||
fn skip_whitespace_or_comment<'a, E>(r: &mut BytesReader<'a, E>, to_where: ConsumeTo) -> Option<&'a [u8]> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this was a little lazy, and this should really be refactored to not need the inner function. One would probably end up with something cloning the input, but… I don't actually know how to do this properly. |
||
fn skip_whitespace_or_comment<E>(r: &mut BytesReader<'_, E>, to_where: ConsumeTo) { | ||
fn skip_comment<E>(r: &mut BytesReader<'_, E>) -> usize { | ||
if r.peek_eq(b'#') { | ||
r.take_until_opt(b'\n').len() | ||
} else { | ||
0 | ||
} | ||
} | ||
|
||
let (mut last, mut current) = (0, 0); | ||
loop { | ||
current += skip_comment(r); | ||
current += r | ||
.take_while(|c: u8| { | ||
let iwb = c.is_ascii_whitespace(); | ||
iwb && match to_where { | ||
ConsumeTo::NextToken => true, | ||
ConsumeTo::EndOfLine => c != b'\n', | ||
} | ||
}) | ||
.len(); | ||
if last == current { | ||
if let ConsumeTo::EndOfLine = to_where { | ||
r.consume_opt(b'\n'); | ||
} | ||
break; | ||
} | ||
last = current; | ||
} | ||
} | ||
let parsed = r | ||
.take_consumed(|r| skip_whitespace_or_comment(r, to_where)) | ||
.as_dangerous(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here I thought there could be an |
||
if parsed.is_empty() { | ||
None | ||
} else { | ||
Some(parsed) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
mod comments { | ||
use crate::parse::{skip_whitespace_or_comment, ConsumeTo}; | ||
use dangerous::Input; | ||
|
||
macro_rules! decode_span { | ||
($name:ident, $input:literal, $option:path, $range:expr, $explain:literal) => { | ||
#[test] | ||
fn $name() { | ||
let bytes = $input; | ||
let (res, _remaining) = | ||
dangerous::input(bytes).read_infallible(|r| skip_whitespace_or_comment(r, $option)); | ||
assert_eq!( | ||
res.map(dangerous::input) | ||
.and_then(|s| s.span_of(&dangerous::input(bytes))), | ||
Some($range), | ||
$explain | ||
); | ||
} | ||
}; | ||
} | ||
|
||
decode_span!( | ||
no_comment_till_next_token, | ||
b" \n \t\n", | ||
ConsumeTo::NextToken, | ||
0..13, | ||
"it consumes newlines as well, taking everything" | ||
); | ||
|
||
decode_span!( | ||
no_comment_to_end_of_line, | ||
b" \n \t ", | ||
ConsumeTo::EndOfLine, | ||
0..6, | ||
"it consumes only a single line, including the EOF marker" | ||
); | ||
|
||
decode_span!( | ||
comment_to_next_token, | ||
b" #ho \n \t ", | ||
ConsumeTo::NextToken, | ||
0..13, | ||
"comments are the same as whitespace" | ||
); | ||
|
||
decode_span!( | ||
comment_to_end_of_line, | ||
b"# hi \n \t ", | ||
ConsumeTo::EndOfLine, | ||
0..6, | ||
"comments are the same as whitespace" | ||
); | ||
|
||
decode_span!( | ||
whitespace_to_token, | ||
b" a=2 \n \t ", | ||
ConsumeTo::NextToken, | ||
0..3, | ||
"it does not consume tokens" | ||
); | ||
|
||
decode_span!( | ||
whitespace_to_token_on_next_line, | ||
b" \n b=2\t ", | ||
ConsumeTo::NextToken, | ||
0..7, | ||
"it does not consume tokens while skipping lines" | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since
dangerous
uses aRange<usize>
I might just use this instead of an own version of..=
. Probably I had a reason to use an inclusive range in the first place, but it's not entirely clear to me right now what that was 🤦♂️.