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/Dialect.h"
16 #include "mlir/IR/DialectImplementation.h"
17 #include "mlir/IR/StandardTypes.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