Skip to content

Commit 3310645

Browse files
authored
Merge pull request #1055 from boriel-basic/refact/typing
Refact/typing
2 parents a7aef39 + 6bf78fd commit 3310645

36 files changed

+703
-457
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ shell = "pytest tests --no-cov"
7575
[[tool.poe.tasks.format]]
7676
help = "Formats code"
7777
shell = """
78+
ruff check --fix
7879
ruff check --select I --fix .
7980
ruff format .
8081
"""

src/api/check.py

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# See the file CONTRIBUTORS.md for copyright details.
55
# See https://www.gnu.org/licenses/agpl-3.0.html for details.
66
# --------------------------------------------------------------------
7+
from typing import Any
78

89
from src.api import config, errmsg, global_
910
from src.api.constants import CLASS, SCOPE
@@ -82,13 +83,13 @@ def check_is_callable(lineno: int, id_: str) -> bool:
8283

8384

8485
def check_type_is_explicit(lineno: int, id_: str, type_):
85-
assert isinstance(type_, symbols.TYPE)
86+
assert isinstance(type_, symbols.TYPEREF)
8687
if type_.implicit:
8788
if config.OPTIONS.strict:
8889
errmsg.syntax_error_undeclared_type(lineno, id_)
8990

9091

91-
def check_call_arguments(lineno: int, id_: str, args, filename: str):
92+
def check_call_arguments(lineno: int, id_: str, args: symbols.ARGLIST, filename: str) -> bool:
9293
"""Check arguments against function signature.
9394
9495
Checks every argument in a function call against a function.
@@ -128,8 +129,10 @@ def check_call_arguments(lineno: int, id_: str, args, filename: str):
128129
for param in entry.ref.params:
129130
if param.name in named_args:
130131
continue
132+
131133
if param.default_value is None:
132134
break
135+
133136
arg = symbols.ARGUMENT(param.default_value, lineno=lineno, byref=False, name=param.name)
134137
symbols.ARGLIST.make_node(args, arg)
135138
named_args[arg.name] = arg
@@ -151,7 +154,7 @@ def check_call_arguments(lineno: int, id_: str, args, filename: str):
151154

152155
if arg.class_ in (CLASS.var, CLASS.array) and param.class_ != arg.class_:
153156
errmsg.error(lineno, f"Invalid argument '{arg.value}'", fname=arg.filename)
154-
return None
157+
return False
155158

156159
if not arg.typecast(param.type_):
157160
return False
@@ -252,57 +255,62 @@ def check_and_make_label(lbl: str | int | float, lineno):
252255
# ----------------------------------------------------------------------
253256
# Function for checking some arguments
254257
# ----------------------------------------------------------------------
255-
def is_null(*symbols_):
258+
def is_null(*symbols_: Any) -> bool:
256259
"""True if no nodes or all the given nodes are either
257260
None, NOP or empty blocks. For blocks this applies recursively
258261
"""
259262
for sym in symbols_:
260263
if sym is None:
261264
continue
265+
262266
if not isinstance(sym, symbols.SYMBOL):
263267
return False
268+
264269
if sym.token == "NOP":
265270
continue
271+
266272
if sym.token == "BLOCK":
267273
if not is_null(*sym.children):
268274
return False
269275
continue
276+
270277
return False
278+
271279
return True
272280

273281

274-
def is_SYMBOL(token: str, *symbols_: symbols.SYMBOL):
282+
def is_SYMBOL(token: str, *symbols_: symbols.SYMBOL) -> bool:
275283
"""Returns True if ALL the given argument are AST nodes
276284
of the given token (e.g. 'BINARY')
277285
"""
278286
assert all(isinstance(x, symbols.SYMBOL) for x in symbols_)
279287
return all(sym.token == token for sym in symbols_)
280288

281289

282-
def is_LABEL(*p):
290+
def is_LABEL(*p: symbols.SYMBOL) -> bool:
283291
return is_SYMBOL("LABEL", *p)
284292

285293

286-
def is_string(*p):
294+
def is_string(*p: symbols.SYMBOL) -> bool:
287295
"""Returns True if ALL the arguments are AST nodes
288296
containing STRING or string CONSTANTS
289297
"""
290298
return all(is_SYMBOL("STRING", x) or is_const(x) and is_type(Type.string, x) for x in p)
291299

292300

293-
def is_const(*p):
301+
def is_const(*p: symbols.SYMBOL) -> bool:
294302
"""A constant in the program, like CONST a = 5"""
295303
return is_SYMBOL("CONST", *p)
296304

297305

298-
def is_CONST(*p):
306+
def is_CONST(*p: symbols.SYMBOL) -> bool:
299307
"""Not to be confused with the above.
300308
Check it's a CONSTant EXPRession
301309
"""
302310
return is_SYMBOL("CONSTEXPR", *p)
303311

304312

305-
def is_static(*p):
313+
def is_static(*p: symbols.SYMBOL) -> bool:
306314
"""A static value (does not change at runtime)
307315
which is known at compile time
308316
"""
@@ -313,7 +321,12 @@ def is_number(*p):
313321
"""Returns True if ALL the arguments are AST nodes
314322
containing NUMBER or numeric CONSTANTS
315323
"""
316-
return all(isinstance(i, Symbol) and i.token in ("NUMBER", "CONST") and Type.is_numeric(i.type_) for i in p)
324+
return all(
325+
isinstance(i, Symbol)
326+
and i.token in ("NUMBER", "CONST")
327+
and Type.is_numeric(i.type_.type_ if isinstance(i.type_, symbols.TYPEREF) else i.type_)
328+
for i in p
329+
)
317330

318331

319332
def is_static_str(*p):
@@ -330,8 +343,8 @@ def is_var(*p):
330343
def is_unsigned(*p):
331344
"""Returns false unless all types in p are unsigned"""
332345
try:
333-
return all(i.type_.is_basic and Type.is_unsigned(i.type_) for i in p)
334-
except Exception:
346+
return all(i.type_.final.is_basic and Type.is_unsigned(i.type_.final) for i in p)
347+
except (Exception,):
335348
pass
336349

337350
return False
@@ -341,7 +354,7 @@ def is_signed(*p):
341354
"""Returns false unless all types in p are signed"""
342355
try:
343356
return all(i.type_.is_basic and Type.is_signed(i.type_) for i in p)
344-
except Exception:
357+
except (Exception,):
345358
pass
346359

347360
return False
@@ -350,8 +363,8 @@ def is_signed(*p):
350363
def is_numeric(*p):
351364
"""Returns false unless all elements in p are of numerical type"""
352365
try:
353-
return all(i.type_.is_basic and Type.is_numeric(i.type_) for i in p)
354-
except Exception:
366+
return all(i.type_.final.is_basic and Type.is_numeric(i.type_.final) for i in p)
367+
except (Exception,):
355368
pass
356369

357370
return False
@@ -361,7 +374,7 @@ def is_type(type_, *p):
361374
"""True if all args have the same type"""
362375
try:
363376
return all(i.type_ == type_ for i in p)
364-
except Exception:
377+
except (Exception,):
365378
pass
366379

367380
return False
@@ -373,7 +386,7 @@ def is_dynamic(*p): # TODO: Explain this better
373386
"""
374387
try:
375388
return not any(i.scope == SCOPE.global_ and i.is_basic and i.type_ != Type.string for i in p)
376-
except Exception:
389+
except (Exception,):
377390
pass
378391

379392
return False
@@ -384,11 +397,22 @@ def is_callable(*p):
384397
return all(x.token == "FUNCTION" for x in p)
385398

386399

387-
def is_block_accessed(block):
388-
"""Returns True if a block is "accessed". A block of code is accessed if
389-
it has a LABEL and it is used in a GOTO, GO SUB or @address access
390-
:param block: A block of code (AST node)
391-
:return: True / False depending if it has labels accessed or not
400+
def is_block_accessed(block: Symbol):
401+
"""
402+
Checks if a code block or any of its nested children has been accessed.
403+
404+
This function evaluates whether a specific block has been accessed. If the
405+
block is not directly accessed, it recursively checks its child blocks.
406+
407+
Args:
408+
block: The code block to be evaluated. The block is assumed to have
409+
attributes `accessed` (a boolean indicating if the block has
410+
been accessed) and `children` (an iterable of nested child
411+
blocks).
412+
413+
Returns:
414+
bool: True if the block or at least one of its nested child blocks has
415+
been accessed; False otherwise.
392416
"""
393417
if is_LABEL(block) and block.accessed:
394418
return True
@@ -401,17 +425,17 @@ def is_temporary_value(node) -> bool:
401425
return node.token not in ("STRING", "VAR") and node.t[0] not in ("_", "#")
402426

403427

404-
def common_type(a: symbols.TYPE | Type | None, b: symbols.TYPE | Type | None) -> symbols.TYPE | Type | None:
428+
def common_type(a: symbols.TYPING, b: symbols.TYPING) -> symbols.TYPE | None:
405429
"""Returns a type which is common for both a and b types.
406430
Returns None if no common types allowed.
407431
"""
408-
if a is None or b is None:
409-
return None
432+
assert isinstance(a, symbols.TYPING)
433+
assert isinstance(b, symbols.TYPING)
410434

411-
if not isinstance(a, symbols.TYPE):
435+
if isinstance(a, symbols.TYPEREF):
412436
a = a.type_
413437

414-
if not isinstance(b, symbols.TYPE):
438+
if isinstance(b, symbols.TYPEREF):
415439
b = b.type_
416440

417441
if a == b: # Both types are the same?
@@ -449,7 +473,7 @@ def common_type(a: symbols.TYPE | Type | None, b: symbols.TYPE | Type | None) ->
449473
return result
450474

451475

452-
def is_ender(node) -> bool:
476+
def is_ender(node: symbols.SYMBOL) -> bool:
453477
"""Returns whether this node ends a block, that is, the following instruction won't be
454478
executed after this one
455479
"""
@@ -468,7 +492,7 @@ def is_ender(node) -> bool:
468492
}
469493

470494

471-
def check_class(node, class_: CLASS, lineno: int) -> bool:
495+
def check_class(node: symbols.ID, class_: CLASS, lineno: int) -> bool:
472496
"""Returns whether the given node has CLASS.unknown or the given class_.
473497
It False, it will emit a syntax error
474498
"""

0 commit comments

Comments
 (0)