Fix comments parsing.

This commit is contained in:
Stephen Chung 2020-12-18 16:07:19 +08:00
parent fc54fdc907
commit 719f0babbf
3 changed files with 100 additions and 31 deletions

View File

@ -16,6 +16,7 @@ Bug fixes
* Constants are no longer propagated by the optimizer if shadowed by a non-constant variable. * Constants are no longer propagated by the optimizer if shadowed by a non-constant variable.
* Constants passed as the `this` parameter to Rhai functions now throws an error if assigned to. * Constants passed as the `this` parameter to Rhai functions now throws an error if assigned to.
* Generic type parameter of `Engine::register_iterator` is `IntoIterator` instead of `Iterator`. * Generic type parameter of `Engine::register_iterator` is `IntoIterator` instead of `Iterator`.
* Fixes parsing of block comments ending with `**/` or inner blocks starting with `//*`.
Breaking changes Breaking changes
---------------- ----------------

View File

@ -918,41 +918,41 @@ fn eat_next(stream: &mut impl InputStream, pos: &mut Position) -> Option<char> {
} }
/// Scan for a block comment until the end. /// Scan for a block comment until the end.
fn scan_comment( fn scan_block_comment(
stream: &mut impl InputStream, stream: &mut impl InputStream,
mut level: usize, mut level: usize,
pos: &mut Position, pos: &mut Position,
comment: &mut String, comment: &mut Option<String>,
) -> usize { ) -> usize {
while let Some(c) = stream.get_next() { while let Some(c) = stream.get_next() {
pos.advance(); pos.advance();
if !comment.is_empty() { if let Some(ref mut comment) = comment {
comment.push(c); comment.push(c);
} }
match c { match c {
'/' => { '/' => {
if let Some(c2) = stream.get_next() { if let Some(c2) = stream.peek_next() {
if !comment.is_empty() { if c2 == '*' {
eat_next(stream, pos);
if let Some(ref mut comment) = comment {
comment.push(c2); comment.push(c2);
} }
if c2 == '*' {
level += 1; level += 1;
} }
} }
pos.advance();
} }
'*' => { '*' => {
if let Some(c2) = stream.get_next() { if let Some(c2) = stream.peek_next() {
if !comment.is_empty() { if c2 == '/' {
eat_next(stream, pos);
if let Some(ref mut comment) = comment {
comment.push(c2); comment.push(c2);
} }
if c2 == '/' {
level -= 1; level -= 1;
} }
} }
pos.advance();
} }
'\n' => pos.new_line(), '\n' => pos.new_line(),
_ => (), _ => (),
@ -1032,11 +1032,16 @@ fn get_next_token_inner(
// Still inside a comment? // Still inside a comment?
if state.comment_level > 0 { if state.comment_level > 0 {
let start_pos = *pos; let start_pos = *pos;
let mut comment = String::new(); let mut comment = if state.include_comments {
state.comment_level = scan_comment(stream, state.comment_level, pos, &mut comment); Some(String::new())
} else {
None
};
if state.include_comments || is_doc_comment(&comment) { state.comment_level = scan_block_comment(stream, state.comment_level, pos, &mut comment);
return Some((Token::Comment(comment), start_pos));
if state.include_comments || is_doc_comment(comment.as_ref().unwrap()) {
return Some((Token::Comment(comment.unwrap()), start_pos));
} }
} }
@ -1282,10 +1287,13 @@ fn get_next_token_inner(
('/', '/') => { ('/', '/') => {
eat_next(stream, pos); eat_next(stream, pos);
let mut comment = match stream.peek_next().unwrap() { let mut comment = match stream.peek_next() {
'/' => "///".to_string(), Some('/') => {
_ if state.include_comments => "//".to_string(), eat_next(stream, pos);
_ => String::new(), Some("///".to_string())
}
_ if state.include_comments => Some("//".to_string()),
_ => None,
}; };
while let Some(c) = stream.get_next() { while let Some(c) = stream.get_next() {
@ -1293,30 +1301,33 @@ fn get_next_token_inner(
pos.new_line(); pos.new_line();
break; break;
} }
if let Some(ref mut comment) = comment {
if !comment.is_empty() {
comment.push(c); comment.push(c);
} }
pos.advance(); pos.advance();
} }
if state.include_comments || is_doc_comment(&comment) { if let Some(comment) = comment {
return Some((Token::Comment(comment), start_pos)); return Some((Token::Comment(comment), start_pos));
} }
} }
('/', '*') => { ('/', '*') => {
state.comment_level = 1; state.comment_level = 1;
eat_next(stream, pos); eat_next(stream, pos);
let mut comment = match stream.peek_next().unwrap() { let mut comment = match stream.peek_next() {
'*' => "/**".to_string(), Some('*') => {
_ if state.include_comments => "/*".to_string(), eat_next(stream, pos);
_ => String::new(), Some("/**".to_string())
}
_ if state.include_comments => Some("/*".to_string()),
_ => None,
}; };
state.comment_level = scan_comment(stream, state.comment_level, pos, &mut comment);
if state.include_comments || is_doc_comment(&comment) { state.comment_level =
scan_block_comment(stream, state.comment_level, pos, &mut comment);
if let Some(comment) = comment {
return Some((Token::Comment(comment), start_pos)); return Some((Token::Comment(comment), start_pos));
} }
} }

View File

@ -21,5 +21,62 @@ fn test_comments() -> Result<(), Box<EvalAltResult>> {
42 42
); );
assert_eq!(engine.eval::<()>("/* Hello world */")?, ());
Ok(())
}
#[cfg(not(feature = "no_function"))]
#[test]
fn test_comments_doc() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new();
let ast = engine.compile(
r"
/// Hello world
fn foo() {}
",
)?;
assert_eq!(
ast.iter_functions().next().unwrap().comments[0],
"/// Hello world"
);
assert!(engine
.compile(
r"
/// Hello world
let x = 42;
"
)
.is_err());
let ast = engine.compile(
r"
/** Hello world
** how are you?
**/
fn foo() {}
",
)?;
assert_eq!(
ast.iter_functions().next().unwrap().comments[0],
"/** Hello world\n ** how are you?\n **/"
);
assert!(engine
.compile(
r"
/** Hello world */
let x = 42;
"
)
.is_err());
Ok(()) Ok(())
} }