1 //===- DialectSymbolParser.cpp - MLIR Dialect Symbol Parser --------------===//
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 // This file implements the parser for the dialect symbols, such as extended
10 // attributes and types.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "Parser.h"
15 #include "mlir/IR/BuiltinTypes.h"
16 #include "mlir/IR/Dialect.h"
17 #include "mlir/IR/DialectImplementation.h"
18 #include "llvm/Support/SourceMgr.h"
19
20 using namespace mlir;
21 using namespace mlir::detail;
22 using llvm::MemoryBuffer;
23 using llvm::SMLoc;
24 using llvm::SourceMgr;
25
26 namespace {
27 /// This class provides the main implementation of the DialectAsmParser that
28 /// allows for dialects to parse attributes and types. This allows for dialect
29 /// hooking into the main MLIR parsing logic.
30 class CustomDialectAsmParser : public DialectAsmParser {
31 public:
CustomDialectAsmParser(StringRef fullSpec,Parser & parser)32 CustomDialectAsmParser(StringRef fullSpec, Parser &parser)
33 : fullSpec(fullSpec), nameLoc(parser.getToken().getLoc()),
34 parser(parser) {}
~CustomDialectAsmParser()35 ~CustomDialectAsmParser() override {}
36
37 /// Emit a diagnostic at the specified location and return failure.
emitError(llvm::SMLoc loc,const Twine & message)38 InFlightDiagnostic emitError(llvm::SMLoc loc, const Twine &message) override {
39 return parser.emitError(loc, message);
40 }
41
42 /// Return a builder which provides useful access to MLIRContext, global
43 /// objects like types and attributes.
getBuilder() const44 Builder &getBuilder() const override { return parser.builder; }
45
46 /// Get the location of the next token and store it into the argument. This
47 /// always succeeds.
getCurrentLocation()48 llvm::SMLoc getCurrentLocation() override {
49 return parser.getToken().getLoc();
50 }
51
52 /// Return the location of the original name token.
getNameLoc() const53 llvm::SMLoc getNameLoc() const override { return nameLoc; }
54
55 /// Re-encode the given source location as an MLIR location and return it.
getEncodedSourceLoc(llvm::SMLoc loc)56 Location getEncodedSourceLoc(llvm::SMLoc loc) override {
57 return parser.getEncodedSourceLocation(loc);
58 }
59
60 /// Returns the full specification of the symbol being parsed. This allows
61 /// for using a separate parser if necessary.
getFullSymbolSpec() const62 StringRef getFullSymbolSpec() const override { return fullSpec; }
63
64 /// Parse a floating point value from the stream.
parseFloat(double & result)65 ParseResult parseFloat(double &result) override {
66 bool negative = parser.consumeIf(Token::minus);
67 Token curTok = parser.getToken();
68
69 // Check for a floating point value.
70 if (curTok.is(Token::floatliteral)) {
71 auto val = curTok.getFloatingPointValue();
72 if (!val.hasValue())
73 return emitError(curTok.getLoc(), "floating point value too large");
74 parser.consumeToken(Token::floatliteral);
75 result = negative ? -*val : *val;
76 return success();
77 }
78
79 // TODO: support hex floating point values.
80 return emitError(getCurrentLocation(), "expected floating point literal");
81 }
82
83 /// Parse an optional integer value from the stream.
parseOptionalInteger(uint64_t & result)84 OptionalParseResult parseOptionalInteger(uint64_t &result) override {
85 Token curToken = parser.getToken();
86 if (curToken.isNot(Token::integer, Token::minus))
87 return llvm::None;
88
89 bool negative = parser.consumeIf(Token::minus);
90 Token curTok = parser.getToken();
91 if (parser.parseToken(Token::integer, "expected integer value"))
92 return failure();
93
94 auto val = curTok.getUInt64IntegerValue();
95 if (!val)
96 return emitError(curTok.getLoc(), "integer value too large");
97 result = negative ? -*val : *val;
98 return success();
99 }
100
101 //===--------------------------------------------------------------------===//
102 // Token Parsing
103 //===--------------------------------------------------------------------===//
104
105 /// Parse a `->` token.
parseArrow()106 ParseResult parseArrow() override {
107 return parser.parseToken(Token::arrow, "expected '->'");
108 }
109
110 /// Parses a `->` if present.
parseOptionalArrow()111 ParseResult parseOptionalArrow() override {
112 return success(parser.consumeIf(Token::arrow));
113 }
114
115 /// Parse a '{' token.
parseLBrace()116 ParseResult parseLBrace() override {
117 return parser.parseToken(Token::l_brace, "expected '{'");
118 }
119
120 /// Parse a '{' token if present
parseOptionalLBrace()121 ParseResult parseOptionalLBrace() override {
122 return success(parser.consumeIf(Token::l_brace));
123 }
124
125 /// Parse a `}` token.
parseRBrace()126 ParseResult parseRBrace() override {
127 return parser.parseToken(Token::r_brace, "expected '}'");
128 }
129
130 /// Parse a `}` token if present
parseOptionalRBrace()131 ParseResult parseOptionalRBrace() override {
132 return success(parser.consumeIf(Token::r_brace));
133 }
134
135 /// Parse a `:` token.
parseColon()136 ParseResult parseColon() override {
137 return parser.parseToken(Token::colon, "expected ':'");
138 }
139
140 /// Parse a `:` token if present.
parseOptionalColon()141 ParseResult parseOptionalColon() override {
142 return success(parser.consumeIf(Token::colon));
143 }
144
145 /// Parse a `,` token.
parseComma()146 ParseResult parseComma() override {
147 return parser.parseToken(Token::comma, "expected ','");
148 }
149
150 /// Parse a `,` token if present.
parseOptionalComma()151 ParseResult parseOptionalComma() override {
152 return success(parser.consumeIf(Token::comma));
153 }
154
155 /// Parses a `...` if present.
parseOptionalEllipsis()156 ParseResult parseOptionalEllipsis() override {
157 return success(parser.consumeIf(Token::ellipsis));
158 }
159
160 /// Parse a `=` token.
parseEqual()161 ParseResult parseEqual() override {
162 return parser.parseToken(Token::equal, "expected '='");
163 }
164
165 /// Parse a `=` token if present.
parseOptionalEqual()166 ParseResult parseOptionalEqual() override {
167 return success(parser.consumeIf(Token::equal));
168 }
169
170 /// Parse a '<' token.
parseLess()171 ParseResult parseLess() override {
172 return parser.parseToken(Token::less, "expected '<'");
173 }
174
175 /// Parse a `<` token if present.
parseOptionalLess()176 ParseResult parseOptionalLess() override {
177 return success(parser.consumeIf(Token::less));
178 }
179
180 /// Parse a '>' token.
parseGreater()181 ParseResult parseGreater() override {
182 return parser.parseToken(Token::greater, "expected '>'");
183 }
184
185 /// Parse a `>` token if present.
parseOptionalGreater()186 ParseResult parseOptionalGreater() override {
187 return success(parser.consumeIf(Token::greater));
188 }
189
190 /// Parse a `(` token.
parseLParen()191 ParseResult parseLParen() override {
192 return parser.parseToken(Token::l_paren, "expected '('");
193 }
194
195 /// Parses a '(' if present.
parseOptionalLParen()196 ParseResult parseOptionalLParen() override {
197 return success(parser.consumeIf(Token::l_paren));
198 }
199
200 /// Parse a `)` token.
parseRParen()201 ParseResult parseRParen() override {
202 return parser.parseToken(Token::r_paren, "expected ')'");
203 }
204
205 /// Parses a ')' if present.
parseOptionalRParen()206 ParseResult parseOptionalRParen() override {
207 return success(parser.consumeIf(Token::r_paren));
208 }
209
210 /// Parse a `[` token.
parseLSquare()211 ParseResult parseLSquare() override {
212 return parser.parseToken(Token::l_square, "expected '['");
213 }
214
215 /// Parses a '[' if present.
parseOptionalLSquare()216 ParseResult parseOptionalLSquare() override {
217 return success(parser.consumeIf(Token::l_square));
218 }
219
220 /// Parse a `]` token.
parseRSquare()221 ParseResult parseRSquare() override {
222 return parser.parseToken(Token::r_square, "expected ']'");
223 }
224
225 /// Parses a ']' if present.
parseOptionalRSquare()226 ParseResult parseOptionalRSquare() override {
227 return success(parser.consumeIf(Token::r_square));
228 }
229
230 /// Parses a '?' if present.
parseOptionalQuestion()231 ParseResult parseOptionalQuestion() override {
232 return success(parser.consumeIf(Token::question));
233 }
234
235 /// Parses a '*' if present.
parseOptionalStar()236 ParseResult parseOptionalStar() override {
237 return success(parser.consumeIf(Token::star));
238 }
239
240 /// Parses a quoted string token if present.
parseOptionalString(StringRef * string)241 ParseResult parseOptionalString(StringRef *string) override {
242 if (!parser.getToken().is(Token::string))
243 return failure();
244
245 if (string)
246 *string = parser.getTokenSpelling().drop_front().drop_back();
247 parser.consumeToken();
248 return success();
249 }
250
251 /// Returns true if the current token corresponds to a keyword.
isCurrentTokenAKeyword() const252 bool isCurrentTokenAKeyword() const {
253 return parser.getToken().is(Token::bare_identifier) ||
254 parser.getToken().isKeyword();
255 }
256
257 /// Parse the given keyword if present.
parseOptionalKeyword(StringRef keyword)258 ParseResult parseOptionalKeyword(StringRef keyword) override {
259 // Check that the current token has the same spelling.
260 if (!isCurrentTokenAKeyword() || parser.getTokenSpelling() != keyword)
261 return failure();
262 parser.consumeToken();
263 return success();
264 }
265
266 /// Parse a keyword, if present, into 'keyword'.
parseOptionalKeyword(StringRef * keyword)267 ParseResult parseOptionalKeyword(StringRef *keyword) override {
268 // Check that the current token is a keyword.
269 if (!isCurrentTokenAKeyword())
270 return failure();
271
272 *keyword = parser.getTokenSpelling();
273 parser.consumeToken();
274 return success();
275 }
276
277 //===--------------------------------------------------------------------===//
278 // Attribute Parsing
279 //===--------------------------------------------------------------------===//
280
281 /// Parse an arbitrary attribute and return it in result.
parseAttribute(Attribute & result,Type type)282 ParseResult parseAttribute(Attribute &result, Type type) override {
283 result = parser.parseAttribute(type);
284 return success(static_cast<bool>(result));
285 }
286
287 /// Parse an affine map instance into 'map'.
parseAffineMap(AffineMap & map)288 ParseResult parseAffineMap(AffineMap &map) override {
289 return parser.parseAffineMapReference(map);
290 }
291
292 /// Parse an integer set instance into 'set'.
printIntegerSet(IntegerSet & set)293 ParseResult printIntegerSet(IntegerSet &set) override {
294 return parser.parseIntegerSetReference(set);
295 }
296
297 //===--------------------------------------------------------------------===//
298 // Type Parsing
299 //===--------------------------------------------------------------------===//
300
parseType(Type & result)301 ParseResult parseType(Type &result) override {
302 result = parser.parseType();
303 return success(static_cast<bool>(result));
304 }
305
parseDimensionList(SmallVectorImpl<int64_t> & dimensions,bool allowDynamic)306 ParseResult parseDimensionList(SmallVectorImpl<int64_t> &dimensions,
307 bool allowDynamic) override {
308 return parser.parseDimensionListRanked(dimensions, allowDynamic);
309 }
310
parseOptionalType(Type & result)311 OptionalParseResult parseOptionalType(Type &result) override {
312 return parser.parseOptionalType(result);
313 }
314
315 private:
316 /// The full symbol specification.
317 StringRef fullSpec;
318
319 /// The source location of the dialect symbol.
320 SMLoc nameLoc;
321
322 /// The main parser.
323 Parser &parser;
324 };
325 } // namespace
326
327 /// Parse the body of a pretty dialect symbol, which starts and ends with <>'s,
328 /// and may be recursive. Return with the 'prettyName' StringRef encompassing
329 /// the entire pretty name.
330 ///
331 /// pretty-dialect-sym-body ::= '<' pretty-dialect-sym-contents+ '>'
332 /// pretty-dialect-sym-contents ::= pretty-dialect-sym-body
333 /// | '(' pretty-dialect-sym-contents+ ')'
334 /// | '[' pretty-dialect-sym-contents+ ']'
335 /// | '{' pretty-dialect-sym-contents+ '}'
336 /// | '[^[<({>\])}\0]+'
337 ///
parsePrettyDialectSymbolName(StringRef & prettyName)338 ParseResult Parser::parsePrettyDialectSymbolName(StringRef &prettyName) {
339 // Pretty symbol names are a relatively unstructured format that contains a
340 // series of properly nested punctuation, with anything else in the middle.
341 // Scan ahead to find it and consume it if successful, otherwise emit an
342 // error.
343 auto *curPtr = getTokenSpelling().data();
344
345 SmallVector<char, 8> nestedPunctuation;
346
347 // Scan over the nested punctuation, bailing out on error and consuming until
348 // we find the end. We know that we're currently looking at the '<', so we
349 // can go until we find the matching '>' character.
350 assert(*curPtr == '<');
351 do {
352 char c = *curPtr++;
353 switch (c) {
354 case '\0':
355 // This also handles the EOF case.
356 return emitError("unexpected nul or EOF in pretty dialect name");
357 case '<':
358 case '[':
359 case '(':
360 case '{':
361 nestedPunctuation.push_back(c);
362 continue;
363
364 case '-':
365 // The sequence `->` is treated as special token.
366 if (*curPtr == '>')
367 ++curPtr;
368 continue;
369
370 case '>':
371 if (nestedPunctuation.pop_back_val() != '<')
372 return emitError("unbalanced '>' character in pretty dialect name");
373 break;
374 case ']':
375 if (nestedPunctuation.pop_back_val() != '[')
376 return emitError("unbalanced ']' character in pretty dialect name");
377 break;
378 case ')':
379 if (nestedPunctuation.pop_back_val() != '(')
380 return emitError("unbalanced ')' character in pretty dialect name");
381 break;
382 case '}':
383 if (nestedPunctuation.pop_back_val() != '{')
384 return emitError("unbalanced '}' character in pretty dialect name");
385 break;
386
387 default:
388 continue;
389 }
390 } while (!nestedPunctuation.empty());
391
392 // Ok, we succeeded, remember where we stopped, reset the lexer to know it is
393 // consuming all this stuff, and return.
394 state.lex.resetPointer(curPtr);
395
396 unsigned length = curPtr - prettyName.begin();
397 prettyName = StringRef(prettyName.begin(), length);
398 consumeToken();
399 return success();
400 }
401
402 /// Parse an extended dialect symbol.
403 template <typename Symbol, typename SymbolAliasMap, typename CreateFn>
parseExtendedSymbol(Parser & p,Token::Kind identifierTok,SymbolAliasMap & aliases,CreateFn && createSymbol)404 static Symbol parseExtendedSymbol(Parser &p, Token::Kind identifierTok,
405 SymbolAliasMap &aliases,
406 CreateFn &&createSymbol) {
407 // Parse the dialect namespace.
408 StringRef identifier = p.getTokenSpelling().drop_front();
409 auto loc = p.getToken().getLoc();
410 p.consumeToken(identifierTok);
411
412 // If there is no '<' token following this, and if the typename contains no
413 // dot, then we are parsing a symbol alias.
414 if (p.getToken().isNot(Token::less) && !identifier.contains('.')) {
415 // Check for an alias for this type.
416 auto aliasIt = aliases.find(identifier);
417 if (aliasIt == aliases.end())
418 return (p.emitError("undefined symbol alias id '" + identifier + "'"),
419 nullptr);
420 return aliasIt->second;
421 }
422
423 // Otherwise, we are parsing a dialect-specific symbol. If the name contains
424 // a dot, then this is the "pretty" form. If not, it is the verbose form that
425 // looks like <"...">.
426 std::string symbolData;
427 auto dialectName = identifier;
428
429 // Handle the verbose form, where "identifier" is a simple dialect name.
430 if (!identifier.contains('.')) {
431 // Consume the '<'.
432 if (p.parseToken(Token::less, "expected '<' in dialect type"))
433 return nullptr;
434
435 // Parse the symbol specific data.
436 if (p.getToken().isNot(Token::string))
437 return (p.emitError("expected string literal data in dialect symbol"),
438 nullptr);
439 symbolData = p.getToken().getStringValue();
440 loc = llvm::SMLoc::getFromPointer(p.getToken().getLoc().getPointer() + 1);
441 p.consumeToken(Token::string);
442
443 // Consume the '>'.
444 if (p.parseToken(Token::greater, "expected '>' in dialect symbol"))
445 return nullptr;
446 } else {
447 // Ok, the dialect name is the part of the identifier before the dot, the
448 // part after the dot is the dialect's symbol, or the start thereof.
449 auto dotHalves = identifier.split('.');
450 dialectName = dotHalves.first;
451 auto prettyName = dotHalves.second;
452 loc = llvm::SMLoc::getFromPointer(prettyName.data());
453
454 // If the dialect's symbol is followed immediately by a <, then lex the body
455 // of it into prettyName.
456 if (p.getToken().is(Token::less) &&
457 prettyName.bytes_end() == p.getTokenSpelling().bytes_begin()) {
458 if (p.parsePrettyDialectSymbolName(prettyName))
459 return nullptr;
460 }
461
462 symbolData = prettyName.str();
463 }
464
465 // Record the name location of the type remapped to the top level buffer.
466 llvm::SMLoc locInTopLevelBuffer = p.remapLocationToTopLevelBuffer(loc);
467 p.getState().symbols.nestedParserLocs.push_back(locInTopLevelBuffer);
468
469 // Call into the provided symbol construction function.
470 Symbol sym = createSymbol(dialectName, symbolData, loc);
471
472 // Pop the last parser location.
473 p.getState().symbols.nestedParserLocs.pop_back();
474 return sym;
475 }
476
477 /// Parses a symbol, of type 'T', and returns it if parsing was successful. If
478 /// parsing failed, nullptr is returned. The number of bytes read from the input
479 /// string is returned in 'numRead'.
480 template <typename T, typename ParserFn>
parseSymbol(StringRef inputStr,MLIRContext * context,SymbolState & symbolState,ParserFn && parserFn,size_t * numRead=nullptr)481 static T parseSymbol(StringRef inputStr, MLIRContext *context,
482 SymbolState &symbolState, ParserFn &&parserFn,
483 size_t *numRead = nullptr) {
484 SourceMgr sourceMgr;
485 auto memBuffer = MemoryBuffer::getMemBuffer(
486 inputStr, /*BufferName=*/"<mlir_parser_buffer>",
487 /*RequiresNullTerminator=*/false);
488 sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
489 ParserState state(sourceMgr, context, symbolState);
490 Parser parser(state);
491
492 Token startTok = parser.getToken();
493 T symbol = parserFn(parser);
494 if (!symbol)
495 return T();
496
497 // If 'numRead' is valid, then provide the number of bytes that were read.
498 Token endTok = parser.getToken();
499 if (numRead) {
500 *numRead = static_cast<size_t>(endTok.getLoc().getPointer() -
501 startTok.getLoc().getPointer());
502
503 // Otherwise, ensure that all of the tokens were parsed.
504 } else if (startTok.getLoc() != endTok.getLoc() && endTok.isNot(Token::eof)) {
505 parser.emitError(endTok.getLoc(), "encountered unexpected token");
506 return T();
507 }
508 return symbol;
509 }
510
511 /// Parse an extended attribute.
512 ///
513 /// extended-attribute ::= (dialect-attribute | attribute-alias)
514 /// dialect-attribute ::= `#` dialect-namespace `<` `"` attr-data `"` `>`
515 /// dialect-attribute ::= `#` alias-name pretty-dialect-sym-body?
516 /// attribute-alias ::= `#` alias-name
517 ///
parseExtendedAttr(Type type)518 Attribute Parser::parseExtendedAttr(Type type) {
519 Attribute attr = parseExtendedSymbol<Attribute>(
520 *this, Token::hash_identifier, state.symbols.attributeAliasDefinitions,
521 [&](StringRef dialectName, StringRef symbolData,
522 llvm::SMLoc loc) -> Attribute {
523 // Parse an optional trailing colon type.
524 Type attrType = type;
525 if (consumeIf(Token::colon) && !(attrType = parseType()))
526 return Attribute();
527
528 // If we found a registered dialect, then ask it to parse the attribute.
529 if (Dialect *dialect =
530 builder.getContext()->getOrLoadDialect(dialectName)) {
531 return parseSymbol<Attribute>(
532 symbolData, state.context, state.symbols, [&](Parser &parser) {
533 CustomDialectAsmParser customParser(symbolData, parser);
534 return dialect->parseAttribute(customParser, attrType);
535 });
536 }
537
538 // Otherwise, form a new opaque attribute.
539 return OpaqueAttr::getChecked(
540 Identifier::get(dialectName, state.context), symbolData,
541 attrType ? attrType : NoneType::get(state.context),
542 getEncodedSourceLocation(loc));
543 });
544
545 // Ensure that the attribute has the same type as requested.
546 if (attr && type && attr.getType() != type) {
547 emitError("attribute type different than expected: expected ")
548 << type << ", but got " << attr.getType();
549 return nullptr;
550 }
551 return attr;
552 }
553
554 /// Parse an extended type.
555 ///
556 /// extended-type ::= (dialect-type | type-alias)
557 /// dialect-type ::= `!` dialect-namespace `<` `"` type-data `"` `>`
558 /// dialect-type ::= `!` alias-name pretty-dialect-attribute-body?
559 /// type-alias ::= `!` alias-name
560 ///
parseExtendedType()561 Type Parser::parseExtendedType() {
562 return parseExtendedSymbol<Type>(
563 *this, Token::exclamation_identifier, state.symbols.typeAliasDefinitions,
564 [&](StringRef dialectName, StringRef symbolData,
565 llvm::SMLoc loc) -> Type {
566 // If we found a registered dialect, then ask it to parse the type.
567 auto *dialect = state.context->getOrLoadDialect(dialectName);
568
569 if (dialect) {
570 return parseSymbol<Type>(
571 symbolData, state.context, state.symbols, [&](Parser &parser) {
572 CustomDialectAsmParser customParser(symbolData, parser);
573 return dialect->parseType(customParser);
574 });
575 }
576
577 // Otherwise, form a new opaque type.
578 return OpaqueType::getChecked(
579 Identifier::get(dialectName, state.context), symbolData,
580 state.context, getEncodedSourceLocation(loc));
581 });
582 }
583
584 //===----------------------------------------------------------------------===//
585 // mlir::parseAttribute/parseType
586 //===----------------------------------------------------------------------===//
587
588 /// Parses a symbol, of type 'T', and returns it if parsing was successful. If
589 /// parsing failed, nullptr is returned. The number of bytes read from the input
590 /// string is returned in 'numRead'.
591 template <typename T, typename ParserFn>
parseSymbol(StringRef inputStr,MLIRContext * context,size_t & numRead,ParserFn && parserFn)592 static T parseSymbol(StringRef inputStr, MLIRContext *context, size_t &numRead,
593 ParserFn &&parserFn) {
594 SymbolState aliasState;
595 return parseSymbol<T>(
596 inputStr, context, aliasState,
597 [&](Parser &parser) {
598 SourceMgrDiagnosticHandler handler(
599 const_cast<llvm::SourceMgr &>(parser.getSourceMgr()),
600 parser.getContext());
601 return parserFn(parser);
602 },
603 &numRead);
604 }
605
parseAttribute(StringRef attrStr,MLIRContext * context)606 Attribute mlir::parseAttribute(StringRef attrStr, MLIRContext *context) {
607 size_t numRead = 0;
608 return parseAttribute(attrStr, context, numRead);
609 }
parseAttribute(StringRef attrStr,Type type)610 Attribute mlir::parseAttribute(StringRef attrStr, Type type) {
611 size_t numRead = 0;
612 return parseAttribute(attrStr, type, numRead);
613 }
614
parseAttribute(StringRef attrStr,MLIRContext * context,size_t & numRead)615 Attribute mlir::parseAttribute(StringRef attrStr, MLIRContext *context,
616 size_t &numRead) {
617 return parseSymbol<Attribute>(attrStr, context, numRead, [](Parser &parser) {
618 return parser.parseAttribute();
619 });
620 }
parseAttribute(StringRef attrStr,Type type,size_t & numRead)621 Attribute mlir::parseAttribute(StringRef attrStr, Type type, size_t &numRead) {
622 return parseSymbol<Attribute>(
623 attrStr, type.getContext(), numRead,
624 [type](Parser &parser) { return parser.parseAttribute(type); });
625 }
626
parseType(StringRef typeStr,MLIRContext * context)627 Type mlir::parseType(StringRef typeStr, MLIRContext *context) {
628 size_t numRead = 0;
629 return parseType(typeStr, context, numRead);
630 }
631
parseType(StringRef typeStr,MLIRContext * context,size_t & numRead)632 Type mlir::parseType(StringRef typeStr, MLIRContext *context, size_t &numRead) {
633 return parseSymbol<Type>(typeStr, context, numRead,
634 [](Parser &parser) { return parser.parseType(); });
635 }
636