brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.5 KiB · 1b5f53e Raw
205 lines · cpp
1//===- lib/DebugInfo/Symbolize/Markup.cpp ------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file defines the log symbolizer markup data model and parser.11///12//===----------------------------------------------------------------------===//13 14#include "llvm/DebugInfo/Symbolize/Markup.h"15 16#include "llvm/ADT/STLExtras.h"17 18namespace llvm {19namespace symbolize {20 21// Matches the following:22//   "\033[0m"23//   "\033[1m"24//   "\033[30m" -- "\033[37m"25static const char SGRSyntaxStr[] = "\033\\[([0-1]|3[0-7])m";26 27MarkupParser::MarkupParser(StringSet<> MultilineTags)28    : MultilineTags(std::move(MultilineTags)), SGRSyntax(SGRSyntaxStr) {}29 30static StringRef takeTo(StringRef Str, StringRef::iterator Pos) {31  return Str.take_front(Pos - Str.begin());32}33static void advanceTo(StringRef &Str, StringRef::iterator Pos) {34  Str = Str.drop_front(Pos - Str.begin());35}36 37void MarkupParser::parseLine(StringRef Line) {38  Buffer.clear();39  NextIdx = 0;40  FinishedMultiline.clear();41  this->Line = Line;42}43 44std::optional<MarkupNode> MarkupParser::nextNode() {45  // Pull something out of the buffer if possible.46  if (!Buffer.empty()) {47    if (NextIdx < Buffer.size())48      return std::move(Buffer[NextIdx++]);49    NextIdx = 0;50    Buffer.clear();51  }52 53  // The buffer is empty, so parse the next bit of the line.54 55  if (Line.empty())56    return std::nullopt;57 58  if (!InProgressMultiline.empty()) {59    if (std::optional<StringRef> MultilineEnd = parseMultiLineEnd(Line)) {60      llvm::append_range(InProgressMultiline, *MultilineEnd);61      assert(FinishedMultiline.empty() &&62             "At most one multi-line element can be finished at a time.");63      FinishedMultiline.swap(InProgressMultiline);64      // Parse the multi-line element as if it were contiguous.65      advanceTo(Line, MultilineEnd->end());66      return *parseElement(FinishedMultiline);67    }68 69    // The whole line is part of the multi-line element.70    llvm::append_range(InProgressMultiline, Line);71    Line = Line.drop_front(Line.size());72    return std::nullopt;73  }74 75  // Find the first valid markup element, if any.76  if (std::optional<MarkupNode> Element = parseElement(Line)) {77    parseTextOutsideMarkup(takeTo(Line, Element->Text.begin()));78    Buffer.push_back(std::move(*Element));79    advanceTo(Line, Element->Text.end());80    return nextNode();81  }82 83  // Since there were no valid elements remaining, see if the line opens a84  // multi-line element.85  if (std::optional<StringRef> MultilineBegin = parseMultiLineBegin(Line)) {86    // Emit any text before the element.87    parseTextOutsideMarkup(takeTo(Line, MultilineBegin->begin()));88 89    // Begin recording the multi-line element.90    llvm::append_range(InProgressMultiline, *MultilineBegin);91    Line = Line.drop_front(Line.size());92    return nextNode();93  }94 95  // The line doesn't contain any more markup elements, so emit it as text.96  parseTextOutsideMarkup(Line);97  Line = Line.drop_front(Line.size());98  return nextNode();99}100 101void MarkupParser::flush() {102  Buffer.clear();103  NextIdx = 0;104  Line = {};105  if (InProgressMultiline.empty())106    return;107  FinishedMultiline.swap(InProgressMultiline);108  parseTextOutsideMarkup(FinishedMultiline);109}110 111// Finds and returns the next valid markup element in the given line. Returns112// std::nullopt if the line contains no valid elements.113std::optional<MarkupNode> MarkupParser::parseElement(StringRef Line) {114  while (true) {115    // Find next element using begin and end markers.116    size_t BeginPos = Line.find("{{{");117    if (BeginPos == StringRef::npos)118      return std::nullopt;119    size_t EndPos = Line.find("}}}", BeginPos + 3);120    if (EndPos == StringRef::npos)121      return std::nullopt;122    EndPos += 3;123    MarkupNode Element;124    Element.Text = Line.slice(BeginPos, EndPos);125    Line = Line.substr(EndPos);126 127    // Parse tag.128    StringRef Content = Element.Text.drop_front(3).drop_back(3);129    StringRef FieldsContent;130    std::tie(Element.Tag, FieldsContent) = Content.split(':');131    if (Element.Tag.empty())132      continue;133 134    // Parse fields.135    if (!FieldsContent.empty())136      FieldsContent.split(Element.Fields, ":");137    else if (Content.back() == ':')138      Element.Fields.push_back(FieldsContent);139 140    return Element;141  }142}143 144static MarkupNode textNode(StringRef Text) {145  MarkupNode Node;146  Node.Text = Text;147  return Node;148}149 150// Parses a region of text known to be outside any markup elements. Such text151// may still contain SGR control codes, so the region is further subdivided into152// control codes and true text regions.153void MarkupParser::parseTextOutsideMarkup(StringRef Text) {154  if (Text.empty())155    return;156  SmallVector<StringRef> Matches;157  while (SGRSyntax.match(Text, &Matches)) {158    // Emit any text before the SGR element.159    if (Matches.begin()->begin() != Text.begin())160      Buffer.push_back(textNode(takeTo(Text, Matches.begin()->begin())));161 162    Buffer.push_back(textNode(*Matches.begin()));163    advanceTo(Text, Matches.begin()->end());164  }165  if (!Text.empty())166    Buffer.push_back(textNode(Text));167}168 169// Given that a line doesn't contain any valid markup, see if it ends with the170// start of a multi-line element. If so, returns the beginning.171std::optional<StringRef> MarkupParser::parseMultiLineBegin(StringRef Line) {172  // A multi-line begin marker must be the last one on the line.173  size_t BeginPos = Line.rfind("{{{");174  if (BeginPos == StringRef::npos)175    return std::nullopt;176  size_t BeginTagPos = BeginPos + 3;177 178  // If there are any end markers afterwards, the begin marker cannot belong to179  // a multi-line element.180  size_t EndPos = Line.find("}}}", BeginTagPos);181  if (EndPos != StringRef::npos)182    return std::nullopt;183 184  // Check whether the tag is registered multi-line.185  size_t EndTagPos = Line.find(':', BeginTagPos);186  if (EndTagPos == StringRef::npos)187    return std::nullopt;188  StringRef Tag = Line.slice(BeginTagPos, EndTagPos);189  if (!MultilineTags.contains(Tag))190    return std::nullopt;191  return Line.substr(BeginPos);192}193 194// See if the line begins with the ending of an in-progress multi-line element.195// If so, return the ending.196std::optional<StringRef> MarkupParser::parseMultiLineEnd(StringRef Line) {197  size_t EndPos = Line.find("}}}");198  if (EndPos == StringRef::npos)199    return std::nullopt;200  return Line.take_front(EndPos + 3);201}202 203} // end namespace symbolize204} // end namespace llvm205