diff --git a/Interpreter/interpreter.go b/Interpreter/interpreter.go index 24f9245..50f41c2 100644 --- a/Interpreter/interpreter.go +++ b/Interpreter/interpreter.go @@ -8,6 +8,7 @@ import ( "github.com/shubhdevelop/YAPL/YaplErrors" "github.com/shubhdevelop/YAPL/ast" "github.com/shubhdevelop/YAPL/environment" + "github.com/shubhdevelop/YAPL/state" ) type Interpreter struct { @@ -262,6 +263,10 @@ func (i *Interpreter) VisitLogicalExpr(expr ast.Logical) interface{} { func (i *Interpreter) VisitWhileStmtStmt(stmt ast.WhileStmt) interface{} { for i.isTruthy(i.evaluate(stmt.Condition)) { i.execute(stmt.Body) + if state.AbruptCompletion { + state.AbruptCompletion = false + break + } } return nil } @@ -276,5 +281,13 @@ func (i *Interpreter) executeBlock(statements []ast.Stmt, environment *environme i.Environment = environment for _, stmt := range statements { i.execute(stmt) + if state.AbruptCompletion { + break + } } } + +func (i *Interpreter) VisitBreakStmtStmt(stmt ast.BreakStmt) interface{} { + state.AbruptCompletion = true + return nil +} diff --git a/README.md b/README.md index b62d077..00a1529 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ YAPL (Yet Another Programming Language) is a dynamically-typed, interpreted prog ### Reserved Keywords ``` -and, class, else, false, for, fun, if, nil, or, print, return, super, this, true, var, while +and, break, class, else, false, for, fun, if, nil, or, print, return, super, this, true, var, while ``` **Note**: `class`, `fun`, `return`, `super`, and `this` are reserved for future implementation. @@ -123,6 +123,21 @@ while (i < 5) { for (var j = 0; j < 3; j = j + 1) { print "Iteration: " + j; } + +// Break statement +var a = 1; +while (a < 100) { + while (a < 40) { + if (a == 39) { + break; // Breaks out of inner loop only + } + print a; + a = a + 1; + } + print "now outside"; + print a; + a = a + 1; +} ``` #### Block Scoping @@ -193,7 +208,7 @@ This implementation represents a mature interpreter with comprehensive language ### ✅ Implemented Features - **Complete Expression System**: All arithmetic, comparison, logical, and unary operations -- **Control Flow**: `if`/`else` statements, `while` loops, and `for` loops (desugared to while) +- **Control Flow**: `if`/`else` statements, `while` loops, `for` loops (desugared to while), and `break` statements - **Variable Management**: Declaration, assignment, and proper scoping with block environments - **Data Types**: Numbers (float64), strings, booleans, and nil - **Error Handling**: Comprehensive lexical, parse, and runtime error reporting @@ -243,6 +258,7 @@ This implementation represents a mature interpreter with comprehensive language - **If Statement**: `if (condition) statement else statement` - **While Loop**: `while (condition) statement` - **For Loop**: `for (initializer; condition; increment) statement` +- **Break Statement**: `break;` (exits the innermost loop) - **Block Statement**: `{ statement1; statement2; ... }` #### **Variables** @@ -251,6 +267,23 @@ This implementation represents a mature interpreter with comprehensive language - **Scope**: Block-scoped variables with proper environment nesting - **Lookup**: Variables are looked up in the current scope and enclosing scopes +#### **Break Statement** +- **Syntax**: `break;` +- **Purpose**: Exits the innermost loop (while or for) immediately +- **Scope**: Only affects the current loop level, outer loops continue normally +- **Usage**: Must be used inside a loop body +- **Example**: + ```yapl + var i = 0; + while (i < 10) { + if (i == 5) { + break; // Exits the while loop when i equals 5 + } + print i; + i = i + 1; + } + ``` + #### **Comments** - **Single-line comments**: `// This is a comment` diff --git a/Scanner/scanner.go b/Scanner/scanner.go index f122c0c..23dabcc 100644 --- a/Scanner/scanner.go +++ b/Scanner/scanner.go @@ -36,6 +36,7 @@ var KeywordMap = map[string]token.TokenType{ "true": token.TRUE, "var": token.VAR, "while": token.WHILE, + "break": token.BREAK, } func (s *Scanner) isAtEnd() bool { diff --git a/Token/token.go b/Token/token.go index 0db702c..c8e382d 100644 --- a/Token/token.go +++ b/Token/token.go @@ -53,6 +53,7 @@ const ( TRUE VAR WHILE + BREAK // End of file EOF @@ -66,7 +67,7 @@ func (t TokenType) String() string { "LESS", "LESS_EQUAL", "IDENTIFIER", "STRING", "NUMBER", "AND", "CLASS", "ELSE", "FALSE", "FUN", "FOR", "IF", "NIL", "OR", - "PRINT", "RETURN", "SUPER", "THIS", "TRUE", "VAR", "WHILE", + "PRINT", "RETURN", "SUPER", "THIS", "TRUE", "VAR", "WHILE", "BREAK", "EOF", }[t] } diff --git a/ast/stmt.go b/ast/stmt.go index 8152799..9b35831 100644 --- a/ast/stmt.go +++ b/ast/stmt.go @@ -11,6 +11,7 @@ type StmtVisitor interface { VisitPrintStmtStmt(stmt PrintStmt) interface{} VisitVarStmtStmt(stmt VarStmt) interface{} VisitWhileStmtStmt(stmt WhileStmt) interface{} + VisitBreakStmtStmt(stmt BreakStmt) interface{} } type Stmt interface { @@ -69,3 +70,10 @@ func (n WhileStmt) Accept(visitor StmtVisitor) interface{} { return visitor.VisitWhileStmtStmt(n) } +type BreakStmt struct { +} + +func (n BreakStmt) Accept(visitor StmtVisitor) interface{} { + return visitor.VisitBreakStmtStmt(n) +} + diff --git a/parser/parser.go b/parser/parser.go index ee4aeb7..b3d5647 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -6,6 +6,7 @@ import ( "github.com/shubhdevelop/YAPL/Token" "github.com/shubhdevelop/YAPL/YaplErrors" "github.com/shubhdevelop/YAPL/ast" + "github.com/shubhdevelop/YAPL/state" ) type Parser struct { @@ -272,7 +273,9 @@ func (p *Parser) statement() ast.Stmt { if p.match(token.PRINT) { return p.printStatement() } - + if p.match(token.BREAK) { + return p.breakStatement() + } if p.match(token.WHILE) { return p.whileStatement() } @@ -307,7 +310,9 @@ func (p *Parser) forStatement() ast.Stmt { } p.consume(token.RIGHT_PAREN, "Expect ')' after for clauses.") + state.CanInsertBreakStatement = true body := p.statement() + state.CanInsertBreakStatement = false if increment != nil { body = ast.BlockStmt{ @@ -361,7 +366,10 @@ func (p *Parser) whileStatement() ast.Stmt { p.consume(token.LEFT_PAREN, "Expect '(' after 'while'.") condition := p.expression() p.consume(token.RIGHT_PAREN, "Expect ')' after condition.") + + state.CanInsertBreakStatement = true body := p.statement() + state.CanInsertBreakStatement = false return ast.WhileStmt{ Condition: condition, Body: body, @@ -369,6 +377,15 @@ func (p *Parser) whileStatement() ast.Stmt { } +func (p *Parser) breakStatement() ast.Stmt { + if !state.CanInsertBreakStatement { + p.error(p.peek(), "breakStatement can only exist inside valid iterator") + + } + p.consume(token.SEMICOLON, "Expect ';' after value.") + return ast.BreakStmt{} +} + func (p *Parser) block() []ast.Stmt { statements := []ast.Stmt{} for !p.check(token.RIGHT_BRACE) && !p.isAtEnd() { diff --git a/printer/generateAst.go b/printer/generateAst.go index 3124816..d268789 100644 --- a/printer/generateAst.go +++ b/printer/generateAst.go @@ -125,5 +125,6 @@ func main() { "PrintStmt : Expr expression", "VarStmt : token.Token name, Expr initializer", "WhileStmt: Expr condition, Stmt body", + "BreakStmt: ", }, []string{"github.com/shubhdevelop/YAPL/Token"}) } diff --git a/state/state.go b/state/state.go index 140ea38..bb3732c 100644 --- a/state/state.go +++ b/state/state.go @@ -2,3 +2,5 @@ package state var HadError bool = false var HadRuntimeError = false +var CanInsertBreakStatement = false +var AbruptCompletion = false