feat: 添加for-in循环支持,更新解析器、解释器和相关测试用例

This commit is contained in:
0264408
2026-06-16 19:10:26 +08:00
parent 75c1d230ad
commit 8b20580402
6 changed files with 215 additions and 3 deletions
+60
View File
@@ -134,6 +134,20 @@ impl Parser {
fn for_statement(&mut self) -> Result<Stmt, RuntimeError> {
self.consume(TokenKind::LeftParen, "Expected '(' after 'for'.")?;
// Detect for-in: for (Identifier in expr) body
if let TokenKind::Identifier(var_name) = &self.peek().kind {
let var_name = var_name.clone();
if matches!(self.peek_next().kind, TokenKind::In) {
self.advance(); // consume identifier
self.advance(); // consume 'in'
let iterable = self.expression()?;
self.consume(TokenKind::RightParen, "Expected ')' after for-in expression.")?;
let body = Box::new(self.statement()?);
return Ok(Stmt::ForIn { var_name, iterable, body });
}
}
let initializer = if self.match_kind(&[TokenKind::Semicolon]) {
None
} else if self.match_kind(&[TokenKind::Let]) {
@@ -534,6 +548,10 @@ impl Parser {
&self.tokens[self.current]
}
fn peek_next(&self) -> &Token {
&self.tokens[self.current + 1]
}
fn previous(&self) -> &Token {
&self.tokens[self.current - 1]
}
@@ -1257,6 +1275,48 @@ mod tests {
}
}
#[test]
fn parse_for_in_array() {
match parse_stmt("for (x in arr) { print(x); }") {
Stmt::ForIn { var_name, iterable, body } => {
assert_eq!(var_name, "x");
assert!(matches!(iterable, Expr::Variable(_)));
assert!(matches!(*body, Stmt::Block(_)));
}
e => panic!("Expected ForIn, got {:?}", e),
}
}
#[test]
fn parse_for_in_with_object_literal() {
match parse_stmt("for (k in {a:1,b:2}) { print(k); }") {
Stmt::ForIn { var_name, iterable, .. } => {
assert_eq!(var_name, "k");
assert!(matches!(iterable, Expr::ObjectLiteral { .. }));
}
e => panic!("Expected ForIn, got {:?}", e),
}
}
#[test]
fn parse_for_in_with_expression() {
match parse_stmt("for (v in get_list()) { }") {
Stmt::ForIn { var_name, .. } => {
assert_eq!(var_name, "v");
}
e => panic!("Expected ForIn, got {:?}", e),
}
}
#[test]
fn parse_for_in_distinct_from_c_style() {
// "for (x = 0; ...)" is C-style for, not for-in
match parse_stmt("for (x = 0; x < 10; x = x + 1) { }") {
Stmt::For { .. } => {}
e => panic!("Expected C-style For, got {:?}", e),
}
}
#[test]
fn parse_block() {
match parse_stmt("{ let x = 1; x; }") {