Skip to content

Commit e1c0c12

Browse files
committed
Optimize code
* Ensure strings functions always return something * Warns if the user forgets to return something * Removes dead code in the AST (-O2)
1 parent cbaa442 commit e1c0c12

4 files changed

Lines changed: 63 additions & 11 deletions

File tree

src/api/errmsg.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,16 @@ def warning_fastcall_with_N_parameters(lineno: int, kind: str, id_: str, num_par
176176
def warning_func_is_never_called(lineno: int, func_name: str, fname: Optional[str] = None):
177177
warning(lineno, f"Function '{func_name}' is never called and has been ignored", fname=fname)
178178

179+
180+
@register_warning('180')
181+
def warning_unreachable_code(lineno: int, fname: Optional[str] = None):
182+
warning(lineno, "Unreachable code", fname=fname)
183+
184+
185+
@register_warning('190')
186+
def warning_function_should_return_a_value(lineno: int, func_name: str, fname: Optional[str] = None):
187+
warning(lineno, f"Function '{func_name}' should return a value", fname=fname)
188+
179189
# endregion
180190

181191
# region [Syntax Errors]

src/api/optimize.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import types
55

66
from typing import NamedTuple
7+
from typing import Optional
78
from typing import Set
89

910
import src.api.global_ as gl
@@ -156,6 +157,12 @@ def visit_FUNCTION(self, node):
156157
yield node
157158
else:
158159
node.visited = True
160+
if node.body.token == 'BLOCK' and (not node.body or node.body[-1].token != 'RETURN'):
161+
# String functions must *ALWAYS* return a value.
162+
# Put a sentinel ("dummy") return "" sentence that will be removed if other is detected
163+
lineno = node.lineno if not node.body else node.body[-1].lineno
164+
errmsg.warning_function_should_return_a_value(lineno, node.name, node.filename)
165+
node.body.append(symbols.ASM('\nld hl, 0\n', lineno, node.filename, is_sentinel=True))
159166
yield (yield self.generic_visit(node))
160167

161168
def visit_LET(self, node):
@@ -195,9 +202,41 @@ def visit_UNARY(self, node):
195202
yield (yield self.generic_visit(node))
196203

197204
def visit_BLOCK(self, node):
205+
warning_emitted = False
206+
i = 0
207+
while i < len(node):
208+
sentence = node[i]
209+
if chk.is_ender(sentence):
210+
j = i + 1
211+
while j < len(node) - 1:
212+
if chk.is_LABEL(node[j]):
213+
break
214+
215+
if node[j].token == 'FUNCDECL':
216+
j += 1
217+
continue
218+
219+
if node[j].is_sentinel: # "Sentinel" instructions can be freely removed
220+
node.pop(j)
221+
continue
222+
223+
if node[j].token == 'ASM':
224+
break # User's ASM must always be left there
225+
226+
if not warning_emitted and self.O_LEVEL > 0:
227+
warning_emitted = True
228+
errmsg.warning_unreachable_code(lineno=node[j].lineno, fname=node[j].filename)
229+
230+
if self.O_LEVEL < 2:
231+
break
232+
233+
node.pop(j)
234+
i += 1
235+
198236
if self.O_LEVEL >= 1 and chk.is_null(node):
199237
yield self.NOP
200238
return
239+
201240
yield (yield self.generic_visit(node))
202241

203242
def visit_IF(self, node):
@@ -309,12 +348,12 @@ class VarDependency(NamedTuple):
309348

310349

311350
class VariableVisitor(GenericVisitor):
312-
_original_variable = None
351+
_original_variable: Optional[symbols.VAR] = None
313352
_parent_variable = None
314-
_visited = set()
353+
_visited: Set[symbols.SYMBOL] = set()
315354

316355
@staticmethod
317-
def generic_visit(node):
356+
def generic_visit(node: symbols.SYMBOL): # type: ignore
318357
if node not in VariableVisitor._visited:
319358
VariableVisitor._visited.add(node)
320359
for i in range(len(node.children)):
@@ -332,7 +371,7 @@ def has_circular_dependency(self, var_dependency: VarDependency) -> bool:
332371
return False
333372

334373
def get_var_dependencies(self, var_entry: symbols.VAR):
335-
visited: Set[symbols.Var] = set()
374+
visited: Set[symbols.VAR] = set()
336375
result = set()
337376

338377
def visit_var(entry):

src/libzxbc/zxbparser.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -239,16 +239,16 @@ def make_strslice(lineno, s, lower, upper):
239239
return symbols.STRSLICE.make_node(lineno, s, lower, upper)
240240

241241

242-
def make_sentence(lineno: int, sentence, *args, **kwargs):
242+
def make_sentence(lineno: int, sentence: str, *args, sentinel=False):
243243
""" Wrapper: returns a Sentence node
244244
"""
245-
return symbols.SENTENCE(lineno, gl.FILENAME, *([sentence] + list(args)), **kwargs)
245+
return symbols.SENTENCE(lineno, gl.FILENAME, sentence, *args, is_sentinel=sentinel)
246246

247247

248-
def make_asm_sentence(asm, lineno):
248+
def make_asm_sentence(asm: str, lineno: int, sentinel: bool = False):
249249
""" Creates a node for an ASM inline sentence
250250
"""
251-
return symbols.ASM(asm, lineno)
251+
return symbols.ASM(asm, lineno, gl.FILENAME, is_sentinel=sentinel)
252252

253253

254254
def make_block(*args):
@@ -513,7 +513,7 @@ def p_start(p):
513513
sys.exit(1)
514514

515515
ast = p[0] = p[1]
516-
__end = make_sentence(p.lexer.lineno, 'END', make_number(0, lineno=p.lexer.lineno))
516+
__end = make_sentence(p.lexer.lineno, 'END', make_number(0, lineno=p.lexer.lineno), sentinel=True)
517517

518518
if not is_null(ast):
519519
ast.appendChild(__end)
@@ -2054,7 +2054,7 @@ def p_return(p):
20542054
return
20552055

20562056
if FUNCTION_LEVEL[-1].kind != KIND.sub:
2057-
error(p.lineno(1), 'Syntax Error: Functions must RETURN a value, or use EXIT FUNCTION instead.')
2057+
error(p.lineno(1), 'Syntax Error: Function must RETURN a value.')
20582058
p[0] = None
20592059
return
20602060

@@ -3129,7 +3129,7 @@ def p_function_body(p):
31293129
error(p.lineno(i), "Unexpected token 'END %s'. Should be 'END %s'" % (b.upper(), a.upper()))
31303130
p[0] = None
31313131
else:
3132-
p[0] = None if p[1] == 'END' else p[1]
3132+
p[0] = make_block() if p[1] == 'END' else p[1]
31333133

31343134

31353135
def p_type_def_empty(p):

tests/functional/ltee4.asm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ _hsGetName:
5959
push ix
6060
ld ix, 0
6161
add ix, sp
62+
#line 4
63+
ld hl, 0
64+
#line 5
6265
_hsGetName__leave:
6366
ld sp, ix
6467
pop ix

0 commit comments

Comments
 (0)