Skip to content

Commit 8f839ab

Browse files
committed
feature: implement temporary labels in zxbasm
1 parent 3ff4442 commit 8f839ab

7 files changed

Lines changed: 70 additions & 26 deletions

File tree

src/parsetab/tabs.dbm.bak

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
'zxbpp', (0, 76970)
2-
'asmparse', (77312, 271961)
3-
'zxnext_asmparse', (349696, 302573)
4-
'zxbparser', (652288, 703160)
2+
'asmparse', (77312, 272467)
3+
'zxnext_asmparse', (350208, 303081)
4+
'zxbparser', (653312, 703160)

src/parsetab/tabs.dbm.dat

1 KB
Binary file not shown.

src/parsetab/tabs.dbm.dir

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
'zxbpp', (0, 76970)
2-
'asmparse', (77312, 271961)
3-
'zxnext_asmparse', (349696, 302573)
4-
'zxbparser', (652288, 703160)
2+
'asmparse', (77312, 272467)
3+
'zxnext_asmparse', (350208, 303081)
4+
'zxbparser', (653312, 703160)

src/zxbasm/asmlex.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,12 @@ def t_BIN(self, t):
274274
t.type = 'INTEGER'
275275
return t
276276

277+
def t_INITIAL_TMPLABEL(self, t):
278+
r'[0-9]+[FfBb]'
279+
t.type = 'ID'
280+
t.value = t.value.upper()
281+
return t
282+
277283
def t_INITIAL_preproc_INTEGER(self, t):
278284
r'[0-9]+' # an integer decimal number
279285
t.value = int(t.value)

src/zxbasm/asmparse.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,9 @@ def p_asms_asms_asm(p):
143143

144144
def p_asm_label(p):
145145
""" asm : ID
146+
| INTEGER
146147
"""
147-
MEMORY.declare_label(p[1], p.lineno(1))
148+
MEMORY.declare_label(str(p[1]), p.lineno(1))
148149

149150

150151
def p_asm_ld8(p):

src/zxbasm/label.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from src.api.errmsg import error
22
from src.zxbasm import global_ as asm_gl
3-
from src.zxbasm import expr
43

54

65
class Label:
@@ -31,7 +30,7 @@ def defined(self):
3130
"""
3231
return self.value is not None
3332

34-
def define(self, value, lineno, namespace=None):
33+
def define(self, value, lineno: int, namespace=None):
3534
""" Defines label value. It can be anything. Even an AST
3635
"""
3736
if self.defined:
@@ -41,21 +40,22 @@ def define(self, value, lineno, namespace=None):
4140
self.lineno = lineno
4241
self.namespace = asm_gl.NAMESPACE if namespace is None else namespace
4342

44-
def resolve(self, lineno):
45-
""" Evaluates label value. Exits with error (unresolved) if value is none
46-
"""
47-
if not self.defined:
48-
error(lineno, "Undeclared label '%s'" % self.name)
49-
50-
if isinstance(self.value, expr.Expr):
51-
return self.value.eval()
52-
53-
return self.value
54-
5543
@property
5644
def is_temporary(self):
57-
return self._name.isdecimal()
45+
return self._name[0].isdecimal()
46+
47+
@property
48+
def direction(self) -> int:
49+
""" Direction to search for this label (-1, 1)
50+
"""
51+
return 0 if not self.is_temporary else {'B': -1, 'F': 1}.get(self._name[-1], 0)
5852

5953
@property
6054
def name(self):
61-
return self._name
55+
return self._name if not self.is_temporary else self._name.strip('BF')
56+
57+
def __eq__(self, other):
58+
if isinstance(other, Label):
59+
return other.name == self.name and other.namespace == self.namespace
60+
61+
return False

src/zxbasm/memory.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from collections import defaultdict
2+
from bisect import bisect_left, bisect_right
23
from typing import Dict, List, Optional, Tuple
34

45
from src.api import global_ as gl
@@ -16,6 +17,8 @@ class Memory:
1617
""" A class to describe memory
1718
"""
1819
MAX_MEM = 65535 # Max memory limit
20+
_tmp_labels: Dict[int, Dict[str, Label]]
21+
_tmp_labels_lines: List[int]
1922

2023
def __init__(self, org: int = 0):
2124
""" Initializes the origin of code.
@@ -24,10 +27,9 @@ def __init__(self, org: int = 0):
2427
self.memory_bytes: Dict[int, int] = {} # An array (associative) containing memory bytes
2528
self.local_labels: List[Dict[str, Label]] = [{}] # Local labels in the current memory scope
2629
self.global_labels = self.local_labels[0] # Global memory labels
27-
self.tmp_labels: Dict[int, Dict[str, Label]] = defaultdict(dict)
28-
self.tmp_labels_lines: List[int] = []
2930
self.ORG = org # last ORG value set
3031
self.scopes: List[int] = []
32+
self.clear_temporary_labels()
3133

3234
# Origins of code for asm mnemonics.
3335
# This will store corresponding asm instructions
@@ -38,6 +40,7 @@ def enter_proc(self, lineno: int):
3840
"""
3941
self.local_labels.append({}) # Add a new context
4042
self.scopes.append(lineno)
43+
self.clear_temporary_labels()
4144
__DEBUG__('Entering scope level %i at line %i' % (len(self.scopes), lineno))
4245

4346
def set_org(self, value: int, lineno: int):
@@ -46,6 +49,7 @@ def set_org(self, value: int, lineno: int):
4649
if value < 0 or value > self.MAX_MEM:
4750
error(lineno, "Memory ORG out of range [0 .. 65535]. Current value: %i" % value)
4851

52+
self.clear_temporary_labels()
4953
self.index = self.ORG = value
5054

5155
@staticmethod
@@ -57,7 +61,8 @@ def id_name(label: str, namespace: Optional[str] = None) -> Tuple[str, str]:
5761
if namespace is None:
5862
namespace = asm_gl.NAMESPACE
5963

60-
if label.isdecimal(): # temporary labels are just integer numbers
64+
# temporary labels are just integer numbers
65+
if label.isdecimal() or label[-1] in 'BF' and label[:-1].isdecimal():
6166
return label, namespace
6267

6368
if not label.startswith(DOT):
@@ -85,6 +90,7 @@ def __set_byte(self, byte: int, lineno: int):
8590
def exit_proc(self, lineno: int):
8691
""" Exits current procedure. Local labels are transferred to global
8792
scope unless they have been marked as local ones.
93+
Temporary labels are "forgotten", and used ones must be resolved at this point.
8894
8995
Raises an error if no current local context (stack underflow)
9096
"""
@@ -112,12 +118,31 @@ def exit_proc(self, lineno: int):
112118

113119
self.local_labels.pop() # Removes current context
114120
self.scopes.pop()
121+
self.clear_temporary_labels()
115122

116123
def set_memory_slot(self):
117124
if self.org not in self.orgs:
118125
self.orgs[self.org] = [] # Declares an empty memory slot if not already done
119126
self.memory_bytes[self.org] = 0 # Declares an empty memory slot if not already done
120127

128+
def resolve_temporary_label(self, label: Label):
129+
if label.direction == -1:
130+
idx = bisect_right(self._tmp_labels_lines, label.lineno)
131+
for line in self._tmp_labels_lines[:idx][::-1]:
132+
if label == self._tmp_labels[line].get(label.name):
133+
label.value = self._tmp_labels[line][label.name].value
134+
return
135+
elif label.direction == +1:
136+
idx = bisect_left(self._tmp_labels_lines, label.lineno)
137+
for line in self._tmp_labels_lines[idx:]:
138+
if label == self._tmp_labels[line].get(label.name):
139+
label.value = self._tmp_labels[line][label.name].value
140+
return
141+
142+
def clear_temporary_labels(self):
143+
self._tmp_labels_lines = []
144+
self._tmp_labels = defaultdict(dict)
145+
121146
def add_instruction(self, instr: Asm):
122147
""" This will insert an asm instruction at the current memory position
123148
in a t-uple as (mnemonic, params).
@@ -142,8 +167,12 @@ def dump(self):
142167
align = []
143168

144169
for label in self.global_labels.values():
170+
if label.is_temporary:
171+
self.resolve_temporary_label(label)
172+
145173
if not label.defined:
146-
error(label.lineno, "Undefined GLOBAL label '%s'" % label.name)
174+
label_type = 'temporary' if label.is_temporary else 'GLOBAL'
175+
error(label.lineno, f"Undefined {label_type} label '%s'" % label.name)
147176

148177
for i in range(org, max(self.memory_bytes.keys()) + 1):
149178
if gl.has_errors:
@@ -201,6 +230,14 @@ def declare_label(
201230
else:
202231
__DEBUG__(f"Declaring '{ex_label}' in {lineno}")
203232

233+
if label.isdecimal(): # Temporary label?
234+
assert not self._tmp_labels_lines or self._tmp_labels_lines[-1] <= lineno, "Temporary label out of order"
235+
if not self._tmp_labels_lines or self._tmp_labels_lines[-1] != lineno:
236+
self._tmp_labels_lines.append(lineno)
237+
238+
self._tmp_labels[lineno][ex_label] = Label(ex_label, lineno, value, False, namespace, is_address=True)
239+
return
240+
204241
if ex_label in self.local_labels[-1].keys():
205242
self.local_labels[-1][ex_label].define(value, lineno)
206243
self.local_labels[-1][ex_label].is_address = is_address

0 commit comments

Comments
 (0)