diff --git a/Interpreter/interpreter.go b/Interpreter/interpreter.go index 50f41c2..d714a25 100644 --- a/Interpreter/interpreter.go +++ b/Interpreter/interpreter.go @@ -263,6 +263,11 @@ 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.ContinueException { + state.ContinueException = false + continue + } if state.AbruptCompletion { state.AbruptCompletion = false break @@ -281,8 +286,11 @@ func (i *Interpreter) executeBlock(statements []ast.Stmt, environment *environme i.Environment = environment for _, stmt := range statements { i.execute(stmt) + if state.ContinueException { + return // Exit the block immediately for continue, let while loop handle the flag + } if state.AbruptCompletion { - break + return // Exit the block immediately for break, let while loop handle the flag } } } @@ -291,3 +299,8 @@ func (i *Interpreter) VisitBreakStmtStmt(stmt ast.BreakStmt) interface{} { state.AbruptCompletion = true return nil } + +func (i *Interpreter) VisitContinueStmtStmt(stmt ast.ContinueStmt) interface{} { + state.ContinueException = true + return nil +} diff --git a/Scanner/scanner.go b/Scanner/scanner.go index 23dabcc..b4a12b3 100644 --- a/Scanner/scanner.go +++ b/Scanner/scanner.go @@ -20,23 +20,24 @@ type Scanner struct { } var KeywordMap = map[string]token.TokenType{ - "and": token.AND, - "class": token.CLASS, - "else": token.ELSE, - "false": token.FALSE, - "for": token.FOR, - "fun": token.FUN, - "if": token.IF, - "nil": token.NIL, - "or": token.OR, - "print": token.PRINT, - "return": token.RETURN, - "super": token.SUPER, - "this": token.THIS, - "true": token.TRUE, - "var": token.VAR, - "while": token.WHILE, - "break": token.BREAK, + "and": token.AND, + "class": token.CLASS, + "else": token.ELSE, + "false": token.FALSE, + "for": token.FOR, + "fun": token.FUN, + "if": token.IF, + "nil": token.NIL, + "or": token.OR, + "print": token.PRINT, + "return": token.RETURN, + "super": token.SUPER, + "this": token.THIS, + "true": token.TRUE, + "var": token.VAR, + "while": token.WHILE, + "break": token.BREAK, + "continue": token.CONTINUE, } func (s *Scanner) isAtEnd() bool { diff --git a/Token/token.go b/Token/token.go index c8e382d..8f26887 100644 --- a/Token/token.go +++ b/Token/token.go @@ -54,6 +54,7 @@ const ( VAR WHILE BREAK + CONTINUE // End of file EOF @@ -67,7 +68,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", "BREAK", + "PRINT", "RETURN", "SUPER", "THIS", "TRUE", "VAR", "WHILE", "BREAK", "CONTINUE", "EOF", }[t] } diff --git a/ast/stmt.go b/ast/stmt.go index 9b35831..fc8cdb1 100644 --- a/ast/stmt.go +++ b/ast/stmt.go @@ -12,6 +12,7 @@ type StmtVisitor interface { VisitVarStmtStmt(stmt VarStmt) interface{} VisitWhileStmtStmt(stmt WhileStmt) interface{} VisitBreakStmtStmt(stmt BreakStmt) interface{} + VisitContinueStmtStmt(stmt ContinueStmt) interface{} } type Stmt interface { @@ -77,3 +78,10 @@ func (n BreakStmt) Accept(visitor StmtVisitor) interface{} { return visitor.VisitBreakStmtStmt(n) } +type ContinueStmt struct { +} + +func (n ContinueStmt) Accept(visitor StmtVisitor) interface{} { + return visitor.VisitContinueStmtStmt(n) +} + diff --git a/parser/parser.go b/parser/parser.go index b3d5647..9bc436d 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -273,6 +273,9 @@ func (p *Parser) statement() ast.Stmt { if p.match(token.PRINT) { return p.printStatement() } + if p.match(token.CONTINUE) { + return p.continueStatement() + } if p.match(token.BREAK) { return p.breakStatement() } @@ -310,9 +313,9 @@ func (p *Parser) forStatement() ast.Stmt { } p.consume(token.RIGHT_PAREN, "Expect ')' after for clauses.") - state.CanInsertBreakStatement = true + state.CanInsertBreakOrContinueStatement = true body := p.statement() - state.CanInsertBreakStatement = false + state.CanInsertBreakOrContinueStatement = false if increment != nil { body = ast.BlockStmt{ @@ -367,18 +370,26 @@ func (p *Parser) whileStatement() ast.Stmt { condition := p.expression() p.consume(token.RIGHT_PAREN, "Expect ')' after condition.") - state.CanInsertBreakStatement = true + state.CanInsertBreakOrContinueStatement = true body := p.statement() - state.CanInsertBreakStatement = false + state.CanInsertBreakOrContinueStatement = false return ast.WhileStmt{ Condition: condition, Body: body, } } +func (p *Parser) continueStatement() ast.Stmt { + if !state.CanInsertBreakOrContinueStatement { + p.error(p.peek(), "continueStatemen can only exist inside valid iterator") + + } + p.consume(token.SEMICOLON, "Expect ';' after value.") + return ast.ContinueStmt{} +} func (p *Parser) breakStatement() ast.Stmt { - if !state.CanInsertBreakStatement { + if !state.CanInsertBreakOrContinueStatement { p.error(p.peek(), "breakStatement can only exist inside valid iterator") } diff --git a/printer/generateAst.go b/printer/generateAst.go index d268789..85f87f4 100644 --- a/printer/generateAst.go +++ b/printer/generateAst.go @@ -126,5 +126,6 @@ func main() { "VarStmt : token.Token name, Expr initializer", "WhileStmt: Expr condition, Stmt body", "BreakStmt: ", + "ContinueStmt: ", }, []string{"github.com/shubhdevelop/YAPL/Token"}) } diff --git a/state/state.go b/state/state.go index bb3732c..0687f39 100644 --- a/state/state.go +++ b/state/state.go @@ -2,5 +2,6 @@ package state var HadError bool = false var HadRuntimeError = false -var CanInsertBreakStatement = false +var CanInsertBreakOrContinueStatement = false var AbruptCompletion = false +var ContinueException = false