Environment
- pyqasm version: 1.2.0 (
main @ 036ad4d)
- Python version: 3.12
- Operating system: macOS 15
What happened?
dumps() emits an extern function call without its terminating semicolon, so the generated
program is not valid OpenQASM and will not reparse.
import openqasm3, pyqasm
m = pyqasm.loads("""OPENQASM 3.0;
extern my_extern(int);
my_extern(3);
""")
m.unroll()
out = pyqasm.dumps(m)
print(out)
openqasm3.parse(out)
OPENQASM 3.0;
extern my_extern(int);
my_extern(3)
openqasm3.parser.QASM3ParsingError
With any statement following the call, the two run together on one line:
qubit[1] q;
my_extern(3)x q[0]; # line 5:12 no viable alternative at input 'my_extern(3)'
The cause is the node type, not the printer. The unrolled AST holds a bare FunctionCall
where a statement belongs:
>>> [type(s).__name__ for s in m.unrolled_ast.statements]
['ExternDeclaration', 'FunctionCall']
FunctionCall is an expression node, so openqasm3.printer renders it as an expression —
no semicolon, no line break. It should be wrapped in an ExpressionStatement.
Scope is narrow: only extern calls are affected. Subroutine calls and builtin calls both
round-trip correctly.
extern call QASM3ParsingError tail: ['qubit[1] q;', 'my_extern(3)x q[0];']
extern, no follow QASM3ParsingError tail: ['qubit[1] q;', 'my_extern(3)']
subroutine call reparses OK tail: ['x q[0];', 'x q[0];']
builtin call reparses OK tail: ['qubit[1] q;', 'x q[0];']
Suggestions (Optional)
Wrap the call in qasm3_ast.ExpressionStatement before it is appended to the output
statements — _handle_extern_function_cleanup (src/pyqasm/visitor.py:591) is where the
extern statement list is assembled.
Worth a round-trip assertion in the tests either way: openqasm3.parse(pyqasm.dumps(m)) over
the existing extern fixtures would have caught this, and would catch the same class of bug for
any other node that reaches the statement list unwrapped.
Found while reviewing #425, which touches the expression-statement path but does not cause or
fix this — it reproduces identically on main.
Environment
main@ 036ad4d)What happened?
dumps()emits an extern function call without its terminating semicolon, so the generatedprogram is not valid OpenQASM and will not reparse.
With any statement following the call, the two run together on one line:
The cause is the node type, not the printer. The unrolled AST holds a bare
FunctionCallwhere a statement belongs:
FunctionCallis an expression node, soopenqasm3.printerrenders it as an expression —no semicolon, no line break. It should be wrapped in an
ExpressionStatement.Scope is narrow: only extern calls are affected. Subroutine calls and builtin calls both
round-trip correctly.
Suggestions (Optional)
Wrap the call in
qasm3_ast.ExpressionStatementbefore it is appended to the outputstatements —
_handle_extern_function_cleanup(src/pyqasm/visitor.py:591) is where theextern statement list is assembled.
Worth a round-trip assertion in the tests either way:
openqasm3.parse(pyqasm.dumps(m))overthe existing extern fixtures would have caught this, and would catch the same class of bug for
any other node that reaches the statement list unwrapped.
Found while reviewing #425, which touches the expression-statement path but does not cause or
fix this — it reproduces identically on
main.