diff --git a/break_continue b/break_continue new file mode 100755 index 0000000000000000000000000000000000000000..cfd37731eb8860721157cfa9df94d32d0fcd6661 GIT binary patch literal 33472 zcmX^A>+L@t1_nk31_lNu1_lN}1_lNN76yh^1_h81gCqk3g9ifxLwtOIqq}RAYmh%! z7=-|9MiIf}fXwM&1ep`!8WDmj57zI1D#ifivobI+fG`VGIzGN6wW0*d0lN#;yaX1A zXEs2UfcPMPLAeYJEDQ~BHdudgNoi6sTqr&s)jR=KhOAx7zYD`$OCSNom>nIQ{Z}783bQ&Gwgix|Gzi`LqpKtYR8rTn;9k^V2;0rZ1;l0 z@;g=>j^FW;nPH*^Bg0Py28Ia?1tJq!7#OA?>oH(pm>|I9@RNZ_{+enE7dY%?puR(< zL179Cb5Jn_!k}^iqzuFc(J%~(Hy#E?1_7{1U{P4s2XjV=(GVC7fzc2c4S~@R7!85Z z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c z4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C z(GVC7fzc2c4S~@R7!85Z5Eu=C(Gb8A0?=L6SY$?pMnhmU1V%$(Gz3ONU^E0qLtr!n zMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ON zU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU z1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E2i5&{a)yU#&au7j@h24R@R$d^@v z?h=QIgV_#j4Dfr|q4&8nf-c66FDS~)D@g;ZWsGMK0x>}1%nT1W7#Nu1b2H*oD^in7 zOH$)AQWH~Bix@a_6EpJ|co`TTaMXZQGpH~yFz_)lFx+8*+!GJd&d&(B`5fdvF$M+( zentic&>h?$KIm?4Mu<|de$c(!AgsU;kHxxpuyrtJ#uumNq~>Lo=0XJGGxHEP+yA=9 zbAW+?aW>R@AhSh&-Q!_mU|?)#U}6DX{tj|lIs*ek1W1yBfq|2Op;1AB34HZCR0uD% z0V>OwRFs;S9iN<^SCW}mn#wSzU1@8&cESseWtpkp8RndP^0-<2`#;`!32%xN+)_JK z*VeDu6p?@IW!eFj>lZI(GxlS?_Yi0_Hk+&^FkdqvoFc= zX&-sHcU*b5_)>J!gQt_2w!Ed(d_wxUI`Xu%^#T`v(s^@GCrFSd;OW9?87xiG6)|sL Lo#$sf3=TE`3=nC@ literal 0 HcmV?d00001 diff --git a/examples/break_continue.sel b/examples/break_continue.sel new file mode 100644 index 0000000..30951a7 --- /dev/null +++ b/examples/break_continue.sel @@ -0,0 +1,9 @@ +orbit (wax int i = 0; i < 10; i = i + 1) { + eclipse (i == 3) { + continue; + }; + eclipse (i == 7) { + break; + }; + whisper i; +}; \ No newline at end of file diff --git a/selenium/ast.py b/selenium/ast.py index 5fdc7c9..eae28b0 100644 --- a/selenium/ast.py +++ b/selenium/ast.py @@ -73,6 +73,16 @@ class ReturnStmt: value: Optional["Expr"] +@dataclass(slots=True) +class BreakStmt: + pass + + +@dataclass(slots=True) +class ContinueStmt: + pass + + @dataclass(slots=True) class PrintStmt: value: "Expr" @@ -120,5 +130,5 @@ class Cast: Expr = Union[Literal, VarRef, Unary, Binary, Call, Cast] -Stmt = Union[VarDecl, Assign, IfStmt, WhileStmt, ForStmt, ReturnStmt, PrintStmt, ExprStmt, Block] -TopLevel = Union[VarDecl, FunctionDecl, Assign, IfStmt, WhileStmt, ForStmt, ReturnStmt, PrintStmt, ExprStmt, Block] +Stmt = Union[VarDecl, Assign, IfStmt, WhileStmt, ForStmt, ReturnStmt, BreakStmt, ContinueStmt, PrintStmt, ExprStmt, Block] +TopLevel = Union[VarDecl, FunctionDecl, Assign, IfStmt, WhileStmt, ForStmt, ReturnStmt, BreakStmt, ContinueStmt, PrintStmt, ExprStmt, Block] diff --git a/selenium/codegen_c.py b/selenium/codegen_c.py index 405caca..0462cc6 100644 --- a/selenium/codegen_c.py +++ b/selenium/codegen_c.py @@ -7,8 +7,10 @@ from .ast import ( Assign, Binary, Block, + BreakStmt, Call, Cast, + ContinueStmt, Expr, ExprStmt, ForStmt, @@ -110,6 +112,10 @@ class CCodeGenerator: self._writeline("return;") else: self._writeline(f"return {self._expr(item.value)};") + elif isinstance(item, BreakStmt): + self._writeline("break;") + elif isinstance(item, ContinueStmt): + self._writeline("continue;") elif isinstance(item, PrintStmt): self._emit_print(item.value) elif isinstance(item, ExprStmt): diff --git a/selenium/lexer.py b/selenium/lexer.py index 00e0ccc..8e78d55 100644 --- a/selenium/lexer.py +++ b/selenium/lexer.py @@ -24,6 +24,8 @@ KEYWORDS = { "shadow": "SHADOW", "tide": "TIDE", "orbit": "ORBIT", + "break": "BREAK", + "continue": "CONTINUE", "whisper": "WHISPER", "return": "RETURN", "cast": "CAST", diff --git a/selenium/parser.py b/selenium/parser.py index f6aa688..5611cf5 100644 --- a/selenium/parser.py +++ b/selenium/parser.py @@ -6,8 +6,10 @@ from .ast import ( Assign, Binary, Block, + BreakStmt, Call, Cast, + ContinueStmt, Expr, ExprStmt, ForStmt, @@ -150,6 +152,14 @@ class Parser: self._consume(";", "Expected ';' after return") return ReturnStmt(value) + if self._match("BREAK"): + self._consume(";", "Expected ';' after break") + return BreakStmt() + + if self._match("CONTINUE"): + self._consume(";", "Expected ';' after continue") + return ContinueStmt() + if self._match("WHISPER"): value = self._expression() self._consume(";", "Expected ';' after whisper") diff --git a/selenium/sema.py b/selenium/sema.py index 876babb..6cb584b 100644 --- a/selenium/sema.py +++ b/selenium/sema.py @@ -7,8 +7,10 @@ from .ast import ( Assign, Binary, Block, + BreakStmt, Call, Cast, + ContinueStmt, Expr, ExprStmt, ForStmt, @@ -90,6 +92,7 @@ class SemanticAnalyzer: self.functions: Dict[str, FunctionInfo] = {} self.current_return: Optional[TypeInfo] = None self.expr_types: Dict[int, TypeInfo] = {} + self.loop_depth = 0 def analyze(self, program: Program) -> Program: for item in program.items: @@ -166,7 +169,9 @@ class SemanticAnalyzer: if isinstance(stmt, WhileStmt): cond_type = self._infer_expr(stmt.condition, scope) self._require_type(cond_type, "bool", "While condition must be bool") + self.loop_depth += 1 self._analyze_block(stmt.body, scope, in_function) + self.loop_depth -= 1 return if isinstance(stmt, ForStmt): @@ -177,7 +182,9 @@ class SemanticAnalyzer: self._require_type(cond_type, "bool", "For condition must be bool") if stmt.increment is not None: self._analyze_stmt(stmt.increment, for_scope, in_function) + self.loop_depth += 1 self._analyze_block(stmt.body, for_scope, in_function) + self.loop_depth -= 1 return if isinstance(stmt, ReturnStmt): @@ -192,6 +199,16 @@ class SemanticAnalyzer: self._require_same_type(self.current_return, value_type, "Return type mismatch") return + if isinstance(stmt, BreakStmt): + if self.loop_depth == 0: + raise SemanticError("Break is only allowed inside loops") + return + + if isinstance(stmt, ContinueStmt): + if self.loop_depth == 0: + raise SemanticError("Continue is only allowed inside loops") + return + if isinstance(stmt, PrintStmt): self._infer_expr(stmt.value, scope) return