From 5bf46297e5bc4f8bbcdecc0d4e5b97a2d0d21af6 Mon Sep 17 00:00:00 2001 From: Sy Date: Sun, 1 Dec 2024 10:25:52 +0300 Subject: [PATCH] Fix signed integers. Make numeric input return 0 if it can't return a valid number, for consistency with other SPL implementations --- shakespearelang/_input.py | 20 ++++++++-- shakespearelang/tests/test_numeric_input.py | 41 ++++++++++----------- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/shakespearelang/_input.py b/shakespearelang/_input.py index 039a5b9..671c9a0 100644 --- a/shakespearelang/_input.py +++ b/shakespearelang/_input.py @@ -10,17 +10,31 @@ def consume_numeric_input(self): try: self._ensure_input_buffer() except EOFError: - raise ShakespeareRuntimeError("End of file encountered.") from None + return 0 + sign = self._consume_sign_if_present() number = self._consume_digits() + + if number is None: + if sign: + self._input_buffer = sign + self._input_buffer + return 0 + self._consume_newline_if_present() - return number + return -number if sign == "-" else number def _consume_newline_if_present(self): if self._input_buffer and self._input_buffer[0] == "\n": self._input_buffer = self._input_buffer[1:] + def _consume_sign_if_present(self): + if self._input_buffer and (sign := self._input_buffer[0]) in ["+", "-"]: + self._input_buffer = self._input_buffer[1:] + return sign + + return "" + def _consume_digits(self): number_input = "" while self._input_buffer and self._input_buffer[0].isdigit(): @@ -28,7 +42,7 @@ def _consume_digits(self): self._input_buffer = self._input_buffer[1:] if len(number_input) == 0: - raise ShakespeareRuntimeError("No numeric input was given.") + return None return int(number_input) diff --git a/shakespearelang/tests/test_numeric_input.py b/shakespearelang/tests/test_numeric_input.py index a53b3f7..76b9126 100644 --- a/shakespearelang/tests/test_numeric_input.py +++ b/shakespearelang/tests/test_numeric_input.py @@ -10,6 +10,19 @@ def test_correctly_parses_number(monkeypatch, capsys): s.run_event("[Enter Romeo and Juliet]") s.run_sentence("Listen to your heart!", "Juliet") assert s.state.character_by_name("Romeo").value == 4257 + + monkeypatch.setattr("sys.stdin", StringIO("-3211")) + s = Shakespeare("Foo. Juliet, a test. Romeo, a test.") + s.run_event("[Enter Romeo and Juliet]") + s.run_sentence("Listen to your heart!", "Juliet") + assert s.state.character_by_name("Romeo").value == -3211 + + monkeypatch.setattr("sys.stdin", StringIO("+2")) + s = Shakespeare("Foo. Juliet, a test. Romeo, a test.") + s.run_event("[Enter Romeo and Juliet]") + s.run_sentence("Listen to your heart!", "Juliet") + assert s.state.character_by_name("Romeo").value == 2 + captured = capsys.readouterr() assert captured.out == "" assert captured.err == "" @@ -42,39 +55,24 @@ def test_consumes_trailing_newline(monkeypatch, capsys): assert s.state.character_by_name("Romeo").value == -1 -def test_errors_without_digits(monkeypatch, capsys): +def test_no_digits_consumed(monkeypatch, capsys): monkeypatch.setattr("sys.stdin", StringIO("a123")) s = Shakespeare("Foo. Juliet, a test. Romeo, a test.") s.run_event("[Enter Romeo and Juliet]") - - with pytest.raises(ShakespeareRuntimeError) as exc: - s.run_sentence("Listen to your heart!", "Juliet") - assert "no numeric input" in str(exc.value).lower() - assert ">>Listen to your heart!<<" in str(exc.value) - assert exc.value.interpreter == s - assert s.state.character_by_name("Romeo").value == 0 - - monkeypatch.setattr("sys.stdin", StringIO("a123")) - with pytest.raises(ShakespeareRuntimeError) as exc: - s.run_sentence("Listen to your heart!", "Juliet") - assert "no numeric input" in str(exc.value).lower() - assert ">>Listen to your heart!<<" in str(exc.value) - assert exc.value.interpreter == s + s.state.character_by_name("Romeo").value = 24 + s.run_sentence("Listen to your heart!", "Juliet") assert s.state.character_by_name("Romeo").value == 0 captured = capsys.readouterr() assert captured.out == "" assert captured.err == "" -def test_errors_on_eof(monkeypatch, capsys): +def test_eof(monkeypatch, capsys): monkeypatch.setattr("sys.stdin", StringIO("")) s = Shakespeare("Foo. Juliet, a test. Romeo, a test.") s.run_event("[Enter Romeo and Juliet]") - with pytest.raises(ShakespeareRuntimeError) as exc: - s.run_sentence("Listen to your heart!", "Juliet") - assert "end of file" in str(exc.value).lower() - assert ">>Listen to your heart!<<" in str(exc.value) - assert exc.value.interpreter == s + s.state.character_by_name("Romeo").value = 42 + s.run_sentence("Listen to your heart!", "Juliet") assert s.state.character_by_name("Romeo").value == 0 captured = capsys.readouterr() assert captured.out == "" @@ -106,6 +104,7 @@ def test_conditional(monkeypatch, capsys): assert captured.out == "" assert captured.err == "" + def test_interactive_style(monkeypatch, capsys): monkeypatch.setattr("sys.stdin", StringIO("4257\n3211")) s = Shakespeare("Foo. Juliet, a test. Romeo, a test.", input_style="interactive")