Skip to content

Commit 47edfc0

Browse files
highlight issue fix bug
1 parent 83e928a commit 47edfc0

File tree

2 files changed

+143
-18
lines changed

2 files changed

+143
-18
lines changed

python/mqt/debugger/dap/dap_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ def _build_parse_error_highlight(self, line: int, column: int, detail: str) -> d
532532
line_index = line - 1
533533
line_text = lines[line_index]
534534

535-
if column <= 1 and line_index > 0:
535+
if column <= 1 and line_index > 0 and not line_text.strip():
536536
prev_index = line_index - 1
537537
while prev_index >= 0 and not lines[prev_index].strip():
538538
prev_index -= 1

src/common/parsing/CodePreprocessing.cpp

Lines changed: 142 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
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

3435
namespace {
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

Comments
 (0)