From d5d3b9f42133d5f68857280e7995158b813c3e07 Mon Sep 17 00:00:00 2001 From: Alvin <524715@vistacollege.nl> Date: Tue, 22 Jul 2025 19:49:07 +0200 Subject: [PATCH] feat: Implement for loops, break, and continue statements --- examples/loops_advanced.fem | 21 +++++++++++++++++ src/interpreter.py | 45 +++++++++++++++++++++++++++++++++++-- src/lexer.py | 15 +++++++++++++ src/parser.py | 14 +++++++++++- 4 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 examples/loops_advanced.fem diff --git a/examples/loops_advanced.fem b/examples/loops_advanced.fem new file mode 100644 index 0000000..d74712b --- /dev/null +++ b/examples/loops_advanced.fem @@ -0,0 +1,21 @@ +my_list is ["apple", "banana", "cherry"] +Tomgirl item is my_list Femboycore + UwU Boy item +Periodt + +my_string is "Femboy" +Tomgirl char is my_string Femboycore + UwU Boy char +Periodt + +counter is 0 +Otokonoko counter < 10 Femboycore + counter is counter + 1 + Femboy Feminine counter == 5 Femboycore + Break + Periodt + Femboy Feminine counter == 2 Femboycore + Continue + Periodt + UwU Boy counter +Periodt \ No newline at end of file diff --git a/src/interpreter.py b/src/interpreter.py index ac623fd..decbe0a 100644 --- a/src/interpreter.py +++ b/src/interpreter.py @@ -104,7 +104,36 @@ class Interpreter: def visit_WhileStatement(self, node): while self.visit(node.condition): - self.visit(node.body) + try: + self.visit(node.body) + except BreakLoop: + break + except ContinueLoop: + continue + + def visit_ForStatement(self, node): + iterable = self.visit(node.iterable) + if not isinstance(iterable, (list, str)): + raise TypeError(f"'for' loop can only iterate over lists or strings, got {type(iterable).__name__}") + + for item in iterable: + self.scope_stack.append({}) + self.current_scope[node.var_name] = item + try: + self.visit(node.body) + except BreakLoop: + self.scope_stack.pop() + break + except ContinueLoop: + self.scope_stack.pop() + continue + self.scope_stack.pop() + + def visit_BreakStatement(self, node): + raise BreakLoop() + + def visit_ContinueStatement(self, node): + raise ContinueLoop() def visit_List(self, node): elements = [self.visit(element) for element in node.elements] @@ -186,4 +215,16 @@ class Interpreter: class ReturnValue(Exception): def __init__(self, value): - self.value = value \ No newline at end of file + self.value = value + +class BreakLoop(Exception): + pass + +class ContinueLoop(Exception): + pass + +class BreakLoop(Exception): + pass + +class ContinueLoop(Exception): + pass \ No newline at end of file diff --git a/src/lexer.py b/src/lexer.py index 417a3f4..fa75d77 100644 --- a/src/lexer.py +++ b/src/lexer.py @@ -149,6 +149,21 @@ class Lexer: if re.match(r'\bCringe\b', self.text[self.pos:]): self.pos += len('Cringe') return Token('CRINGE', False) + if re.match(r'\bGhosted\b', self.text[self.pos:]): + self.pos += len('Ghosted') + return Token('NULL', None) + if re.match(r'\bTomgirl\b', self.text[self.pos:]): + self.pos += len('Tomgirl') + return Token('FOR', 'Tomgirl') + if re.match(r'\bSlay\b', self.text[self.pos:]): + self.pos += len('Slay') + return Token('PASS', 'Slay') + if re.match(r'\bBreak\b', self.text[self.pos:]): + self.pos += len('Break') + return Token('BREAK', 'Break') + if re.match(r'\bContinue\b', self.text[self.pos:]): + self.pos += len('Continue') + return Token('CONTINUE', 'Continue') if re.match(r'\band\b', self.text[self.pos:]): self.pos += len('and') return Token('AND', 'and') diff --git a/src/parser.py b/src/parser.py index 1f0b7cc..075860b 100644 --- a/src/parser.py +++ b/src/parser.py @@ -81,6 +81,18 @@ class BreakStatement(AST): class ContinueStatement(AST): pass +class ForStatement(AST): + def __init__(self, var_name, iterable, body): + self.var_name = var_name + self.iterable = iterable + self.body = body + +class BreakStatement(AST): + pass + +class ContinueStatement(AST): + pass + class FunctionDefinition(AST): def __init__(self, name, parameters, body): self.name = name @@ -452,4 +464,4 @@ class Parser: self.consume('DOT') # Assuming DOT token for property access property_name_token = self.get_current_token() self.consume('ID') - return PropertyAccess(target_node, property_name_token.value) \ No newline at end of file + return PropertyAccess(target_node, property_name_token.value)