Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
- docs

env:
PYTHON_VERSION: 3.12.3
PYTHON_VERSION: 3.14.3

jobs:
build:
Expand All @@ -19,7 +19,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
Expand All @@ -34,7 +34,7 @@ jobs:
poetry config virtualenvs.in-project true

- name: Set up poetry cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: .venv
key: venv-${{ runner.os }}-py-${{ env.PYTHON_VERSION }}-${{ hashFiles('poetry.lock') }}
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ repos:

- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.9.4
rev: v0.15.12
hooks:
# Run the linter.
- id: ruff
Expand Down
6 changes: 3 additions & 3 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ license-files = [
"LICENSE.txt"
]
readme = "README.md"
requires-python = ">=3.11"
requires-python = ">=3.12"
keywords = ["compiler", "zxspectrum", "BASIC", "z80"]
classifiers = [
"Development Status :: 5 - Production/Stable",
Expand Down Expand Up @@ -130,7 +130,7 @@ follow_imports = "skip"

[tool.ruff]
line-length = 120
target-version = "py311"
target-version = "py314"
exclude = [
".venv/",
"venv/",
Expand Down
4 changes: 2 additions & 2 deletions src/api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def load_config_from_file(
try:
cfg = configparser.ConfigParser()
cfg.read(filename, encoding="utf-8")
except (configparser.DuplicateSectionError, configparser.DuplicateOptionError):
except configparser.DuplicateSectionError, configparser.DuplicateOptionError:
errmsg.msg_output(f"Invalid config file '{filename}': it has duplicated fields")
if stop_on_error:
sys.exit(1)
Expand Down Expand Up @@ -157,7 +157,7 @@ def save_config_into_file(
if os.path.exists(filename):
try:
cfg.read(filename, encoding="utf-8")
except (configparser.DuplicateSectionError, configparser.DuplicateOptionError):
except configparser.DuplicateSectionError, configparser.DuplicateOptionError:
errmsg.msg_output(f"Invalid config file '{filename}': it has duplicated fields")
if stop_on_error:
sys.exit(1)
Expand Down
2 changes: 1 addition & 1 deletion src/api/optimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def filter_inorder(
node,
filter_func: Callable[[Any], bool],
child_selector: Callable[[Ast], bool] = lambda x: True,
) -> Generator[Ast, None, None]:
) -> Generator[Ast]:
"""Visit the tree inorder, but only those that return true for filter_func and visiting children which
return true for child_selector.
"""
Expand Down
3 changes: 1 addition & 2 deletions src/api/symboltable/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
# --------------------------------------------------------------------

from collections import OrderedDict
from typing import Optional

from src.api.config import OPTIONS
from src.symbols.id_ import SymbolID
Expand Down Expand Up @@ -37,7 +36,7 @@ class Scope:
myFunct will be output as _myFunct_a.
"""

def __init__(self, namespace: str = "", parent_scope: Optional["Scope"] = None):
def __init__(self, namespace: str = "", parent_scope: Scope | None = None):
from src.symbols.funcdecl import SymbolFUNCDECL

self.symbols: dict[str, SymbolID] = OrderedDict()
Expand Down
6 changes: 3 additions & 3 deletions src/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@
T = TypeVar("T")


def first(iter_: Iterable[T], default: T | None = None) -> T | None:
def first[T](iter_: Iterable[T], default: T | None = None) -> T | None:
"""Return the first element of an Iterable, or None if it's empty or
there are no more elements to return."""
return next(iter(iter_), default)


def sfirst(iter_: Iterable[T]) -> T:
def sfirst[T](iter_: Iterable[T]) -> T:
"""Return the first element of an Iterable, or fails if it's empty"""
return next(iter(iter_))

Expand Down Expand Up @@ -175,7 +175,7 @@ def eval_to_num(expr: str) -> int | float | None:
if it was non-numeric."""
try:
result = eval(expr, {}, {})
except (NameError, SyntaxError, ValueError):
except NameError, SyntaxError, ValueError:
return None

if isinstance(result, int | float):
Expand Down
8 changes: 2 additions & 6 deletions src/arch/z80/optimizer/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# --------------------------------------------------------------------

from collections.abc import Iterable, Mapping
from typing import Any, Final, TypeVar, cast
from typing import Any, Final, cast

from . import patterns

Expand Down Expand Up @@ -48,10 +48,6 @@
)


T = TypeVar("T")
K = TypeVar("K")


# All 'single' registers (even f FLAG one). SP is not decomposable so it's 'single' already
ALL_REGS: Final[frozenset[str]] = frozenset(
[
Expand Down Expand Up @@ -493,7 +489,7 @@ def HI16_val(x: int | str | None) -> str:
return f"0{HL_SEP}{x}".split(HL_SEP)[-2]


def dict_intersection(dict_a: Mapping[K, T], dict_b: Mapping[K, T]) -> dict[K, T]:
def dict_intersection[K, T](dict_a: Mapping[K, T], dict_b: Mapping[K, T]) -> dict[K, T]:
"""Given 2 dictionaries a, b, returns a new one which contains the common key/pair values.
e.g. for {'a': 1, 'b': 'x'}, {'a': 'q', 'b': 'x', 'c': 2} returns {'b': 'x'}

Expand Down
4 changes: 2 additions & 2 deletions src/arch/z80/peephole/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def read_opt(opt_path: str) -> OptPattern | None:
errmsg.warning(define_.lineno, "this template will be ignored", fpath)
return None

except (ValueError, KeyError, TypeError):
except ValueError, KeyError, TypeError:
errmsg.warning(1, "There is an error in this template and it will be ignored", fpath)
else:
MAXLEN = max(len(pattern_.patt), MAXLEN or 0)
Expand All @@ -95,7 +95,7 @@ def read_opts(folder_path: str, result: list[OptPattern] | None = None) -> list[

try:
files_to_read = [f for f in os.listdir(folder_path) if f.endswith(".opt")]
except (FileNotFoundError, NotADirectoryError, PermissionError):
except FileNotFoundError, NotADirectoryError, PermissionError:
return result

for fname in files_to_read:
Expand Down
4 changes: 2 additions & 2 deletions src/arch/z80/peephole/evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ def eval(self, vars_: dict[str, Any] | None = None) -> str | Evaluator | list[An
try:
oper = FN(self.expression[0])
assert oper in UNARY
except (AssertionError, ValueError):
except AssertionError, ValueError:
raise ValueError(f"Invalid unary operator '{self.expression[0]}'")

operand = self.expression[1].eval(vars_)
Expand All @@ -248,7 +248,7 @@ def eval(self, vars_: dict[str, Any] | None = None) -> str | Evaluator | list[An
try:
oper = FN(self.expression[1])
assert oper in BINARY
except (AssertionError, ValueError):
except AssertionError, ValueError:
raise ValueError(f"Invalid binary operator '{self.expression[1]}'")

# Do lazy evaluation
Expand Down
4 changes: 2 additions & 2 deletions src/ast_/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ def token(self):

class NodeVisitor(GenericNodeVisitor[Ast]):
def _visit(self, node: Ast):
meth: Callable[[Ast], Generator[Ast | Any, Any, None]] = getattr(
meth: Callable[[Ast], Generator[Ast | Any, Any]] = getattr(
self,
f"visit_{node.token}",
self.generic_visit,
)
return meth(node)

def generic_visit(self, node: Ast) -> Generator[Ast | Any, Any, None]:
def generic_visit(self, node: Ast) -> Generator[Ast | Any, Any]:
for i, child in enumerate(node.children):
node.children[i] = yield self.visit(child)

Expand Down
20 changes: 9 additions & 11 deletions src/ast_/visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,19 @@
from abc import abstractmethod
from collections.abc import Generator
from types import GeneratorType
from typing import Final, Generic, NamedTuple, TypeVar
from typing import Final, NamedTuple

__all__: Final[tuple[str, ...]] = ("GenericNodeVisitor",)

_T = TypeVar("_T")

class ToVisit[T](NamedTuple):
obj: T

class ToVisit(NamedTuple, Generic[_T]):
obj: _T


class GenericNodeVisitor(Generic[_T]):
def visit(self, node: _T | None) -> _T | Generator[_T | None, None, None] | None:
stack: list[_T | GeneratorType] = [ToVisit[_T](node) if node is not None else None]
last_result: _T | None = None
class GenericNodeVisitor[T]:
def visit(self, node: T | None) -> T | Generator[T | None] | None:
stack: list[T | GeneratorType] = [ToVisit[T](node) if node is not None else None]
last_result: T | None = None

while stack:
try:
Expand All @@ -36,7 +34,7 @@ def visit(self, node: _T | None) -> _T | Generator[_T | None, None, None] | None
return last_result

@abstractmethod
def _visit(self, node: _T): ...
def _visit(self, node: T): ...

@abstractmethod
def generic_visit(self, node: _T) -> Generator[_T | None, None, None]: ...
def generic_visit(self, node: T) -> Generator[T | None]: ...
2 changes: 1 addition & 1 deletion src/zxbpp/prepro/id_.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def __str__(self):
return self.name

@staticmethod
def __dumptable(table: "prepro.DefinesTable") -> None:
def __dumptable(table: prepro.DefinesTable) -> None:
"""Dumps table on screen for debugging purposes"""
for k, v in table.table.items():
sys.stdout.write(f"{k}\t<--- {v} {type(v)}")
Expand Down
7 changes: 3 additions & 4 deletions src/zxbpp/prepro/macrocall.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import copy
import re
from typing import Union

from src.api.debug import __DEBUG__
from src.zxbpp import prepro
Expand All @@ -26,7 +25,7 @@ class MacroCall:

__slots__ = "callargs", "fname", "id_", "lineno", "table"

def __init__(self, fname: str, lineno: int, table: "prepro.DefinesTable", id_: Union["MacroCall", str], args=None):
def __init__(self, fname: str, lineno: int, table: prepro.DefinesTable, id_: MacroCall | str, args=None):
"""Initializes the object with the ID table, the ID name and
optionally, the passed args.
"""
Expand All @@ -44,7 +43,7 @@ def eval(arg) -> str:
"""
return str(arg()) # Evaluate the arg (could be a macrocall)

def __call__(self, symbolTable: "prepro.DefinesTable" = None) -> str:
def __call__(self, symbolTable: prepro.DefinesTable | None = None) -> str:
"""Execute the macro call using LAZY evaluation"""
if isinstance(self.id_, MacroCall):
self.id_ = self.id_()
Expand Down Expand Up @@ -102,7 +101,7 @@ def __call__(self, symbolTable: "prepro.DefinesTable" = None) -> str:
tmp = id_(table, self)
return tmp

def is_defined(self, symbolTable: "prepro.DefinesTable" = None) -> bool:
def is_defined(self, symbolTable: prepro.DefinesTable | None = None) -> bool:
"""True if this macro has been defined"""
if symbolTable is None:
symbolTable = self.table
Expand Down
4 changes: 2 additions & 2 deletions src/zxbpp/prepro/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def __init__(self, fname: str, lineno: int, table: DefinesTable, left: MacroCall
self.left = left
self.right = right

def __call__(self, symbolTable: DefinesTable = None) -> str:
def __call__(self, symbolTable: DefinesTable | None = None) -> str:
return self.left(symbolTable).rstrip() + self.right(symbolTable).lstrip()


Expand All @@ -41,5 +41,5 @@ def stringize(s: str) -> str:
s = s.replace('"', '""')
return f'"{s}"'

def __call__(self, symbolTable: DefinesTable = None) -> str:
def __call__(self, symbolTable: DefinesTable | None = None) -> str:
return self.stringize(self.macro_call(symbolTable))
6 changes: 3 additions & 3 deletions tests/functional/cmdline/test_cmdline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
>>> os.environ['COLUMNS'] = '80'

>>> process_file('arch/zx48k/arrbase1.bas', ['-q', '-S', '-O --mmap arrbase1.map'])
usage: zxbc.py [-h] [-d] [-O OPTIMIZE] [-o OUTPUT_FILE]
[-T | -t | -A | -E | --parse-only | -f {asm,bin,ir,sna,tap,tzx,z80}]
[-B] [-a] [-S ORG] [-e STDERR] [--array-base ARRAY_BASE]
usage: zxbc.py [-h] [-d] [-O OPTIMIZE] [-o OUTPUT_FILE] [-T | -t | -A | -E |
--parse-only | -f {asm,bin,ir,sna,tap,tzx,z80}] [-B] [-a]
[-S ORG] [-e STDERR] [--array-base ARRAY_BASE]
[--string-base STRING_BASE] [-Z] [-H HEAP_SIZE]
[--heap-address HEAP_ADDRESS] [--debug-memory] [--debug-array]
[--strict-bool] [--enable-break] [--explicit] [-D DEFINES]
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def __exit__(self, type_, value, traceback):
if self.error_level or not self.keep_file: # command failure or remove file?
try:
os.unlink(self.fname)
except (OSError, FileNotFoundError):
except OSError, FileNotFoundError:
pass # Ok. It might be that it wasn't created


Expand Down
Loading