feat: 新增块注释及测试
This commit is contained in:
@@ -92,6 +92,9 @@ impl Lexer {
|
||||
self.advance();
|
||||
}
|
||||
return None;
|
||||
} else if self.match_char('*') {
|
||||
// 多行注释 /* ... */
|
||||
return self.block_comment(line, column, errors);
|
||||
} else if self.match_char('=') {
|
||||
TokenKind::SlashEqual
|
||||
} else {
|
||||
@@ -225,6 +228,32 @@ impl Lexer {
|
||||
self.current >= self.src.len()
|
||||
}
|
||||
|
||||
/// consume multi-line comment, tracking line numbers for error reporting
|
||||
fn block_comment(
|
||||
&mut self,
|
||||
start_line: usize,
|
||||
start_column: usize,
|
||||
errors: &mut Vec<RuntimeError>,
|
||||
) -> Option<Token> {
|
||||
while !self.is_at_end() {
|
||||
let c = self.advance();
|
||||
if c == '\n' {
|
||||
self.line += 1;
|
||||
self.column = 1;
|
||||
} else if c == '*' && !self.is_at_end() && self.peek() == '/' {
|
||||
self.advance(); // consume closing '/'
|
||||
return None;
|
||||
}
|
||||
}
|
||||
// EOF in block comment
|
||||
errors.push(RuntimeError::lex(
|
||||
"Unterminated block comment (missing */)",
|
||||
start_line,
|
||||
start_column,
|
||||
));
|
||||
None
|
||||
}
|
||||
|
||||
fn string_literal(
|
||||
&mut self,
|
||||
line: usize,
|
||||
|
||||
@@ -282,6 +282,58 @@ fn comment_between_tokens() {
|
||||
assert_eq!(kinds, vec![TokenKind::Number(1.0), TokenKind::Number(2.0)]);
|
||||
}
|
||||
|
||||
// ----- block comments /* ... */ -----
|
||||
|
||||
#[test]
|
||||
fn block_comment_ignored() {
|
||||
let kinds = tokenize("42 /* comment */ 99");
|
||||
assert_eq!(kinds, vec![TokenKind::Number(42.0), TokenKind::Number(99.0)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_block_comment() {
|
||||
let kinds = tokenize("42/**/99");
|
||||
assert_eq!(kinds, vec![TokenKind::Number(42.0), TokenKind::Number(99.0)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_comment_multiline() {
|
||||
let kinds = tokenize("1 /* line 1\nline 2\nline 3 */ 2");
|
||||
assert_eq!(kinds, vec![TokenKind::Number(1.0), TokenKind::Number(2.0)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_comment_only() {
|
||||
let kinds = tokenize("/* just a block comment */");
|
||||
assert!(kinds.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_comment_with_asterisks_inside() {
|
||||
let kinds = tokenize("1 /*** weird ***/ 2");
|
||||
assert_eq!(kinds, vec![TokenKind::Number(1.0), TokenKind::Number(2.0)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_comment_line_numbers_tracked() {
|
||||
let (tokens, _errors) = tokenize_full("/* line1\nline2 */\n42");
|
||||
let num = tokens.iter().find(|t| matches!(t.kind, TokenKind::Number(_))).unwrap();
|
||||
assert_eq!(num.line, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unterminated_block_comment() {
|
||||
let (_, errors) = tokenize_full("/* no closing");
|
||||
assert_eq!(errors.len(), 1);
|
||||
assert!(errors[0].to_string().contains("Unterminated block comment"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_then_line_comment() {
|
||||
let kinds = tokenize("/* block */ // line\n42");
|
||||
assert_eq!(kinds, vec![TokenKind::Number(42.0)]);
|
||||
}
|
||||
|
||||
// ----- whitespace -----
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user