Skip to content

Commit 47e5ff5

Browse files
authored
Merge pull request #677 from boriel/refact/visitors
Refact/visitors
2 parents ec25a6a + 909d6f6 commit 47e5ff5

18 files changed

+489
-457
lines changed

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@ build-backend = "poetry.masonry.api"
5050

5151
[tool.black]
5252
line-length = 120
53-
target-version = ['py38']
53+
target-version = ['py310']
5454

5555
[tool.poe.tasks]
5656
[[tool.poe.tasks.lint]]
5757
help = "Check code style and typing..."
5858
shell = """
5959
flake8 src tests &&
60-
isort --check stc tests
60+
isort --check stc tests &&
6161
black --check src tests
6262
"""
6363

src/arch/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
def set_target_arch(target_arch: str):
2525
global target
26-
assert target_arch in AVAILABLE_ARCHITECTURES
26+
assert target_arch in AVAILABLE_ARCHITECTURES, f"Invalid target architecture '{target_arch}'"
2727
target = importlib.import_module(f".{target_arch}", "src.arch")
2828
__DEBUG__(f"Target architecture set to {target_arch}")
2929

src/arch/z80/__init__.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
import src.api.global_
66
from src.api.constants import TYPE
77
from src.arch.z80 import beep
8-
from src.arch.z80.translator import * # noqa
98

10-
__all__ = [
11-
"beep",
12-
]
9+
from .visitor.function_translator import FunctionTranslator
10+
from .visitor.translator import Translator
11+
from .visitor.var_translator import VarTranslator
12+
13+
__all__ = "beep", "FunctionTranslator", "Translator", "VarTranslator"
1314

1415

1516
# -----------------------------------------

src/arch/z80/backend/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,6 @@
3131
"TMP_STORAGES",
3232
"Backend",
3333
"engine",
34+
"Quad",
35+
"ICInfo",
3436
)

src/arch/z80/backend/icinstruction.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
__all__ = ("ICInstruction",)
77

8+
89
# HINT: Do not use Enums here. They cannot be subclassed
910
class ICInstruction:
1011
ADDU8 = "addu8"

src/arch/z80/backend/main.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import re
44
from collections import defaultdict
5-
from typing import Iterable
65

76
from src.api.config import OPTIONS
87
from src.api.options import Action

src/arch/z80/visitor/__init__.py

Whitespace-only changes.
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
from src.api import global_ as gl
2+
from src.api.constants import SCOPE, TYPE
3+
from src.api.global_ import optemps
4+
from src.arch.z80 import backend
5+
from src.arch.z80.backend.runtime import Labels as RuntimeLabel
6+
from src.arch.z80.visitor.translator_visitor import TranslatorVisitor
7+
from src.symbols.type_ import Type
8+
9+
10+
class BuiltinTranslator(TranslatorVisitor):
11+
"""BUILTIN functions visitor. Eg. LEN(a$) or SIN(x)"""
12+
13+
REQUIRES = backend.REQUIRES
14+
15+
# region STRING Functions
16+
def visit_INKEY(self, node):
17+
self.runtime_call(RuntimeLabel.INKEY, Type.string.size)
18+
19+
def visit_IN(self, node):
20+
self.ic_in(node.children[0].t)
21+
22+
def visit_CODE(self, node):
23+
self.ic_fparam(gl.PTR_TYPE, node.operand.t)
24+
if node.operand.token not in ("STRING", "VAR") and node.operand.t != "_":
25+
self.ic_fparam(TYPE.ubyte, 1) # If the argument is not a variable, it must be freed
26+
else:
27+
self.ic_fparam(TYPE.ubyte, 0)
28+
29+
self.runtime_call(RuntimeLabel.ASC, Type.ubyte.size) # Expect a char code
30+
31+
def visit_CHR(self, node):
32+
self.ic_fparam(gl.STR_INDEX_TYPE, len(node.operand)) # Number of args
33+
self.runtime_call(RuntimeLabel.CHR, node.size)
34+
35+
def visit_STR(self, node):
36+
self.ic_fparam(TYPE.float, node.children[0].t)
37+
self.runtime_call(RuntimeLabel.STR_FAST, node.type_.size)
38+
39+
def visit_LEN(self, node):
40+
self.ic_lenstr(node.t, node.operand.t)
41+
42+
def visit_VAL(self, node):
43+
self.ic_fparam(gl.PTR_TYPE, node.operand.t)
44+
if node.operand.token not in ("STRING", "VAR") and node.operand.t != "_":
45+
self.ic_fparam(TYPE.ubyte, 1) # If the argument is not a variable, it must be freed
46+
else:
47+
self.ic_fparam(TYPE.ubyte, 0)
48+
49+
self.runtime_call(RuntimeLabel.VAL, node.type_.size)
50+
51+
# endregion
52+
53+
def visit_ABS(self, node):
54+
self.ic_abs(node.children[0].type_, node.t, node.children[0].t)
55+
56+
def visit_RND(self, node): # A special "ZEROARY" function with no parameters
57+
self.runtime_call(RuntimeLabel.RND, Type.float_.size)
58+
59+
def visit_PEEK(self, node):
60+
self.ic_load(node.type_, node.t, "*" + str(node.children[0].t))
61+
62+
# region MATH Functions
63+
def visit_SIN(self, node):
64+
self.ic_fparam(node.operand.type_, node.operand.t)
65+
self.runtime_call(RuntimeLabel.SIN, node.size)
66+
67+
def visit_COS(self, node):
68+
self.ic_fparam(node.operand.type_, node.operand.t)
69+
self.runtime_call(RuntimeLabel.COS, node.size)
70+
71+
def visit_TAN(self, node):
72+
self.ic_fparam(node.operand.type_, node.operand.t)
73+
self.runtime_call(RuntimeLabel.TAN, node.size)
74+
75+
def visit_ASN(self, node):
76+
self.ic_fparam(node.operand.type_, node.operand.t)
77+
self.runtime_call(RuntimeLabel.ASN, node.size)
78+
79+
def visit_ACS(self, node):
80+
self.ic_fparam(node.operand.type_, node.operand.t)
81+
self.runtime_call(RuntimeLabel.ACS, node.size)
82+
83+
def visit_ATN(self, node):
84+
self.ic_fparam(node.operand.type_, node.operand.t)
85+
self.runtime_call(RuntimeLabel.ATN, node.size)
86+
87+
def visit_EXP(self, node):
88+
self.ic_fparam(node.operand.type_, node.operand.t)
89+
self.runtime_call(RuntimeLabel.EXP, node.size)
90+
91+
def visit_LN(self, node):
92+
self.ic_fparam(node.operand.type_, node.operand.t)
93+
self.runtime_call(RuntimeLabel.LN, node.size)
94+
95+
def visit_SGN(self, node):
96+
s = self.TSUFFIX(node.operand.type_)
97+
self.ic_fparam(node.operand.type_, node.operand.t)
98+
99+
label = {
100+
"i8": RuntimeLabel.SGNI8,
101+
"u8": RuntimeLabel.SGNU8,
102+
"i16": RuntimeLabel.SGNI16,
103+
"u16": RuntimeLabel.SGNU16,
104+
"i32": RuntimeLabel.SGNI32,
105+
"u32": RuntimeLabel.SGNU32,
106+
"f16": RuntimeLabel.SGNF16,
107+
"f": RuntimeLabel.SGNF,
108+
}[s]
109+
self.runtime_call(label, node.size)
110+
111+
def visit_SQR(self, node):
112+
self.ic_fparam(node.operand.type_, node.operand.t)
113+
self.runtime_call(RuntimeLabel.SQR, node.size)
114+
115+
# endregion
116+
117+
def visit_LBOUND(self, node):
118+
yield node.operands[1]
119+
self.ic_param(gl.BOUND_TYPE, node.operands[1].t)
120+
entry = node.operands[0]
121+
if entry.scope == SCOPE.global_:
122+
self.ic_fparam(gl.PTR_TYPE, "#{}".format(entry.mangled))
123+
elif entry.scope == SCOPE.parameter:
124+
self.ic_pload(gl.PTR_TYPE, entry.t, entry.offset)
125+
t1 = optemps.new_t()
126+
self.ic_fparam(gl.PTR_TYPE, t1)
127+
elif entry.scope == SCOPE.local:
128+
self.ic_paddr(-entry.offset, entry.t)
129+
t1 = optemps.new_t()
130+
self.ic_fparam(gl.PTR_TYPE, t1)
131+
self.runtime_call(RuntimeLabel.LBOUND, self.TYPE(gl.BOUND_TYPE).size)
132+
133+
def visit_UBOUND(self, node):
134+
yield node.operands[1]
135+
self.ic_param(gl.BOUND_TYPE, node.operands[1].t)
136+
entry = node.operands[0]
137+
if entry.scope == SCOPE.global_:
138+
self.ic_fparam(gl.PTR_TYPE, "#{}".format(entry.mangled))
139+
elif entry.scope == SCOPE.parameter:
140+
self.ic_pload(gl.PTR_TYPE, entry.t, entry.offset)
141+
t1 = optemps.new_t()
142+
self.ic_fparam(gl.PTR_TYPE, t1)
143+
elif entry.scope == SCOPE.local:
144+
self.ic_paddr(-entry.offset, entry.t)
145+
t1 = optemps.new_t()
146+
self.ic_fparam(gl.PTR_TYPE, t1)
147+
self.runtime_call(RuntimeLabel.UBOUND, self.TYPE(gl.BOUND_TYPE).size)
148+
149+
def visit_USR_STR(self, node):
150+
# USR ADDR
151+
self.ic_fparam(TYPE.string, node.children[0].t)
152+
self.runtime_call(RuntimeLabel.USR_STR, node.type_.size)
153+
154+
def visit_USR(self, node):
155+
"""Machine code call from basic"""
156+
self.ic_fparam(gl.PTR_TYPE, node.children[0].t)
157+
self.runtime_call(RuntimeLabel.USR, node.type_.size)
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import src.api
2+
from src.api import global_ as gl
3+
from src.api.config import OPTIONS
4+
from src.api.constants import CLASS, CONVENTION, SCOPE, TYPE
5+
from src.api.debug import __DEBUG__
6+
from src.api.global_ import optemps
7+
from src.arch.z80 import backend
8+
from src.arch.z80.backend.runtime import Labels as RuntimeLabel
9+
from src.arch.z80.visitor.translator import LabelledData
10+
from src.arch.zx48k.backend import Backend
11+
from src.symbols import sym as symbols
12+
13+
from .translator import Translator
14+
15+
16+
class FunctionTranslator(Translator):
17+
REQUIRES = backend.REQUIRES
18+
19+
def __init__(self, backend: Backend, function_list: list[symbols.ID]):
20+
if function_list is None:
21+
function_list = []
22+
super().__init__(backend)
23+
24+
assert isinstance(function_list, list)
25+
assert all(x.token == "FUNCTION" for x in function_list)
26+
self.functions = function_list
27+
28+
def _local_array_load(self, scope, local_var):
29+
t2 = optemps.new_t()
30+
if scope == SCOPE.parameter:
31+
self.ic_pload(gl.PTR_TYPE, t2, "%i" % (local_var.offset - self.TYPE(gl.PTR_TYPE).size))
32+
elif scope == SCOPE.local:
33+
self.ic_pload(gl.PTR_TYPE, t2, "%i" % -(local_var.offset - self.TYPE(gl.PTR_TYPE).size))
34+
self.ic_fparam(gl.PTR_TYPE, t2)
35+
36+
def start(self):
37+
while self.functions:
38+
f = self.functions.pop(0)
39+
__DEBUG__("Translating function " + f.__repr__())
40+
self.visit(f)
41+
42+
def visit_FUNCTION(self, node):
43+
bound_tables = []
44+
45+
self.ic_label(node.mangled)
46+
if node.convention == CONVENTION.fastcall:
47+
self.ic_enter("__fastcall__")
48+
else:
49+
self.ic_enter(node.locals_size)
50+
51+
for local_var in node.local_symbol_table.values():
52+
if not local_var.accessed: # HINT: This should never happen as values() is already filtered
53+
src.api.errmsg.warning_not_used(local_var.lineno, local_var.name)
54+
# HINT: Cannot optimize local variables now, since the offsets are already calculated
55+
# if self.O_LEVEL > 1:
56+
# return
57+
58+
if local_var.class_ == CLASS.array and local_var.scope == SCOPE.local:
59+
bound_ptrs = [] # Bound tables pointers (empty if not used)
60+
lbound_label = local_var.mangled + ".__LBOUND__"
61+
ubound_label = local_var.mangled + ".__UBOUND__"
62+
63+
if local_var.lbound_used or local_var.ubound_used:
64+
bound_ptrs = ["0", "0"] # NULL by default
65+
if local_var.lbound_used:
66+
bound_ptrs[0] = lbound_label
67+
if local_var.ubound_used:
68+
bound_ptrs[1] = ubound_label
69+
70+
if bound_ptrs:
71+
OPTIONS["__DEFINES"].value["__ZXB_USE_LOCAL_ARRAY_WITH_BOUNDS__"] = ""
72+
73+
if local_var.lbound_used:
74+
l = ["%04X" % bound.lower for bound in local_var.bounds]
75+
bound_tables.append(LabelledData(lbound_label, l))
76+
77+
if local_var.ubound_used:
78+
l = ["%04X" % bound.upper for bound in local_var.bounds]
79+
bound_tables.append(LabelledData(ubound_label, l))
80+
81+
l = [len(local_var.bounds) - 1] + [x.count for x in local_var.bounds[1:]] # TODO Check this
82+
q = []
83+
for x in l:
84+
q.append("%02X" % (x & 0xFF))
85+
q.append("%02X" % ((x & 0xFF) >> 8))
86+
87+
q.append("%02X" % local_var.type_.size)
88+
r = []
89+
if local_var.default_value is not None:
90+
r.extend(self.array_default_value(local_var.type_, local_var.default_value))
91+
self.ic_larrd(local_var.offset, q, local_var.size, r, bound_ptrs) # Initializes array bounds
92+
elif local_var.class_ == CLASS.const or local_var.scope == SCOPE.parameter:
93+
continue
94+
else: # Local vars always defaults to 0, so if 0 we do nothing
95+
if (
96+
local_var.token != "FUNCTION"
97+
and local_var.default_value is not None
98+
and local_var.default_value != 0
99+
):
100+
if (
101+
isinstance(local_var.default_value, symbols.CONSTEXPR)
102+
and local_var.default_value.token == "CONSTEXPR"
103+
):
104+
self.ic_lvarx(local_var.type_, local_var.offset, [self.traverse_const(local_var.default_value)])
105+
else:
106+
q = self.default_value(local_var.type_, local_var.default_value)
107+
self.ic_lvard(local_var.offset, q)
108+
109+
for i in node.ref.body:
110+
yield i
111+
112+
self.ic_label("%s__leave" % node.mangled)
113+
114+
# Now free any local string from memory.
115+
preserve_hl = False
116+
if node.convention == CONVENTION.stdcall:
117+
for local_var in node.local_symbol_table.values():
118+
scope = local_var.scope
119+
if local_var.type_ == self.TYPE(TYPE.string):
120+
if local_var.class_ == CLASS.const:
121+
continue
122+
# Only if it's string we free it
123+
if local_var.class_ != CLASS.array: # Ok just free it
124+
if scope == SCOPE.local or (scope == SCOPE.parameter and not local_var.byref):
125+
if not preserve_hl:
126+
preserve_hl = True
127+
self.ic_exchg()
128+
129+
offset = -local_var.offset if scope == SCOPE.local else local_var.offset
130+
self.ic_fpload(TYPE.string, local_var.t, offset)
131+
self.runtime_call(RuntimeLabel.MEM_FREE, 0)
132+
else: # This is an array of strings, we must free it unless it's a by_ref array
133+
if scope == SCOPE.local or (scope == SCOPE.parameter and not local_var.byref):
134+
if not preserve_hl:
135+
preserve_hl = True
136+
self.ic_exchg()
137+
138+
self.ic_param(gl.BOUND_TYPE, local_var.count)
139+
self._local_array_load(scope, local_var)
140+
self.runtime_call(RuntimeLabel.ARRAYSTR_FREE_MEM, 0)
141+
142+
if (
143+
local_var.class_ == CLASS.array
144+
and local_var.type_ != self.TYPE(TYPE.string)
145+
and (scope == SCOPE.local or (scope == SCOPE.parameter and not local_var.byref))
146+
):
147+
if not preserve_hl:
148+
preserve_hl = True
149+
self.ic_exchg()
150+
151+
self._local_array_load(scope, local_var)
152+
self.runtime_call(RuntimeLabel.MEM_FREE, 0)
153+
154+
if preserve_hl:
155+
self.ic_exchg()
156+
157+
if node.convention == CONVENTION.fastcall:
158+
self.ic_leave(CONVENTION.to_string(node.convention))
159+
else:
160+
self.ic_leave(node.ref.params.size)
161+
162+
for bound_table in bound_tables:
163+
self.ic_vard(bound_table.label, bound_table.data)
164+
165+
def visit_FUNCDECL(self, node):
166+
"""Nested scope functions"""
167+
self.functions.append(node.entry)

0 commit comments

Comments
 (0)