diff --git a/CHANGELOG.md b/CHANGELOG.md index 25cedc3b..ab07017e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Types of changes: ### Removed ### Fixed +- Fixed `loads("")` raising `AttributeError` instead of `ValidationError` for empty string input. ([#445](https://github.com/qBraid/pyqasm/pull/445)) - Fixed qubit register ranges dropping their final qubit during unrolling. Endpoints are now inclusive for gates, aliases, barriers, and measurements. Code using an exclusive upper bound must lower it by one, for example `q[0:1]` instead of `q[0:2]` to select two qubits. ([#432](https://github.com/qBraid/pyqasm/issues/432)) - Fixed `cs` and `csdg` from `stdgates.inc` being rejected during unrolling. They now decompose to controlled phase shifts. ([#439](https://github.com/qBraid/pyqasm/issues/439)) - Fixed Clifford+T rebasing for exact `rx`, `ry`, and `rz` rotations at multiples of π/4. These gates now decompose instead of disappearing, while angles outside the exact basis raise `RebaseError` instead of producing an incorrect result. ([#428](https://github.com/qBraid/pyqasm/issues/428)) diff --git a/src/pyqasm/entrypoint.py b/src/pyqasm/entrypoint.py index 00aaf2db..0f5641fe 100644 --- a/src/pyqasm/entrypoint.py +++ b/src/pyqasm/entrypoint.py @@ -184,6 +184,8 @@ def loads(program: openqasm3.ast.Program | str, **kwargs) -> QasmModule: program = openqasm3.parse(program) except openqasm3.parser.QASM3ParsingError as err: raise ValidationError(f"Failed to parse OpenQASM string: {err}") from err + except AttributeError as err: + raise ValidationError(f"Failed to parse OpenQASM string: {err}") from err elif not isinstance(program, openqasm3.ast.Program): raise TypeError("Input quantum program must be of type 'str' or 'openqasm3.ast.Program'.") elif include_dir is not None: diff --git a/src/pyqasm/visitor.py b/src/pyqasm/visitor.py index a1afb6f7..a625199d 100644 --- a/src/pyqasm/visitor.py +++ b/src/pyqasm/visitor.py @@ -2710,11 +2710,10 @@ def _visit_function_call( if isinstance(subroutine_def, qasm3_ast.ExternDeclaration): self._in_extern_function = True global_scope = self._scope_manager.get_global_scope() - result.append( - PulseValidator.validate_and_process_extern_function_call( - statement, global_scope, self._module._device_cycle_time - ) + extern_call = PulseValidator.validate_and_process_extern_function_call( + statement, global_scope, self._module._device_cycle_time ) + result.append(qasm3_ast.ExpressionStatement(expression=extern_call)) else: for function_op in subroutine_def.body: if isinstance(function_op, qasm3_ast.ReturnStatement):