Skip to content

Commit e1edaa1

Browse files
authored
Merge pull request #422 from boriel/feature/code_refact
Feature/code refact
2 parents efbb565 + 37b28ed commit e1edaa1

18 files changed

Lines changed: 473 additions & 295 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
*.pyc
44
.cache/
55
.pytest_cache/
6+
.mypy_cache/
7+
.pytype/
68
.idea/
79
.python-version
810
.tox/

src/api/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@
88
# This program is Free Software and is released under the terms of
99
# the GNU General License
1010
# ----------------------------------------------------------------------
11+
12+
from src.api import debug # noqa
13+
from src.api import errors # noqa
14+
from src.api import errmsg # noqa

src/api/utils.py

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,16 @@
55
import errno
66
import shelve
77
import signal
8+
89
from functools import wraps
9-
from typing import NamedTuple, List, Any
10+
11+
from typing import NamedTuple
12+
from typing import List
13+
from typing import Any
14+
from typing import Optional
15+
from typing import Callable
16+
from typing import IO
17+
from typing import Iterable
1018

1119
from . import constants
1220
from . import global_
@@ -35,7 +43,7 @@ class DataRef(NamedTuple):
3543
datas: List[Any]
3644

3745

38-
def read_txt_file(fname):
46+
def read_txt_file(fname: str) -> str:
3947
"""Reads a txt file, regardless of its encoding
4048
"""
4149
encodings = ['utf-8-sig', 'cp1252']
@@ -54,90 +62,104 @@ def read_txt_file(fname):
5462
return ''
5563

5664

57-
def open_file(fname, mode='rb', encoding='utf-8'):
65+
def open_file(fname: str, mode: str = 'rb', encoding: str = 'utf-8') -> IO[Any]:
5866
""" An open() wrapper for PY2 and PY3 which allows encoding
5967
:param fname: file name (string)
6068
:param mode: file mode (string) optional
6169
:param encoding: optional encoding (string). Ignored in python2 or if not in text mode
6270
:return: an open file handle
6371
"""
64-
if 't' not in mode:
65-
kwargs = {}
66-
else:
67-
kwargs = {'encoding': encoding}
72+
if 't' not in mode or not encoding:
73+
return open(fname, mode)
6874

69-
return open(fname, mode, **kwargs)
75+
return open(fname, mode, encoding=encoding)
7076

7177

72-
def sanitize_filename(fname):
78+
def sanitize_filename(fname: str) -> str:
7379
""" Given a file name (string) returns it with back-slashes reversed.
7480
This is to make all BASIC programs compatible in all OSes
7581
"""
7682
return fname.replace('\\', '/')
7783

7884

79-
def current_data_label():
85+
def current_data_label() -> str:
8086
""" Returns a data label to which all labels must point to, until
8187
a new DATA line is declared
8288
"""
8389
return '__DATA__{0}'.format(len(global_.DATAS))
8490

8591

86-
def flatten_list(x):
92+
def flatten_list(x: Iterable[Any], iterables=(list, )) -> List[Any]:
93+
""" Flattens a nested iterable and returns it as a List.
94+
Nested iterables will be flattened recursively (default only nested lists)
95+
"""
8796
result = []
8897

89-
for l in x:
90-
if not isinstance(l, list):
91-
result.append(l)
98+
for elem in x:
99+
if not isinstance(elem, iterables):
100+
result.append(elem)
92101
else:
93-
result.extend(flatten_list(l))
102+
result.extend(flatten_list(elem))
94103

95104
return result
96105

97106

98-
def parse_int(str_num):
107+
def parse_int(num: Optional[str]) -> Optional[int]:
99108
""" Given an integer number, return its value,
100109
or None if it could not be parsed.
101110
Allowed formats: DECIMAL, HEXA (0xnnn, $nnnn or nnnnh)
102-
:param str_num: (string) the number to be parsed
103-
:return: an integer number or None if it could not be parsedd
111+
An hexadecimal number is ambiguous if it starts with a letter (i.e. A0h can be a label),
112+
and won't be parsed. Such numbers must be prefixed with 0 digit (i.e. 0A0h)
113+
:param num: (string) the number to be parsed
114+
:return: an integer number or None if it could not be parsed
104115
"""
105-
str_num = (str_num or "").strip().upper()
106-
if not str_num:
116+
num = (num or "").strip().upper()
117+
if not num:
107118
return None
108119

109120
base = 10
110-
if str_num.startswith('0X'):
121+
if num[:2] == '0X':
111122
base = 16
112-
str_num = str_num[2:]
113-
if str_num.endswith('H'):
123+
elif num[-1] == 'H':
124+
if num[0] not in '0123456789':
125+
return None
114126
base = 16
115-
str_num = str_num[:-1]
116-
if str_num.startswith('$'):
127+
num = num[:-1]
128+
elif num[0] == '$':
117129
base = 16
118-
str_num = str_num[1:]
130+
num = num[1:]
131+
elif num[0] == '%':
132+
base = 2
133+
num = num[1:]
134+
elif num[-1] == 'B':
135+
if num[0] not in '01':
136+
return None
137+
base = 2
138+
num = num[:-1]
119139

120140
try:
121-
return int(str_num, base)
141+
return int(num, base)
122142
except ValueError:
123-
return None
143+
pass
144+
145+
return None
124146

125147

126-
def load_object(key):
148+
def load_object(key: str) -> Any:
127149
return SHELVE[key] if key in SHELVE else None
128150

129151

130-
def save_object(key, obj):
152+
def save_object(key: str, obj: Any) -> Any:
131153
SHELVE[key] = obj
132154
SHELVE.sync()
133155
return obj
134156

135157

136-
def get_or_create(key, fn):
158+
def get_or_create(key: str, fn: Callable[[], Any]) -> Any:
137159
return load_object(key) or save_object(key, fn())
138160

139161

140-
def get_final_value(symbol: symbols.SYMBOL):
162+
def get_final_value(symbol: symbols.SYMBOL) -> Any:
141163
assert check.is_static(symbol)
142164
result = symbol
143165
while hasattr(result, 'value'):

src/arch/zx48k/backend/__init__.py

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2325,15 +2325,19 @@ def emit(mem, optimize=True):
23252325
# Optimization patterns: at this point no more than -O2
23262326
patterns = [x for x in engine.PATTERNS if x.level <= min(OPTIONS.optimization, 2)]
23272327

2328-
def output_join(output, new_chunk, optimize=True):
2328+
def output_join(output: List[str], new_chunk: List[str], optimize: bool = True):
23292329
""" Extends output instruction list
23302330
performing a little peep-hole optimization (O1)
23312331
"""
23322332
base_index = len(output)
23332333
output.extend(new_chunk)
2334+
2335+
if not optimize:
2336+
return
2337+
23342338
i = max(0, base_index - engine.MAXLEN)
23352339

2336-
while optimize and i < len(output):
2340+
while i < len(output):
23372341
if not engine.apply_match(output, patterns, index=i): # Nothing changed
23382342
i += 1
23392343
else:
@@ -2347,32 +2351,31 @@ def output_join(output, new_chunk, optimize=True):
23472351

23482352
if optimize and OPTIONS.optimization > 1:
23492353
# Remove unused labels
2350-
while True:
2351-
to_remove = []
23522354

2353-
for i, ins in enumerate(output):
2354-
ins = ins[:-1]
2355-
if ins not in TMP_LABELS:
2355+
label_used = {}
2356+
label_to_delete = {}
2357+
2358+
for i, ins in enumerate(output):
2359+
try_label = ins[:-1]
2360+
if try_label in TMP_LABELS:
2361+
if label_used.get(try_label):
2362+
label_to_delete.pop(try_label, None)
23562363
continue
23572364

2358-
for j, ins2 in enumerate(output):
2359-
if j == i:
2360-
continue
2361-
if ins in Asm.opers(ins2):
2362-
break
2363-
else:
2364-
to_remove.append(i)
2365+
label_to_delete[try_label] = i
2366+
continue
23652367

2366-
if not to_remove:
2367-
break
2368+
for op in Asm.opers(ins):
2369+
if op in TMP_LABELS:
2370+
label_used[op] = True
2371+
label_to_delete.pop(op, None)
23682372

2369-
to_remove.reverse()
2370-
for i in to_remove:
2371-
output.pop(i)
2373+
for i in sorted(label_to_delete.values(), reverse=True):
2374+
output.pop(i)
23722375

2373-
tmp = output
2374-
output = []
2375-
output_join(output, tmp)
2376+
tmp = output
2377+
output = []
2378+
output_join(output, tmp)
23762379

23772380
for i in sorted(REQUIRES):
23782381
output.append('#include once <%s>' % i)

0 commit comments

Comments
 (0)