From 65c32ff0fb54b57c0ec1807caf46e8c088e4b7ce Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Mon, 18 May 2026 23:39:14 -0700 Subject: [PATCH 1/2] fix(engine): report diagnostic for empty source instead of IndexError --- CHANGELOG.md | 1 + src/bespokeasm/assembler/engine.py | 5 +++++ test/test_engine.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b59a87..2820487 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Changes that are planned but not implemented yet: * Added semantic label-usage highlighting in Vim: references to labels defined in the buffer are highlighted distinctly from arbitrary identifiers. * Added hover-equivalent documentation in Vim: pressing `K` over a mnemonic, register, directive, expression function, or predefined symbol opens its documentation in a preview window. An optional auto-popup variant (vim 8.2+ / Neovim) is available via `g:bespokeasm__auto_hover`. * Improved unit test coverage +* Fixed an `IndexError` crash when compiling a source file with no compilable lines; a clear diagnostic error is now reported instead. ## [0.7.3] * Added configurable mnemonic decorators (`+`, `-`, `++`, `--`, `!`, `@`) so instruction variants can use prefixed or suffixed decorated mnemonics such as `m+`, `m-`, and `++inc`. diff --git a/src/bespokeasm/assembler/engine.py b/src/bespokeasm/assembler/engine.py index f5a23a9..6431314 100644 --- a/src/bespokeasm/assembler/engine.py +++ b/src/bespokeasm/assembler/engine.py @@ -196,6 +196,11 @@ def assemble_bytecode(self): # Sort lines according to their assigned address. This allows for .org directives compilable_line_obs.sort(key=lambda x: x.address) + if len(compilable_line_obs) == 0: + diagnostic_reporter.error( + None, + 'source file contains no compilable lines', + ) max_generated_address = compilable_line_obs[-1].address line_dict = { lobj.address: lobj diff --git a/test/test_engine.py b/test/test_engine.py index a8ba77a..6622741 100644 --- a/test/test_engine.py +++ b/test/test_engine.py @@ -314,6 +314,34 @@ def test_address_overlap_is_fatal(self): assembler.assemble_bytecode() self.assertIn('overlaps', str(ctx.exception)) + def test_empty_source_reports_diagnostic(self): + """An empty source file reports a friendly error instead of IndexError.""" + fp = pkg_resources.files(config_files).joinpath('test_instruction_operands.yaml') + config_path = str(fp) + with tempfile.TemporaryDirectory() as temp_dir: + asm_path = os.path.join(temp_dir, 'empty.asm') + with open(asm_path, 'w') as handle: + handle.write('') + + assembler = Assembler( + source_file=asm_path, + config_file=config_path, + generate_binary=False, + output_file=None, + binary_start=None, + binary_end=None, + binary_fill_value=0, + enable_pretty_print=False, + pretty_print_format=None, + pretty_print_output=None, + is_verbose=0, + include_paths=[temp_dir], + predefined=[], + ) + with self.assertRaises(SystemExit) as ctx: + assembler.assemble_bytecode() + self.assertIn('no compilable lines', str(ctx.exception)) + def test_msb_mismatch_in_second_pass_reports_line_diagnostic(self): fp = pkg_resources.files(config_files).joinpath('test_valid_address_enforcement.yaml') config_path = str(fp) From 1b4bc516fff1fd790022da07d524e7c7fe4c5965 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Tue, 19 May 2026 12:52:27 -0700 Subject: [PATCH 2/2] refactor(engine): guard empty source before sort and use truthiness check --- src/bespokeasm/assembler/engine.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bespokeasm/assembler/engine.py b/src/bespokeasm/assembler/engine.py index 6431314..a0837f7 100644 --- a/src/bespokeasm/assembler/engine.py +++ b/src/bespokeasm/assembler/engine.py @@ -195,12 +195,12 @@ def assemble_bytecode(self): compilable_line_obs.extend(predefined_line_obs) # Sort lines according to their assigned address. This allows for .org directives - compilable_line_obs.sort(key=lambda x: x.address) - if len(compilable_line_obs) == 0: + if not compilable_line_obs: diagnostic_reporter.error( None, 'source file contains no compilable lines', ) + compilable_line_obs.sort(key=lambda x: x.address) max_generated_address = compilable_line_obs[-1].address line_dict = { lobj.address: lobj