Add break continue
This commit is contained in:
@@ -34,6 +34,14 @@ print("List:", numbers);
|
||||
print("First element (index 0):", numbers[0]);
|
||||
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
|
||||
print("\n=== Map Demo ===");
|
||||
map config = {"host": "localhost", "port": "8080"};
|
||||
|
||||
@@ -183,6 +183,14 @@ struct ReturnStmt : public Stmt {
|
||||
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 {
|
||||
std::string name;
|
||||
std::vector<std::pair<std::string, std::string>> parameters; // (type, name)
|
||||
|
||||
@@ -106,6 +106,10 @@ void Interpreter::execute_statement(const Stmt& stmt) {
|
||||
exec_for(*for_stmt);
|
||||
} else if (auto* return_stmt = dynamic_cast<const ReturnStmt*>(&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)) {
|
||||
exec_function_decl(*func_decl);
|
||||
} 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) {
|
||||
try {
|
||||
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))) {
|
||||
try {
|
||||
execute_statement(*stmt.body);
|
||||
} catch (const ContinueException&) {
|
||||
// Continue: proceed to increment
|
||||
}
|
||||
|
||||
if (stmt.increment) {
|
||||
evaluate(*stmt.increment);
|
||||
}
|
||||
}
|
||||
} catch (const BreakException&) {
|
||||
// Break: exit the loop
|
||||
} catch (...) {
|
||||
environment = previous;
|
||||
throw;
|
||||
@@ -431,6 +450,14 @@ void Interpreter::exec_return(const ReturnStmt& stmt) {
|
||||
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) {
|
||||
auto func_decl = std::make_shared<FunctionDecl>(stmt);
|
||||
auto func = std::make_shared<FunctionValue>(stmt.name, func_decl);
|
||||
|
||||
@@ -20,6 +20,9 @@ public:
|
||||
explicit ReturnException(ValuePtr value) : value(std::move(value)) {}
|
||||
};
|
||||
|
||||
class BreakException : public std::exception {};
|
||||
class ContinueException : public std::exception {};
|
||||
|
||||
class Environment {
|
||||
public:
|
||||
std::shared_ptr<Environment> parent;
|
||||
@@ -138,6 +141,8 @@ private:
|
||||
void exec_while(const WhileStmt& stmt);
|
||||
void exec_for(const ForStmt& 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_class_decl(const ClassDecl& stmt);
|
||||
|
||||
|
||||
@@ -224,6 +224,8 @@ TokenType Lexer::get_keyword_type(const std::string& text) const {
|
||||
{"and", TokenType::AND},
|
||||
{"or", TokenType::OR},
|
||||
{"this", TokenType::THIS},
|
||||
{"continue", TokenType::CONTINUE},
|
||||
{"break", TokenType::BREAK},
|
||||
};
|
||||
|
||||
auto it = keywords.find(text);
|
||||
|
||||
@@ -12,7 +12,7 @@ enum class TokenType {
|
||||
// Keywords
|
||||
CLASS, FUNC, NUMBER, STRING, BOOL, LIST, MAP,
|
||||
IF, ELSE, WHILE, FOR, RETURN, VAR,
|
||||
TRUE, FALSE, NIL, THIS,
|
||||
TRUE, FALSE, NIL, THIS, CONTINUE, BREAK,
|
||||
|
||||
// Operators
|
||||
PLUS, MINUS, STAR, SLASH, PERCENT,
|
||||
|
||||
@@ -179,6 +179,8 @@ StmtPtr Parser::statement() {
|
||||
if (match({TokenType::WHILE})) return while_statement();
|
||||
if (match({TokenType::FOR})) return for_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();
|
||||
|
||||
return expression_statement();
|
||||
@@ -247,6 +249,16 @@ StmtPtr Parser::return_statement() {
|
||||
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() {
|
||||
std::vector<StmtPtr> statements;
|
||||
|
||||
|
||||
@@ -44,6 +44,8 @@ private:
|
||||
StmtPtr while_statement();
|
||||
StmtPtr for_statement();
|
||||
StmtPtr return_statement();
|
||||
StmtPtr break_statement();
|
||||
StmtPtr continue_statement();
|
||||
StmtPtr block_statement();
|
||||
StmtPtr expression_statement();
|
||||
|
||||
|
||||
@@ -148,3 +148,72 @@ TEST_CASE("interpreter performance: simple loop", "[perf][script]") {
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user