mirror of https://github.com/Qiskit/qiskit.git
172 lines
6.4 KiB
Python
172 lines
6.4 KiB
Python
# This code is part of Qiskit.
|
|
#
|
|
# (C) Copyright IBM 2023
|
|
#
|
|
# This code is licensed under the Apache License, Version 2.0. You may
|
|
# obtain a copy of this license in the LICENSE.txt file in the root directory
|
|
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
|
|
#
|
|
# Any modifications or derivative works of this code must retain this
|
|
# copyright notice, and modified files need to carry a notice indicating
|
|
# that they have been altered from the originals.
|
|
|
|
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
|
|
|
|
import ddt
|
|
|
|
import qiskit.qasm2
|
|
from test import QiskitTestCase # pylint: disable=wrong-import-order
|
|
|
|
|
|
@ddt.ddt
|
|
class TestLexer(QiskitTestCase):
|
|
# Most of the lexer is fully exercised in the parser tests. These tests here are really mopping
|
|
# up some error messages and whatnot that might otherwise be missed.
|
|
|
|
def test_pathological_formatting(self):
|
|
# This is deliberately _terribly_ formatted, included multiple blanks lines in quick
|
|
# succession and comments in places you really wouldn't expect to see comments.
|
|
program = """
|
|
OPENQASM
|
|
|
|
|
|
// do we really need a comment here?
|
|
|
|
2.0//and another comment very squished up
|
|
;
|
|
|
|
include // this line introduces a file import
|
|
"qelib1.inc" // this is the file imported
|
|
; // this is a semicolon
|
|
|
|
gate // we're making a gate
|
|
bell( // void, with loose parenthesis in comment )
|
|
) a,//
|
|
b{h a;cx a //,,,,
|
|
,b;}
|
|
|
|
qreg // a quantum register
|
|
q
|
|
[ // a square bracket
|
|
|
|
|
|
|
|
|
|
2];bell q[0],//
|
|
q[1];creg c[2];measure q->c;"""
|
|
parsed = qiskit.qasm2.loads(program)
|
|
expected_unrolled = qiskit.QuantumCircuit(
|
|
qiskit.QuantumRegister(2, "q"), qiskit.ClassicalRegister(2, "c")
|
|
)
|
|
expected_unrolled.h(0)
|
|
expected_unrolled.cx(0, 1)
|
|
expected_unrolled.measure([0, 1], [0, 1])
|
|
self.assertEqual(parsed.decompose(), expected_unrolled)
|
|
|
|
@ddt.data("0.25", "00.25", "2.5e-1", "2.5e-01", "0.025E+1", ".25", ".025e1", "25e-2")
|
|
def test_float_lexes(self, number):
|
|
program = f"qreg q[1]; U({number}, 0, 0) q[0];"
|
|
parsed = qiskit.qasm2.loads(program)
|
|
self.assertEqual(list(parsed.data[0].operation.params), [0.25, 0, 0])
|
|
|
|
def test_no_decimal_float_rejected_in_strict_mode(self):
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError,
|
|
r"\[strict\] all floats must include a decimal point",
|
|
):
|
|
qiskit.qasm2.loads("OPENQASM 2.0; qreg q[1]; U(25e-2, 0, 0) q[0];", strict=True)
|
|
|
|
@ddt.data("", "qre", "cre", ".")
|
|
def test_non_ascii_bytes_error(self, prefix):
|
|
token = f"{prefix}\xff"
|
|
with self.assertRaisesRegex(qiskit.qasm2.QASM2ParseError, "encountered a non-ASCII byte"):
|
|
qiskit.qasm2.loads(token)
|
|
|
|
def test_integers_cannot_start_with_zero(self):
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, "integers cannot have leading zeroes"
|
|
):
|
|
qiskit.qasm2.loads("0123")
|
|
|
|
@ddt.data("", "+", "-")
|
|
def test_float_exponents_must_have_a_digit(self, sign):
|
|
token = f"12.34e{sign}"
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, "needed to see an integer exponent"
|
|
):
|
|
qiskit.qasm2.loads(token)
|
|
|
|
def test_non_builtins_cannot_be_capitalised(self):
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, "identifiers cannot start with capital"
|
|
):
|
|
qiskit.qasm2.loads("Qubit")
|
|
|
|
def test_unterminated_filename_is_invalid(self):
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, "unexpected end-of-file while lexing string literal"
|
|
):
|
|
qiskit.qasm2.loads('include "qelib1.inc')
|
|
|
|
def test_filename_with_linebreak_is_invalid(self):
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, "unexpected line break while lexing string literal"
|
|
):
|
|
qiskit.qasm2.loads('include "qe\nlib1.inc";')
|
|
|
|
def test_strict_single_quoted_path_rejected(self):
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, r"\[strict\] paths must be in double quotes"
|
|
):
|
|
qiskit.qasm2.loads("OPENQASM 2.0; include 'qelib1.inc';", strict=True)
|
|
|
|
def test_version_must_have_word_boundary_after(self):
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, r"expected a word boundary after a version"
|
|
):
|
|
qiskit.qasm2.loads("OPENQASM 2a;")
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, r"expected a word boundary after a version"
|
|
):
|
|
qiskit.qasm2.loads("OPENQASM 2.0a;")
|
|
|
|
def test_no_boundary_float_in_version_position(self):
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, r"expected a word boundary after a float"
|
|
):
|
|
qiskit.qasm2.loads("OPENQASM .5a;")
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, r"expected a word boundary after a float"
|
|
):
|
|
qiskit.qasm2.loads("OPENQASM 0.2e1a;")
|
|
|
|
def test_integers_must_have_word_boundaries_after(self):
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, r"expected a word boundary after an integer"
|
|
):
|
|
qiskit.qasm2.loads("OPENQASM 2.0; qreg q[2a];")
|
|
|
|
def test_floats_must_have_word_boundaries_after(self):
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, r"expected a word boundary after a float"
|
|
):
|
|
qiskit.qasm2.loads("OPENQASM 2.0; qreg q[1]; U(2.0a, 0, 0) q[0];")
|
|
|
|
def test_single_equals_is_rejected(self):
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, r"single equals '=' is never valid"
|
|
):
|
|
qiskit.qasm2.loads("if (a = 2) U(0, 0, 0) q[0];")
|
|
|
|
def test_bare_dot_is_not_valid_float(self):
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, r"expected a numeric fractional part"
|
|
):
|
|
qiskit.qasm2.loads("qreg q[0]; U(2 + ., 0, 0) q[0];")
|
|
|
|
def test_invalid_token(self):
|
|
with self.assertRaisesRegex(
|
|
qiskit.qasm2.QASM2ParseError, r"encountered '!', which doesn't match"
|
|
):
|
|
qiskit.qasm2.loads("!")
|