From a1e7d6de809cf167f3528fdac23fb8bffbfac1d7 Mon Sep 17 00:00:00 2001 From: Chris Elenbaas Date: Mon, 8 Jun 2026 16:28:08 +0200 Subject: [PATCH 1/3] Update lexer and add tests. --- src/v3x/CqasmLexer.g4 | 2 +- test/v3x/cpp/test_analyzer.cpp | 31 ++++++++++++++++++++++++++++ test/v3x/python/test_v3x_analyzer.py | 28 +++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/v3x/CqasmLexer.g4 b/src/v3x/CqasmLexer.g4 index 23140ae5..d99d0cd5 100644 --- a/src/v3x/CqasmLexer.g4 +++ b/src/v3x/CqasmLexer.g4 @@ -49,7 +49,7 @@ TERNARY_CONDITIONAL_OP: '?'; // Keywords VERSION: 'version' -> pushMode(VERSION_STATEMENT); // version -MEASURE: 'measure'; // non-unitary instructions +MEASURE: 'measure' ('X' | 'Y' | 'Z')?; // non-unitary instructions RESET: 'reset'; INIT: 'init'; BARRIER: 'barrier'; diff --git a/test/v3x/cpp/test_analyzer.cpp b/test/v3x/cpp/test_analyzer.cpp index 82a411bc..ef2f9f1a 100644 --- a/test/v3x/cpp/test_analyzer.cpp +++ b/test/v3x/cpp/test_analyzer.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -48,6 +49,36 @@ TEST_F(AnalyzerAnalyzeTest, parser_returns_errors) { EXPECT_THAT(error.what(), ::testing::HasSubstr(parse_error_message)); } +TEST_F(AnalyzerAnalyzeTest, analyze_string_with_measure_aliases) { + auto analyzer = Analyzer{}; + analyzer.register_default_constants(); + analyzer.register_default_functions(); + analyzer.register_default_instructions(); + + const auto program = std::string{ + "version 3\n" + "qubit qx\n" + "qubit qy\n" + "qubit qz\n" + "bit bx\n" + "bit by\n" + "bit bz\n" + "bx = measureX qx\n" + "by = measureY qy\n" + "bz = measureZ qz\n" + }; + + const auto& analysis_result = analyzer.analyze_string(program, "input.cq"); + + EXPECT_TRUE(analysis_result.errors.empty()); + ASSERT_TRUE(static_cast(analysis_result.root)); + + const auto semantic_dump = fmt::format("{}", *analysis_result.root); + EXPECT_THAT(semantic_dump, ::testing::HasSubstr("instruction_ref: measureX(bit, qubit)")); + EXPECT_THAT(semantic_dump, ::testing::HasSubstr("instruction_ref: measureY(bit, qubit)")); + EXPECT_THAT(semantic_dump, ::testing::HasSubstr("instruction_ref: measureZ(bit, qubit)")); +} + //--------------// // AnalyzerTest // //--------------// diff --git a/test/v3x/python/test_v3x_analyzer.py b/test/v3x/python/test_v3x_analyzer.py index 207d4d13..4a9b4715 100644 --- a/test/v3x/python/test_v3x_analyzer.py +++ b/test/v3x/python/test_v3x_analyzer.py @@ -25,6 +25,34 @@ def test_measure_instruction_overload(self): expected_errors = ["Error at :1:29..36: instruction 'measure' does not have an overload with 2 parameters."] self.assertEqual(errors, expected_errors) + def test_measure_aliases(self): + program_str = ( + "version 3;" + "qubit qx;qubit qy;qubit qz;" + "bit bx;bit by;bit bz;" + "bx = measureX qx;" + "by = measureY qy;" + "bz = measureZ qz" + ) + v3x_analyzer = cq.Analyzer() + ast = v3x_analyzer.analyze_string(program_str) + + for statement, expected_name, expected_bit, expected_qubit in zip( + ast.block.statements, + ("measureX", "measureY", "measureZ"), + ("bx", "by", "bz"), + ("qx", "qy", "qz"), + ): + self.assertEqual(statement.name, expected_name) + + bit_operand = statement.operands[0] + self.assertEqual(bit_operand.variable.name, expected_bit) + self.assertIsInstance(bit_operand.variable.typ, cq.types.Bit) + + qubit_operand = statement.operands[1] + self.assertEqual(qubit_operand.variable.name, expected_qubit) + self.assertIsInstance(qubit_operand.variable.typ, cq.types.Qubit) + def test_parse_string_returning_ast(self): program_str = "version 3;qubit[5] q;bit[5] b;H q[0:4];b = measure q" v3x_analyzer = cq.Analyzer() From b3aecb96edc2b6079e4aba285b27073b3e15b945 Mon Sep 17 00:00:00 2001 From: Chris Elenbaas Date: Tue, 9 Jun 2026 11:24:51 +0200 Subject: [PATCH 2/3] Add conan file, to sidestep m4 in MSYS2 error. --- conan/profiles/tests-debug-local | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 conan/profiles/tests-debug-local diff --git a/conan/profiles/tests-debug-local b/conan/profiles/tests-debug-local new file mode 100644 index 00000000..123b67b8 --- /dev/null +++ b/conan/profiles/tests-debug-local @@ -0,0 +1,7 @@ +include(tests-debug) + +[platform_tool_requires] +m4/1.4.19 + +[buildenv] +PATH+=(path)C:/msys64/usr/bin From 70f77ee9f9efad63566ab306c4a349ea98b7a8c1 Mon Sep 17 00:00:00 2001 From: Chris Elenbaas Date: Tue, 9 Jun 2026 11:54:03 +0200 Subject: [PATCH 3/3] Replace static_cast with well_formed check. --- test/v3x/cpp/test_analyzer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/v3x/cpp/test_analyzer.cpp b/test/v3x/cpp/test_analyzer.cpp index ef2f9f1a..a908a677 100644 --- a/test/v3x/cpp/test_analyzer.cpp +++ b/test/v3x/cpp/test_analyzer.cpp @@ -71,7 +71,7 @@ TEST_F(AnalyzerAnalyzeTest, analyze_string_with_measure_aliases) { const auto& analysis_result = analyzer.analyze_string(program, "input.cq"); EXPECT_TRUE(analysis_result.errors.empty()); - ASSERT_TRUE(static_cast(analysis_result.root)); + ASSERT_TRUE(analysis_result.root.is_well_formed()); const auto semantic_dump = fmt::format("{}", *analysis_result.root); EXPECT_THAT(semantic_dump, ::testing::HasSubstr("instruction_ref: measureX(bit, qubit)"));