use super::{token::TokenTy, Lexer};
pub const SINGLE_LINE_COMMENT_PREFIX: &str = "//";
pub const MULTI_LINE_COMMENT_START: &str = "/*";
pub const MULTI_LINE_COMMENT_END: &str = "*/";
pub fn try_match_single_line_comment(lexer: &Lexer) -> (usize, Option<TokenTy>) {
let mut fork: Lexer = lexer.fork();
if fork.consume(SINGLE_LINE_COMMENT_PREFIX) {
let is_inner_doc: bool = fork.matches("/") && !fork.matches("//");
let is_outer_doc: bool = fork.matches("!");
while !fork.remaining.is_empty() && !fork.matches("\r") && !fork.matches("\n") {
fork.consume_any();
}
let variant: Option<TokenTy> = match (is_inner_doc, is_outer_doc) {
(true, false) => Some(TokenTy::InnerDocComment),
(false, true) => Some(TokenTy::OuterDocComment),
(false, false) => None,
(true, true) => unreachable!("It is impossible for the `remaining` fragment to start with an `!` and a `/` simultaneously.")
};
return (fork.offset_from(lexer), variant);
}
(0, None)
}
pub fn try_match_block_comment(lexer: &Lexer) -> (usize, Option<TokenTy>) {
if lexer.matches("/***/") {
return (5, None);
}
if lexer.matches("/**/") {
return (4, None);
}
let mut fork: Lexer = lexer.fork();
if fork.consume(MULTI_LINE_COMMENT_START) {
let is_outer_doc: bool = fork.matches("!");
let is_inner_doc: bool = fork.matches("*") && !fork.matches("**");
while !fork.matches(MULTI_LINE_COMMENT_END) {
if fork.matches(MULTI_LINE_COMMENT_START) {
let (nested_comment_bytes, _) = try_match_block_comment(&fork);
unsafe { fork.advance_unchecked(nested_comment_bytes) };
continue;
}
if fork.remaining.is_empty() {
let bytes_consumed: usize = fork.offset_from(lexer);
return (bytes_consumed, Some(TokenTy::UnterminatedBlockComment));
}
fork.consume_any();
}
let consumed_comment_terminator: bool = fork.consume(MULTI_LINE_COMMENT_END);
debug_assert!(consumed_comment_terminator, "comment is actually terminated");
let variant: Option<TokenTy> = match (is_inner_doc, is_outer_doc) {
(true, false) => Some(TokenTy::InnerBlockDocComment),
(false, true) => Some(TokenTy::OuterBlockDocComment),
(false, false) => None,
(true, true) => {
unreachable!("Lexer should not match multiple comment types at once.")
}
};
return (fork.offset_from(lexer), variant);
}
(0, None)
}
#[cfg(test)]
mod tests {
use super::Lexer;
#[test]
fn ignored_single_line_comment() {
let mut lexer = Lexer::new_test("// test comment ");
assert!(lexer.next_token().is_none());
assert_eq!(lexer.remaining.len(), 0);
}
}