blob: eb584c6b47ad44ff68e5dc81a47de4e33acd9a05 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
#include "markdown.h"
#include <yql/essentials/utils/yql_panic.h>
#include <contrib/libs/re2/re2/re2.h>
#include <util/generic/yexception.h>
namespace NSQLComplete {
class TMarkdownParser {
public:
void Parse(IInputStream& markdown, TMarkdownCallback&& onSection) {
for (TString line; markdown.ReadLine(line) != 0;) {
if (IsSkipping_) {
if (IsSectionHeader(line)) {
ResetSection(std::move(line));
IsSkipping_ = false;
} else {
// Skip
}
} else {
if (IsSectionHeader(line)) {
onSection(std::move(Section_));
ResetSection(std::move(line));
} else {
line.append('\n');
Section_.Body.append(std::move(line));
}
}
}
if (!IsSkipping_) {
onSection(std::move(Section_));
}
}
private:
void ResetSection(TString&& line) {
Section_ = TMarkdownSection();
TString content;
std::optional<TString> dummy;
std::optional<TString> anchor;
YQL_ENSURE(
RE2::FullMatch(line, SectionHeaderRegex_, &content, &dummy, &anchor),
"line '" << line << "' does not match regex '"
<< SectionHeaderRegex_.pattern() << "'");
Section_.Header.Content = std::move(content);
if (anchor) {
Section_.Header.Anchor = std::move(*anchor);
}
}
bool IsSectionHeader(TStringBuf line) const {
return HeaderDepth(line) == 2;
}
size_t HeaderDepth(TStringBuf line) const {
size_t pos = line.find_first_not_of('#');
return pos != TStringBuf::npos ? pos : 0;
}
RE2 SectionHeaderRegex_{R"re(## ([^#]+)(\s+{(#[a-z0-9\-_]+)})?)re"};
bool IsSkipping_ = true;
TMarkdownSection Section_;
};
void ParseMarkdown(IInputStream& markdown, TMarkdownCallback&& onSection) {
TMarkdownParser parser;
parser.Parse(markdown, std::forward<TMarkdownCallback>(onSection));
}
} // namespace NSQLComplete
|