CIRCT 23.0.0git
Loading...
Searching...
No Matches
ScanStrings.cpp
Go to the documentation of this file.
1//===- ScanStrings.cpp - Scan format string conversion --------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10
11using namespace mlir;
12using namespace circt;
13using namespace ImportVerilog;
14using moore::IntFormat;
15
16namespace {
17
18//===----------------------------------------------------------------------===//
19// Scan format string parser
20//===----------------------------------------------------------------------===//
21
22static bool parseScanFormat(StringRef format,
23 function_ref<void(StringRef text)> onText,
24 function_ref<void(char specifier, bool suppress,
25 std::optional<uint32_t> maxWidth)>
26 onArg,
27 function_ref<void(StringRef msg)> onError) {
28
29 size_t i = 0;
30 size_t textStart = 0;
31
32 while (i < format.size()) {
33 if (format[i] != '%') {
34 ++i;
35 continue;
36 }
37
38 if (i > textStart)
39 onText(format.substr(textStart, i - textStart));
40
41 ++i; // consume '%'
42 if (i >= format.size()) {
43 onError("unexpected end of format string after '%'");
44 return false;
45 }
46
47 if (format[i] == '%') {
48 onText("%");
49 ++i;
50 textStart = i;
51 continue;
52 }
53
54 // Optional assignment suppression '*'.
55 bool suppress = false;
56 if (format[i] == '*') {
57 suppress = true;
58 ++i;
59 if (i >= format.size()) {
60 onError("unexpected end of format string after '%*'");
61 return false;
62 }
63 }
64
65 // Optional maximum field width.
66 std::optional<uint32_t> maxWidth;
67 if (std::isdigit(format[i])) {
68 uint32_t w = 0;
69 while (i < format.size() && std::isdigit(format[i]))
70 w = w * 10 + (format[i++] - '0');
71 maxWidth = w;
72 if (w == 0) {
73 onError("field width must be at least 1");
74 return false;
75 }
76 }
77
78 if (i >= format.size()) {
79 onError("unexpected end of format string: missing conversion specifier");
80 return false;
81 }
82
83 char specifier = format[i++];
84 onArg(specifier, suppress, maxWidth);
85 textStart = i;
86 }
87
88 // Flush any trailing literal text.
89 if (textStart < format.size())
90 onText(format.substr(textStart));
91
92 return true;
93}
94
95struct ScanStringParser {
96 Context &context;
97 OpBuilder &builder;
98 /// Remaining destination arguments (lvalue expressions to write into).
99 /// Suppressed assignments do not consume from this list.
100 ArrayRef<const slang::ast::Expression *> destinations;
101 /// Current location for ops and diagnostics.
102 Location loc;
103 /// The current tail of the consuming chain.
104 Value cursor;
105 /// Accumulated (unwrapped destination expression, scanned value, matched)
106 /// tuples to be assigned with a moore.blocking_assign by the caller.
107 SmallVector<std::tuple<const slang::ast::Expression *, Value, Value>>
108 assignments;
109
110 ScanStringParser(Context &context, Value initialCursor,
111 ArrayRef<const slang::ast::Expression *> destinations,
112 Location loc)
113 : context(context), builder(context.builder), destinations(destinations),
114 loc(loc), cursor(initialCursor) {}
115
116 /// Thread the consuming chain and collect (destination, value, matched)
117 /// assignments tuples.
118 FailureOr<Context::ScanStringResult> parse(StringRef formatStr) {
119 bool anyFailure = false;
120
121 auto onText = [&](StringRef text) {
122 if (!anyFailure)
123 emitLiteral(text);
124 };
125
126 auto onArg = [&](char specifier, bool suppress,
127 std::optional<uint32_t> maxWidth) {
128 if (anyFailure)
129 return;
130 if (failed(emitScanArg(specifier, suppress, maxWidth)))
131 anyFailure = true;
132 };
133
134 auto onError = [&](StringRef msg) {
135 if (!anyFailure) {
136 mlir::emitError(loc) << "scan format string error: " << msg;
137 anyFailure = true;
138 }
139 };
140
141 if (!parseScanFormat(formatStr, onText, onArg, onError) || anyFailure)
142 return failure();
143
144 return Context::ScanStringResult{cursor, std::move(assignments)};
145 }
146
147 /// Emit a literal text fragment that must match verbatim in the input.
148 void emitLiteral(StringRef text) {
149 if (text.empty())
150 return;
151 cursor =
152 moore::ScanLiteralOp::create(builder, loc, cursor, text).getRemaining();
153 }
154
155 /// Strip an outer AssignmentExpression wrapper if present,
156 /// returning the actual lvalue subexpression.
157 static const slang::ast::Expression *
158 unwrapDest(const slang::ast::Expression *expr) {
159 if (auto *assign = expr->as_if<slang::ast::AssignmentExpression>())
160 return &assign->left();
161 return expr;
162 }
163
164 /// Consume the next destination argument (if not suppressed) and emit a
165 /// scan fragment op.
166 LogicalResult emitScanArg(char specifier, bool suppress,
167 std::optional<uint32_t> maxWidth) {
168 auto specifierLower = std::tolower(specifier);
169
170 // Hierarchical path specifier (%m), no argument consumed.
171 if (specifierLower == 'm') {
172 cursor = moore::ScanHierPathMatchOp::create(builder, loc, cursor)
173 .getRemaining();
174 return success();
175 }
176
177 IntegerAttr maxWidthAttr = nullptr;
178 if (maxWidth)
179 maxWidthAttr = builder.getI32IntegerAttr(*maxWidth);
180
181 const slang::ast::Expression *destExpr = nullptr;
182 if (!suppress) {
183 if (destinations.empty())
184 return mlir::emitError(loc)
185 << "too few arguments for scan format specifier '%" << specifier
186 << "'";
187 destExpr = unwrapDest(destinations.front());
188 destinations = destinations.drop_front();
189 }
190
191 switch (specifierLower) {
192 // Integer specifiers (%b, %o, %d, %h, %x)
193 case 'b':
194 return emitScanInt(destExpr, suppress, IntFormat::Binary, maxWidthAttr);
195 case 'o':
196 return emitScanInt(destExpr, suppress, IntFormat::Octal, maxWidthAttr);
197 case 'd':
198 return emitScanInt(destExpr, suppress, IntFormat::Decimal, maxWidthAttr);
199 case 'h':
200 case 'x':
201 return emitScanInt(destExpr, suppress,
202 std::isupper(specifier) ? IntFormat::HexUpper
203 : IntFormat::HexLower,
204 maxWidthAttr);
205
206 // Floating-point specifiers (%f, %e, %g)
207 case 'f':
208 case 'e':
209 case 'g':
210 return emitScanReal(destExpr, suppress, maxWidthAttr);
211
212 // Time specifier (%t)
213 case 't':
214 return emitScanTime(destExpr, suppress, maxWidthAttr);
215
216 // Character specifier (%c)
217 case 'c':
218 return emitScanChar(destExpr, suppress);
219
220 // String specifier (%s)
221 case 's':
222 return emitScanStr(destExpr, suppress, maxWidthAttr);
223
224 // Unformatted binary (%u, %z)
225 case 'u':
226 return emitScanUnformatted(destExpr, suppress, /*fourValue=*/false);
227 case 'z':
228 return emitScanUnformatted(destExpr, suppress, /*fourValue=*/true);
229
230 default:
231 return mlir::emitError(loc)
232 << "unknown scan format specifier '%" << specifier << "'";
233 }
234 }
235
236 LogicalResult emitScanInt(const slang::ast::Expression *destExpr,
237 bool suppress, IntFormat format,
238 IntegerAttr maxWidth) {
239 auto scanStringTy = moore::ScanStringType::get(builder.getContext());
240
241 if (suppress) {
242 auto op = moore::ScanIntOp::create(builder, loc, TypeRange{scanStringTy},
243 cursor, format, maxWidth);
244 cursor = op.getRemaining();
245 return success();
246 }
247
248 auto mooreIntTy =
249 llvm::dyn_cast<moore::IntType>(context.convertType(*destExpr->type));
250 if (!mooreIntTy)
251 return mlir::emitError(loc)
252 << "destination of integer scan specifier must be an integer";
253 auto mlirIntTy = builder.getIntegerType(mooreIntTy.getWidth());
254 auto matchedTy = moore::IntType::getInt(builder.getContext(), 1);
255 auto op = moore::ScanIntOp::create(builder, loc, scanStringTy, mlirIntTy,
256 matchedTy, cursor, format, maxWidth);
257 cursor = op.getRemaining();
258 auto mooreVal = moore::FromBuiltinIntOp::create(builder, loc, op.getValue())
259 .getResult();
260 if (mooreIntTy.getDomain() == moore::Domain::FourValued)
261 mooreVal = moore::IntToLogicOp::create(builder, loc, mooreIntTy, mooreVal)
262 .getResult();
263 assignments.push_back({destExpr, mooreVal, op.getMatched()});
264 return success();
265 }
266
267 LogicalResult emitScanReal(const slang::ast::Expression *destExpr,
268 bool suppress, IntegerAttr maxWidth) {
269 auto scanStringTy = moore::ScanStringType::get(builder.getContext());
270 if (suppress) {
271 auto op = moore::ScanRealOp::create(builder, loc, TypeRange{scanStringTy},
272 cursor, maxWidth);
273 cursor = op.getRemaining();
274 return success();
275 }
276
277 auto valueTy =
278 llvm::dyn_cast<moore::RealType>(context.convertType(*destExpr->type));
279 if (!valueTy)
280 return mlir::emitError(loc)
281 << "destination of real scan specifier must be a real";
282 auto matchedTy = moore::IntType::getInt(builder.getContext(), 1);
283 auto op = moore::ScanRealOp::create(
284 builder, loc, TypeRange{scanStringTy, valueTy, matchedTy}, cursor,
285 maxWidth);
286 cursor = op.getRemaining();
287 assignments.push_back({destExpr, op.getValue(), op.getMatched()});
288 return success();
289 }
290
291 LogicalResult emitScanTime(const slang::ast::Expression *destExpr,
292 bool suppress, IntegerAttr maxWidth) {
293 auto scanStringTy = moore::ScanStringType::get(builder.getContext());
294 if (suppress) {
295 auto op = moore::ScanTimeOp::create(builder, loc, TypeRange{scanStringTy},
296 cursor, maxWidth);
297 cursor = op.getRemaining();
298 return success();
299 }
300 auto valueTy = moore::TimeType::get(builder.getContext());
301 auto matchedTy = moore::IntType::getInt(builder.getContext(), 1);
302 auto op = moore::ScanTimeOp::create(
303 builder, loc, TypeRange{scanStringTy, valueTy, matchedTy}, cursor,
304 maxWidth);
305 cursor = op.getRemaining();
306 assignments.push_back({destExpr, op.getValue(), op.getMatched()});
307 return success();
308 }
309
310 LogicalResult emitScanChar(const slang::ast::Expression *destExpr,
311 bool suppress) {
312 auto scanStringTy = moore::ScanStringType::get(builder.getContext());
313 if (suppress) {
314 auto op = moore::ScanCharOp::create(builder, loc, TypeRange{scanStringTy},
315 cursor);
316 cursor = op.getRemaining();
317 return success();
318 }
319 auto mooreIntTy =
320 llvm::dyn_cast<moore::IntType>(context.convertType(*destExpr->type));
321 if (!mooreIntTy)
322 return mlir::emitError(loc)
323 << "destination of char scan specifier must be an integer";
324 auto mlirIntTy = builder.getIntegerType(mooreIntTy.getWidth());
325 auto matchedTy = moore::IntType::getInt(builder.getContext(), 1);
326 auto op = moore::ScanCharOp::create(
327 builder, loc, TypeRange{scanStringTy, mlirIntTy, matchedTy}, cursor);
328 cursor = op.getRemaining();
329 auto mooreVal = moore::FromBuiltinIntOp::create(builder, loc, op.getValue())
330 .getResult();
331 if (mooreIntTy.getDomain() == moore::Domain::FourValued)
332 mooreVal = moore::IntToLogicOp::create(builder, loc, mooreIntTy, mooreVal)
333 .getResult();
334 assignments.push_back({destExpr, mooreVal, op.getMatched()});
335 return success();
336 }
337
338 LogicalResult emitScanStr(const slang::ast::Expression *destExpr,
339 bool suppress, IntegerAttr maxWidth) {
340 auto scanStringTy = moore::ScanStringType::get(builder.getContext());
341 if (suppress) {
342 auto op = moore::ScanStrOp::create(builder, loc, TypeRange{scanStringTy},
343 cursor, maxWidth);
344 cursor = op.getRemaining();
345 return success();
346 }
347 auto stringTy = moore::StringType::get(builder.getContext());
348 auto matchedTy = moore::IntType::getInt(builder.getContext(), 1);
349 auto op = moore::ScanStrOp::create(
350 builder, loc, TypeRange{scanStringTy, stringTy, matchedTy}, cursor,
351 maxWidth);
352 cursor = op.getRemaining();
353 Value val = op.getValue();
354 auto destTy = context.convertType(*destExpr->type);
355 if (auto intTy = llvm::dyn_cast<moore::IntType>(destTy)) {
356 val =
357 moore::StringToIntOp::create(builder, loc, intTy.getTwoValued(), val)
358 .getResult();
359 if (intTy.getDomain() == moore::Domain::FourValued)
360 val = moore::IntToLogicOp::create(builder, loc, intTy, val).getResult();
361 } else if (!llvm::isa<moore::StringType>(destTy)) {
362 return mlir::emitError(loc)
363 << "destination of string scan specifier must be a string or "
364 "integer type";
365 }
366 assignments.push_back({destExpr, val, op.getMatched()});
367 return success();
368 }
369
370 LogicalResult emitScanUnformatted(const slang::ast::Expression *destExpr,
371 bool suppress, bool fourValue) {
372 auto scanStringTy = moore::ScanStringType::get(builder.getContext());
373
374 if (suppress) {
375 auto op = moore::ScanUnformattedOp::create(
376 builder, loc, TypeRange{scanStringTy}, cursor, fourValue);
377 cursor = op.getRemaining();
378 return success();
379 }
380 auto mooreIntTy =
381 llvm::dyn_cast<moore::IntType>(context.convertType(*destExpr->type));
382 if (!mooreIntTy)
383 return mlir::emitError(loc)
384 << "destination of unformatted scan specifier must be an integer";
385
386 auto mlirIntTy = builder.getIntegerType(mooreIntTy.getWidth());
387 auto matchedTy = moore::IntType::getInt(builder.getContext(), 1);
388 auto op = moore::ScanUnformattedOp::create(
389 builder, loc, TypeRange{scanStringTy, mlirIntTy, matchedTy}, cursor,
390 fourValue);
391 cursor = op.getRemaining();
392 auto mooreVal = moore::FromBuiltinIntOp::create(builder, loc, op.getValue())
393 .getResult();
394 if (mooreIntTy.getDomain() == moore::Domain::FourValued)
395 mooreVal = moore::IntToLogicOp::create(builder, loc, mooreIntTy, mooreVal)
396 .getResult();
397 assignments.push_back({destExpr, mooreVal, op.getMatched()});
398 return success();
399 }
400};
401
402} // namespace
403
404FailureOr<Context::ScanStringResult> Context::convertScanString(
405 StringRef formatStr, Value initialCursor,
406 std::span<const slang::ast::Expression *const> destinations, Location loc) {
407 ScanStringParser parser(*this, initialCursor,
408 ArrayRef(destinations.data(), destinations.size()),
409 loc);
410 return parser.parse(formatStr);
411}
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
Type convertType(const slang::ast::Type &type, LocationAttr loc={})
Convert a slang type into an MLIR type.
Definition Types.cpp:224