2020#include " common/parsing/Utils.hpp"
2121
2222#include < algorithm>
23+ #include < cctype>
2324#include < cstddef>
2425#include < iterator>
2526#include < map>
@@ -33,6 +34,121 @@ namespace mqt::debugger {
3334
3435namespace {
3536
37+ bool isDigits (const std::string& text) {
38+ if (text.empty ()) {
39+ return false ;
40+ }
41+ return std::all_of (text.begin (), text.end (),
42+ [](unsigned char c) { return std::isdigit (c) != 0 ; });
43+ }
44+
45+ struct LineColumn {
46+ size_t line = 1 ;
47+ size_t column = 1 ;
48+ };
49+
50+ LineColumn lineColumnForOffset (const std::string& code, size_t offset) {
51+ LineColumn location;
52+ const auto lineStartPos = code.rfind (' \n ' , offset);
53+ const size_t lineStart = (lineStartPos == std::string::npos)
54+ ? 0
55+ : static_cast <size_t >(lineStartPos + 1 );
56+ location.line = 1 ;
57+ for (size_t i = 0 ; i < lineStart; i++) {
58+ if (code[i] == ' \n ' ) {
59+ location.line ++;
60+ }
61+ }
62+ location.column = offset - lineStart + 1 ;
63+ return location;
64+ }
65+
66+ LineColumn lineColumnForTarget (const std::string& code, size_t instructionStart,
67+ const std::string& target) {
68+ LineColumn location = lineColumnForOffset (code, instructionStart);
69+ const auto lineStartPos = code.rfind (' \n ' , instructionStart);
70+ const size_t lineStart = (lineStartPos == std::string::npos)
71+ ? 0
72+ : static_cast <size_t >(lineStartPos + 1 );
73+ auto lineEndPos = code.find (' \n ' , instructionStart);
74+ const size_t lineEnd = (lineEndPos == std::string::npos)
75+ ? code.size ()
76+ : static_cast <size_t >(lineEndPos);
77+ const auto lineText = code.substr (lineStart, lineEnd - lineStart);
78+ if (!target.empty ()) {
79+ const auto targetPos = lineText.find (target);
80+ if (targetPos != std::string::npos) {
81+ location.column = targetPos + 1 ;
82+ return location;
83+ }
84+ }
85+ const auto nonSpace = lineText.find_first_not_of (" \t " );
86+ if (nonSpace != std::string::npos) {
87+ location.column = nonSpace + 1 ;
88+ }
89+ return location;
90+ }
91+
92+ std::string formatParseError (const std::string& code, size_t instructionStart,
93+ const std::string& detail,
94+ const std::string& target = " " ) {
95+ const auto location = lineColumnForTarget (code, instructionStart, target);
96+ return " <input>:" + std::to_string (location.line ) + " :" +
97+ std::to_string (location.column ) + " : " + detail;
98+ }
99+
100+ void validateTargets (const std::string& code, size_t instructionStart,
101+ const std::vector<std::string>& targets,
102+ const std::map<std::string, size_t >& definedRegisters,
103+ const std::vector<std::string>& shadowedRegisters,
104+ const std::string& context) {
105+ for (const auto & target : targets) {
106+ if (target.empty ()) {
107+ continue ;
108+ }
109+ const auto open = target.find (' [' );
110+ if (open == std::string::npos) {
111+ continue ;
112+ }
113+ const auto close = target.find (' ]' , open + 1 );
114+ if (open == 0 || close == std::string::npos ||
115+ close != target.size () - 1 ) {
116+ throw ParsingError (
117+ formatParseError (code, instructionStart,
118+ " Invalid target qubit " + target + context + " ." ,
119+ target));
120+ }
121+ const auto registerName = target.substr (0 , open);
122+ const auto indexText = target.substr (open + 1 , close - open - 1 );
123+ if (!isDigits (indexText)) {
124+ throw ParsingError (
125+ formatParseError (code, instructionStart,
126+ " Invalid target qubit " + target + context + " ." ,
127+ target));
128+ }
129+ size_t registerIndex = 0 ;
130+ try {
131+ registerIndex = std::stoul (indexText);
132+ } catch (const std::exception&) {
133+ throw ParsingError (
134+ formatParseError (code, instructionStart,
135+ " Invalid target qubit " + target + context + " ." ,
136+ target));
137+ }
138+ if (std::ranges::find (shadowedRegisters, registerName) !=
139+ shadowedRegisters.end ()) {
140+ continue ;
141+ }
142+ const auto found = definedRegisters.find (registerName);
143+ if (found == definedRegisters.end () || found->second <= registerIndex) {
144+ throw ParsingError (
145+ formatParseError (code, instructionStart,
146+ " Invalid target qubit " + target + context + " ." ,
147+ target));
148+ }
149+ }
150+ }
151+
36152/* *
37153 * @brief Sweep a given code string for blocks and replace them with a unique
38154 * identifier.
@@ -332,7 +448,12 @@ preprocessCode(const std::string& code, size_t startIndex,
332448 auto isAssert = isAssertion (line);
333449 auto blockPos = line.find (" $__block" );
334450
335- const size_t trueStart = pos + blocksOffset;
451+ const auto leadingPos =
452+ blocksRemoved.find_first_not_of (" \t\r\n " , pos);
453+ const size_t trueStart =
454+ ((leadingPos != std::string::npos && leadingPos < end) ? leadingPos
455+ : pos) +
456+ blocksOffset;
336457
337458 Block block{.valid = false , .code = " " };
338459 if (blockPos != std::string::npos) {
@@ -358,7 +479,20 @@ preprocessCode(const std::string& code, size_t startIndex,
358479 replaceString (replaceString (trimmedLine, " creg" , " " ), " qreg" , " " ));
359480 const auto parts = splitString (declaration, {' [' , ' ]' });
360481 const auto & name = parts[0 ];
361- const auto size = std::stoi (parts[1 ]);
482+ const auto sizeText = parts.size () > 1 ? parts[1 ] : " " ;
483+ if (name.empty () || !isDigits (sizeText)) {
484+ throw ParsingError (formatParseError (
485+ code, trueStart,
486+ " Invalid register declaration " + trimmedLine + " ." ));
487+ }
488+ size_t size = 0 ;
489+ try {
490+ size = std::stoul (sizeText);
491+ } catch (const std::exception&) {
492+ throw ParsingError (formatParseError (
493+ code, trueStart,
494+ " Invalid register declaration " + trimmedLine + " ." ));
495+ }
362496 definedRegisters.insert ({name, size});
363497 }
364498
@@ -423,25 +557,16 @@ preprocessCode(const std::string& code, size_t startIndex,
423557 auto a = parseAssertion (line, block.code );
424558 unfoldAssertionTargetRegisters (*a, definedRegisters, shadowedRegisters);
425559 a->validate ();
426- for (const auto & target : a->getTargetQubits ()) {
427- if (std::ranges::find (shadowedRegisters, target) !=
428- shadowedRegisters.end ()) {
429- continue ;
430- }
431- const auto registerName = variableBaseName (target);
432- const auto registerIndex =
433- std::stoul (splitString (splitString (target, ' [' )[1 ], ' ]' )[0 ]);
434-
435- if (!definedRegisters.contains (registerName) ||
436- definedRegisters[registerName] <= registerIndex) {
437- throw ParsingError (" Invalid target qubit " + target +
438- " in assertion." );
439- }
440- }
560+ validateTargets (code, trueStart, a->getTargetQubits (), definedRegisters,
561+ shadowedRegisters, " in assertion" );
441562 instructions.emplace_back (i, line, a, a->getTargetQubits (), trueStart,
442563 trueEnd, i + 1 , isFunctionCall, calledFunction,
443564 false , false , block);
444565 } else {
566+ if (!isVariableDeclaration (line)) {
567+ validateTargets (code, trueStart, targets, definedRegisters,
568+ shadowedRegisters, " " );
569+ }
445570 std::unique_ptr<Assertion> a (nullptr );
446571 instructions.emplace_back (i, line, a, targets, trueStart, trueEnd, i + 1 ,
447572 isFunctionCall, calledFunction, false , false ,
0 commit comments