Add basic lexing and parsing support for SSA operands and definitions.  This
isn't actually constructing IR objects yet, it is eating the tokens and
discarding them.

PiperOrigin-RevId: 203616265
diff --git a/lib/Parser/Parser.cpp b/lib/Parser/Parser.cpp
index f9849a7..94d1468 100644
--- a/lib/Parser/Parser.cpp
+++ b/lib/Parser/Parser.cpp
@@ -188,17 +188,23 @@
                                         const AffineMapParserState &state,
                                         AffineExpr *&result);
 
+  // SSA
+  ParseResult parseSSAUse();
+  ParseResult parseOptionalSSAUseList(Token::Kind endToken);
+  ParseResult parseSSAUseAndType();
+  ParseResult parseOptionalSSAUseAndTypeList(Token::Kind endToken);
+
   // Functions.
   ParseResult parseFunctionSignature(StringRef &name, FunctionType *&type);
   ParseResult parseExtFunc();
   ParseResult parseCFGFunc();
-  ParseResult parseMLFunc();
   ParseResult parseBasicBlock(CFGFunctionParserState &functionState);
   Statement *parseStatement(ParentType parent);
 
   OperationInst *parseCFGOperation(CFGFunctionParserState &functionState);
   TerminatorInst *parseTerminator(CFGFunctionParserState &functionState);
 
+  ParseResult parseMLFunc();
   ForStmt *parseForStmt(ParentType parent);
   IfStmt *parseIfStmt(ParentType parent);
   ParseResult parseNestedStatements(NodeStmt *parent);
@@ -658,7 +664,7 @@
 ///  affine-map-def ::= affine-map-id `=` affine-map-inline
 ///
 ParseResult Parser::parseAffineMapDef() {
-  assert(curToken.is(Token::affine_map_identifier));
+  assert(curToken.is(Token::hash_identifier));
 
   StringRef affineMapId = curToken.getSpelling().drop_front();
 
@@ -667,7 +673,7 @@
   if (entry)
     return emitError("redefinition of affine map id '" + affineMapId + "'");
 
-  consumeToken(Token::affine_map_identifier);
+  consumeToken(Token::hash_identifier);
 
   // Parse the '='
   if (!consumeIf(Token::equal))
@@ -1092,9 +1098,67 @@
 }
 
 //===----------------------------------------------------------------------===//
-// Functions
+// SSA
 //===----------------------------------------------------------------------===//
 
+/// Parse a SSA operand for an instruction or statement.
+///
+///   ssa-use ::= ssa-id | ssa-constant
+///
+ParseResult Parser::parseSSAUse() {
+  if (curToken.is(Token::percent_identifier)) {
+    StringRef name = curToken.getSpelling().drop_front();
+    consumeToken(Token::percent_identifier);
+    // TODO: Return this use.
+    (void)name;
+    return ParseSuccess;
+  }
+
+  // TODO: Parse SSA constants.
+
+  return emitError("expected SSA operand");
+}
+
+/// Parse a (possibly empty) list of SSA operands.
+///
+///   ssa-use-list ::= ssa-use (`,` ssa-use)*
+///   ssa-use-list-opt ::= ssa-use-list?
+///
+ParseResult Parser::parseOptionalSSAUseList(Token::Kind endToken) {
+  // TODO: Build and return this.
+  return parseCommaSeparatedList(
+      endToken, [&]() -> ParseResult { return parseSSAUse(); });
+}
+
+/// Parse an SSA use with an associated type.
+///
+///   ssa-use-and-type ::= ssa-use `:` type
+ParseResult Parser::parseSSAUseAndType() {
+  if (parseSSAUse())
+    return ParseFailure;
+
+  if (!consumeIf(Token::colon))
+    return emitError("expected ':' and type for SSA operand");
+
+  if (!parseType())
+    return ParseFailure;
+
+  return ParseSuccess;
+}
+
+/// Parse a (possibly empty) list of SSA operands with types.
+///
+///   ssa-use-and-type-list ::= ssa-use-and-type (`,` ssa-use-and-type)*
+///
+ParseResult Parser::parseOptionalSSAUseAndTypeList(Token::Kind endToken) {
+  // TODO: Build and return this.
+  return parseCommaSeparatedList(
+      endToken, [&]() -> ParseResult { return parseSSAUseAndType(); });
+}
+
+//===----------------------------------------------------------------------===//
+// Functions
+//===----------------------------------------------------------------------===//
 
 /// Parse a function signature, starting with a name and including the parameter
 /// list.
@@ -1237,7 +1301,13 @@
   // Add the block to the function.
   functionState.function->push_back(block);
 
-  // TODO: parse bb argument list.
+  // If an argument list is present, parse it.
+  if (consumeIf(Token::l_paren)) {
+    if (parseOptionalSSAUseAndTypeList(Token::r_paren))
+      return ParseFailure;
+
+    // TODO: attach it.
+  }
 
   if (!consumeIf(Token::colon))
     return emitError("expected ':' after basic block name");
@@ -1280,7 +1350,13 @@
 OperationInst *Parser::
 parseCFGOperation(CFGFunctionParserState &functionState) {
 
-  // TODO: parse ssa-id.
+  StringRef resultID;
+  if (curToken.is(Token::percent_identifier)) {
+    resultID = curToken.getSpelling().drop_front();
+    consumeToken();
+    if (!consumeIf(Token::equal))
+      return (emitError("expected '=' after SSA name"), nullptr);
+  }
 
   if (curToken.isNot(Token::string))
     return (emitError("expected operation name in quotes"), nullptr);
@@ -1294,9 +1370,8 @@
   if (!consumeIf(Token::l_paren))
     return (emitError("expected '(' to start operand list"), nullptr);
 
-  // TODO: Parse operands.
-  if (!consumeIf(Token::r_paren))
-    return (emitError("expected ')' in operand list"), nullptr);
+  // Parse the operand list.
+  parseOptionalSSAUseList(Token::r_paren);
 
   SmallVector<NamedAttribute, 4> attributes;
   if (curToken.is(Token::l_brace)) {
@@ -1304,6 +1379,7 @@
       return nullptr;
   }
 
+  // TODO: Don't drop result name and operand names on the floor.
   auto nameId = Identifier::get(name, context);
   return new OperationInst(nameId, attributes, context);
 }
@@ -1334,6 +1410,7 @@
       return (emitError("expected basic block name"), nullptr);
     return new BranchInst(destBB);
   }
+    // TODO: cond_br.
   }
 }
 
@@ -1501,7 +1578,7 @@
       if (parseCFGFunc()) return nullptr;
       break;
 
-    case Token::affine_map_identifier:
+    case Token::hash_identifier:
       if (parseAffineMapDef()) return nullptr;
       break;