Created
April 30, 2014 09:43
-
-
Save zdne/55b7a38f52ca642e123b to your computer and use it in GitHub Desktop.
New API Blueprint parser structure
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <string> | |
#include <vector> | |
#include <regex> | |
enum MarkdownNodeType { | |
RootMarkdownNodeType = 0, | |
HeaderMarkdownNodeType, | |
ListItemMarkdownNodeType, | |
ParagraphMarkdownNodeType, | |
UndefinedMarkdownNodeType = -1 | |
}; | |
struct MarkdownNode { | |
MarkdownNodeType type; | |
std::string text; | |
std::vector<MarkdownNode> children; | |
}; | |
struct Resource { | |
std::string name; | |
std::string description; | |
}; | |
struct Parameter { | |
std::string name; | |
std::string description; | |
std::vector<Parameter> nested; // Illegal! | |
}; | |
struct Parameters { | |
std::string description; | |
std::vector<Parameter> parametes; | |
}; | |
struct Blueprint { | |
std::string name; | |
std::string description; | |
std::vector<Resource> resources; | |
Parameters parameters; | |
}; | |
enum SectionType { | |
BlueprintSectionType = 0, | |
ResoureSectionType, | |
ParametersSectionType, | |
ParameterSectionType, | |
UndefinedSectionType = -1 | |
}; | |
typedef int Result; | |
typedef std::vector<MarkdownNode> Nodes; | |
typedef std::vector<MarkdownNode>::const_iterator NodeIterator; | |
// Forward declarations; | |
bool recognize_signature(const NodeIterator& node); | |
template<typename T> | |
struct Processor; | |
//---- | |
template<typename T> | |
struct ProcessorBase { | |
static NodeIterator process_signature(const NodeIterator& node, T& out) { | |
return ++NodeIterator(node); | |
} | |
static NodeIterator process_description(const NodeIterator& node, T& out) { | |
out.description += node->text; | |
return ++NodeIterator(node); | |
} | |
static NodeIterator process_content(const NodeIterator& node, T& out) { | |
return ++NodeIterator(node); | |
} | |
static NodeIterator process_nested_section(const NodeIterator& node, | |
const Nodes& siblings, | |
SectionType& type, | |
T& out) { | |
return node; | |
} | |
static NodeIterator process_unexpected_node(const NodeIterator& node, | |
const Nodes& siblings, | |
T& out) { | |
return ++NodeIterator(node); | |
} | |
static bool is_description_node(const NodeIterator& node) { | |
return !Processor<T>::is_content_node(node) && | |
Processor<T>::nested_section_node(node) == UndefinedSectionType && | |
!recognize_signature(node); | |
} | |
static bool is_content_node(const NodeIterator& node) { | |
return false; | |
} | |
static bool is_unexpected_node(const NodeIterator& node) { | |
return !recognize_signature(node); | |
} | |
static SectionType nested_section_node(const NodeIterator& node) { | |
return UndefinedSectionType; | |
} | |
static SectionType defines_section(const NodeIterator& node) { | |
return UndefinedSectionType; | |
} | |
}; | |
template<typename T> | |
struct Processor : public ProcessorBase<T> { | |
}; | |
//---- | |
struct HeaderSectionAdapter { | |
static const NodeIterator working_node(const NodeIterator& seed) { | |
return seed; | |
} | |
static const Nodes& working_node_collection(const NodeIterator& seed, const Nodes& siblings) { | |
return siblings; | |
} | |
static const NodeIterator seed_node(const NodeIterator& seed, const Nodes& siblings, const NodeIterator& cur) { | |
return cur; | |
} | |
}; | |
struct ListSectionAdapter { | |
static const NodeIterator working_node(const NodeIterator& seed) { | |
return seed->children.begin(); | |
} | |
static const Nodes& working_node_collection(const NodeIterator& seed, const Nodes& siblings) { | |
return seed->children; | |
} | |
static const NodeIterator seed_node(const NodeIterator& seed, const Nodes& siblings, const NodeIterator& cur) { | |
if (seed == siblings.end()) | |
return seed; | |
return ++NodeIterator(seed); | |
} | |
}; | |
template<typename T, typename Adapter> | |
struct SectionParser { | |
static NodeIterator parse(const NodeIterator& node, const Nodes& siblings, T& out) { | |
NodeIterator cur = Adapter::working_node(node); | |
const Nodes& collection = Adapter::working_node_collection(node, siblings); | |
NodeIterator lastCur = cur; | |
cur = Processor<T>::process_signature(cur, out); | |
if (lastCur == cur) | |
return Adapter::seed_node(node, siblings, cur); | |
// Description nodes | |
while(cur != collection.end() && | |
Processor<T>::is_description_node(cur)) { | |
lastCur = cur; | |
cur = Processor<T>::process_description(cur, out); | |
if (lastCur == cur) | |
return Adapter::seed_node(node, siblings, cur);; | |
} | |
// Content nodes | |
while(cur != collection.end() && | |
Processor<T>::is_content_node(cur)) { | |
lastCur = cur; | |
cur = Processor<T>::process_content(cur, out); | |
if (lastCur == cur) | |
return Adapter::seed_node(node, siblings, cur);; | |
} | |
// Nested sections | |
while(cur != collection.end()) { | |
lastCur = cur; | |
SectionType nested_type = Processor<T>::nested_section_node(cur); | |
if (nested_type != UndefinedSectionType) { | |
cur = Processor<T>::process_nested_section(cur, collection, nested_type, out); | |
} | |
else if (Processor<T>::is_unexpected_node(cur)) { | |
cur = Processor<T>::process_unexpected_node(cur, collection, out); | |
} | |
if (lastCur == cur) | |
break; | |
} | |
return Adapter::seed_node(node, siblings, cur); | |
} | |
}; | |
//------------------ | |
template<> | |
struct Processor<Resource> : public ProcessorBase<Resource> { | |
static SectionType defines_section(const NodeIterator& node) { | |
if (node->type == HeaderMarkdownNodeType) { | |
std::regex pattern("^Resource", std::regex_constants::extended); | |
if (std::regex_search(node->text, pattern)) | |
return ResoureSectionType; | |
} | |
return UndefinedSectionType; | |
} | |
}; | |
typedef SectionParser<Resource, HeaderSectionAdapter> ResourceParser; | |
//------------------ | |
template<> | |
struct Processor<Parameter> : public ProcessorBase<Parameter> { | |
static NodeIterator process_nested_section(const NodeIterator& node, | |
const Nodes& siblings, | |
SectionType type, | |
Parameter& out) { | |
NodeIterator cur = node; | |
switch (type) { | |
case ParameterSectionType: | |
{ | |
Parameter param; | |
cur = SectionParser<Parameter, ListSectionAdapter>::parse(cur, siblings, param); | |
out.nested.emplace_back(param); | |
} | |
break; | |
default: | |
break; | |
} | |
return cur; | |
} | |
static SectionType nested_section_node(const NodeIterator& node) { | |
return Processor<Parameter>::defines_section(node); | |
} | |
static SectionType defines_section(const NodeIterator& node) { | |
if (node->type == ListItemMarkdownNodeType | |
&& !node->children.empty()) { | |
std::regex pattern("^`.+`$", std::regex_constants::extended); | |
if (std::regex_search(node->children.front().text, pattern)) | |
return ParameterSectionType; | |
} | |
return UndefinedSectionType; | |
} | |
}; | |
typedef SectionParser<Parameter, ListSectionAdapter> ParameterParser; | |
//------------------ | |
template<> | |
struct Processor<Parameters> : public ProcessorBase <Parameters> { | |
static NodeIterator process_nested_section(const NodeIterator& node, | |
const Nodes& siblings, | |
SectionType type, | |
Parameters& out) { | |
NodeIterator cur = node; | |
switch (type) { | |
case ParameterSectionType: | |
{ | |
Parameter param; | |
cur = ParameterParser::parse(cur, siblings, param); | |
out.parametes.emplace_back(param); | |
} | |
break; | |
default: | |
break; | |
} | |
return cur; | |
} | |
static SectionType nested_section_node(const NodeIterator& node) { | |
return Processor<Parameter>::defines_section(node); | |
} | |
static SectionType defines_section(const NodeIterator& node) { | |
if (node->type == ListItemMarkdownNodeType | |
&& !node->children.empty()) { | |
std::regex pattern("^Parameters$", std::regex_constants::extended); | |
if (std::regex_search(node->children.front().text, pattern)) | |
return ParametersSectionType; | |
} | |
return UndefinedSectionType; | |
} | |
}; | |
typedef SectionParser<Parameters, ListSectionAdapter> ParametersParser; | |
//------------------ | |
template<> | |
struct Processor<Blueprint> : public ProcessorBase <Blueprint> { | |
static NodeIterator process_nested_section(const NodeIterator& node, | |
const Nodes& siblings, | |
SectionType type, | |
Blueprint& out) { | |
NodeIterator cur = node; | |
switch (type) { | |
case ResoureSectionType: | |
{ | |
Resource resource; | |
cur = ResourceParser::parse(cur, siblings, resource); | |
out.resources.push_back(resource); | |
} | |
break; | |
case ParametersSectionType: | |
{ | |
cur = ParametersParser::parse(cur, siblings, out.parameters); | |
} | |
break; | |
default: | |
break; | |
} | |
return cur; | |
} | |
static SectionType nested_section_node(const NodeIterator& node) { | |
SectionType type = Processor<Resource>::defines_section(node); | |
if (type != UndefinedSectionType) | |
return type; | |
type = Processor<Parameters>::defines_section(node); | |
return type; | |
} | |
}; | |
typedef SectionParser<Blueprint, HeaderSectionAdapter > BlueprintParser; | |
//------------------ | |
bool recognize_signature(const NodeIterator& node) | |
{ | |
return Processor<Resource>::defines_section(node) != UndefinedSectionType || | |
Processor<Parameters>::defines_section(node) != UndefinedSectionType; | |
} | |
//------------------ | |
int main() | |
{ | |
MarkdownNode root = {RootMarkdownNodeType}; | |
root.children.emplace_back(MarkdownNode{HeaderMarkdownNodeType, "My API" }); | |
root.children.emplace_back(MarkdownNode{ParagraphMarkdownNodeType, "Description first paragraph" }); | |
root.children.emplace_back(MarkdownNode{ParagraphMarkdownNodeType, "Description second paragraph" }); | |
root.children.emplace_back(MarkdownNode{HeaderMarkdownNodeType, "Resource A" }); | |
root.children.emplace_back(MarkdownNode{ParagraphMarkdownNodeType, "Resource A description" }); | |
root.children.emplace_back(MarkdownNode{HeaderMarkdownNodeType, "Resource B" }); | |
root.children.emplace_back(MarkdownNode{ParagraphMarkdownNodeType, "Resource B description" }); | |
root.children.emplace_back(MarkdownNode{ListItemMarkdownNodeType}); | |
root.children.back().children.emplace_back(MarkdownNode{ParagraphMarkdownNodeType, "Parameters" }); | |
root.children.back().children.emplace_back(MarkdownNode{ListItemMarkdownNodeType}); | |
root.children.back().children.back().children.emplace_back(MarkdownNode{ParagraphMarkdownNodeType, "`A`"}); | |
root.children.back().children.back().children.emplace_back(MarkdownNode{ParagraphMarkdownNodeType, "Param A description"}); | |
root.children.back().children.emplace_back(MarkdownNode{ParagraphMarkdownNodeType, "BLBOST"}); | |
root.children.back().children.emplace_back(MarkdownNode{ListItemMarkdownNodeType}); | |
root.children.back().children.back().children.emplace_back(MarkdownNode{ParagraphMarkdownNodeType, "`B`"}); | |
root.children.back().children.back().children.emplace_back(MarkdownNode{ParagraphMarkdownNodeType, "Param B description"}); | |
root.children.back().children.back().children.emplace_back(MarkdownNode{ListItemMarkdownNodeType}); | |
root.children.back().children.back().children.back().children.emplace_back(MarkdownNode{ParagraphMarkdownNodeType, "`C`"}); | |
root.children.back().children.back().children.back().children.emplace_back(MarkdownNode{ParagraphMarkdownNodeType, "Param `C` description"}); | |
root.children.emplace_back(MarkdownNode{HeaderMarkdownNodeType, "Resource C" }); | |
root.children.emplace_back(MarkdownNode{ParagraphMarkdownNodeType, "Resource C description" }); | |
Blueprint out; | |
BlueprintParser::parse(root.children.begin(), root.children, out); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment