1 | //===- AsmParserImpl.h - MLIR AsmParserImpl Class ---------------*- C++ -*-===// |
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 | |
9 | #ifndef MLIR_LIB_ASMPARSER_ASMPARSERIMPL_H |
10 | #define MLIR_LIB_ASMPARSER_ASMPARSERIMPL_H |
11 | |
12 | #include "Parser.h" |
13 | #include "mlir/AsmParser/AsmParserState.h" |
14 | #include "mlir/IR/Builders.h" |
15 | #include "mlir/IR/OpImplementation.h" |
16 | #include "llvm/Support/Base64.h" |
17 | #include <optional> |
18 | |
19 | namespace mlir { |
20 | namespace detail { |
21 | //===----------------------------------------------------------------------===// |
22 | // AsmParserImpl |
23 | //===----------------------------------------------------------------------===// |
24 | |
25 | /// This class provides the implementation of the generic parser methods within |
26 | /// AsmParser. |
27 | template <typename BaseT> |
28 | class AsmParserImpl : public BaseT { |
29 | public: |
30 | AsmParserImpl(SMLoc nameLoc, Parser &parser) |
31 | : nameLoc(nameLoc), parser(parser) {} |
32 | ~AsmParserImpl() override = default; |
33 | |
34 | /// Return the location of the original name token. |
35 | SMLoc getNameLoc() const override { return nameLoc; } |
36 | |
37 | //===--------------------------------------------------------------------===// |
38 | // Utilities |
39 | //===--------------------------------------------------------------------===// |
40 | |
41 | /// Return if any errors were emitted during parsing. |
42 | bool didEmitError() const { return emittedError; } |
43 | |
44 | /// Emit a diagnostic at the specified location and return failure. |
45 | InFlightDiagnostic emitError(SMLoc loc, const Twine &message) override { |
46 | emittedError = true; |
47 | return parser.emitError(loc, message); |
48 | } |
49 | |
50 | /// Return a builder which provides useful access to MLIRContext, global |
51 | /// objects like types and attributes. |
52 | Builder &getBuilder() const override { return parser.builder; } |
53 | |
54 | /// Get the location of the next token and store it into the argument. This |
55 | /// always succeeds. |
56 | SMLoc getCurrentLocation() override { return parser.getToken().getLoc(); } |
57 | |
58 | /// Re-encode the given source location as an MLIR location and return it. |
59 | Location getEncodedSourceLoc(SMLoc loc) override { |
60 | return parser.getEncodedSourceLocation(loc); |
61 | } |
62 | |
63 | //===--------------------------------------------------------------------===// |
64 | // Token Parsing |
65 | //===--------------------------------------------------------------------===// |
66 | |
67 | using Delimiter = AsmParser::Delimiter; |
68 | |
69 | /// Parse a `->` token. |
70 | ParseResult parseArrow() override { |
71 | return parser.parseToken(expectedToken: Token::arrow, message: "expected '->'" ); |
72 | } |
73 | |
74 | /// Parses a `->` if present. |
75 | ParseResult parseOptionalArrow() override { |
76 | return success(isSuccess: parser.consumeIf(kind: Token::arrow)); |
77 | } |
78 | |
79 | /// Parse a '{' token. |
80 | ParseResult parseLBrace() override { |
81 | return parser.parseToken(expectedToken: Token::l_brace, message: "expected '{'" ); |
82 | } |
83 | |
84 | /// Parse a '{' token if present |
85 | ParseResult parseOptionalLBrace() override { |
86 | return success(isSuccess: parser.consumeIf(kind: Token::l_brace)); |
87 | } |
88 | |
89 | /// Parse a `}` token. |
90 | ParseResult parseRBrace() override { |
91 | return parser.parseToken(expectedToken: Token::r_brace, message: "expected '}'" ); |
92 | } |
93 | |
94 | /// Parse a `}` token if present |
95 | ParseResult parseOptionalRBrace() override { |
96 | return success(isSuccess: parser.consumeIf(kind: Token::r_brace)); |
97 | } |
98 | |
99 | /// Parse a `:` token. |
100 | ParseResult parseColon() override { |
101 | return parser.parseToken(expectedToken: Token::colon, message: "expected ':'" ); |
102 | } |
103 | |
104 | /// Parse a `:` token if present. |
105 | ParseResult parseOptionalColon() override { |
106 | return success(isSuccess: parser.consumeIf(kind: Token::colon)); |
107 | } |
108 | |
109 | /// Parse a `,` token. |
110 | ParseResult parseComma() override { |
111 | return parser.parseToken(expectedToken: Token::comma, message: "expected ','" ); |
112 | } |
113 | |
114 | /// Parse a `,` token if present. |
115 | ParseResult parseOptionalComma() override { |
116 | return success(isSuccess: parser.consumeIf(kind: Token::comma)); |
117 | } |
118 | |
119 | /// Parses a `...`. |
120 | ParseResult parseEllipsis() override { |
121 | return parser.parseToken(expectedToken: Token::ellipsis, message: "expected '...'" ); |
122 | } |
123 | |
124 | /// Parses a `...` if present. |
125 | ParseResult parseOptionalEllipsis() override { |
126 | return success(isSuccess: parser.consumeIf(kind: Token::ellipsis)); |
127 | } |
128 | |
129 | /// Parse a `=` token. |
130 | ParseResult parseEqual() override { |
131 | return parser.parseToken(expectedToken: Token::equal, message: "expected '='" ); |
132 | } |
133 | |
134 | /// Parse a `=` token if present. |
135 | ParseResult parseOptionalEqual() override { |
136 | return success(isSuccess: parser.consumeIf(kind: Token::equal)); |
137 | } |
138 | |
139 | /// Parse a '<' token. |
140 | ParseResult parseLess() override { |
141 | return parser.parseToken(expectedToken: Token::less, message: "expected '<'" ); |
142 | } |
143 | |
144 | /// Parse a `<` token if present. |
145 | ParseResult parseOptionalLess() override { |
146 | return success(isSuccess: parser.consumeIf(kind: Token::less)); |
147 | } |
148 | |
149 | /// Parse a '>' token. |
150 | ParseResult parseGreater() override { |
151 | return parser.parseToken(expectedToken: Token::greater, message: "expected '>'" ); |
152 | } |
153 | |
154 | /// Parse a `>` token if present. |
155 | ParseResult parseOptionalGreater() override { |
156 | return success(isSuccess: parser.consumeIf(kind: Token::greater)); |
157 | } |
158 | |
159 | /// Parse a `(` token. |
160 | ParseResult parseLParen() override { |
161 | return parser.parseToken(expectedToken: Token::l_paren, message: "expected '('" ); |
162 | } |
163 | |
164 | /// Parses a '(' if present. |
165 | ParseResult parseOptionalLParen() override { |
166 | return success(isSuccess: parser.consumeIf(kind: Token::l_paren)); |
167 | } |
168 | |
169 | /// Parse a `)` token. |
170 | ParseResult parseRParen() override { |
171 | return parser.parseToken(expectedToken: Token::r_paren, message: "expected ')'" ); |
172 | } |
173 | |
174 | /// Parses a ')' if present. |
175 | ParseResult parseOptionalRParen() override { |
176 | return success(isSuccess: parser.consumeIf(kind: Token::r_paren)); |
177 | } |
178 | |
179 | /// Parse a `[` token. |
180 | ParseResult parseLSquare() override { |
181 | return parser.parseToken(expectedToken: Token::l_square, message: "expected '['" ); |
182 | } |
183 | |
184 | /// Parses a '[' if present. |
185 | ParseResult parseOptionalLSquare() override { |
186 | return success(isSuccess: parser.consumeIf(kind: Token::l_square)); |
187 | } |
188 | |
189 | /// Parse a `]` token. |
190 | ParseResult parseRSquare() override { |
191 | return parser.parseToken(expectedToken: Token::r_square, message: "expected ']'" ); |
192 | } |
193 | |
194 | /// Parses a ']' if present. |
195 | ParseResult parseOptionalRSquare() override { |
196 | return success(isSuccess: parser.consumeIf(kind: Token::r_square)); |
197 | } |
198 | |
199 | /// Parses a '?' token. |
200 | ParseResult parseQuestion() override { |
201 | return parser.parseToken(expectedToken: Token::question, message: "expected '?'" ); |
202 | } |
203 | |
204 | /// Parses a '?' if present. |
205 | ParseResult parseOptionalQuestion() override { |
206 | return success(isSuccess: parser.consumeIf(kind: Token::question)); |
207 | } |
208 | |
209 | /// Parses a '*' token. |
210 | ParseResult parseStar() override { |
211 | return parser.parseToken(expectedToken: Token::star, message: "expected '*'" ); |
212 | } |
213 | |
214 | /// Parses a '*' if present. |
215 | ParseResult parseOptionalStar() override { |
216 | return success(isSuccess: parser.consumeIf(kind: Token::star)); |
217 | } |
218 | |
219 | /// Parses a '+' token. |
220 | ParseResult parsePlus() override { |
221 | return parser.parseToken(expectedToken: Token::plus, message: "expected '+'" ); |
222 | } |
223 | |
224 | /// Parses a '+' token if present. |
225 | ParseResult parseOptionalPlus() override { |
226 | return success(isSuccess: parser.consumeIf(kind: Token::plus)); |
227 | } |
228 | |
229 | /// Parse a '|' token. |
230 | ParseResult parseVerticalBar() override { |
231 | return parser.parseToken(expectedToken: Token::vertical_bar, message: "expected '|'" ); |
232 | } |
233 | |
234 | /// Parse a '|' token if present. |
235 | ParseResult parseOptionalVerticalBar() override { |
236 | return success(isSuccess: parser.consumeIf(kind: Token::vertical_bar)); |
237 | } |
238 | |
239 | /// Parses a quoted string token if present. |
240 | ParseResult parseOptionalString(std::string *string) override { |
241 | if (!parser.getToken().is(k: Token::string)) |
242 | return failure(); |
243 | |
244 | if (string) |
245 | *string = parser.getToken().getStringValue(); |
246 | parser.consumeToken(); |
247 | return success(); |
248 | } |
249 | |
250 | /// Parses a Base64 encoded string of bytes. |
251 | ParseResult parseBase64Bytes(std::vector<char> *bytes) override { |
252 | auto loc = getCurrentLocation(); |
253 | if (!parser.getToken().is(k: Token::string)) |
254 | return emitError(loc, message: "expected string" ); |
255 | |
256 | if (bytes) { |
257 | // decodeBase64 doesn't modify its input so we can use the token spelling |
258 | // and just slice off the quotes/whitespaces if there are any. Whitespace |
259 | // and quotes cannot appear as part of a (standard) base64 encoded string, |
260 | // so this is safe to do. |
261 | StringRef b64QuotedString = parser.getTokenSpelling(); |
262 | StringRef b64String = |
263 | b64QuotedString.ltrim(Chars: "\" \t\n\v\f\r" ).rtrim(Chars: "\" \t\n\v\f\r" ); |
264 | if (auto err = llvm::decodeBase64(Input: b64String, Output&: *bytes)) |
265 | return emitError(loc, message: toString(E: std::move(err))); |
266 | } |
267 | |
268 | parser.consumeToken(); |
269 | return success(); |
270 | } |
271 | |
272 | /// Parse a floating point value from the stream. |
273 | ParseResult parseFloat(double &result) override { |
274 | bool isNegative = parser.consumeIf(kind: Token::minus); |
275 | Token curTok = parser.getToken(); |
276 | SMLoc loc = curTok.getLoc(); |
277 | |
278 | // Check for a floating point value. |
279 | if (curTok.is(k: Token::floatliteral)) { |
280 | auto val = curTok.getFloatingPointValue(); |
281 | if (!val) |
282 | return emitError(loc, message: "floating point value too large" ); |
283 | parser.consumeToken(kind: Token::floatliteral); |
284 | result = isNegative ? -*val : *val; |
285 | return success(); |
286 | } |
287 | |
288 | // Check for a hexadecimal float value. |
289 | if (curTok.is(k: Token::integer)) { |
290 | std::optional<APFloat> apResult; |
291 | if (failed(result: parser.parseFloatFromIntegerLiteral( |
292 | result&: apResult, tok: curTok, isNegative, semantics: APFloat::IEEEdouble(), |
293 | /*typeSizeInBits=*/typeSizeInBits: 64))) |
294 | return failure(); |
295 | |
296 | parser.consumeToken(kind: Token::integer); |
297 | result = apResult->convertToDouble(); |
298 | return success(); |
299 | } |
300 | |
301 | return emitError(loc, message: "expected floating point literal" ); |
302 | } |
303 | |
304 | /// Parse an optional integer value from the stream. |
305 | OptionalParseResult parseOptionalInteger(APInt &result) override { |
306 | return parser.parseOptionalInteger(result); |
307 | } |
308 | |
309 | /// Parse a list of comma-separated items with an optional delimiter. If a |
310 | /// delimiter is provided, then an empty list is allowed. If not, then at |
311 | /// least one element will be parsed. |
312 | ParseResult parseCommaSeparatedList(Delimiter delimiter, |
313 | function_ref<ParseResult()> parseElt, |
314 | StringRef contextMessage) override { |
315 | return parser.parseCommaSeparatedList(delimiter, parseElementFn: parseElt, contextMessage); |
316 | } |
317 | |
318 | //===--------------------------------------------------------------------===// |
319 | // Keyword Parsing |
320 | //===--------------------------------------------------------------------===// |
321 | |
322 | ParseResult parseKeyword(StringRef keyword, const Twine &msg) override { |
323 | if (parser.getToken().isCodeCompletion()) |
324 | return parser.codeCompleteExpectedTokens(tokens: keyword); |
325 | |
326 | auto loc = getCurrentLocation(); |
327 | if (parseOptionalKeyword(keyword)) |
328 | return emitError(loc, message: "expected '" ) << keyword << "'" << msg; |
329 | return success(); |
330 | } |
331 | using AsmParser::parseKeyword; |
332 | |
333 | /// Parse the given keyword if present. |
334 | ParseResult parseOptionalKeyword(StringRef keyword) override { |
335 | if (parser.getToken().isCodeCompletion()) |
336 | return parser.codeCompleteOptionalTokens(tokens: keyword); |
337 | |
338 | // Check that the current token has the same spelling. |
339 | if (!parser.isCurrentTokenAKeyword() || |
340 | parser.getTokenSpelling() != keyword) |
341 | return failure(); |
342 | parser.consumeToken(); |
343 | return success(); |
344 | } |
345 | |
346 | /// Parse a keyword, if present, into 'keyword'. |
347 | ParseResult parseOptionalKeyword(StringRef *keyword) override { |
348 | // Check that the current token is a keyword. |
349 | if (!parser.isCurrentTokenAKeyword()) |
350 | return failure(); |
351 | |
352 | *keyword = parser.getTokenSpelling(); |
353 | parser.consumeToken(); |
354 | return success(); |
355 | } |
356 | |
357 | /// Parse a keyword if it is one of the 'allowedKeywords'. |
358 | ParseResult |
359 | parseOptionalKeyword(StringRef *keyword, |
360 | ArrayRef<StringRef> allowedKeywords) override { |
361 | if (parser.getToken().isCodeCompletion()) |
362 | return parser.codeCompleteOptionalTokens(tokens: allowedKeywords); |
363 | |
364 | // Check that the current token is a keyword. |
365 | if (!parser.isCurrentTokenAKeyword()) |
366 | return failure(); |
367 | |
368 | StringRef currentKeyword = parser.getTokenSpelling(); |
369 | if (llvm::is_contained(Range&: allowedKeywords, Element: currentKeyword)) { |
370 | *keyword = currentKeyword; |
371 | parser.consumeToken(); |
372 | return success(); |
373 | } |
374 | |
375 | return failure(); |
376 | } |
377 | |
378 | /// Parse an optional keyword or string and set instance into 'result'.` |
379 | ParseResult parseOptionalKeywordOrString(std::string *result) override { |
380 | StringRef keyword; |
381 | if (succeeded(parseOptionalKeyword(&keyword))) { |
382 | *result = keyword.str(); |
383 | return success(); |
384 | } |
385 | |
386 | return parseOptionalString(string: result); |
387 | } |
388 | |
389 | //===--------------------------------------------------------------------===// |
390 | // Attribute Parsing |
391 | //===--------------------------------------------------------------------===// |
392 | |
393 | /// Parse an arbitrary attribute and return it in result. |
394 | ParseResult parseAttribute(Attribute &result, Type type) override { |
395 | result = parser.parseAttribute(type); |
396 | return success(isSuccess: static_cast<bool>(result)); |
397 | } |
398 | |
399 | /// Parse a custom attribute with the provided callback, unless the next |
400 | /// token is `#`, in which case the generic parser is invoked. |
401 | ParseResult parseCustomAttributeWithFallback( |
402 | Attribute &result, Type type, |
403 | function_ref<ParseResult(Attribute &result, Type type)> parseAttribute) |
404 | override { |
405 | if (parser.getToken().isNot(k: Token::hash_identifier)) |
406 | return parseAttribute(result, type); |
407 | result = parser.parseAttribute(type); |
408 | return success(isSuccess: static_cast<bool>(result)); |
409 | } |
410 | |
411 | /// Parse a custom attribute with the provided callback, unless the next |
412 | /// token is `#`, in which case the generic parser is invoked. |
413 | ParseResult parseCustomTypeWithFallback( |
414 | Type &result, |
415 | function_ref<ParseResult(Type &result)> parseType) override { |
416 | if (parser.getToken().isNot(k: Token::exclamation_identifier)) |
417 | return parseType(result); |
418 | result = parser.parseType(); |
419 | return success(isSuccess: static_cast<bool>(result)); |
420 | } |
421 | |
422 | OptionalParseResult parseOptionalAttribute(Attribute &result, |
423 | Type type) override { |
424 | return parser.parseOptionalAttribute(attribute&: result, type); |
425 | } |
426 | OptionalParseResult parseOptionalAttribute(ArrayAttr &result, |
427 | Type type) override { |
428 | return parser.parseOptionalAttribute(attribute&: result, type); |
429 | } |
430 | OptionalParseResult parseOptionalAttribute(StringAttr &result, |
431 | Type type) override { |
432 | return parser.parseOptionalAttribute(attribute&: result, type); |
433 | } |
434 | OptionalParseResult parseOptionalAttribute(SymbolRefAttr &result, |
435 | Type type) override { |
436 | return parser.parseOptionalAttribute(result, type); |
437 | } |
438 | |
439 | /// Parse a named dictionary into 'result' if it is present. |
440 | ParseResult parseOptionalAttrDict(NamedAttrList &result) override { |
441 | if (parser.getToken().isNot(k: Token::l_brace)) |
442 | return success(); |
443 | return parser.parseAttributeDict(attributes&: result); |
444 | } |
445 | |
446 | /// Parse a named dictionary into 'result' if the `attributes` keyword is |
447 | /// present. |
448 | ParseResult parseOptionalAttrDictWithKeyword(NamedAttrList &result) override { |
449 | if (failed(parseOptionalKeyword("attributes" ))) |
450 | return success(); |
451 | return parser.parseAttributeDict(attributes&: result); |
452 | } |
453 | |
454 | /// Parse an affine map instance into 'map'. |
455 | ParseResult parseAffineMap(AffineMap &map) override { |
456 | return parser.parseAffineMapReference(map); |
457 | } |
458 | |
459 | /// Parse an affine expr instance into 'expr' using the already computed |
460 | /// mapping from symbols to affine expressions in 'symbolSet'. |
461 | ParseResult |
462 | parseAffineExpr(ArrayRef<std::pair<StringRef, AffineExpr>> symbolSet, |
463 | AffineExpr &expr) override { |
464 | return parser.parseAffineExprReference(symbolSet, expr); |
465 | } |
466 | |
467 | /// Parse an integer set instance into 'set'. |
468 | ParseResult parseIntegerSet(IntegerSet &set) override { |
469 | return parser.parseIntegerSetReference(set); |
470 | } |
471 | |
472 | //===--------------------------------------------------------------------===// |
473 | // Identifier Parsing |
474 | //===--------------------------------------------------------------------===// |
475 | |
476 | /// Parse an optional @-identifier and store it (without the '@' symbol) in a |
477 | /// string attribute named 'attrName'. |
478 | ParseResult parseOptionalSymbolName(StringAttr &result) override { |
479 | Token atToken = parser.getToken(); |
480 | if (atToken.isNot(k: Token::at_identifier)) |
481 | return failure(); |
482 | |
483 | result = getBuilder().getStringAttr(atToken.getSymbolReference()); |
484 | parser.consumeToken(); |
485 | |
486 | // If we are populating the assembly parser state, record this as a symbol |
487 | // reference. |
488 | if (parser.getState().asmState) { |
489 | parser.getState().asmState->addUses(SymbolRefAttr::get(result), |
490 | atToken.getLocRange()); |
491 | } |
492 | return success(); |
493 | } |
494 | |
495 | //===--------------------------------------------------------------------===// |
496 | // Resource Parsing |
497 | //===--------------------------------------------------------------------===// |
498 | |
499 | /// Parse a handle to a resource within the assembly format. |
500 | FailureOr<AsmDialectResourceHandle> |
501 | parseResourceHandle(Dialect *dialect) override { |
502 | const auto *interface = dyn_cast<OpAsmDialectInterface>(Val: dialect); |
503 | if (!interface) { |
504 | return parser.emitError() << "dialect '" << dialect->getNamespace() |
505 | << "' does not expect resource handles" ; |
506 | } |
507 | StringRef resourceName; |
508 | return parser.parseResourceHandle(dialect: interface, name&: resourceName); |
509 | } |
510 | |
511 | //===--------------------------------------------------------------------===// |
512 | // Type Parsing |
513 | //===--------------------------------------------------------------------===// |
514 | |
515 | /// Parse a type. |
516 | ParseResult parseType(Type &result) override { |
517 | return failure(isFailure: !(result = parser.parseType())); |
518 | } |
519 | |
520 | /// Parse an optional type. |
521 | OptionalParseResult parseOptionalType(Type &result) override { |
522 | return parser.parseOptionalType(type&: result); |
523 | } |
524 | |
525 | /// Parse an arrow followed by a type list. |
526 | ParseResult parseArrowTypeList(SmallVectorImpl<Type> &result) override { |
527 | if (parseArrow() || parser.parseFunctionResultTypes(elements&: result)) |
528 | return failure(); |
529 | return success(); |
530 | } |
531 | |
532 | /// Parse an optional arrow followed by a type list. |
533 | ParseResult |
534 | parseOptionalArrowTypeList(SmallVectorImpl<Type> &result) override { |
535 | if (!parser.consumeIf(kind: Token::arrow)) |
536 | return success(); |
537 | return parser.parseFunctionResultTypes(elements&: result); |
538 | } |
539 | |
540 | /// Parse a colon followed by a type. |
541 | ParseResult parseColonType(Type &result) override { |
542 | return failure(isFailure: parser.parseToken(expectedToken: Token::colon, message: "expected ':'" ) || |
543 | !(result = parser.parseType())); |
544 | } |
545 | |
546 | /// Parse a colon followed by a type list, which must have at least one type. |
547 | ParseResult parseColonTypeList(SmallVectorImpl<Type> &result) override { |
548 | if (parser.parseToken(expectedToken: Token::colon, message: "expected ':'" )) |
549 | return failure(); |
550 | return parser.parseTypeListNoParens(elements&: result); |
551 | } |
552 | |
553 | /// Parse an optional colon followed by a type list, which if present must |
554 | /// have at least one type. |
555 | ParseResult |
556 | parseOptionalColonTypeList(SmallVectorImpl<Type> &result) override { |
557 | if (!parser.consumeIf(kind: Token::colon)) |
558 | return success(); |
559 | return parser.parseTypeListNoParens(elements&: result); |
560 | } |
561 | |
562 | ParseResult parseDimensionList(SmallVectorImpl<int64_t> &dimensions, |
563 | bool allowDynamic, |
564 | bool withTrailingX) override { |
565 | return parser.parseDimensionListRanked(dimensions, allowDynamic, |
566 | withTrailingX); |
567 | } |
568 | |
569 | ParseResult parseXInDimensionList() override { |
570 | return parser.parseXInDimensionList(); |
571 | } |
572 | |
573 | LogicalResult pushCyclicParsing(const void *opaquePointer) override { |
574 | return success(isSuccess: parser.getState().cyclicParsingStack.insert(X: opaquePointer)); |
575 | } |
576 | |
577 | void popCyclicParsing() override { |
578 | parser.getState().cyclicParsingStack.pop_back(); |
579 | } |
580 | |
581 | //===--------------------------------------------------------------------===// |
582 | // Code Completion |
583 | //===--------------------------------------------------------------------===// |
584 | |
585 | /// Parse a keyword, or an empty string if the current location signals a code |
586 | /// completion. |
587 | ParseResult parseKeywordOrCompletion(StringRef *keyword) override { |
588 | Token tok = parser.getToken(); |
589 | if (tok.isCodeCompletion() && tok.getSpelling().empty()) { |
590 | *keyword = "" ; |
591 | return success(); |
592 | } |
593 | return parseKeyword(keyword); |
594 | } |
595 | |
596 | /// Signal the code completion of a set of expected tokens. |
597 | void codeCompleteExpectedTokens(ArrayRef<StringRef> tokens) override { |
598 | Token tok = parser.getToken(); |
599 | if (tok.isCodeCompletion() && tok.getSpelling().empty()) |
600 | (void)parser.codeCompleteExpectedTokens(tokens); |
601 | } |
602 | |
603 | protected: |
604 | /// The source location of the dialect symbol. |
605 | SMLoc nameLoc; |
606 | |
607 | /// The main parser. |
608 | Parser &parser; |
609 | |
610 | /// A flag that indicates if any errors were emitted during parsing. |
611 | bool emittedError = false; |
612 | }; |
613 | } // namespace detail |
614 | } // namespace mlir |
615 | |
616 | #endif // MLIR_LIB_ASMPARSER_ASMPARSERIMPL_H |
617 | |