oaknut-basic 12.9.0__tar.gz → 12.10.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. {oaknut_basic-12.9.0/src/oaknut_basic.egg-info → oaknut_basic-12.10.1}/PKG-INFO +1 -1
  2. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut/basic/__init__.py +1 -1
  3. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut/basic/numbering.py +27 -1
  4. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut/basic/tokeniser.py +7 -10
  5. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1/src/oaknut_basic.egg-info}/PKG-INFO +1 -1
  6. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_numbering.py +7 -0
  7. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/LICENSE +0 -0
  8. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/README.md +0 -0
  9. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/pyproject.toml +0 -0
  10. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/setup.cfg +0 -0
  11. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut/basic/cli.py +0 -0
  12. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut/basic/datafile.py +0 -0
  13. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut/basic/detokeniser.py +0 -0
  14. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut/basic/exceptions.py +0 -0
  15. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut/basic/float5.py +0 -0
  16. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut/basic/linenumber.py +0 -0
  17. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut/basic/scanner.py +0 -0
  18. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut/basic/tokens.py +0 -0
  19. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut_basic.egg-info/SOURCES.txt +0 -0
  20. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut_basic.egg-info/dependency_links.txt +0 -0
  21. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut_basic.egg-info/entry_points.txt +0 -0
  22. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut_basic.egg-info/requires.txt +0 -0
  23. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/src/oaknut_basic.egg-info/top_level.txt +0 -0
  24. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_basic.py +0 -0
  25. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_cli.py +0 -0
  26. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_crunch_rules.py +0 -0
  27. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_data_cli.py +0 -0
  28. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_datafile.py +0 -0
  29. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_detokeniser.py +0 -0
  30. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_float5.py +0 -0
  31. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_keyword_coverage.py +0 -0
  32. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_linenumber.py +0 -0
  33. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_rom_golden.py +0 -0
  34. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_rom_golden_detokenise.py +0 -0
  35. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_scanner.py +0 -0
  36. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_tokeniser.py +0 -0
  37. {oaknut_basic-12.9.0 → oaknut_basic-12.10.1}/tests/test_tokens.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oaknut-basic
3
- Version: 12.9.0
3
+ Version: 12.10.1
4
4
  Summary: BBC BASIC tools: program tokeniser/de-tokeniser and PRINT#/INPUT# data-file reader/writer
5
5
  Author-email: Robert Smallshire <robert@smallshire.org.uk>
6
6
  License-Expression: MIT
@@ -79,7 +79,7 @@ from oaknut.basic.tokens import (
79
79
  TOKEN_TO_KEYWORD,
80
80
  )
81
81
 
82
- __version__ = "12.9.0"
82
+ __version__ = "12.10.1"
83
83
 
84
84
  # Canonical load addresses for BBC BASIC programs on each host.
85
85
  # Programs saved by *SAVE on a real machine use these by default.
@@ -14,9 +14,35 @@ stream plumbing on top.
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
+ import re
18
+
17
19
  DEFAULT_LINE_NUMBER = 10
18
20
  DEFAULT_LINE_STEP = 10
19
21
 
22
+ # A BBC BASIC line ends only at a carriage return (or a host newline);
23
+ # Python's str.splitlines() additionally breaks on &0B, &0C, &1C-&1E and
24
+ # others, which occur legitimately inside string literals (mode-7 / VDU
25
+ # control codes), so split strictly on the real terminators only. This
26
+ # definition is shared with the tokeniser, which imports it from here.
27
+ LINE_SEPARATOR_RE = re.compile(r"\r\n|\r|\n")
28
+
29
+
30
+ def split_source_lines(source: str) -> list[str]:
31
+ """Split BBC BASIC source into lines on real terminators only.
32
+
33
+ Matches :meth:`str.splitlines` semantics for the terminators a BBC
34
+ BASIC line actually ends on (``"\\n"``, ``"\\r"``, ``"\\r\\n"``) — an
35
+ empty *source* yields no lines and a single trailing terminator is
36
+ not treated as introducing a final empty line — but, unlike
37
+ :meth:`str.splitlines`, never breaks on the in-string control codes
38
+ (&0B, &0C, &1C-&1E, ...) that BBC BASIC string literals legitimately
39
+ contain.
40
+ """
41
+ lines = LINE_SEPARATOR_RE.split(source)
42
+ if lines and lines[-1] == "":
43
+ lines.pop()
44
+ return lines
45
+
20
46
 
21
47
  def number_lines(
22
48
  source: str,
@@ -54,7 +80,7 @@ def number_lines(
54
80
 
55
81
  number = start
56
82
  numbered = []
57
- for line in source.splitlines():
83
+ for line in split_source_lines(source):
58
84
  numbered.append(f"{number} {line}")
59
85
  number += step
60
86
 
@@ -27,8 +27,6 @@ the CLI does it at its I/O boundary.
27
27
 
28
28
  from __future__ import annotations
29
29
 
30
- import re
31
-
32
30
  from oaknut.basic.exceptions import (
33
31
  AlreadyNumberedError,
34
32
  LineNumberOrderError,
@@ -37,7 +35,12 @@ from oaknut.basic.exceptions import (
37
35
  UnnumberedLineError,
38
36
  )
39
37
  from oaknut.basic.linenumber import MAX_LINE_NUMBER, encode_line_number
40
- from oaknut.basic.numbering import DEFAULT_LINE_NUMBER, DEFAULT_LINE_STEP, number_lines
38
+ from oaknut.basic.numbering import (
39
+ DEFAULT_LINE_NUMBER,
40
+ DEFAULT_LINE_STEP,
41
+ LINE_SEPARATOR_RE,
42
+ number_lines,
43
+ )
41
44
  from oaknut.basic.tokens import (
42
45
  FLAG_CONDITIONAL,
43
46
  FLAG_FN_PROC,
@@ -56,15 +59,9 @@ from oaknut.basic.tokens import (
56
59
  _CR = 0x0D
57
60
  _END_MARKER = 0xFF
58
61
 
59
- # A BBC BASIC line ends only at a carriage return (or a host newline);
60
- # Python's str.splitlines() additionally breaks on &0B, &0C, &1C-&1E and
61
- # others, which occur legitimately inside string literals (mode-7 / VDU
62
- # control codes), so split strictly on the real terminators only.
63
- _LINE_SEPARATOR_RE = re.compile(r"\r\n|\r|\n")
64
-
65
62
 
66
63
  def _split_source_lines(source: str) -> list[str]:
67
- return _LINE_SEPARATOR_RE.split(source)
64
+ return LINE_SEPARATOR_RE.split(source)
68
65
 
69
66
  # Keyword entries grouped by first character, preserving ROM order within
70
67
  # each group, so the crunch only scans the relevant group.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oaknut-basic
3
- Version: 12.9.0
3
+ Version: 12.10.1
4
4
  Summary: BBC BASIC tools: program tokeniser/de-tokeniser and PRINT#/INPUT# data-file reader/writer
5
5
  Author-email: Robert Smallshire <robert@smallshire.org.uk>
6
6
  License-Expression: MIT
@@ -47,6 +47,13 @@ class TestNumberLines:
47
47
  def test_blank_lines_are_numbered_too(self):
48
48
  assert basic.number_lines("A\n\nB") == "10 A\n20 \n30 B"
49
49
 
50
+ def test_control_codes_in_string_literal_do_not_split_the_line(self):
51
+ # VDU / mode-7 control codes (here &0C, &1C) appear legitimately
52
+ # inside string literals; str.splitlines() would break on them,
53
+ # corrupting a single program line into several numbered lines.
54
+ source = 'PRINT "' + chr(0x0C) + chr(0x1C) + 'banner"'
55
+ assert basic.number_lines(source) == '10 PRINT "\x0c\x1cbanner"'
56
+
50
57
  def test_internal_references_are_left_untouched(self):
51
58
  # The facade only prepends numbers; it does not rewrite GOTO etc.
52
59
  source = "GOTO 20\nEND"
File without changes
File without changes
File without changes