Add break continue

This commit is contained in:
2026-01-16 00:01:23 +08:00
parent df84159ed8
commit 9c5a3397d9
9 changed files with 137 additions and 4 deletions

View File

@@ -34,6 +34,14 @@ print("List:", numbers);
print("First element (index 0):", numbers[0]); print("First element (index 0):", numbers[0]);
print("Third element (index 2):", numbers[2]); print("Third element (index 2):", numbers[2]);
for(number i = 0; i < len(numbers); i = i + 1) {
print("List element", numbers[i]);
}
while(true) {
print("test break");
break;
}
// Test maps // Test maps
print("\n=== Map Demo ==="); print("\n=== Map Demo ===");
map config = {"host": "localhost", "port": "8080"}; map config = {"host": "localhost", "port": "8080"};

View File

@@ -183,6 +183,14 @@ struct ReturnStmt : public Stmt {
explicit ReturnStmt(ExprPtr value = nullptr) : value(std::move(value)) {} explicit ReturnStmt(ExprPtr value = nullptr) : value(std::move(value)) {}
}; };
struct BreakStmt : public Stmt {
BreakStmt() = default;
};
struct ContinueStmt : public Stmt {
ContinueStmt() = default;
};
struct FunctionDecl : public Stmt { struct FunctionDecl : public Stmt {
std::string name; std::string name;
std::vector<std::pair<std::string, std::string>> parameters; // (type, name) std::vector<std::pair<std::string, std::string>> parameters; // (type, name)

View File

@@ -106,6 +106,10 @@ void Interpreter::execute_statement(const Stmt& stmt) {
exec_for(*for_stmt); exec_for(*for_stmt);
} else if (auto* return_stmt = dynamic_cast<const ReturnStmt*>(&stmt)) { } else if (auto* return_stmt = dynamic_cast<const ReturnStmt*>(&stmt)) {
exec_return(*return_stmt); exec_return(*return_stmt);
} else if (auto* break_stmt = dynamic_cast<const BreakStmt*>(&stmt)) {
exec_break(*break_stmt);
} else if (auto* continue_stmt = dynamic_cast<const ContinueStmt*>(&stmt)) {
exec_continue(*continue_stmt);
} else if (auto* func_decl = dynamic_cast<const FunctionDecl*>(&stmt)) { } else if (auto* func_decl = dynamic_cast<const FunctionDecl*>(&stmt)) {
exec_function_decl(*func_decl); exec_function_decl(*func_decl);
} else if (auto* class_decl = dynamic_cast<const ClassDecl*>(&stmt)) { } else if (auto* class_decl = dynamic_cast<const ClassDecl*>(&stmt)) {
@@ -397,8 +401,17 @@ void Interpreter::exec_if(const IfStmt& stmt) {
} }
void Interpreter::exec_while(const WhileStmt& stmt) { void Interpreter::exec_while(const WhileStmt& stmt) {
while (is_truthy(evaluate(*stmt.condition))) { try {
execute_statement(*stmt.body); while (is_truthy(evaluate(*stmt.condition))) {
try {
execute_statement(*stmt.body);
} catch (const ContinueException&) {
// Continue: just proceed to the next iteration (check condition again)
continue;
}
}
} catch (const BreakException&) {
// Break: exit the loop
} }
} }
@@ -412,12 +425,18 @@ void Interpreter::exec_for(const ForStmt& stmt) {
} }
while (!stmt.condition || is_truthy(evaluate(*stmt.condition))) { while (!stmt.condition || is_truthy(evaluate(*stmt.condition))) {
execute_statement(*stmt.body); try {
execute_statement(*stmt.body);
} catch (const ContinueException&) {
// Continue: proceed to increment
}
if (stmt.increment) { if (stmt.increment) {
evaluate(*stmt.increment); evaluate(*stmt.increment);
} }
} }
} catch (const BreakException&) {
// Break: exit the loop
} catch (...) { } catch (...) {
environment = previous; environment = previous;
throw; throw;
@@ -431,6 +450,14 @@ void Interpreter::exec_return(const ReturnStmt& stmt) {
throw ReturnException(value); throw ReturnException(value);
} }
void Interpreter::exec_break(const BreakStmt& stmt) {
throw BreakException();
}
void Interpreter::exec_continue(const ContinueStmt& stmt) {
throw ContinueException();
}
void Interpreter::exec_function_decl(const FunctionDecl& stmt) { void Interpreter::exec_function_decl(const FunctionDecl& stmt) {
auto func_decl = std::make_shared<FunctionDecl>(stmt); auto func_decl = std::make_shared<FunctionDecl>(stmt);
auto func = std::make_shared<FunctionValue>(stmt.name, func_decl); auto func = std::make_shared<FunctionValue>(stmt.name, func_decl);

View File

@@ -20,6 +20,9 @@ public:
explicit ReturnException(ValuePtr value) : value(std::move(value)) {} explicit ReturnException(ValuePtr value) : value(std::move(value)) {}
}; };
class BreakException : public std::exception {};
class ContinueException : public std::exception {};
class Environment { class Environment {
public: public:
std::shared_ptr<Environment> parent; std::shared_ptr<Environment> parent;
@@ -138,6 +141,8 @@ private:
void exec_while(const WhileStmt& stmt); void exec_while(const WhileStmt& stmt);
void exec_for(const ForStmt& stmt); void exec_for(const ForStmt& stmt);
void exec_return(const ReturnStmt& stmt); void exec_return(const ReturnStmt& stmt);
void exec_break(const BreakStmt& stmt);
void exec_continue(const ContinueStmt& stmt);
void exec_function_decl(const FunctionDecl& stmt); void exec_function_decl(const FunctionDecl& stmt);
void exec_class_decl(const ClassDecl& stmt); void exec_class_decl(const ClassDecl& stmt);

View File

@@ -224,6 +224,8 @@ TokenType Lexer::get_keyword_type(const std::string& text) const {
{"and", TokenType::AND}, {"and", TokenType::AND},
{"or", TokenType::OR}, {"or", TokenType::OR},
{"this", TokenType::THIS}, {"this", TokenType::THIS},
{"continue", TokenType::CONTINUE},
{"break", TokenType::BREAK},
}; };
auto it = keywords.find(text); auto it = keywords.find(text);

View File

@@ -12,7 +12,7 @@ enum class TokenType {
// Keywords // Keywords
CLASS, FUNC, NUMBER, STRING, BOOL, LIST, MAP, CLASS, FUNC, NUMBER, STRING, BOOL, LIST, MAP,
IF, ELSE, WHILE, FOR, RETURN, VAR, IF, ELSE, WHILE, FOR, RETURN, VAR,
TRUE, FALSE, NIL, THIS, TRUE, FALSE, NIL, THIS, CONTINUE, BREAK,
// Operators // Operators
PLUS, MINUS, STAR, SLASH, PERCENT, PLUS, MINUS, STAR, SLASH, PERCENT,

View File

@@ -179,6 +179,8 @@ StmtPtr Parser::statement() {
if (match({TokenType::WHILE})) return while_statement(); if (match({TokenType::WHILE})) return while_statement();
if (match({TokenType::FOR})) return for_statement(); if (match({TokenType::FOR})) return for_statement();
if (match({TokenType::RETURN})) return return_statement(); if (match({TokenType::RETURN})) return return_statement();
if (match({TokenType::BREAK})) return break_statement();
if (match({TokenType::CONTINUE})) return continue_statement();
if (match({TokenType::LEFT_BRACE})) return block_statement(); if (match({TokenType::LEFT_BRACE})) return block_statement();
return expression_statement(); return expression_statement();
@@ -247,6 +249,16 @@ StmtPtr Parser::return_statement() {
return std::make_unique<ReturnStmt>(std::move(value)); return std::make_unique<ReturnStmt>(std::move(value));
} }
StmtPtr Parser::break_statement() {
consume(TokenType::SEMICOLON, "Expected ';' after 'break'.");
return std::make_unique<BreakStmt>();
}
StmtPtr Parser::continue_statement() {
consume(TokenType::SEMICOLON, "Expected ';' after 'continue'.");
return std::make_unique<ContinueStmt>();
}
StmtPtr Parser::block_statement() { StmtPtr Parser::block_statement() {
std::vector<StmtPtr> statements; std::vector<StmtPtr> statements;

View File

@@ -44,6 +44,8 @@ private:
StmtPtr while_statement(); StmtPtr while_statement();
StmtPtr for_statement(); StmtPtr for_statement();
StmtPtr return_statement(); StmtPtr return_statement();
StmtPtr break_statement();
StmtPtr continue_statement();
StmtPtr block_statement(); StmtPtr block_statement();
StmtPtr expression_statement(); StmtPtr expression_statement();

View File

@@ -148,3 +148,72 @@ TEST_CASE("interpreter performance: simple loop", "[perf][script]") {
REQUIRE(r_num->value == 499500.0); REQUIRE(r_num->value == 499500.0);
}; };
} }
TEST_CASE("loop break", "[script][loop]") {
State state;
const char* script = R"(
number sum = 0;
for (number i = 0; i < 10; i = i + 1) {
if (i == 5) {
break;
}
sum = sum + i;
}
)";
REQUIRE(state.do_string(script));
auto sum_val = state.get_global("sum");
REQUIRE(sum_val);
auto sum_num = std::dynamic_pointer_cast<NumberValue>(sum_val);
REQUIRE(sum_num->value == 10.0); // 0+1+2+3+4 = 10
}
TEST_CASE("loop continue", "[script][loop]") {
State state;
const char* script = R"(
number sum = 0;
for (number i = 0; i < 5; i = i + 1) {
if (i == 2) {
continue;
}
sum = sum + i;
}
)";
REQUIRE(state.do_string(script));
auto sum_val = state.get_global("sum");
REQUIRE(sum_val);
auto sum_num = std::dynamic_pointer_cast<NumberValue>(sum_val);
REQUIRE(sum_num->value == 8.0); // 0+1+3+4 = 8
}
TEST_CASE("while break and continue", "[script][loop]") {
State state;
const char* script = R"(
number i = 0;
number sum = 0;
while (i < 10) {
i = i + 1;
if (i == 3) {
continue;
}
if (i == 6) {
break;
}
sum = sum + i;
}
)";
REQUIRE(state.do_string(script));
auto sum_val = state.get_global("sum");
REQUIRE(sum_val);
auto sum_num = std::dynamic_pointer_cast<NumberValue>(sum_val);
REQUIRE(sum_num->value == 12.0);
// 1st iter: i=1, sum=1
// 2nd iter: i=2, sum=1+2=3
// 3rd iter: i=3, continue
// 4th iter: i=4, sum=3+4=7
// 5th iter: i=5, sum=7+5=12
// 6th iter: i=6, break
// Result should be 12.0
}