uint32_t
AppleObjCTypeEncodingParser::ReadNumber(lldb_utility::StringLexer &type) {
  uint32_t total = 0;
  while (type.HasAtLeast(1) && isdigit(type.Peek()))
    total = 10 * total + (type.Next() - '0');
  return total;
}
std::string
AppleObjCTypeEncodingParser::ReadStructName(lldb_utility::StringLexer &type) {
  StreamString buffer;
  while (type.HasAtLeast(1) && type.Peek() != '=')
    buffer.Printf("%c", type.Next());
  return buffer.GetString();
}
clang::QualType AppleObjCTypeEncodingParser::BuildAggregate(
    clang::ASTContext &ast_ctx, lldb_utility::StringLexer &type,
    bool for_expression, char opener, char closer, uint32_t kind) {
  if (!type.NextIf(opener))
    return clang::QualType();
  std::string name(ReadStructName(type));

  // We do not handle templated classes/structs at the moment. If the name has
  // a < in it, we are going to abandon this. We're still obliged to parse it,
  // so we just set a flag that means "Don't actually build anything."

  const bool is_templated = name.find('<') != std::string::npos;

  if (!type.NextIf('='))
    return clang::QualType();
  bool in_union = true;
  std::vector<StructElement> elements;
  while (in_union && type.HasAtLeast(1)) {
    if (type.NextIf(closer)) {
      in_union = false;
      break;
    } else {
      auto element = ReadStructElement(ast_ctx, type, for_expression);
      if (element.type.isNull())
        break;
      else
        elements.push_back(element);
    }
  }
  if (in_union)
    return clang::QualType();

  if (is_templated)
    return clang::QualType(); // This is where we bail out.  Sorry!

  ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
  if (!lldb_ctx)
    return clang::QualType();
  CompilerType union_type(lldb_ctx->CreateRecordType(
      nullptr, lldb::eAccessPublic, name.c_str(), kind, lldb::eLanguageTypeC));
  if (union_type) {
    ClangASTContext::StartTagDeclarationDefinition(union_type);

    unsigned int count = 0;
    for (auto element : elements) {
      if (element.name.empty()) {
        StreamString elem_name;
        elem_name.Printf("__unnamed_%u", count);
        element.name = elem_name.GetString();
      }
      ClangASTContext::AddFieldToRecordType(
          union_type, element.name.c_str(),
          CompilerType(&ast_ctx, element.type), lldb::eAccessPublic,
          element.bitfield);
      ++count;
    }
    ClangASTContext::CompleteTagDeclarationDefinition(union_type);
  }
  return ClangUtil::GetQualType(union_type);
}
std::string
AppleObjCTypeEncodingParser::ReadQuotedString(lldb_utility::StringLexer& type)
{
    StreamString buffer;
    while (type.HasAtLeast(1) && type.Peek() != '"')
        buffer.Printf("%c",type.Next());
    StringLexer::Character next = type.Next();
    assert (next == '"');
    return buffer.GetString();
}
AppleObjCTypeEncodingParser::StructElement
AppleObjCTypeEncodingParser::ReadStructElement (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression)
{
    StructElement retval;
    if (type.NextIf('"'))
        retval.name = ReadQuotedString(type);
    if (!type.NextIf('"'))
        return retval;
    uint32_t bitfield_size = 0;
    retval.type = BuildType(ast_ctx, type, for_expression, &bitfield_size);
    retval.bitfield = bitfield_size;
    return retval;
}
clang::QualType
AppleObjCTypeEncodingParser::BuildArray (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression)
{
    if (!type.NextIf('['))
        return clang::QualType();
    uint32_t size = ReadNumber(type);
    clang::QualType element_type(BuildType(ast_ctx, type, for_expression));
    if (!type.NextIf(']'))
        return clang::QualType();
    ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
    if (!lldb_ctx)
        return clang::QualType();
    ClangASTType array_type(lldb_ctx->CreateArrayType(ClangASTType(&ast_ctx,element_type.getAsOpaquePtr()), size, false));
    return array_type.GetQualType();
}
// the runtime can emit these in the form of @"SomeType", giving more specifics
// this would be interesting for expression parser interop, but since we actually try
// to avoid exposing the ivar info to the expression evaluator, consume but ignore the type info
// and always return an 'id'; if anything, dynamic typing will resolve things for us anyway
clang::QualType
AppleObjCTypeEncodingParser::BuildObjCObjectPointerType (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression)
{
    if (!type.NextIf('@'))
        return clang::QualType();
    
    std::string name;
    
    if (type.NextIf('"'))
    {
        // We have to be careful here.  We're used to seeing
        //   @"NSString"
        // but in records it is possible that the string following an @ is the name of the next field and @ means "id".
        // This is the case if anything unquoted except for "}", the end of the type, or another name follows the quoted string.
        //
        // E.g.
        // - @"NSString"@ means "id, followed by a field named NSString of type id"
        // - @"NSString"} means "a pointer to NSString and the end of the struct"
        // - @"NSString""nextField" means "a pointer to NSString and a field named nextField"
        // - @"NSString" followed by the end of the string means "a pointer to NSString"
        //
        // As a result, the rule is: If we see @ followed by a quoted string, we peek.
        // - If we see }, ), ], the end of the string, or a quote ("), the quoted string is a class name.
        // - If we see anything else, the quoted string is a field name and we push it back onto type.

        name = ReadQuotedString(type);
        
        if (type.HasAtLeast(1))
        {
            switch (type.Peek())
            {
            default:
                // roll back
                type.PutBack(name.length() + 2); // undo our consumption of the string and of the quotes
                name.clear();
                break;
            case '}':
            case ')':
            case ']':
            case '"':
                // the quoted string is a class name – see the rule
                break;
            }
        }
        else
        {
            // the quoted string is a class name – see the rule
        }
    }
    
    if (for_expression && !name.empty())
    {
        size_t less_than_pos = name.find_first_of('<');
        
        if (less_than_pos != std::string::npos)
        {
            if (less_than_pos == 0)
                return ast_ctx.getObjCIdType();
            else
                name.erase(less_than_pos);
        }
        
        DeclVendor *decl_vendor = m_runtime.GetDeclVendor();
        
        assert (decl_vendor); // how are we parsing type encodings for expressions if a type vendor isn't in play?
        
        const bool append = false;
        const uint32_t max_matches = 1;
        std::vector<clang::NamedDecl *> decls;
        
        uint32_t num_types = decl_vendor->FindDecls(ConstString(name),
                                                    append,
                                                    max_matches,
                                                    decls);

        // The user can forward-declare something that has no definition.  The runtime doesn't prohibit this at all.
        // This is a rare and very weird case.  We keep this assert in debug builds so we catch other weird cases.
#ifdef LLDB_CONFIGURATION_DEBUG
        assert(num_types);
#else
        if (!num_types)
            return ast_ctx.getObjCIdType();
#endif
        
        return ClangASTContext::GetTypeForDecl(decls[0]).GetPointerType().GetQualType();
    }
    else
    {
        // We're going to resolve this dynamically anyway, so just smile and wave.
        return ast_ctx.getObjCIdType();
    }
}