text: //===-- FormatEntity.cpp --------------------------------------------------===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===----------------------------------------------------------------------===//
  
  #include "lldb/Core/FormatEntity.h"
  
  #include "lldb/Core/Address.h"
  #include "lldb/Core/AddressRange.h"
  #include "lldb/Core/Debugger.h"
  #include "lldb/Core/DumpRegisterValue.h"
  #include "lldb/Core/Module.h"
  #include "lldb/Core/ValueObject.h"
  #include "lldb/Core/ValueObjectVariable.h"
  #include "lldb/DataFormatters/DataVisualization.h"
  #include "lldb/DataFormatters/FormatClasses.h"
  #include "lldb/DataFormatters/FormatManager.h"
  #include "lldb/DataFormatters/TypeSummary.h"
  #include "lldb/Expression/ExpressionVariable.h"
  #include "lldb/Interpreter/CommandInterpreter.h"
  #include "lldb/Symbol/Block.h"
  #include "lldb/Symbol/CompileUnit.h"
  #include "lldb/Symbol/CompilerType.h"
  #include "lldb/Symbol/Function.h"
  #include "lldb/Symbol/LineEntry.h"
  #include "lldb/Symbol/Symbol.h"
  #include "lldb/Symbol/SymbolContext.h"
  #include "lldb/Symbol/VariableList.h"
  #include "lldb/Target/ExecutionContext.h"
  #include "lldb/Target/ExecutionContextScope.h"
  #include "lldb/Target/Language.h"
  #include "lldb/Target/Process.h"
  #include "lldb/Target/RegisterContext.h"
  #include "lldb/Target/SectionLoadList.h"
  #include "lldb/Target/StackFrame.h"
  #include "lldb/Target/StopInfo.h"
  #include "lldb/Target/Target.h"
  #include "lldb/Target/Thread.h"
  #include "lldb/Utility/AnsiTerminal.h"
  #include "lldb/Utility/ArchSpec.h"
  #include "lldb/Utility/ConstString.h"
  #include "lldb/Utility/FileSpec.h"
  #include "lldb/Utility/Log.h"
  #include "lldb/Utility/Logging.h"
  #include "lldb/Utility/RegisterValue.h"
  #include "lldb/Utility/Stream.h"
  #include "lldb/Utility/StreamString.h"
  #include "lldb/Utility/StringList.h"
  #include "lldb/Utility/StructuredData.h"
  #include "lldb/lldb-defines.h"
  #include "lldb/lldb-forward.h"
  #include "llvm/ADT/STLExtras.h"
  #include "llvm/ADT/StringRef.h"
  #include "llvm/ADT/Triple.h"
  #include "llvm/Support/Compiler.h"
  
  #include <ctype.h>
  #include <inttypes.h>
  #include <memory>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <type_traits>
  #include <utility>
  
  namespace lldb_private {
  class ScriptInterpreter;
  }
  namespace lldb_private {
  struct RegisterInfo;
  }
  
  using namespace lldb;
  using namespace lldb_private;
  
  enum FileKind { FileError = 0, Basename, Dirname, Fullpath };
  
  #define ENTRY(n, t)                                                            \
    { n, nullptr, FormatEntity::Entry::Type::t, 0, 0, nullptr, false }
  #define ENTRY_VALUE(n, t, v)                                                   \
    { n, nullptr, FormatEntity::Entry::Type::t, v, 0, nullptr, false }
  #define ENTRY_CHILDREN(n, t, c)                                                \
    {                                                                            \
      n, nullptr, FormatEntity::Entry::Type::t, 0,                               \
          static_cast<uint32_t>(llvm::array_lengthof(c)), c, false               \
    }
  #define ENTRY_CHILDREN_KEEP_SEP(n, t, c)                                       \
    {                                                                            \
      n, nullptr, FormatEntity::Entry::Type::t, 0,                               \
          static_cast<uint32_t>(llvm::array_lengthof(c)), c, true                \
    }
  #define ENTRY_STRING(n, s)                                                     \
    { n, s, FormatEntity::Entry::Type::EscapeCode, 0, 0, nullptr, false }
  static FormatEntity::Entry::Definition g_string_entry[] = {
      ENTRY("*", ParentString)};
  
  static FormatEntity::Entry::Definition g_addr_entries[] = {
      ENTRY("load", AddressLoad),
      ENTRY("file", AddressFile),
      ENTRY("load", AddressLoadOrFile),
  };
  
  static FormatEntity::Entry::Definition g_file_child_entries[] = {
      ENTRY_VALUE("basename", ParentNumber, FileKind::Basename),
      ENTRY_VALUE("dirname", ParentNumber, FileKind::Dirname),
      ENTRY_VALUE("fullpath", ParentNumber, FileKind::Fullpath)};
  
  static FormatEntity::Entry::Definition g_frame_child_entries[] = {
      ENTRY("index", FrameIndex),
      ENTRY("pc", FrameRegisterPC),
      ENTRY("fp", FrameRegisterFP),
      ENTRY("sp", FrameRegisterSP),
      ENTRY("flags", FrameRegisterFlags),
      ENTRY("no-debug", FrameNoDebug),
      ENTRY_CHILDREN("reg", FrameRegisterByName, g_string_entry),
      ENTRY("is-artificial", FrameIsArtificial),
  };
  
  static FormatEntity::Entry::Definition g_function_child_entries[] = {
      ENTRY("id", FunctionID),
      ENTRY("name", FunctionName),
      ENTRY("name-without-args", FunctionNameNoArgs),
      ENTRY("name-with-args", FunctionNameWithArgs),
      ENTRY("mangled-name", FunctionMangledName),
      ENTRY("addr-offset", FunctionAddrOffset),
      ENTRY("concrete-only-addr-offset-no-padding", FunctionAddrOffsetConcrete),
      ENTRY("line-offset", FunctionLineOffset),
      ENTRY("pc-offset", FunctionPCOffset),
      ENTRY("initial-function", FunctionInitial),
      ENTRY("changed", FunctionChanged),
      ENTRY("is-optimized", FunctionIsOptimized)};
  
  static FormatEntity::Entry::Definition g_line_child_entries[] = {
      ENTRY_CHILDREN("file", LineEntryFile, g_file_child_entries),
      ENTRY("number", LineEntryLineNumber),
      ENTRY("column", LineEntryColumn),
      ENTRY("start-addr", LineEntryStartAddress),
      ENTRY("end-addr", LineEntryEndAddress),
  };
  
  static FormatEntity::Entry::Definition g_module_child_entries[] = {
      ENTRY_CHILDREN("file", ModuleFile, g_file_child_entries),
  };
  
  static FormatEntity::Entry::Definition g_process_child_entries[] = {
      ENTRY("id", ProcessID),
      ENTRY_VALUE("name", ProcessFile, FileKind::Basename),
      ENTRY_CHILDREN("file", ProcessFile, g_file_child_entries),
  };
  
  static FormatEntity::Entry::Definition g_svar_child_entries[] = {
      ENTRY("*", ParentString)};
  
  static FormatEntity::Entry::Definition g_var_child_entries[] = {
      ENTRY("*", ParentString)};
  
  static FormatEntity::Entry::Definition g_thread_child_entries[] = {
      ENTRY("id", ThreadID),
      ENTRY("protocol_id", ThreadProtocolID),
      ENTRY("index", ThreadIndexID),
      ENTRY_CHILDREN("info", ThreadInfo, g_string_entry),
      ENTRY("queue", ThreadQueue),
      ENTRY("name", ThreadName),
      ENTRY("stop-reason", ThreadStopReason),
      ENTRY("stop-reason-raw", ThreadStopReasonRaw),
      ENTRY("return-value", ThreadReturnValue),
      ENTRY("completed-expression", ThreadCompletedExpression),
  };
  
  static FormatEntity::Entry::Definition g_target_child_entries[] = {
      ENTRY("arch", TargetArch),
  };
  
  #define _TO_STR2(_val) #_val
  #define _TO_STR(_val) _TO_STR2(_val)
  
  static FormatEntity::Entry::Definition g_ansi_fg_entries[] = {
      ENTRY_STRING("black",
                   ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLACK) ANSI_ESC_END),
      ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_RED) ANSI_ESC_END),
      ENTRY_STRING("green",
                   ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_GREEN) ANSI_ESC_END),
      ENTRY_STRING("yellow",
                   ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_YELLOW) ANSI_ESC_END),
      ENTRY_STRING("blue",
                   ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLUE) ANSI_ESC_END),
      ENTRY_STRING("purple",
                   ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_PURPLE) ANSI_ESC_END),
      ENTRY_STRING("cyan",
                   ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_CYAN) ANSI_ESC_END),
      ENTRY_STRING("white",
                   ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_WHITE) ANSI_ESC_END),
  };
  
  static FormatEntity::Entry::Definition g_ansi_bg_entries[] = {
      ENTRY_STRING("black",
                   ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLACK) ANSI_ESC_END),
      ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_RED) ANSI_ESC_END),
      ENTRY_STRING("green",
                   ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_GREEN) ANSI_ESC_END),
      ENTRY_STRING("yellow",
                   ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_YELLOW) ANSI_ESC_END),
      ENTRY_STRING("blue",
                   ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLUE) ANSI_ESC_END),
      ENTRY_STRING("purple",
                   ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_PURPLE) ANSI_ESC_END),
      ENTRY_STRING("cyan",
                   ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_CYAN) ANSI_ESC_END),
      ENTRY_STRING("white",
                   ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_WHITE) ANSI_ESC_END),
  };
  
  static FormatEntity::Entry::Definition g_ansi_entries[] = {
      ENTRY_CHILDREN("fg", Invalid, g_ansi_fg_entries),
      ENTRY_CHILDREN("bg", Invalid, g_ansi_bg_entries),
      ENTRY_STRING("normal",
                   ANSI_ESC_START _TO_STR(ANSI_CTRL_NORMAL) ANSI_ESC_END),
      ENTRY_STRING("bold", ANSI_ESC_START _TO_STR(ANSI_CTRL_BOLD) ANSI_ESC_END),
      ENTRY_STRING("faint", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAINT) ANSI_ESC_END),
      ENTRY_STRING("italic",
                   ANSI_ESC_START _TO_STR(ANSI_CTRL_ITALIC) ANSI_ESC_END),
      ENTRY_STRING("underline",
                   ANSI_ESC_START _TO_STR(ANSI_CTRL_UNDERLINE) ANSI_ESC_END),
      ENTRY_STRING("slow-blink",
                   ANSI_ESC_START _TO_STR(ANSI_CTRL_SLOW_BLINK) ANSI_ESC_END),
      ENTRY_STRING("fast-blink",
                   ANSI_ESC_START _TO_STR(ANSI_CTRL_FAST_BLINK) ANSI_ESC_END),
      ENTRY_STRING("negative",
                   ANSI_ESC_START _TO_STR(ANSI_CTRL_IMAGE_NEGATIVE) ANSI_ESC_END),
      ENTRY_STRING("conceal",
                   ANSI_ESC_START _TO_STR(ANSI_CTRL_CONCEAL) ANSI_ESC_END),
      ENTRY_STRING("crossed-out",
                   ANSI_ESC_START _TO_STR(ANSI_CTRL_CROSSED_OUT) ANSI_ESC_END),
  };
  
  static FormatEntity::Entry::Definition g_script_child_entries[] = {
      ENTRY("frame", ScriptFrame),   ENTRY("process", ScriptProcess),
      ENTRY("target", ScriptTarget), ENTRY("thread", ScriptThread),
      ENTRY("var", ScriptVariable),  ENTRY("svar", ScriptVariableSynthetic),
      ENTRY("thread", ScriptThread),
  };
  
  static FormatEntity::Entry::Definition g_top_level_entries[] = {
      ENTRY_CHILDREN("addr", AddressLoadOrFile, g_addr_entries),
      ENTRY("addr-file-or-load", AddressLoadOrFile),
      ENTRY_CHILDREN("ansi", Invalid, g_ansi_entries),
      ENTRY("current-pc-arrow", CurrentPCArrow),
      ENTRY_CHILDREN("file", File, g_file_child_entries),
      ENTRY("language", Lang),
      ENTRY_CHILDREN("frame", Invalid, g_frame_child_entries),
      ENTRY_CHILDREN("function", Invalid, g_function_child_entries),
      ENTRY_CHILDREN("line", Invalid, g_line_child_entries),
      ENTRY_CHILDREN("module", Invalid, g_module_child_entries),
      ENTRY_CHILDREN("process", Invalid, g_process_child_entries),
      ENTRY_CHILDREN("script", Invalid, g_script_child_entries),
      ENTRY_CHILDREN_KEEP_SEP("svar", VariableSynthetic, g_svar_child_entries),
      ENTRY_CHILDREN("thread", Invalid, g_thread_child_entries),
      ENTRY_CHILDREN("target", Invalid, g_target_child_entries),
      ENTRY_CHILDREN_KEEP_SEP("var", Variable, g_var_child_entries),
  };
  
  static FormatEntity::Entry::Definition g_root =
      ENTRY_CHILDREN("<root>", Root, g_top_level_entries);
  
  FormatEntity::Entry::Entry(llvm::StringRef s)
      : string(s.data(), s.size()), printf_format(), children(),
        definition(nullptr), type(Type::String), fmt(lldb::eFormatDefault),
        number(0), deref(false) {}
  
  FormatEntity::Entry::Entry(char ch)
      : string(1, ch), printf_format(), children(), definition(nullptr),
        type(Type::String), fmt(lldb::eFormatDefault), number(0), deref(false) {}
  
  void FormatEntity::Entry::AppendChar(char ch) {
    if (children.empty() || children.back().type != Entry::Type::String)
      children.push_back(Entry(ch));
    else
      children.back().string.append(1, ch);
  }
  
  void FormatEntity::Entry::AppendText(const llvm::StringRef &s) {
    if (children.empty() || children.back().type != Entry::Type::String)
      children.push_back(Entry(s));
    else
      children.back().string.append(s.data(), s.size());
  }
  
  void FormatEntity::Entry::AppendText(const char *cstr) {
    return AppendText(llvm::StringRef(cstr));
  }
  
  Status FormatEntity::Parse(const llvm::StringRef &format_str, Entry &entry) {
    entry.Clear();
    entry.type = Entry::Type::Root;
    llvm::StringRef modifiable_format(format_str);
    return ParseInternal(modifiable_format, entry, 0);
  }
  
  #define ENUM_TO_CSTR(eee)                                                      \
    case FormatEntity::Entry::Type::eee:                                         \
      return #eee
  
  const char *FormatEntity::Entry::TypeToCString(Type t) {
    switch (t) {
      ENUM_TO_CSTR(Invalid);
      ENUM_TO_CSTR(ParentNumber);
      ENUM_TO_CSTR(ParentString);
      ENUM_TO_CSTR(EscapeCode);
      ENUM_TO_CSTR(Root);
      ENUM_TO_CSTR(String);
      ENUM_TO_CSTR(Scope);
      ENUM_TO_CSTR(Variable);
      ENUM_TO_CSTR(VariableSynthetic);
      ENUM_TO_CSTR(ScriptVariable);
      ENUM_TO_CSTR(ScriptVariableSynthetic);
      ENUM_TO_CSTR(AddressLoad);
      ENUM_TO_CSTR(AddressFile);
      ENUM_TO_CSTR(AddressLoadOrFile);
      ENUM_TO_CSTR(ProcessID);
      ENUM_TO_CSTR(ProcessFile);
      ENUM_TO_CSTR(ScriptProcess);
      ENUM_TO_CSTR(ThreadID);
      ENUM_TO_CSTR(ThreadProtocolID);
      ENUM_TO_CSTR(ThreadIndexID);
      ENUM_TO_CSTR(ThreadName);
      ENUM_TO_CSTR(ThreadQueue);
      ENUM_TO_CSTR(ThreadStopReason);
      ENUM_TO_CSTR(ThreadStopReasonRaw);
      ENUM_TO_CSTR(ThreadReturnValue);
      ENUM_TO_CSTR(ThreadCompletedExpression);
      ENUM_TO_CSTR(ScriptThread);
      ENUM_TO_CSTR(ThreadInfo);
      ENUM_TO_CSTR(TargetArch);
      ENUM_TO_CSTR(ScriptTarget);
      ENUM_TO_CSTR(ModuleFile);
      ENUM_TO_CSTR(File);
      ENUM_TO_CSTR(Lang);
      ENUM_TO_CSTR(FrameIndex);
      ENUM_TO_CSTR(FrameNoDebug);
      ENUM_TO_CSTR(FrameRegisterPC);
      ENUM_TO_CSTR(FrameRegisterSP);
      ENUM_TO_CSTR(FrameRegisterFP);
      ENUM_TO_CSTR(FrameRegisterFlags);
      ENUM_TO_CSTR(FrameRegisterByName);
      ENUM_TO_CSTR(FrameIsArtificial);
      ENUM_TO_CSTR(ScriptFrame);
      ENUM_TO_CSTR(FunctionID);
      ENUM_TO_CSTR(FunctionDidChange);
      ENUM_TO_CSTR(FunctionInitialFunction);
      ENUM_TO_CSTR(FunctionName);
      ENUM_TO_CSTR(FunctionNameWithArgs);
      ENUM_TO_CSTR(FunctionNameNoArgs);
      ENUM_TO_CSTR(FunctionMangledName);
      ENUM_TO_CSTR(FunctionAddrOffset);
      ENUM_TO_CSTR(FunctionAddrOffsetConcrete);
      ENUM_TO_CSTR(FunctionLineOffset);
      ENUM_TO_CSTR(FunctionPCOffset);
      ENUM_TO_CSTR(FunctionInitial);
      ENUM_TO_CSTR(FunctionChanged);
      ENUM_TO_CSTR(FunctionIsOptimized);
      ENUM_TO_CSTR(LineEntryFile);
      ENUM_TO_CSTR(LineEntryLineNumber);
      ENUM_TO_CSTR(LineEntryColumn);
      ENUM_TO_CSTR(LineEntryStartAddress);
      ENUM_TO_CSTR(LineEntryEndAddress);
      ENUM_TO_CSTR(CurrentPCArrow);
    }
    return "???";
  }
  
  #undef ENUM_TO_CSTR
  
  void FormatEntity::Entry::Dump(Stream &s, int depth) const {
    s.Printf("%*.*s%-20s: ", depth * 2, depth * 2, "", TypeToCString(type));
    if (fmt != eFormatDefault)
      s.Printf("lldb-format = %s, ", FormatManager::GetFormatAsCString(fmt));
    if (!string.empty())
      s.Printf("string = \"%s\"", string.c_str());
    if (!printf_format.empty())
      s.Printf("printf_format = \"%s\"", printf_format.c_str());
    if (number != 0)
      s.Printf("number = %" PRIu64 " (0x%" PRIx64 "), ", number, number);
    if (deref)
      s.Printf("deref = true, ");
    s.EOL();
    for (const auto &child : children) {
      child.Dump(s, depth + 1);
    }
  }
  
  template <typename T>
  static bool RunScriptFormatKeyword(Stream &s, const SymbolContext *sc,
                                     const ExecutionContext *exe_ctx, T t,
                                     const char *script_function_name) {
    Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
  
    if (target) {
      ScriptInterpreter *script_interpreter =
          target->GetDebugger().GetScriptInterpreter();
      if (script_interpreter) {
        Status error;
        std::string script_output;
  
        if (script_interpreter->RunScriptFormatKeyword(script_function_name, t,
                                                       script_output, error) &&
            error.Success()) {
          s.Printf("%s", script_output.c_str());
          return true;
        } else {
          s.Printf("<error: %s>", error.AsCString());
        }
      }
    }
    return false;
  }
  
  static bool DumpAddressAndContent(Stream &s, const SymbolContext *sc,
                                    const ExecutionContext *exe_ctx,
                                    const Address &addr,
                                    bool print_file_addr_or_load_addr) {
    Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
    addr_t vaddr = LLDB_INVALID_ADDRESS;
    if (exe_ctx && !target->GetSectionLoadList().IsEmpty())
      vaddr = addr.GetLoadAddress(target);
    if (vaddr == LLDB_INVALID_ADDRESS)
      vaddr = addr.GetFileAddress();
  
    if (vaddr != LLDB_INVALID_ADDRESS) {
      int addr_width = 0;
      if (exe_ctx && target) {
        addr_width = target->GetArchitecture().GetAddressByteSize() * 2;
      }
      if (addr_width == 0)
        addr_width = 16;
      if (print_file_addr_or_load_addr) {
        ExecutionContextScope *exe_scope = nullptr;
        if (exe_ctx)
          exe_scope = exe_ctx->GetBestExecutionContextScope();
        addr.Dump(&s, exe_scope, Address::DumpStyleLoadAddress,
                  Address::DumpStyleModuleWithFileAddress, 0);
      } else {
        s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr);
      }
      return true;
    }
    return false;
  }
  
  static bool DumpAddressOffsetFromFunction(Stream &s, const SymbolContext *sc,
                                            const ExecutionContext *exe_ctx,
                                            const Address &format_addr,
                                            bool concrete_only, bool no_padding,
                                            bool print_zero_offsets) {
    if (format_addr.IsValid()) {
      Address func_addr;
  
      if (sc) {
        if (sc->function) {
          func_addr = sc->function->GetAddressRange().GetBaseAddress();
          if (sc->block && !concrete_only) {
            // Check to make sure we aren't in an inline function. If we are, use
            // the inline block range that contains "format_addr" since blocks
            // can be discontiguous.
            Block *inline_block = sc->block->GetContainingInlinedBlock();
            AddressRange inline_range;
            if (inline_block && inline_block->GetRangeContainingAddress(
                                    format_addr, inline_range))
              func_addr = inline_range.GetBaseAddress();
          }
        } else if (sc->symbol && sc->symbol->ValueIsAddress())
          func_addr = sc->symbol->GetAddressRef();
      }
  
      if (func_addr.IsValid()) {
        const char *addr_offset_padding = no_padding ? "" : " ";
  
        if (func_addr.GetSection() == format_addr.GetSection()) {
          addr_t func_file_addr = func_addr.GetFileAddress();
          addr_t addr_file_addr = format_addr.GetFileAddress();
          if (addr_file_addr > func_file_addr ||
              (addr_file_addr == func_file_addr && print_zero_offsets)) {
            s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding,
                     addr_file_addr - func_file_addr);
          } else if (addr_file_addr < func_file_addr) {
            s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding,
                     func_file_addr - addr_file_addr);
          }
          return true;
        } else {
          Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
          if (target) {
            addr_t func_load_addr = func_addr.GetLoadAddress(target);
            addr_t addr_load_addr = format_addr.GetLoadAddress(target);
            if (addr_load_addr > func_load_addr ||
                (addr_load_addr == func_load_addr && print_zero_offsets)) {
              s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding,
                       addr_load_addr - func_load_addr);
            } else if (addr_load_addr < func_load_addr) {
              s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding,
                       func_load_addr - addr_load_addr);
            }
            return true;
          }
        }
      }
    }
    return false;
  }
  
  static bool ScanBracketedRange(llvm::StringRef subpath,
                                 size_t &close_bracket_index,
                                 const char *&var_name_final_if_array_range,
                                 int64_t &index_lower, int64_t &index_higher) {
    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
    close_bracket_index = llvm::StringRef::npos;
    const size_t open_bracket_index = subpath.find('[');
    if (open_bracket_index == llvm::StringRef::npos) {
      LLDB_LOGF(log,
                "[ScanBracketedRange] no bracketed range, skipping entirely");
      return false;
    }
  
    close_bracket_index = subpath.find(']', open_bracket_index + 1);
  
    if (close_bracket_index == llvm::StringRef::npos) {
      LLDB_LOGF(log,
                "[ScanBracketedRange] no bracketed range, skipping entirely");
      return false;
    } else {
      var_name_final_if_array_range = subpath.data() + open_bracket_index;
  
      if (close_bracket_index - open_bracket_index == 1) {
        LLDB_LOGF(
            log,
            "[ScanBracketedRange] '[]' detected.. going from 0 to end of data");
        index_lower = 0;
      } else {
        const size_t separator_index = subpath.find('-', open_bracket_index + 1);
  
        if (separator_index == llvm::StringRef::npos) {
          const char *index_lower_cstr = subpath.data() + open_bracket_index + 1;
          index_lower = ::strtoul(index_lower_cstr, nullptr, 0);
          index_higher = index_lower;
          LLDB_LOGF(log,
                    "[ScanBracketedRange] [%" PRId64
                    "] detected, high index is same",
                    index_lower);
        } else {
          const char *index_lower_cstr = subpath.data() + open_bracket_index + 1;
          const char *index_higher_cstr = subpath.data() + separator_index + 1;
          index_lower = ::strtoul(index_lower_cstr, nullptr, 0);
          index_higher = ::strtoul(index_higher_cstr, nullptr, 0);
          LLDB_LOGF(log,
                    "[ScanBracketedRange] [%" PRId64 "-%" PRId64 "] detected",
                    index_lower, index_higher);
        }
        if (index_lower > index_higher && index_higher > 0) {
          LLDB_LOGF(log, "[ScanBracketedRange] swapping indices");
          const int64_t temp = index_lower;
          index_lower = index_higher;
          index_higher = temp;
        }
      }
    }
    return true;
  }
  
  static bool DumpFile(Stream &s, const FileSpec &file, FileKind file_kind) {
    switch (file_kind) {
    case FileKind::FileError:
      break;
  
    case FileKind::Basename:
      if (file.GetFilename()) {
        s << file.GetFilename();
        return true;
      }
      break;
  
    case FileKind::Dirname:
      if (file.GetDirectory()) {
        s << file.GetDirectory();
        return true;
      }
      break;
  
    case FileKind::Fullpath:
      if (file) {
        s << file;
        return true;
      }
      break;
    }
    return false;
  }
  
  static bool DumpRegister(Stream &s, StackFrame *frame, RegisterKind reg_kind,
                           uint32_t reg_num, Format format)
  
  {
    if (frame) {
      RegisterContext *reg_ctx = frame->GetRegisterContext().get();
  
      if (reg_ctx) {
        const uint32_t lldb_reg_num =
            reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num);
        if (lldb_reg_num != LLDB_INVALID_REGNUM) {
          const RegisterInfo *reg_info =
              reg_ctx->GetRegisterInfoAtIndex(lldb_reg_num);
          if (reg_info) {
            RegisterValue reg_value;
            if (reg_ctx->ReadRegister(reg_info, reg_value)) {
              DumpRegisterValue(reg_value, &s, reg_info, false, false, format);
              return true;
            }
          }
        }
      }
    }
    return false;
  }
  
  static ValueObjectSP ExpandIndexedExpression(ValueObject *valobj, size_t index,
                                               StackFrame *frame,
                                               bool deref_pointer) {
    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
    const char *ptr_deref_format = "[%d]";
    std::string ptr_deref_buffer(10, 0);
    ::sprintf(&ptr_deref_buffer[0], ptr_deref_format, index);
    LLDB_LOGF(log, "[ExpandIndexedExpression] name to deref: %s",
              ptr_deref_buffer.c_str());
    ValueObject::GetValueForExpressionPathOptions options;
    ValueObject::ExpressionPathEndResultType final_value_type;
    ValueObject::ExpressionPathScanEndReason reason_to_stop;
    ValueObject::ExpressionPathAftermath what_next =
        (deref_pointer ? ValueObject::eExpressionPathAftermathDereference
                       : ValueObject::eExpressionPathAftermathNothing);
    ValueObjectSP item = valobj->GetValueForExpressionPath(
        ptr_deref_buffer.c_str(), &reason_to_stop, &final_value_type, options,
        &what_next);
    if (!item) {
      LLDB_LOGF(log,
                "[ExpandIndexedExpression] ERROR: why stopping = %d,"
                " final_value_type %d",
                reason_to_stop, final_value_type);
    } else {
      LLDB_LOGF(log,
                "[ExpandIndexedExpression] ALL RIGHT: why stopping = %d,"
                " final_value_type %d",
                reason_to_stop, final_value_type);
    }
    return item;
  }
  
  static char ConvertValueObjectStyleToChar(
      ValueObject::ValueObjectRepresentationStyle style) {
    switch (style) {
    case ValueObject::eValueObjectRepresentationStyleLanguageSpecific:
      return '@';
    case ValueObject::eValueObjectRepresentationStyleValue:
      return 'V';
    case ValueObject::eValueObjectRepresentationStyleLocation:
      return 'L';
    case ValueObject::eValueObjectRepresentationStyleSummary:
      return 'S';
    case ValueObject::eValueObjectRepresentationStyleChildrenCount:
      return '#';
    case ValueObject::eValueObjectRepresentationStyleType:
      return 'T';
    case ValueObject::eValueObjectRepresentationStyleName:
      return 'N';
    case ValueObject::eValueObjectRepresentationStyleExpressionPath:
      return '>';
    }
    return '\0';
  }
  
  static bool DumpValue(Stream &s, const SymbolContext *sc,
                        const ExecutionContext *exe_ctx,
                        const FormatEntity::Entry &entry, ValueObject *valobj) {
    if (valobj == nullptr)
      return false;
  
    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
    Format custom_format = eFormatInvalid;
    ValueObject::ValueObjectRepresentationStyle val_obj_display =
        entry.string.empty()
            ? ValueObject::eValueObjectRepresentationStyleValue
            : ValueObject::eValueObjectRepresentationStyleSummary;
  
    bool do_deref_pointer = entry.deref;
    bool is_script = false;
    switch (entry.type) {
    case FormatEntity::Entry::Type::ScriptVariable:
      is_script = true;
      break;
  
    case FormatEntity::Entry::Type::Variable:
      custom_format = entry.fmt;
      val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number;
      break;
  
    case FormatEntity::Entry::Type::ScriptVariableSynthetic:
      is_script = true;
      LLVM_FALLTHROUGH;
    case FormatEntity::Entry::Type::VariableSynthetic:
      custom_format = entry.fmt;
      val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number;
      if (!valobj->IsSynthetic()) {
        valobj = valobj->GetSyntheticValue().get();
        if (valobj == nullptr)
          return false;
      }
      break;
  
    default:
      return false;
    }
  
    if (valobj == nullptr)
      return false;
  
    ValueObject::ExpressionPathAftermath what_next =
        (do_deref_pointer ? ValueObject::eExpressionPathAftermathDereference
                          : ValueObject::eExpressionPathAftermathNothing);
    ValueObject::GetValueForExpressionPathOptions options;
    options.DontCheckDotVsArrowSyntax()
        .DoAllowBitfieldSyntax()
        .DoAllowFragileIVar()
        .SetSyntheticChildrenTraversal(
            ValueObject::GetValueForExpressionPathOptions::
                SyntheticChildrenTraversal::Both);
    ValueObject *target = nullptr;
    const char *var_name_final_if_array_range = nullptr;
    size_t close_bracket_index = llvm::StringRef::npos;
    int64_t index_lower = -1;
    int64_t index_higher = -1;
    bool is_array_range = false;
    bool was_plain_var = false;
    bool was_var_format = false;
    bool was_var_indexed = false;
    ValueObject::ExpressionPathScanEndReason reason_to_stop =
        ValueObject::eExpressionPathScanEndReasonEndOfString;
    ValueObject::ExpressionPathEndResultType final_value_type =
        ValueObject::eExpressionPathEndResultTypePlain;
  
    if (is_script) {
      return RunScriptFormatKeyword(s, sc, exe_ctx, valobj, entry.string.c_str());
    }
  
    llvm::StringRef subpath(entry.string);
    // simplest case ${var}, just print valobj's value
    if (entry.string.empty()) {
      if (entry.printf_format.empty() && entry.fmt == eFormatDefault &&
          entry.number == ValueObject::eValueObjectRepresentationStyleValue)
        was_plain_var = true;
      else
        was_var_format = true;
      target = valobj;
    } else // this is ${var.something} or multiple .something nested
    {
      if (entry.string[0] == '[')
        was_var_indexed = true;
      ScanBracketedRange(subpath, close_bracket_index,
                         var_name_final_if_array_range, index_lower,
                         index_higher);
  
      Status error;
  
      const std::string &expr_path = entry.string;
  
      LLDB_LOGF(log, "[Debugger::FormatPrompt] symbol to expand: %s",
                expr_path.c_str());
  
      target =
          valobj
              ->GetValueForExpressionPath(expr_path.c_str(), &reason_to_stop,
                                          &final_value_type, options, &what_next)
              .get();
  
      if (!target) {
        LLDB_LOGF(log,
                  "[Debugger::FormatPrompt] ERROR: why stopping = %d,"
                  " final_value_type %d",
                  reason_to_stop, final_value_type);
        return false;
      } else {
        LLDB_LOGF(log,
                  "[Debugger::FormatPrompt] ALL RIGHT: why stopping = %d,"
                  " final_value_type %d",
                  reason_to_stop, final_value_type);
        target = target
                     ->GetQualifiedRepresentationIfAvailable(
                         target->GetDynamicValueType(), true)
                     .get();
      }
    }
  
    is_array_range =
        (final_value_type ==
             ValueObject::eExpressionPathEndResultTypeBoundedRange ||
         final_value_type ==
             ValueObject::eExpressionPathEndResultTypeUnboundedRange);
  
    do_deref_pointer =
        (what_next == ValueObject::eExpressionPathAftermathDereference);
  
    if (do_deref_pointer && !is_array_range) {
      // I have not deref-ed yet, let's do it
      // this happens when we are not going through
      // GetValueForVariableExpressionPath to get to the target ValueObject
      Status error;
      target = target->Dereference(error).get();
      if (error.Fail()) {
        LLDB_LOGF(log, "[Debugger::FormatPrompt] ERROR: %s\n",
                  error.AsCString("unknown"));
        return false;
      }
      do_deref_pointer = false;
    }
  
    if (!target) {
      LLDB_LOGF(log, "[Debugger::FormatPrompt] could not calculate target for "
                     "prompt expression");
      return false;
    }
  
    // we do not want to use the summary for a bitfield of type T:n if we were
    // originally dealing with just a T - that would get us into an endless
    // recursion
    if (target->IsBitfield() && was_var_indexed) {
      // TODO: check for a (T:n)-specific summary - we should still obey that
      StreamString bitfield_name;
      bitfield_name.Printf("%s:%d", target->GetTypeName().AsCString(),
                           target->GetBitfieldBitSize());
      auto type_sp = std::make_shared<TypeNameSpecifierImpl>(
          bitfield_name.GetString(), false);
      if (val_obj_display ==
              ValueObject::eValueObjectRepresentationStyleSummary &&
          !DataVisualization::GetSummaryForType(type_sp))
        val_obj_display = ValueObject::eValueObjectRepresentationStyleValue;
    }
  
    // TODO use flags for these
    const uint32_t type_info_flags =
        target->GetCompilerType().GetTypeInfo(nullptr);
    bool is_array = (type_info_flags & eTypeIsArray) != 0;
    bool is_pointer = (type_info_flags & eTypeIsPointer) != 0;
    bool is_aggregate = target->GetCompilerType().IsAggregateType();
  
    if ((is_array || is_pointer) && (!is_array_range) &&
        val_obj_display ==
            ValueObject::eValueObjectRepresentationStyleValue) // this should be
                                                               // wrong, but there
                                                               // are some
                                                               // exceptions
    {
      StreamString str_temp;
      LLDB_LOGF(log,
                "[Debugger::FormatPrompt] I am into array || pointer && !range");
  
      if (target->HasSpecialPrintableRepresentation(val_obj_display,
                                                    custom_format)) {
        // try to use the special cases
        bool success = target->DumpPrintableRepresentation(
            str_temp, val_obj_display, custom_format);
        LLDB_LOGF(log, "[Debugger::FormatPrompt] special cases did%s match",
                  success ? "" : "n't");
  
        // should not happen
        if (success)
          s << str_temp.GetString();
        return true;
      } else {
        if (was_plain_var) // if ${var}
        {
          s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
        } else if (is_pointer) // if pointer, value is the address stored
        {
          target->DumpPrintableRepresentation(
              s, val_obj_display, custom_format,
              ValueObject::PrintableRepresentationSpecialCases::eDisable);
        }
        return true;
      }
    }
  
    // if directly trying to print ${var}, and this is an aggregate, display a
    // nice type @ location message
    if (is_aggregate && was_plain_var) {
      s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
      return true;
    }
  
    // if directly trying to print ${var%V}, and this is an aggregate, do not let
    // the user do it
    if (is_aggregate &&
        ((was_var_format &&
          val_obj_display ==
              ValueObject::eValueObjectRepresentationStyleValue))) {
      s << "<invalid use of aggregate type>";
      return true;
    }
  
    if (!is_array_range) {
      LLDB_LOGF(log,
                "[Debugger::FormatPrompt] dumping ordinary printable output");
      return target->DumpPrintableRepresentation(s, val_obj_display,
                                                 custom_format);
    } else {
      LLDB_LOGF(log,
                "[Debugger::FormatPrompt] checking if I can handle as array");
      if (!is_array && !is_pointer)
        return false;
      LLDB_LOGF(log, "[Debugger::FormatPrompt] handle as array");
      StreamString special_directions_stream;
      llvm::StringRef special_directions;
      if (close_bracket_index != llvm::StringRef::npos &&
          subpath.size() > close_bracket_index) {
        ConstString additional_data(subpath.drop_front(close_bracket_index + 1));
        special_directions_stream.Printf("${%svar%s", do_deref_pointer ? "*" : "",
                                         additional_data.GetCString());
  
        if (entry.fmt != eFormatDefault) {
          const char format_char =
              FormatManager::GetFormatAsFormatChar(entry.fmt);
          if (format_char != '\0')
            special_directions_stream.Printf("%%%c", format_char);
          else {
            const char *format_cstr =
                FormatManager::GetFormatAsCString(entry.fmt);
            special_directions_stream.Printf("%%%s", format_cstr);
          }
        } else if (entry.number != 0) {
          const char style_char = ConvertValueObjectStyleToChar(
              (ValueObject::ValueObjectRepresentationStyle)entry.number);
          if (style_char)
            special_directions_stream.Printf("%%%c", style_char);
        }
        special_directions_stream.PutChar('}');
        special_directions =
            llvm::StringRef(special_directions_stream.GetString());
      }
  
      // let us display items index_lower thru index_higher of this array
      s.PutChar('[');
  
      if (index_higher < 0)
        index_higher = valobj->GetNumChildren() - 1;
  
      uint32_t max_num_children =
          target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
  
      bool success = true;
      for (int64_t index = index_lower; index <= index_higher; ++index) {
        ValueObject *item =
            ExpandIndexedExpression(target, index, exe_ctx->GetFramePtr(), false)
                .get();
  
        if (!item) {
          LLDB_LOGF(log,
                    "[Debugger::FormatPrompt] ERROR in getting child item at "
                    "index %" PRId64,
                    index);
        } else {
          LLDB_LOGF(
              log,
              "[Debugger::FormatPrompt] special_directions for child item: %s",
              special_directions.data() ? special_directions.data() : "");
        }
  
        if (special_directions.empty()) {
          success &= item->DumpPrintableRepresentation(s, val_obj_display,
                                                       custom_format);
        } else {
          success &= FormatEntity::FormatStringRef(
              special_directions, s, sc, exe_ctx, nullptr, item, false, false);
        }
  
        if (--max_num_children == 0) {
          s.PutCString(", ...");
          break;
        }
  
        if (index < index_higher)
          s.PutChar(',');
      }
      s.PutChar(']');
      return success;
    }
  }
  
  static bool DumpRegister(Stream &s, StackFrame *frame, const char *reg_name,
                           Format format) {
    if (frame) {
      RegisterContext *reg_ctx = frame->GetRegisterContext().get();
  
      if (reg_ctx) {
        const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
        if (reg_info) {
          RegisterValue reg_value;
          if (reg_ctx->ReadRegister(reg_info, reg_value)) {
            DumpRegisterValue(reg_value, &s, reg_info, false, false, format);
            return true;
          }
        }
      }
    }
    return false;
  }
  
  static bool FormatThreadExtendedInfoRecurse(
      const FormatEntity::Entry &entry,
      const StructuredData::ObjectSP &thread_info_dictionary,
      const SymbolContext *sc, const ExecutionContext *exe_ctx, Stream &s) {
    llvm::StringRef path(entry.string);
  
    StructuredData::ObjectSP value =
        thread_info_dictionary->GetObjectForDotSeparatedPath(path);
  
    if (value) {
      if (value->GetType() == eStructuredDataTypeInteger) {
        const char *token_format = "0x%4.4" PRIx64;
        if (!entry.printf_format.empty())
          token_format = entry.printf_format.c_str();
        s.Printf(token_format, value->GetAsInteger()->GetValue());
        return true;
      } else if (value->GetType() == eStructuredDataTypeFloat) {
        s.Printf("%f", value->GetAsFloat()->GetValue());
        return true;
      } else if (value->GetType() == eStructuredDataTypeString) {
        s.Format("{0}", value->GetAsString()->GetValue());
        return true;
      } else if (value->GetType() == eStructuredDataTypeArray) {
        if (value->GetAsArray()->GetSize() > 0) {
          s.Printf("%zu", value->GetAsArray()->GetSize());
          return true;
        }
      } else if (value->GetType() == eStructuredDataTypeDictionary) {
        s.Printf("%zu",
                 value->GetAsDictionary()->GetKeys()->GetAsArray()->GetSize());
        return true;
      }
    }
  
    return false;
  }
  
  static inline bool IsToken(const char *var_name_begin, const char *var) {
    return (::strncmp(var_name_begin, var, strlen(var)) == 0);
  }
  
  bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s,
                                     const SymbolContext *sc,
                                     const ExecutionContext *exe_ctx,
                                     const Address *addr, ValueObject *valobj,
                                     bool function_changed,
                                     bool initial_function) {
    if (!format_str.empty()) {
      FormatEntity::Entry root;
      Status error = FormatEntity::Parse(format_str, root);
      if (error.Success()) {
        return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj,
                                    function_changed, initial_function);
      }
    }
    return false;
  }
  
  bool FormatEntity::FormatCString(const char *format, Stream &s,
                                   const SymbolContext *sc,
                                   const ExecutionContext *exe_ctx,
                                   const Address *addr, ValueObject *valobj,
                                   bool function_changed, bool initial_function) {
    if (format && format[0]) {
      FormatEntity::Entry root;
      llvm::StringRef format_str(format);
      Status error = FormatEntity::Parse(format_str, root);
      if (error.Success()) {
        return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj,
                                    function_changed, initial_function);
      }
    }
    return false;
  }
  
  bool FormatEntity::Format(const Entry &entry, Stream &s,
                            const SymbolContext *sc,
                            const ExecutionContext *exe_ctx, const Address *addr,
                            ValueObject *valobj, bool function_changed,
                            bool initial_function) {
    switch (entry.type) {
    case Entry::Type::Invalid:
    case Entry::Type::ParentNumber: // Only used for
                                    // FormatEntity::Entry::Definition encoding
    case Entry::Type::ParentString: // Only used for
                                    // FormatEntity::Entry::Definition encoding
      return false;
    case Entry::Type::EscapeCode:
      if (exe_ctx) {
        if (Target *target = exe_ctx->GetTargetPtr()) {
          Debugger &debugger = target->GetDebugger();
          if (debugger.GetUseColor()) {
            s.PutCString(entry.string);
          }
        }
      }
      // Always return true, so colors being disabled is transparent.
      return true;
  
    case Entry::Type::Root:
      for (const auto &child : entry.children) {
        if (!Format(child, s, sc, exe_ctx, addr, valobj, function_changed,
                    initial_function)) {
          return false; // If any item of root fails, then the formatting fails
        }
      }
      return true; // Only return true if all items succeeded
  
    case Entry::Type::String:
      s.PutCString(entry.string);
      return true;
  
    case Entry::Type::Scope: {
      StreamString scope_stream;
      bool success = false;
      for (const auto &child : entry.children) {
        success = Format(child, scope_stream, sc, exe_ctx, addr, valobj,
                         function_changed, initial_function);
        if (!success)
          break;
      }
      // Only if all items in a scope succeed, then do we print the output into
      // the main stream
      if (success)
        s.Write(scope_stream.GetString().data(), scope_stream.GetString().size());
    }
      return true; // Scopes always successfully print themselves
  
    case Entry::Type::Variable:
    case Entry::Type::VariableSynthetic:
    case Entry::Type::ScriptVariable:
    case Entry::Type::ScriptVariableSynthetic:
      return DumpValue(s, sc, exe_ctx, entry, valobj);
  
    case Entry::Type::AddressFile:
    case Entry::Type::AddressLoad:
    case Entry::Type::AddressLoadOrFile:
      return (
          addr != nullptr && addr->IsValid() &&
          DumpAddressAndContent(s, sc, exe_ctx, *addr,
                                entry.type == Entry::Type::AddressLoadOrFile));
  
    case Entry::Type::ProcessID:
      if (exe_ctx) {
        Process *process = exe_ctx->GetProcessPtr();
        if (process) {
          const char *format = "%" PRIu64;
          if (!entry.printf_format.empty())
            format = entry.printf_format.c_str();
          s.Printf(format, process->GetID());
          return true;
        }
      }
      return false;
  
    case Entry::Type::ProcessFile:
      if (exe_ctx) {
        Process *process = exe_ctx->GetProcessPtr();
        if (process) {
          Module *exe_module = process->GetTarget().GetExecutableModulePointer();
          if (exe_module) {
            if (DumpFile(s, exe_module->GetFileSpec(), (FileKind)entry.number))
              return true;
          }
        }
      }
      return false;
  
    case Entry::Type::ScriptProcess:
      if (exe_ctx) {
        Process *process = exe_ctx->GetProcessPtr();
        if (process)
          return RunScriptFormatKeyword(s, sc, exe_ctx, process,
                                        entry.string.c_str());
      }
      return false;
  
    case Entry::Type::ThreadID:
      if (exe_ctx) {
        Thread *thread = exe_ctx->GetThreadPtr();
        if (thread) {
          const char *format = "0x%4.4" PRIx64;
          if (!entry.printf_format.empty()) {
            // Watch for the special "tid" format...
            if (entry.printf_format == "tid") {
              // TODO(zturner): Rather than hardcoding this to be platform
              // specific, it should be controlled by a setting and the default
              // value of the setting can be different depending on the platform.
              Target &target = thread->GetProcess()->GetTarget();
              ArchSpec arch(target.GetArchitecture());
              llvm::Triple::OSType ostype = arch.IsValid()
                                                ? arch.GetTriple().getOS()
                                                : llvm::Triple::UnknownOS;
              if ((ostype == llvm::Triple::FreeBSD) ||
                  (ostype == llvm::Triple::Linux) ||
                  (ostype == llvm::Triple::NetBSD)) {
                format = "%" PRIu64;
              }
            } else {
              format = entry.printf_format.c_str();
            }
          }
          s.Printf(format, thread->GetID());
          return true;
        }
      }
      return false;
  
    case Entry::Type::ThreadProtocolID:
      if (exe_ctx) {
        Thread *thread = exe_ctx->GetThreadPtr();
        if (thread) {
          const char *format = "0x%4.4" PRIx64;
          if (!entry.printf_format.empty())
            format = entry.printf_format.c_str();
          s.Printf(format, thread->GetProtocolID());
          return true;
        }
      }
      return false;
  
    case Entry::Type::ThreadIndexID:
      if (exe_ctx) {
        Thread *thread = exe_ctx->GetThreadPtr();
        if (thread) {
          const char *format = "%" PRIu32;
          if (!entry.printf_format.empty())
            format = entry.printf_format.c_str();
          s.Printf(format, thread->GetIndexID());
          return true;
        }
      }
      return false;
  
    case Entry::Type::ThreadName:
      if (exe_ctx) {
        Thread *thread = exe_ctx->GetThreadPtr();
        if (thread) {
          const char *cstr = thread->GetName();
          if (cstr && cstr[0]) {
            s.PutCString(cstr);
            return true;
          }
        }
      }
      return false;
  
    case Entry::Type::ThreadQueue:
      if (exe_ctx) {
        Thread *thread = exe_ctx->GetThreadPtr();
        if (thread) {
          const char *cstr = thread->GetQueueName();
          if (cstr && cstr[0]) {
            s.PutCString(cstr);
            return true;
          }
        }
      }
      return false;
  
    case Entry::Type::ThreadStopReason:
      if (exe_ctx) {
        if (Thread *thread = exe_ctx->GetThreadPtr()) {
          std::string stop_description = thread->GetStopDescription();
          if (!stop_description.empty()) {
            s.PutCString(stop_description);
            return true;
          }
        }
      }
      return false;
  
    case Entry::Type::ThreadStopReasonRaw:
      if (exe_ctx) {
        if (Thread *thread = exe_ctx->GetThreadPtr()) {
          std::string stop_description = thread->GetStopDescriptionRaw();
          if (!stop_description.empty()) {
            s.PutCString(stop_description);
            return true;
          }
        }
      }
      return false;
  
    case Entry::Type::ThreadReturnValue:
      if (exe_ctx) {
        Thread *thread = exe_ctx->GetThreadPtr();
        if (thread) {
          StopInfoSP stop_info_sp = thread->GetStopInfo();
          if (stop_info_sp && stop_info_sp->IsValid()) {
            ValueObjectSP return_valobj_sp =
                StopInfo::GetReturnValueObject(stop_info_sp);
            if (return_valobj_sp) {
              return_valobj_sp->Dump(s);
              return true;
            }
          }
        }
      }
      return false;
  
    case Entry::Type::ThreadCompletedExpression:
      if (exe_ctx) {
        Thread *thread = exe_ctx->GetThreadPtr();
        if (thread) {
          StopInfoSP stop_info_sp = thread->GetStopInfo();
          if (stop_info_sp && stop_info_sp->IsValid()) {
            ExpressionVariableSP expression_var_sp =
                StopInfo::GetExpressionVariable(stop_info_sp);
            if (expression_var_sp && expression_var_sp->GetValueObject()) {
              expression_var_sp->GetValueObject()->Dump(s);
              return true;
            }
          }
        }
      }
      return false;
  
    case Entry::Type::ScriptThread:
      if (exe_ctx) {
        Thread *thread = exe_ctx->GetThreadPtr();
        if (thread)
          return RunScriptFormatKeyword(s, sc, exe_ctx, thread,
                                        entry.string.c_str());
      }
      return false;
  
    case Entry::Type::ThreadInfo:
      if (exe_ctx) {
        Thread *thread = exe_ctx->GetThreadPtr();
        if (thread) {
          StructuredData::ObjectSP object_sp = thread->GetExtendedInfo();
          if (object_sp &&
              object_sp->GetType() == eStructuredDataTypeDictionary) {
            if (FormatThreadExtendedInfoRecurse(entry, object_sp, sc, exe_ctx, s))
              return true;
          }
        }
      }
      return false;
  
    case Entry::Type::TargetArch:
      if (exe_ctx) {
        Target *target = exe_ctx->GetTargetPtr();
        if (target) {
          const ArchSpec &arch = target->GetArchitecture();
          if (arch.IsValid()) {
            s.PutCString(arch.GetArchitectureName());
            return true;
          }
        }
      }
      return false;
  
    case Entry::Type::ScriptTarget:
      if (exe_ctx) {
        Target *target = exe_ctx->GetTargetPtr();
        if (target)
          return RunScriptFormatKeyword(s, sc, exe_ctx, target,
                                        entry.string.c_str());
      }
      return false;
  
    case Entry::Type::ModuleFile:
      if (sc) {
        Module *module = sc->module_sp.get();
        if (module) {
          if (DumpFile(s, module->GetFileSpec(), (FileKind)entry.number))
            return true;
        }
      }
      return false;
  
    case Entry::Type::File:
      if (sc) {
        CompileUnit *cu = sc->comp_unit;
        if (cu) {
          if (DumpFile(s, cu->GetPrimaryFile(), (FileKind)entry.number))
            return true;
        }
      }
      return false;
  
    case Entry::Type::Lang:
      if (sc) {
        CompileUnit *cu = sc->comp_unit;
        if (cu) {
          const char *lang_name =
              Language::GetNameForLanguageType(cu->GetLanguage());
          if (lang_name) {
            s.PutCString(lang_name);
            return true;
          }
        }
      }
      return false;
  
    case Entry::Type::FrameIndex:
      if (exe_ctx) {
        StackFrame *frame = exe_ctx->GetFramePtr();
        if (frame) {
          const char *format = "%" PRIu32;
          if (!entry.printf_format.empty())
            format = entry.printf_format.c_str();
          s.Printf(format, frame->GetFrameIndex());
          return true;
        }
      }
      return false;
  
    case Entry::Type::FrameRegisterPC:
      if (exe_ctx) {
        StackFrame *frame = exe_ctx->GetFramePtr();
        if (frame) {
          const Address &pc_addr = frame->GetFrameCodeAddress();
          if (pc_addr.IsValid()) {
            if (DumpAddressAndContent(s, sc, exe_ctx, pc_addr, false))
              return true;
          }
        }
      }
      return false;
  
    case Entry::Type::FrameRegisterSP:
      if (exe_ctx) {
        StackFrame *frame = exe_ctx->GetFramePtr();
        if (frame) {
          if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP,
                           (lldb::Format)entry.number))
            return true;
        }
      }
      return false;
  
    case Entry::Type::FrameRegisterFP:
      if (exe_ctx) {
        StackFrame *frame = exe_ctx->GetFramePtr();
        if (frame) {
          if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP,
                           (lldb::Format)entry.number))
            return true;
        }
      }
      return false;
  
    case Entry::Type::FrameRegisterFlags:
      if (exe_ctx) {
        StackFrame *frame = exe_ctx->GetFramePtr();
        if (frame) {
          if (DumpRegister(s, frame, eRegisterKindGeneric,
                           LLDB_REGNUM_GENERIC_FLAGS, (lldb::Format)entry.number))
            return true;
        }
      }
      return false;
  
    case Entry::Type::FrameNoDebug:
      if (exe_ctx) {
        StackFrame *frame = exe_ctx->GetFramePtr();
        if (frame) {
          return !frame->HasDebugInformation();
        }
      }
      return true;
  
    case Entry::Type::FrameRegisterByName:
      if (exe_ctx) {
        StackFrame *frame = exe_ctx->GetFramePtr();
        if (frame) {
          if (DumpRegister(s, frame, entry.string.c_str(),
                           (lldb::Format)entry.number))
            return true;
        }
      }
      return false;
  
    case Entry::Type::FrameIsArtificial: {
      if (exe_ctx)
        if (StackFrame *frame = exe_ctx->GetFramePtr())
          return frame->IsArtificial();
      return false;
    }
  
    case Entry::Type::ScriptFrame:
      if (exe_ctx) {
        StackFrame *frame = exe_ctx->GetFramePtr();
        if (frame)
          return RunScriptFormatKeyword(s, sc, exe_ctx, frame,
                                        entry.string.c_str());
      }
      return false;
  
    case Entry::Type::FunctionID:
      if (sc) {
        if (sc->function) {
          s.Printf("function{0x%8.8" PRIx64 "}", sc->function->GetID());
          return true;
        } else if (sc->symbol) {
          s.Printf("symbol[%u]", sc->symbol->GetID());
          return true;
        }
      }
      return false;
  
    case Entry::Type::FunctionDidChange:
      return function_changed;
  
    case Entry::Type::FunctionInitialFunction:
      return initial_function;
  
    case Entry::Type::FunctionName: {
      Language *language_plugin = nullptr;
      bool language_plugin_handled = false;
      StreamString ss;
      if (sc->function)
        language_plugin = Language::FindPlugin(sc->function->GetLanguage());
      else if (sc->symbol)
        language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
      if (language_plugin) {
        language_plugin_handled = language_plugin->GetFunctionDisplayName(
            sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss);
      }
      if (language_plugin_handled) {
        s << ss.GetString();
        return true;
      } else {
        const char *name = nullptr;
        if (sc->function)
          name = sc->function->GetName().AsCString(nullptr);
        else if (sc->symbol)
          name = sc->symbol->GetName().AsCString(nullptr);
        if (name) {
          s.PutCString(name);
  
          if (sc->block) {
            Block *inline_block = sc->block->GetContainingInlinedBlock();
            if (inline_block) {
              const InlineFunctionInfo *inline_info =
                  sc->block->GetInlinedFunctionInfo();
              if (inline_info) {
                s.PutCString(" [inlined] ");
                inline_info->GetName().Dump(&s);
              }
            }
          }
          return true;
        }
      }
    }
      return false;
  
    case Entry::Type::FunctionNameNoArgs: {
      Language *language_plugin = nullptr;
      bool language_plugin_handled = false;
      StreamString ss;
      if (sc->function)
        language_plugin = Language::FindPlugin(sc->function->GetLanguage());
      else if (sc->symbol)
        language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
      if (language_plugin) {
        language_plugin_handled = language_plugin->GetFunctionDisplayName(
            sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs,
            ss);
      }
      if (language_plugin_handled) {
        s << ss.GetString();
        return true;
      } else {
        ConstString name;
        if (sc->function)
          name = sc->function->GetNameNoArguments();
        else if (sc->symbol)
          name = sc->symbol->GetNameNoArguments();
        if (name) {
          s.PutCString(name.GetCString());
          return true;
        }
      }
    }
      return false;
  
    case Entry::Type::FunctionNameWithArgs: {
      Language *language_plugin = nullptr;
      bool language_plugin_handled = false;
      StreamString ss;
      if (sc->function)
        language_plugin = Language::FindPlugin(sc->function->GetLanguage());
      else if (sc->symbol)
        language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
      if (language_plugin) {
        language_plugin_handled = language_plugin->GetFunctionDisplayName(
            sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss);
      }
      if (language_plugin_handled) {
        s << ss.GetString();
        return true;
      } else {
        // Print the function name with arguments in it
        if (sc->function) {
          ExecutionContextScope *exe_scope =
              exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
          const char *cstr = sc->function->GetName().AsCString(nullptr);
          if (cstr) {
            const InlineFunctionInfo *inline_info = nullptr;
            VariableListSP variable_list_sp;
            bool get_function_vars = true;
            if (sc->block) {
              Block *inline_block = sc->block->GetContainingInlinedBlock();
  
              if (inline_block) {
                get_function_vars = false;
                inline_info = sc->block->GetInlinedFunctionInfo();
                if (inline_info)
                  variable_list_sp = inline_block->GetBlockVariableList(true);
              }
            }
  
            if (get_function_vars) {
              variable_list_sp =
                  sc->function->GetBlock(true).GetBlockVariableList(true);
            }
  
            if (inline_info) {
              s.PutCString(cstr);
              s.PutCString(" [inlined] ");
              cstr = inline_info->GetName().GetCString();
            }
  
            VariableList args;
            if (variable_list_sp)
              variable_list_sp->AppendVariablesWithScope(
                  eValueTypeVariableArgument, args);
            if (args.GetSize() > 0) {
              const char *open_paren = strchr(cstr, '(');
              const char *close_paren = nullptr;
              const char *generic = strchr(cstr, '<');
              // if before the arguments list begins there is a template sign
              // then scan to the end of the generic args before you try to find
              // the arguments list
              if (generic && open_paren && generic < open_paren) {
                int generic_depth = 1;
                ++generic;
                for (; *generic && generic_depth > 0; generic++) {
                  if (*generic == '<')
                    generic_depth++;
                  if (*generic == '>')
                    generic_depth--;
                }
                if (*generic)
                  open_paren = strchr(generic, '(');
                else
                  open_paren = nullptr;
              }
              if (open_paren) {
                if (IsToken(open_paren, "(anonymous namespace)")) {
                  open_paren =
                      strchr(open_paren + strlen("(anonymous namespace)"), '(');
                  if (open_paren)
                    close_paren = strchr(open_paren, ')');
                } else
                  close_paren = strchr(open_paren, ')');
              }
  
              if (open_paren)
                s.Write(cstr, open_paren - cstr + 1);
              else {
                s.PutCString(cstr);
                s.PutChar('(');
              }
              const size_t num_args = args.GetSize();
              for (size_t arg_idx = 0; arg_idx < num_args; ++arg_idx) {
                std::string buffer;
  
                VariableSP var_sp(args.GetVariableAtIndex(arg_idx));
                ValueObjectSP var_value_sp(
                    ValueObjectVariable::Create(exe_scope, var_sp));
                StreamString ss;
                llvm::StringRef var_representation;
                const char *var_name = var_value_sp->GetName().GetCString();
                if (var_value_sp->GetCompilerType().IsValid()) {
                  if (var_value_sp && exe_scope->CalculateTarget())
                    var_value_sp =
                        var_value_sp->GetQualifiedRepresentationIfAvailable(
                            exe_scope->CalculateTarget()
                                ->TargetProperties::GetPreferDynamicValue(),
                            exe_scope->CalculateTarget()
                                ->TargetProperties::GetEnableSyntheticValue());
                  if (var_value_sp->GetCompilerType().IsAggregateType() &&
                      DataVisualization::ShouldPrintAsOneLiner(*var_value_sp)) {
                    static StringSummaryFormat format(
                        TypeSummaryImpl::Flags()
                            .SetHideItemNames(false)
                            .SetShowMembersOneLiner(true),
                        "");
                    format.FormatObject(var_value_sp.get(), buffer,
                                        TypeSummaryOptions());
                    var_representation = buffer;
                  } else
                    var_value_sp->DumpPrintableRepresentation(
                        ss,
                        ValueObject::ValueObjectRepresentationStyle::
                            eValueObjectRepresentationStyleSummary,
                        eFormatDefault,
                        ValueObject::PrintableRepresentationSpecialCases::eAllow,
                        false);
                }
  
                if (!ss.GetString().empty())
                  var_representation = ss.GetString();
                if (arg_idx > 0)
                  s.PutCString(", ");
                if (var_value_sp->GetError().Success()) {
                  if (!var_representation.empty())
                    s.Printf("%s=%s", var_name, var_representation.str().c_str());
                  else
                    s.Printf("%s=%s at %s", var_name,
                             var_value_sp->GetTypeName().GetCString(),
                             var_value_sp->GetLocationAsCString());
                } else
                  s.Printf("%s=<unavailable>", var_name);
              }
  
              if (close_paren)
                s.PutCString(close_paren);
              else
                s.PutChar(')');
  
            } else {
              s.PutCString(cstr);
            }
            return true;
          }
        } else if (sc->symbol) {
          const char *cstr = sc->symbol->GetName().AsCString(nullptr);
          if (cstr) {
            s.PutCString(cstr);
            return true;
          }
        }
      }
    }
      return false;
  
    case Entry::Type::FunctionMangledName: {
      const char *name = nullptr;
      if (sc->symbol)
        name =
            sc->symbol->GetMangled().GetName(Mangled::ePreferMangled).AsCString();
      else if (sc->function)
        name = sc->function->GetMangled()
                   .GetName(Mangled::ePreferMangled)
                   .AsCString();
  
      if (!name)
        return false;
      s.PutCString(name);
  
      if (sc->block->GetContainingInlinedBlock()) {
        if (const InlineFunctionInfo *inline_info =
                sc->block->GetInlinedFunctionInfo()) {
          s.PutCString(" [inlined] ");
          inline_info->GetName().Dump(&s);
        }
      }
      return true;
    }
  
    case Entry::Type::FunctionAddrOffset:
      if (addr) {
        if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, false, false,
                                          false))
          return true;
      }
      return false;
  
    case Entry::Type::FunctionAddrOffsetConcrete:
      if (addr) {
        if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, true, true,
                                          true))
          return true;
      }
      return false;
  
    case Entry::Type::FunctionLineOffset:
      return (DumpAddressOffsetFromFunction(s, sc, exe_ctx,
                                            sc->line_entry.range.GetBaseAddress(),
                                            false, false, false));
  
    case Entry::Type::FunctionPCOffset:
      if (exe_ctx) {
        StackFrame *frame = exe_ctx->GetFramePtr();
        if (frame) {
          if (DumpAddressOffsetFromFunction(s, sc, exe_ctx,
                                            frame->GetFrameCodeAddress(), false,
                                            false, false))
            return true;
        }
      }
      return false;
  
    case Entry::Type::FunctionChanged:
      return function_changed;
  
    case Entry::Type::FunctionIsOptimized: {
      bool is_optimized = false;
      if (sc->function && sc->function->GetIsOptimized()) {
        is_optimized = true;
      }
      return is_optimized;
    }
  
    case Entry::Type::FunctionInitial:
      return initial_function;
  
    case Entry::Type::LineEntryFile:
      if (sc && sc->line_entry.IsValid()) {
        Module *module = sc->module_sp.get();
        if (module) {
          if (DumpFile(s, sc->line_entry.file, (FileKind)entry.number))
            return true;
        }
      }
      return false;
  
    case Entry::Type::LineEntryLineNumber:
      if (sc && sc->line_entry.IsValid()) {
        const char *format = "%" PRIu32;
        if (!entry.printf_format.empty())
          format = entry.printf_format.c_str();
        s.Printf(format, sc->line_entry.line);
        return true;
      }
      return false;
  
    case Entry::Type::LineEntryColumn:
      if (sc && sc->line_entry.IsValid() && sc->line_entry.column) {
        const char *format = "%" PRIu32;
        if (!entry.printf_format.empty())
          format = entry.printf_format.c_str();
        s.Printf(format, sc->line_entry.column);
        return true;
      }
      return false;
  
    case Entry::Type::LineEntryStartAddress:
    case Entry::Type::LineEntryEndAddress:
      if (sc && sc->line_entry.range.GetBaseAddress().IsValid()) {
        Address addr = sc->line_entry.range.GetBaseAddress();
  
        if (entry.type == Entry::Type::LineEntryEndAddress)
          addr.Slide(sc->line_entry.range.GetByteSize());
        if (DumpAddressAndContent(s, sc, exe_ctx, addr, false))
          return true;
      }
      return false;
  
    case Entry::Type::CurrentPCArrow:
      if (addr && exe_ctx && exe_ctx->GetFramePtr()) {
        RegisterContextSP reg_ctx =
            exe_ctx->GetFramePtr()->GetRegisterContextSP();
        if (reg_ctx) {
          addr_t pc_loadaddr = reg_ctx->GetPC();
          if (pc_loadaddr != LLDB_INVALID_ADDRESS) {
            Address pc;
            pc.SetLoadAddress(pc_loadaddr, exe_ctx->GetTargetPtr());
            if (pc == *addr) {
              s.Printf("-> ");
              return true;
            }
          }
        }
        s.Printf("   ");
        return true;
      }
      return false;
    }
    return false;
  }
  
  static bool DumpCommaSeparatedChildEntryNames(
      Stream &s, const FormatEntity::Entry::Definition *parent) {
    if (parent->children) {
      const size_t n = parent->num_children;
      for (size_t i = 0; i < n; ++i) {
        if (i > 0)
          s.PutCString(", ");
        s.Printf("\"%s\"", parent->children[i].name);
      }
      return true;
    }
    return false;
  }
  
  static Status ParseEntry(const llvm::StringRef &format_str,
                           const FormatEntity::Entry::Definition *parent,
                           FormatEntity::Entry &entry) {
    Status error;
  
    const size_t sep_pos = format_str.find_first_of(".[:");
    const char sep_char =
        (sep_pos == llvm::StringRef::npos) ? '\0' : format_str[sep_pos];
    llvm::StringRef key = format_str.substr(0, sep_pos);
  
    const size_t n = parent->num_children;
    for (size_t i = 0; i < n; ++i) {
      const FormatEntity::Entry::Definition *entry_def = parent->children + i;
      if (key.equals(entry_def->name) || entry_def->name[0] == '*') {
        llvm::StringRef value;
        if (sep_char)
          value =
              format_str.substr(sep_pos + (entry_def->keep_separator ? 0 : 1));
        switch (entry_def->type) {
        case FormatEntity::Entry::Type::ParentString:
          entry.string = format_str.str();
          return error; // Success
  
        case FormatEntity::Entry::Type::ParentNumber:
          entry.number = entry_def->data;
          return error; // Success
  
        case FormatEntity::Entry::Type::EscapeCode:
          entry.type = entry_def->type;
          entry.string = entry_def->string;
          return error; // Success
  
        default:
          entry.type = entry_def->type;
          break;
        }
  
        if (value.empty()) {
          if (entry_def->type == FormatEntity::Entry::Type::Invalid) {
            if (entry_def->children) {
              StreamString error_strm;
              error_strm.Printf("'%s' can't be specified on its own, you must "
                                "access one of its children: ",
                                entry_def->name);
              DumpCommaSeparatedChildEntryNames(error_strm, entry_def);
              error.SetErrorStringWithFormat("%s", error_strm.GetData());
            } else if (sep_char == ':') {
              // Any value whose separator is a with a ':' means this value has a
              // string argument that needs to be stored in the entry (like
              // "${script.var:}"). In this case the string value is the empty
              // string which is ok.
            } else {
              error.SetErrorStringWithFormat("%s", "invalid entry definitions");
            }
          }
        } else {
          if (entry_def->children) {
            error = ParseEntry(value, entry_def, entry);
          } else if (sep_char == ':') {
            // Any value whose separator is a with a ':' means this value has a
            // string argument that needs to be stored in the entry (like
            // "${script.var:modulename.function}")
            entry.string = value.str();
          } else {
            error.SetErrorStringWithFormat(
                "'%s' followed by '%s' but it has no children", key.str().c_str(),
                value.str().c_str());
          }
        }
        return error;
      }
    }
    StreamString error_strm;
    if (parent->type == FormatEntity::Entry::Type::Root)
      error_strm.Printf(
          "invalid top level item '%s'. Valid top level items are: ",
          key.str().c_str());
    else
      error_strm.Printf("invalid member '%s' in '%s'. Valid members are: ",
                        key.str().c_str(), parent->name);
    DumpCommaSeparatedChildEntryNames(error_strm, parent);
    error.SetErrorStringWithFormat("%s", error_strm.GetData());
    return error;
  }
  
  static const FormatEntity::Entry::Definition *
  FindEntry(const llvm::StringRef &format_str,
            const FormatEntity::Entry::Definition *parent,
            llvm::StringRef &remainder) {
    Status error;
  
    std::pair<llvm::StringRef, llvm::StringRef> p = format_str.split('.');
    const size_t n = parent->num_children;
    for (size_t i = 0; i < n; ++i) {
      const FormatEntity::Entry::Definition *entry_def = parent->children + i;
      if (p.first.equals(entry_def->name) || entry_def->name[0] == '*') {
        if (p.second.empty()) {
          if (format_str.back() == '.')
            remainder = format_str.drop_front(format_str.size() - 1);
          else
            remainder = llvm::StringRef(); // Exact match
          return entry_def;
        } else {
          if (entry_def->children) {
            return FindEntry(p.second, entry_def, remainder);
          } else {
            remainder = p.second;
            return entry_def;
          }
        }
      }
    }
    remainder = format_str;
    return parent;
  }
  
  Status FormatEntity::ParseInternal(llvm::StringRef &format, Entry &parent_entry,
                                     uint32_t depth) {
    Status error;
    while (!format.empty() && error.Success()) {
      const size_t non_special_chars = format.find_first_of("${}\\");
  
      if (non_special_chars == llvm::StringRef::npos) {
        // No special characters, just string bytes so add them and we are done
        parent_entry.AppendText(format);
        return error;
      }
  
      if (non_special_chars > 0) {
        // We have a special character, so add all characters before these as a
        // plain string
        parent_entry.AppendText(format.substr(0, non_special_chars));
        format = format.drop_front(non_special_chars);
      }
  
      switch (format[0]) {
      case '\0':
        return error;
  
      case '{': {
        format = format.drop_front(); // Skip the '{'
        Entry scope_entry(Entry::Type::Scope);
        error = FormatEntity::ParseInternal(format, scope_entry, depth + 1);
        if (error.Fail())
          return error;
        parent_entry.AppendEntry(std::move(scope_entry));
      } break;
  
      case '}':
        if (depth == 0)
          error.SetErrorString("unmatched '}' character");
        else
          format =
              format
                  .drop_front(); // Skip the '}' as we are at the end of the scope
        return error;
  
      case '\\': {
        format = format.drop_front(); // Skip the '\' character
        if (format.empty()) {
          error.SetErrorString(
              "'\\' character was not followed by another character");
          return error;
        }
  
        const char desens_char = format[0];
        format = format.drop_front(); // Skip the desensitized char character
        switch (desens_char) {
        case 'a':
          parent_entry.AppendChar('\a');
          break;
        case 'b':
          parent_entry.AppendChar('\b');
          break;
        case 'f':
          parent_entry.AppendChar('\f');
          break;
        case 'n':
          parent_entry.AppendChar('\n');
          break;
        case 'r':
          parent_entry.AppendChar('\r');
          break;
        case 't':
          parent_entry.AppendChar('\t');
          break;
        case 'v':
          parent_entry.AppendChar('\v');
          break;
        case '\'':
          parent_entry.AppendChar('\'');
          break;
        case '\\':
          parent_entry.AppendChar('\\');
          break;
        case '0':
          // 1 to 3 octal chars
          {
            // Make a string that can hold onto the initial zero char, up to 3
            // octal digits, and a terminating NULL.
            char oct_str[5] = {0, 0, 0, 0, 0};
  
            int i;
            for (i = 0; (format[i] >= '0' && format[i] <= '7') && i < 4; ++i)
              oct_str[i] = format[i];
  
            // We don't want to consume the last octal character since the main
            // for loop will do this for us, so we advance p by one less than i
            // (even if i is zero)
            format = format.drop_front(i);
            unsigned long octal_value = ::strtoul(oct_str, nullptr, 8);
            if (octal_value <= UINT8_MAX) {
              parent_entry.AppendChar((char)octal_value);
            } else {
              error.SetErrorString("octal number is larger than a single byte");
              return error;
            }
          }
          break;
  
        case 'x':
          // hex number in the format
          if (isxdigit(format[0])) {
            // Make a string that can hold onto two hex chars plus a
            // NULL terminator
            char hex_str[3] = {0, 0, 0};
            hex_str[0] = format[0];
  
            format = format.drop_front();
  
            if (isxdigit(format[0])) {
              hex_str[1] = format[0];
              format = format.drop_front();
            }
  
            unsigned long hex_value = strtoul(hex_str, nullptr, 16);
            if (hex_value <= UINT8_MAX) {
              parent_entry.AppendChar((char)hex_value);
            } else {
              error.SetErrorString("hex number is larger than a single byte");
              return error;
            }
          } else {
            parent_entry.AppendChar(desens_char);
          }
          break;
  
        default:
          // Just desensitize any other character by just printing what came
          // after the '\'
          parent_entry.AppendChar(desens_char);
          break;
        }
      } break;
  
      case '$':
        if (format.size() == 1) {
          // '$' at the end of a format string, just print the '$'
          parent_entry.AppendText("$");
        } else {
          format = format.drop_front(); // Skip the '$'
  
          if (format[0] == '{') {
            format = format.drop_front(); // Skip the '{'
  
            llvm::StringRef variable, variable_format;
            error = FormatEntity::ExtractVariableInfo(format, variable,
                                                      variable_format);
            if (error.Fail())
              return error;
            bool verify_is_thread_id = false;
            Entry entry;
            if (!variable_format.empty()) {
              entry.printf_format = variable_format.str();
  
              // If the format contains a '%' we are going to assume this is a
              // printf style format. So if you want to format your thread ID
              // using "0x%llx" you can use: ${thread.id%0x%llx}
              //
              // If there is no '%' in the format, then it is assumed to be a
              // LLDB format name, or one of the extended formats specified in
              // the switch statement below.
  
              if (entry.printf_format.find('%') == std::string::npos) {
                bool clear_printf = false;
  
                if (FormatManager::GetFormatFromCString(
                        entry.printf_format.c_str(), false, entry.fmt)) {
                  // We have an LLDB format, so clear the printf format
                  clear_printf = true;
                } else if (entry.printf_format.size() == 1) {
                  switch (entry.printf_format[0]) {
                  case '@': // if this is an @ sign, print ObjC description
                    entry.number = ValueObject::
                        eValueObjectRepresentationStyleLanguageSpecific;
                    clear_printf = true;
                    break;
                  case 'V': // if this is a V, print the value using the default
                            // format
                    entry.number =
                        ValueObject::eValueObjectRepresentationStyleValue;
                    clear_printf = true;
                    break;
                  case 'L': // if this is an L, print the location of the value
                    entry.number =
                        ValueObject::eValueObjectRepresentationStyleLocation;
                    clear_printf = true;
                    break;
                  case 'S': // if this is an S, print the summary after all
                    entry.number =
                        ValueObject::eValueObjectRepresentationStyleSummary;
                    clear_printf = true;
                    break;
                  case '#': // if this is a '#', print the number of children
                    entry.number =
                        ValueObject::eValueObjectRepresentationStyleChildrenCount;
                    clear_printf = true;
                    break;
                  case 'T': // if this is a 'T', print the type
                    entry.number =
                        ValueObject::eValueObjectRepresentationStyleType;
                    clear_printf = true;
                    break;
                  case 'N': // if this is a 'N', print the name
                    entry.number =
                        ValueObject::eValueObjectRepresentationStyleName;
                    clear_printf = true;
                    break;
                  case '>': // if this is a '>', print the expression path
                    entry.number = ValueObject::
                        eValueObjectRepresentationStyleExpressionPath;
                    clear_printf = true;
                    break;
                  default:
                    error.SetErrorStringWithFormat("invalid format: '%s'",
                                                   entry.printf_format.c_str());
                    return error;
                  }
                } else if (FormatManager::GetFormatFromCString(
                               entry.printf_format.c_str(), true, entry.fmt)) {
                  clear_printf = true;
                } else if (entry.printf_format == "tid") {
                  verify_is_thread_id = true;
                } else {
                  error.SetErrorStringWithFormat("invalid format: '%s'",
                                                 entry.printf_format.c_str());
                  return error;
                }
  
                // Our format string turned out to not be a printf style format
                // so lets clear the string
                if (clear_printf)
                  entry.printf_format.clear();
              }
            }
  
            // Check for dereferences
            if (variable[0] == '*') {
              entry.deref = true;
              variable = variable.drop_front();
            }
  
            error = ParseEntry(variable, &g_root, entry);
            if (error.Fail())
              return error;
  
            if (verify_is_thread_id) {
              if (entry.type != Entry::Type::ThreadID &&
                  entry.type != Entry::Type::ThreadProtocolID) {
                error.SetErrorString("the 'tid' format can only be used on "
                                     "${thread.id} and ${thread.protocol_id}");
              }
            }
  
            switch (entry.type) {
            case Entry::Type::Variable:
            case Entry::Type::VariableSynthetic:
              if (entry.number == 0) {
                if (entry.string.empty())
                  entry.number =
                      ValueObject::eValueObjectRepresentationStyleValue;
                else
                  entry.number =
                      ValueObject::eValueObjectRepresentationStyleSummary;
              }
              break;
            default:
              // Make sure someone didn't try to dereference anything but ${var}
              // or ${svar}
              if (entry.deref) {
                error.SetErrorStringWithFormat(
                    "${%s} can't be dereferenced, only ${var} and ${svar} can.",
                    variable.str().c_str());
                return error;
              }
            }
            parent_entry.AppendEntry(std::move(entry));
          }
        }
        break;
      }
    }
    return error;
  }
  
  Status FormatEntity::ExtractVariableInfo(llvm::StringRef &format_str,
                                           llvm::StringRef &variable_name,
                                           llvm::StringRef &variable_format) {
    Status error;
    variable_name = llvm::StringRef();
    variable_format = llvm::StringRef();
  
    const size_t paren_pos = format_str.find('}');
    if (paren_pos != llvm::StringRef::npos) {
      const size_t percent_pos = format_str.find('%');
      if (percent_pos < paren_pos) {
        if (percent_pos > 0) {
          if (percent_pos > 1)
            variable_name = format_str.substr(0, percent_pos);
          variable_format =
              format_str.substr(percent_pos + 1, paren_pos - (percent_pos + 1));
        }
      } else {
        variable_name = format_str.substr(0, paren_pos);
      }
      // Strip off elements and the formatting and the trailing '}'
      format_str = format_str.substr(paren_pos + 1);
    } else {
      error.SetErrorStringWithFormat(
          "missing terminating '}' character for '${%s'",
          format_str.str().c_str());
    }
    return error;
  }
  
  bool FormatEntity::FormatFileSpec(const FileSpec &file_spec, Stream &s,
                                    llvm::StringRef variable_name,
                                    llvm::StringRef variable_format) {
    if (variable_name.empty() || variable_name.equals(".fullpath")) {
      file_spec.Dump(s.AsRawOstream());
      return true;
    } else if (variable_name.equals(".basename")) {
      s.PutCString(file_spec.GetFilename().GetStringRef());
      return true;
    } else if (variable_name.equals(".dirname")) {
      s.PutCString(file_spec.GetFilename().GetStringRef());
      return true;
    }
    return false;
  }
  
  static std::string MakeMatch(const llvm::StringRef &prefix,
                               const char *suffix) {
    std::string match(prefix.str());
    match.append(suffix);
    return match;
  }
  
  static void AddMatches(const FormatEntity::Entry::Definition *def,
                         const llvm::StringRef &prefix,
                         const llvm::StringRef &match_prefix,
                         StringList &matches) {
    const size_t n = def->num_children;
    if (n > 0) {
      for (size_t i = 0; i < n; ++i) {
        std::string match = prefix.str();
        if (match_prefix.empty())
          matches.AppendString(MakeMatch(prefix, def->children[i].name));
        else if (strncmp(def->children[i].name, match_prefix.data(),
                         match_prefix.size()) == 0)
          matches.AppendString(
              MakeMatch(prefix, def->children[i].name + match_prefix.size()));
      }
    }
  }
  
  void FormatEntity::AutoComplete(CompletionRequest &request) {
    llvm::StringRef str = request.GetCursorArgumentPrefix();
  
    const size_t dollar_pos = str.rfind('$');
    if (dollar_pos == llvm::StringRef::npos)
      return;
  
    // Hitting TAB after $ at the end of the string add a "{"
    if (dollar_pos == str.size() - 1) {
      std::string match = str.str();
      match.append("{");
      request.AddCompletion(match);
      return;
    }
  
    if (str[dollar_pos + 1] != '{')
      return;
  
    const size_t close_pos = str.find('}', dollar_pos + 2);
    if (close_pos != llvm::StringRef::npos)
      return;
  
    const size_t format_pos = str.find('%', dollar_pos + 2);
    if (format_pos != llvm::StringRef::npos)
      return;
  
    llvm::StringRef partial_variable(str.substr(dollar_pos + 2));
    if (partial_variable.empty()) {
      // Suggest all top level entities as we are just past "${"
      StringList new_matches;
      AddMatches(&g_root, str, llvm::StringRef(), new_matches);
      request.AddCompletions(new_matches);
      return;
    }
  
    // We have a partially specified variable, find it
    llvm::StringRef remainder;
    const FormatEntity::Entry::Definition *entry_def =
        FindEntry(partial_variable, &g_root, remainder);
    if (!entry_def)
      return;
  
    const size_t n = entry_def->num_children;
  
    if (remainder.empty()) {
      // Exact match
      if (n > 0) {
        // "${thread.info" <TAB>
        request.AddCompletion(MakeMatch(str, "."));
      } else {
        // "${thread.id" <TAB>
        request.AddCompletion(MakeMatch(str, "}"));
      }
    } else if (remainder.equals(".")) {
      // "${thread." <TAB>
      StringList new_matches;
      AddMatches(entry_def, str, llvm::StringRef(), new_matches);
      request.AddCompletions(new_matches);
    } else {
      // We have a partial match
      // "${thre" <TAB>
      StringList new_matches;
      AddMatches(entry_def, str, remainder, new_matches);
      request.AddCompletions(new_matches);
    }
  }
  