Add parsing for attributes and attibutes on operations.  Add IR representation
for attributes on operations.  Split Operation out from OperationInst so it
can be shared with OperationStmt one day.

PiperOrigin-RevId: 203325366
diff --git a/lib/Parser/Parser.cpp b/lib/Parser/Parser.cpp
index 8c44bbd..a4d532f 100644
--- a/lib/Parser/Parser.cpp
+++ b/lib/Parser/Parser.cpp
@@ -18,12 +18,12 @@
 // This file implements the parser for the MLIR textual form.
 //
 //===----------------------------------------------------------------------===//
-#include <stack>
 
 #include "mlir/Parser.h"
 #include "Lexer.h"
 #include "mlir/IR/AffineExpr.h"
 #include "mlir/IR/AffineMap.h"
+#include "mlir/IR/Attributes.h"
 #include "mlir/IR/CFGFunction.h"
 #include "mlir/IR/Module.h"
 #include "mlir/IR/MLFunction.h"
@@ -92,9 +92,7 @@
   std::unique_ptr<Module> module;
 
   // A map from affine map identifier to AffineMap.
-  // TODO(andydavis) Remove use of unique_ptr when AffineMaps are bump pointer
-  // allocated.
-  llvm::StringMap<std::unique_ptr<AffineMap>> affineMaps;
+  llvm::StringMap<AffineMap*> affineMapDefinitions;
 
 private:
   // Helper methods.
@@ -153,6 +151,10 @@
   Type *parseType();
   ParseResult parseTypeList(SmallVectorImpl<Type*> &elements);
 
+  // Attribute parsing.
+  Attribute *parseAttribute();
+  ParseResult parseAttributeDict(SmallVectorImpl<NamedAttribute> &attributes);
+
   // Parsing identifiers' lists for polyhedral structures.
   ParseResult parseDimIdList(AffineMapParserState &state);
   ParseResult parseSymbolIdList(AffineMapParserState &state);
@@ -160,7 +162,7 @@
 
   // Polyhedral structures.
   ParseResult parseAffineMapDef();
-  ParseResult parseAffineMapInline(StringRef mapId, AffineMap *&affineMap);
+  AffineMap *parseAffineMapInline(StringRef mapId);
   AffineExpr *parseAffineExpr(const AffineMapParserState &state);
 
   AffineExpr *parseParentheticalExpr(const AffineMapParserState &state);
@@ -543,6 +545,110 @@
 }  // end anonymous namespace
 
 //===----------------------------------------------------------------------===//
+// Attribute parsing.
+//===----------------------------------------------------------------------===//
+
+
+/// Attribute parsing.
+///
+///  attribute-value ::= bool-literal
+///                    | integer-literal
+///                    | float-literal
+///                    | string-literal
+///                    | `[` (attribute-value (`,` attribute-value)*)? `]`
+///
+Attribute *Parser::parseAttribute() {
+  switch (curToken.getKind()) {
+  case Token::kw_true:
+    consumeToken(Token::kw_true);
+    return BoolAttr::get(true, context);
+  case Token::kw_false:
+    consumeToken(Token::kw_false);
+    return BoolAttr::get(false, context);
+
+  case Token::integer: {
+    auto val = curToken.getUInt64IntegerValue();
+    if (!val.hasValue() || (int64_t)val.getValue() < 0)
+      return (emitError("integer too large for attribute"), nullptr);
+    consumeToken(Token::integer);
+    return IntegerAttr::get((int64_t)val.getValue(), context);
+  }
+
+  case Token::minus: {
+    consumeToken(Token::minus);
+    if (curToken.is(Token::integer)) {
+      auto val = curToken.getUInt64IntegerValue();
+      if (!val.hasValue() || (int64_t)-val.getValue() >= 0)
+        return (emitError("integer too large for attribute"), nullptr);
+      consumeToken(Token::integer);
+      return IntegerAttr::get((int64_t)-val.getValue(), context);
+    }
+
+    return (emitError("expected constant integer or floating point value"),
+            nullptr);
+  }
+
+  case Token::string: {
+    auto val = curToken.getStringValue();
+    consumeToken(Token::string);
+    return StringAttr::get(val, context);
+  }
+
+  case Token::l_bracket: {
+    consumeToken(Token::l_bracket);
+    SmallVector<Attribute*, 4> elements;
+
+    auto parseElt = [&]() -> ParseResult {
+      elements.push_back(parseAttribute());
+      return elements.back() ? ParseSuccess : ParseFailure;
+    };
+
+    if (parseCommaSeparatedList(Token::r_bracket, parseElt))
+      return nullptr;
+    return ArrayAttr::get(elements, context);
+  }
+  default:
+    // TODO: Handle floating point.
+    return (emitError("expected constant attribute value"), nullptr);
+  }
+}
+
+
+/// Attribute dictionary.
+///
+///  attribute-dict ::= `{` `}`
+///                   | `{` attribute-entry (`,` attribute-entry)* `}`
+///  attribute-entry ::= bare-id `:` attribute-value
+///
+ParseResult Parser::parseAttributeDict(
+    SmallVectorImpl<NamedAttribute> &attributes) {
+  consumeToken(Token::l_brace);
+
+  auto parseElt = [&]() -> ParseResult {
+    // We allow keywords as attribute names.
+    if (curToken.isNot(Token::bare_identifier, Token::inttype) &&
+        !curToken.isKeyword())
+      return emitError("expected attribute name");
+    auto nameId = Identifier::get(curToken.getSpelling(), context);
+    consumeToken();
+
+    if (!consumeIf(Token::colon))
+      return emitError("expected ':' in attribute list");
+
+    auto attr = parseAttribute();
+    if (!attr) return ParseFailure;
+
+    attributes.push_back({nameId, attr});
+    return ParseSuccess;
+  };
+
+  if (parseCommaSeparatedList(Token::r_brace, parseElt))
+    return ParseFailure;
+
+  return ParseSuccess;
+}
+
+//===----------------------------------------------------------------------===//
 // Polyhedral structures.
 //===----------------------------------------------------------------------===//
 
@@ -554,24 +660,23 @@
   assert(curToken.is(Token::affine_map_identifier));
 
   StringRef affineMapId = curToken.getSpelling().drop_front();
+
+  // Check for redefinitions.
+  auto *&entry = affineMapDefinitions[affineMapId];
+  if (entry)
+    return emitError("redefinition of affine map id '" + affineMapId + "'");
+
   consumeToken(Token::affine_map_identifier);
 
-  // Check that 'affineMapId' is unique.
-  // TODO(andydavis) Add a unit test for this case.
-  if (affineMaps.count(affineMapId) > 0)
-    return emitError("redefinition of affine map id '" + affineMapId + "'");
   // Parse the '='
   if (!consumeIf(Token::equal))
     return emitError("expected '=' in affine map outlined definition");
 
-  AffineMap *affineMap = nullptr;
-  if (parseAffineMapInline(affineMapId, affineMap))
+  entry = parseAffineMapInline(affineMapId);
+  if (!entry)
     return ParseFailure;
 
-  // TODO(bondhugula): Disable adding affineMapId to Parser::affineMaps for now;
-  // instead add to module for easy printing.
-  module->affineMapList.push_back(affineMap);
-
+  module->affineMapList.push_back(entry);
   return ParseSuccess;
 }
 
@@ -946,25 +1051,24 @@
 /// dim-size ::= affine-expr | `min` `(` affine-expr ( `,` affine-expr)+ `)`
 ///
 /// multi-dim-affine-expr ::= `(` affine-expr (`,` affine-expr)* `)
-ParseResult Parser::parseAffineMapInline(StringRef mapId,
-                                         AffineMap *&affineMap) {
+AffineMap *Parser::parseAffineMapInline(StringRef mapId) {
   AffineMapParserState state;
 
   // List of dimensional identifiers.
   if (parseDimIdList(state))
-    return ParseFailure;
+    return nullptr;
 
   // Symbols are optional.
   if (curToken.is(Token::l_bracket)) {
     if (parseSymbolIdList(state))
-      return ParseFailure;
+      return nullptr;
   }
   if (!consumeIf(Token::arrow)) {
-    return (emitError("expected '->' or '['"), ParseFailure);
+    return (emitError("expected '->' or '['"), nullptr);
   }
   if (!consumeIf(Token::l_paren)) {
     emitError("expected '(' at start of affine map range");
-    return ParseFailure;
+    return nullptr;
   }
 
   SmallVector<AffineExpr *, 4> exprs;
@@ -979,12 +1083,11 @@
   // affine expressions); the list cannot be empty.
   // Grammar: multi-dim-affine-expr ::= `(` affine-expr (`,` affine-expr)* `)
   if (parseCommaSeparatedList(Token::r_paren, parseElt, false))
-    return ParseFailure;
+    return nullptr;
 
   // Parsed a valid affine map.
-  affineMap =
-      AffineMap::get(state.getNumDims(), state.getNumSymbols(), exprs, context);
-  return ParseSuccess;
+  return AffineMap::get(state.getNumDims(), state.getNumSymbols(), exprs,
+                        context);
 }
 
 //===----------------------------------------------------------------------===//
@@ -1180,14 +1283,20 @@
   consumeToken(Token::string);
 
   if (!consumeIf(Token::l_paren))
-    return (emitError("expected '(' in operation"), nullptr);
+    return (emitError("expected '(' to start operand list"), nullptr);
 
   // TODO: Parse operands.
   if (!consumeIf(Token::r_paren))
-    return (emitError("expected '(' in operation"), nullptr);
+    return (emitError("expected ')' in operand list"), nullptr);
+
+  SmallVector<NamedAttribute, 4> attributes;
+  if (curToken.is(Token::l_brace)) {
+    if (parseAttributeDict(attributes))
+      return nullptr;
+  }
 
   auto nameId = Identifier::get(name, context);
-  return new OperationInst(nameId);
+  return new OperationInst(nameId, attributes);
 }
 
 
diff --git a/lib/Parser/Token.cpp b/lib/Parser/Token.cpp
index e1e4bed..e6e0ebf 100644
--- a/lib/Parser/Token.cpp
+++ b/lib/Parser/Token.cpp
@@ -48,9 +48,21 @@
   return result;
 }
 
+/// For an integer token, return its value as a uint64_t.  If it doesn't fit,
+/// return None.
+Optional<uint64_t> Token::getUInt64IntegerValue() const {
+  bool isHex = spelling.size() > 1 && spelling[1] == 'x';
+
+  uint64_t result = 0;
+  if (spelling.getAsInteger(isHex ? 0 : 10, result))
+    return None;
+  return result;
+}
+
+
 /// For an inttype token, return its bitwidth.
 Optional<unsigned> Token::getIntTypeBitwidth() const {
- unsigned result = 0;
+  unsigned result = 0;
   if (spelling[1] == '0' ||
       spelling.drop_front().getAsInteger(10, result) ||
       // Arbitrary but large limit on bitwidth.
@@ -74,11 +86,20 @@
 /// token as a string.  Warning: This will abort on markers, identifiers and
 /// literal tokens since they have no fixed spelling.
 StringRef Token::getTokenSpelling(Kind kind) {
-   switch (kind) {
-   default: assert(0 && "This token kind has no fixed spelling");
+  switch (kind) {
+  default: assert(0 && "This token kind has no fixed spelling");
 #define TOK_PUNCTUATION(NAME, SPELLING) case NAME: return SPELLING;
 #define TOK_OPERATOR(NAME, SPELLING) case NAME: return SPELLING;
 #define TOK_KEYWORD(SPELLING) case kw_##SPELLING: return #SPELLING;
 #include "TokenKinds.def"
-   }
+  }
+}
+
+/// Return true if this is one of the keyword token kinds (e.g. kw_if).
+bool Token::isKeyword() const {
+  switch (kind) {
+  default: return false;
+#define TOK_KEYWORD(SPELLING) case kw_##SPELLING: return true;
+#include "TokenKinds.def"
+  }
 }
diff --git a/lib/Parser/Token.h b/lib/Parser/Token.h
index bc9e8e4..73baaac 100644
--- a/lib/Parser/Token.h
+++ b/lib/Parser/Token.h
@@ -67,12 +67,19 @@
     return !isAny(k1, k2, others...);
   }
 
+  /// Return true if this is one of the keyword token kinds (e.g. kw_if).
+  bool isKeyword() const;
+
   // Helpers to decode specific sorts of tokens.
 
   /// For an integer token, return its value as an unsigned.  If it doesn't fit,
   /// return None.
   Optional<unsigned> getUnsignedIntegerValue() const;
 
+  /// For an integer token, return its value as an int64_t.  If it doesn't fit,
+  /// return None.
+  Optional<uint64_t> getUInt64IntegerValue() const;
+
   /// For an inttype token, return its bitwidth.
   Optional<unsigned> getIntTypeBitwidth() const;
 
diff --git a/lib/Parser/TokenKinds.def b/lib/Parser/TokenKinds.def
index f87499f..158852a 100644
--- a/lib/Parser/TokenKinds.def
+++ b/lib/Parser/TokenKinds.def
@@ -87,22 +87,24 @@
 TOK_KEYWORD(affineint)
 TOK_KEYWORD(bf16)
 TOK_KEYWORD(br)
+TOK_KEYWORD(ceildiv)
 TOK_KEYWORD(cfgfunc)
 TOK_KEYWORD(else)
 TOK_KEYWORD(extfunc)
 TOK_KEYWORD(f16)
 TOK_KEYWORD(f32)
 TOK_KEYWORD(f64)
+TOK_KEYWORD(false)
+TOK_KEYWORD(floordiv)
 TOK_KEYWORD(for)
 TOK_KEYWORD(if)
 TOK_KEYWORD(memref)
 TOK_KEYWORD(mlfunc)
+TOK_KEYWORD(mod)
 TOK_KEYWORD(return)
 TOK_KEYWORD(tensor)
+TOK_KEYWORD(true)
 TOK_KEYWORD(vector)
-TOK_KEYWORD(mod)
-TOK_KEYWORD(floordiv)
-TOK_KEYWORD(ceildiv)
 
 #undef TOK_MARKER
 #undef TOK_IDENTIFIER