From 00e7ad5291aec502450bc29c72ff89493a230efb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 17:25:56 +0000 Subject: [PATCH 1/4] Initial plan From 02a048dcbe6f9c7e262a639ef7ed34bb06e2af76 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 17:30:33 +0000 Subject: [PATCH 2/4] Add support for nested loops Agent-Logs-Url: https://github.com/letsbuilda/bpp/sessions/898602c6-472d-43bf-a777-e7eccc8aa770 Co-authored-by: shenanigansd <54628770+shenanigansd@users.noreply.github.com> --- examples/hello-world-nested-loops.bf | 1 + src/bpp/interpreter.py | 35 +++++++++++++++++----------- tests/test_example_files.py | 1 + 3 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 examples/hello-world-nested-loops.bf diff --git a/examples/hello-world-nested-loops.bf b/examples/hello-world-nested-loops.bf new file mode 100644 index 0000000..2627cf1 --- /dev/null +++ b/examples/hello-world-nested-loops.bf @@ -0,0 +1 @@ ++[-[<<[+[--->]-[<<<]]]>>>-]>-.---.>..>.<<<<-.<+.>>>>>.>.<<.<-. diff --git a/src/bpp/interpreter.py b/src/bpp/interpreter.py index 620c6ca..3bff5dd 100644 --- a/src/bpp/interpreter.py +++ b/src/bpp/interpreter.py @@ -28,24 +28,24 @@ def validate_syntax(syntax: Sequence[Sequence[Token]]) -> None: BrainfuckSyntaxError If the syntax is invalid. """ - in_loop = False + depth = 0 loop_started_at_line = 0 loop_started_at_character = 0 for line_index, line in enumerate(syntax): for character_index, token in enumerate(line): if token == Token.LOOP_START: - in_loop = True loop_started_at_line = line_index loop_started_at_character = character_index + depth += 1 if token == Token.LOOP_END: - if not in_loop: + if depth == 0: msg = ( "Syntax error: Unexpected closing bracket in line " - f"{loop_started_at_line + 1} at char {loop_started_at_character + 1}!" + f"{line_index + 1} at char {character_index + 1}!" ) raise BrainfuckSyntaxError(msg) - in_loop = False - if in_loop: + depth -= 1 + if depth > 0: msg = ( "Syntax error: Unclosed bracket in line " f"{loop_started_at_line + 1} at char {loop_started_at_character + 1}!" @@ -144,21 +144,28 @@ def run(self: Self, code: str) -> str: validate_syntax(syntax) tokens = [token for line in syntax for token in line] - last_loop_start = 0 + # Precompute matching bracket pairs to support nested loops. + # validate_syntax guarantees brackets are balanced, so the stack is + # always non-empty when a LOOP_END token is encountered here. + bracket_map: dict[int, int] = {} + stack: list[int] = [] + for i, token in enumerate(tokens): + if token == Token.LOOP_START: + stack.append(i) + elif token == Token.LOOP_END: + j = stack.pop() + bracket_map[j] = i + bracket_map[i] = j + while True: token = tokens[self.current_index] - if token == Token.LOOP_START: - last_loop_start = self.current_index match self.handle_token(token): case ResultState.SUCCESS: pass case ResultState.JUMP_BACKWARD: - self.current_index = last_loop_start + self.current_index = bracket_map[self.current_index] case ResultState.JUMP_FORWARD: - for i in range(self.current_index, len(tokens)): - if tokens[i] == Token.LOOP_END: - self.current_index = i - break + self.current_index = bracket_map[self.current_index] self.current_index += 1 if self.current_index >= len(tokens): diff --git a/tests/test_example_files.py b/tests/test_example_files.py index 7100f97..794c637 100644 --- a/tests/test_example_files.py +++ b/tests/test_example_files.py @@ -12,6 +12,7 @@ ("example_file", "expected_result"), [ (Path("./examples/hello-world.bf"), "Hello, World!"), + (Path("./examples/hello-world-nested-loops.bf"), "hello world"), (Path("./examples/jump.bf"), ""), (Path("./examples/decrement_not_in_memory.bf"), ""), ], From cb13182cd9c38ebcd96228a624160f088c1017ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 17:46:21 +0000 Subject: [PATCH 3/4] Fix KeyError for uninitialized memory cells in output and loop checks Agent-Logs-Url: https://github.com/letsbuilda/bpp/sessions/182e7f1f-ea01-4faa-a8b9-71a1c24368ab Co-authored-by: shenanigansd <54628770+shenanigansd@users.noreply.github.com> --- src/bpp/interpreter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bpp/interpreter.py b/src/bpp/interpreter.py index 3bff5dd..a55f81b 100644 --- a/src/bpp/interpreter.py +++ b/src/bpp/interpreter.py @@ -99,7 +99,7 @@ def decrement_byte_at_current_pointer(self: Self) -> None: def output_current_byte(self: Self) -> None: """Output the ASCII value of the byte at the current position.""" - self.output.write(chr(self.memory[self.current_position])) + self.output.write(chr(self.memory.get(self.current_position, 0))) def get_input(self: Self) -> None: """Output the ASCII value of the byte at the current position.""" @@ -126,10 +126,10 @@ def handle_token(self: Self, token: Token) -> ResultState: # noqa: C901 case Token.INPUT_BYTE: self.get_input() case Token.LOOP_START: - if self.memory[self.current_position] == 0: + if self.memory.get(self.current_position, 0) == 0: return ResultState.JUMP_FORWARD case Token.LOOP_END: - if self.memory[self.current_position] != 0: + if self.memory.get(self.current_position, 0) != 0: return ResultState.JUMP_BACKWARD return ResultState.SUCCESS From 552a4d3fbebacee11ed185cdb0adc4105cc05e1c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 17:50:29 +0000 Subject: [PATCH 4/4] Replace single-letter variable names with descriptive names in run() Agent-Logs-Url: https://github.com/letsbuilda/bpp/sessions/0f676d41-a05c-4154-86f6-3d5253f091f7 Co-authored-by: shenanigansd <54628770+shenanigansd@users.noreply.github.com> --- src/bpp/interpreter.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bpp/interpreter.py b/src/bpp/interpreter.py index a55f81b..a5340ff 100644 --- a/src/bpp/interpreter.py +++ b/src/bpp/interpreter.py @@ -149,13 +149,13 @@ def run(self: Self, code: str) -> str: # always non-empty when a LOOP_END token is encountered here. bracket_map: dict[int, int] = {} stack: list[int] = [] - for i, token in enumerate(tokens): + for token_index, token in enumerate(tokens): if token == Token.LOOP_START: - stack.append(i) + stack.append(token_index) elif token == Token.LOOP_END: - j = stack.pop() - bracket_map[j] = i - bracket_map[i] = j + open_bracket_index = stack.pop() + bracket_map[open_bracket_index] = token_index + bracket_map[token_index] = open_bracket_index while True: token = tokens[self.current_index]