From 3115c2dfeb5477e2c0f7178307ca230a347c7b22 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 17:54:43 +0000 Subject: [PATCH 1/2] Initial plan From 0654aae79cfed7fb669778a0f37b8b2550a62f14 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 17:58:34 +0000 Subject: [PATCH 2/2] Fix syntax error position reporting with comments in source code Agent-Logs-Url: https://github.com/letsbuilda/bpp/sessions/b28bd439-112c-4c0c-851d-721db886836d Co-authored-by: shenanigansd <54628770+shenanigansd@users.noreply.github.com> --- src/bpp/interpreter.py | 14 +++++++------- src/bpp/tokens.py | 17 +++++++++++++++++ tests/test_interpreter.py | 3 +++ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/bpp/interpreter.py b/src/bpp/interpreter.py index a5340ff..e496051 100644 --- a/src/bpp/interpreter.py +++ b/src/bpp/interpreter.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Self from .exceptions import BrainfuckSyntaxError -from .tokens import Token, tokenize +from .tokens import Token, _tokenize_with_positions if TYPE_CHECKING: from collections.abc import Sequence @@ -20,7 +20,7 @@ class ResultState(Enum): JUMP_BACKWARD = auto() -def validate_syntax(syntax: Sequence[Sequence[Token]]) -> None: +def validate_syntax(syntax: Sequence[Sequence[tuple[Token, int]]]) -> None: """Validate the given syntax. Raises @@ -32,16 +32,16 @@ def validate_syntax(syntax: Sequence[Sequence[Token]]) -> None: loop_started_at_line = 0 loop_started_at_character = 0 for line_index, line in enumerate(syntax): - for character_index, token in enumerate(line): + for token, char_position in line: if token == Token.LOOP_START: loop_started_at_line = line_index - loop_started_at_character = character_index + loop_started_at_character = char_position depth += 1 if token == Token.LOOP_END: if depth == 0: msg = ( "Syntax error: Unexpected closing bracket in line " - f"{line_index + 1} at char {character_index + 1}!" + f"{line_index + 1} at char {char_position + 1}!" ) raise BrainfuckSyntaxError(msg) depth -= 1 @@ -140,9 +140,9 @@ def run(self: Self, code: str) -> str: ------- The output of the code. """ - syntax = [tokenize(line) for line in code.split("\n")] + syntax = [_tokenize_with_positions(line) for line in code.split("\n")] validate_syntax(syntax) - tokens = [token for line in syntax for token in line] + tokens = [token for line in syntax for token, _ in line] # Precompute matching bracket pairs to support nested loops. # validate_syntax guarantees brackets are balanced, so the stack is diff --git a/src/bpp/tokens.py b/src/bpp/tokens.py index e49bcef..c3f8703 100644 --- a/src/bpp/tokens.py +++ b/src/bpp/tokens.py @@ -47,3 +47,20 @@ def tokenize(code: str) -> Sequence[Token]: continue tokens.append(token) return tokens + + +def _tokenize_with_positions(code: str) -> Sequence[tuple[Token, int]]: + """Convert text to (token, original_char_index) tuples. + + Returns + ------- + A sequence of (token, original_char_index) tuples preserving the + character's position in the original source string. + """ + tokens = [] + for position, character in enumerate(code): + token = Token.from_character(character) + if token is None: + continue + tokens.append((token, position)) + return tokens diff --git a/tests/test_interpreter.py b/tests/test_interpreter.py index 6f1214d..2ed7901 100644 --- a/tests/test_interpreter.py +++ b/tests/test_interpreter.py @@ -51,6 +51,9 @@ def test_can_input_letter(monkeypatch) -> None: # type: ignore[no-untyped-def] [ ("[", "Syntax error: Unclosed bracket in line 1 at char 1!"), ("]", "Syntax error: Unexpected closing bracket in line 1 at char 1!"), + # Comments before brackets should not affect reported position + (">comment[", r"Syntax error: Unclosed bracket in line 1 at char 9!"), + (">comment]", r"Syntax error: Unexpected closing bracket in line 1 at char 9!"), ], ) def test_invalid_syntax_fails(code: str, error_message: str) -> None: