ext/snowcrash/src/BlueprintParser.h in redsnow-0.1.6 vs ext/snowcrash/src/BlueprintParser.h in redsnow-0.2.0

- old
+ new

@@ -7,341 +7,267 @@ // #ifndef SNOWCRASH_BLUEPRINTPARSER_H #define SNOWCRASH_BLUEPRINTPARSER_H -#include <functional> -#include <sstream> -#include <iterator> -#include "Blueprint.h" -#include "BlueprintParserCore.h" #include "ResourceParser.h" #include "ResourceGroupParser.h" +#include "SectionParser.h" +#include "RegexMatch.h" +#include "CodeBlockUtility.h" -namespace snowcrashconst { - +namespace snowcrash { + const char* const ExpectedAPINameMessage = "expected API name, e.g. '# <API Name>'"; -} -namespace snowcrash { + /** Internal type alias for Collection of Metadata */ + typedef Collection<Metadata>::type MetadataCollection; - /** Internal list items classifier, Blueprint Context */ - template <> - FORCEINLINE SectionType ClassifyInternaListBlock<Blueprint>(const BlockIterator& begin, - const BlockIterator& end) { - return UndefinedSectionType; - } - - /** Block Classifier, Blueprint Context */ - template <> - FORCEINLINE SectionType ClassifyBlock<Blueprint>(const BlockIterator& begin, - const BlockIterator& end, - const SectionType& context) { - - if (HasResourceGroupSignature(*begin) || - HasResourceSignature(*begin)) - return ResourceGroupSectionType; // Treat Resource as anonymous resource group - - return (context == ResourceGroupSectionType) ? UndefinedSectionType : BlueprintSectionType; - } - + typedef Collection<Metadata>::iterator MetadataCollectionIterator; - /** Blueprint SectionType Parser */ + /** + * Blueprint processor + */ template<> - struct SectionParser<Blueprint> { - - static ParseSectionResult ParseSection(const BlueprintSection& section, - const BlockIterator& cur, - BlueprintParserCore& parser, - Blueprint& output) { - - ParseSectionResult result = std::make_pair(Result(), cur); - - switch (section.type) { - - case BlueprintSectionType: - result = HandleBlueprintDescriptionBlock(section, cur, parser, output); - break; - - case ResourceGroupSectionType: - result = HandleResourceGroup(section, cur, parser, output); - break; - - default: - result.first.error = UnexpectedBlockError(section, cur, parser.sourceData); - break; + struct SectionProcessor<Blueprint> : public SectionProcessorBase<Blueprint> { + + static MarkdownNodeIterator processSignature(const MarkdownNodeIterator& node, + const MarkdownNodes& siblings, + SectionParserData& pd, + SectionLayout& layout, + Report& report, + Blueprint& out) { + + MarkdownNodeIterator cur = node; + + while (cur != siblings.end() && + cur->type == mdp::ParagraphMarkdownNodeType) { + + MetadataCollection metadata; + parseMetadata(cur, pd, report, metadata); + + // First block is paragraph and is not metadata (no API name) + if (metadata.empty()) { + return processDescription(cur, siblings, pd, report, out); + } else { + out.metadata.insert(out.metadata.end(), metadata.begin(), metadata.end()); + } + + cur++; } - return result; + // Ideally this parsing metadata should be handled by separate parser + // that way the following check would be covered in SectionParser::parse() + if (cur == siblings.end()) + return cur; + + if (cur->type == mdp::HeaderMarkdownNodeType) { + + SectionType nestedType = nestedSectionType(cur); + + // Resources Groups only, parse as exclusive nested sections + if (nestedType != UndefinedSectionType) { + layout = ExclusiveNestedSectionLayout; + return cur; + } + + out.name = cur->text; + TrimString(out.name); + } else { + + // Any other type of block, add to description + return processDescription(cur, siblings, pd, report, out); + } + + return ++MarkdownNodeIterator(cur); } - - static void Finalize(const SectionBounds& bounds, - BlueprintParserCore& parser, - Blueprint& blueprint, - Result& result) {} - - /** - * \brief Checks API Blueprint name issues warning or error if name does not exists. - * \param cur Cursor to the expected name header block - * \param blueprint Blueprint to check. - * \param parser A parser's instance. - * \param result Check result report. - * \result Returns false if a name is missing AND RequireBlueprintNameOption is set. True otherwise. - */ - static bool CheckBlueprintName(const BlockIterator& cur, - const Blueprint& blueprint, - BlueprintParserCore& parser, - Result& result) { - if (!blueprint.name.empty()) - return true; + static MarkdownNodeIterator processNestedSection(const MarkdownNodeIterator& node, + const MarkdownNodes& siblings, + SectionParserData& pd, + Report& report, + Blueprint& out) { - if (parser.options & RequireBlueprintNameOption) { - - // ERR: No API name specified - result.error = Error(snowcrashconst::ExpectedAPINameMessage, - BusinessError, - MapSourceDataBlock(cur->sourceMap, parser.sourceData)); - return false; + if (pd.sectionContext() == ResourceGroupSectionType || + pd.sectionContext() == ResourceSectionType) { + + ResourceGroup resourceGroup; + MarkdownNodeIterator cur = ResourceGroupParser::parse(node, siblings, pd, report, resourceGroup); + + ResourceGroupIterator duplicate = findResourceGroup(out.resourceGroups, resourceGroup); + + if (duplicate != out.resourceGroups.end()) { + + // WARN: duplicate resource group + std::stringstream ss; + + if (resourceGroup.name.empty()) { + ss << "anonymous group"; + } else { + ss << "group '" << resourceGroup.name << "'"; + } + + ss << " is already defined"; + + mdp::CharactersRangeSet sourceMap = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceData); + report.warnings.push_back(Warning(ss.str(), + DuplicateWarning, + sourceMap)); + } + + out.resourceGroups.push_back(resourceGroup); + + return cur; } - // WARN: No API name specified - result.warnings.push_back(Warning(snowcrashconst::ExpectedAPINameMessage, - APINameWarning, - MapSourceDataBlock(cur->sourceMap, parser.sourceData))); + return node; + } - return true; + static SectionType sectionType(const MarkdownNodeIterator& node) { + + return BlueprintSectionType; } - - // Returns true if given block is first block in document - // after metadata block, false otherwise. - static bool IsFirstBlock(const BlockIterator& cur, - const SectionBounds& bounds, - const Blueprint& blueprint) { - - if (blueprint.metadata.empty()) - return cur == bounds.first; - - return std::distance(bounds.first, cur) == 1; - } - static ParseSectionResult HandleBlueprintDescriptionBlock(const BlueprintSection& section, - const BlockIterator& cur, - BlueprintParserCore& parser, - Blueprint& output) { - - ParseSectionResult result = std::make_pair(Result(), cur); - BlockIterator sectionCur(cur); + static SectionType nestedSectionType(const MarkdownNodeIterator& node) { - // API Name - bool isFirstBlock = IsFirstBlock(cur, section.bounds, output); - if (isFirstBlock && - sectionCur->type == HeaderBlockType) { - - output.name = cur->content; - - // Check ambiguity - CheckHeaderBlock<Blueprint>(section, sectionCur, parser.sourceData, result.first); + SectionType nestedType = UndefinedSectionType; - // Check Name - if (!CheckBlueprintName(sectionCur, output, parser, result.first)) - return result; - - result.second = ++sectionCur; - return result; + // Check if Resource section + nestedType = SectionProcessor<Resource>::sectionType(node); + + if (nestedType != UndefinedSectionType) { + return nestedType; } - - // Metadata - if (isFirstBlock && - sectionCur->type == ParagraphBlockType) { - - result = ParseMetadataBlock(sectionCur, section.bounds, parser, output); - if (result.second != sectionCur) - return result; + + // Check if ResourceGroup section + nestedType = SectionProcessor<ResourceGroup>::sectionType(node); + + if (nestedType != UndefinedSectionType) { + return nestedType; } - - // Description - result = ParseDescriptionBlock<Blueprint>(section, sectionCur, parser.sourceData, output); - - // Check Name - if (isFirstBlock) - CheckBlueprintName(sectionCur, output, parser, result.first); - - return result; + + return UndefinedSectionType; } - - static ParseSectionResult HandleResourceGroup(const BlueprintSection& section, - const BlockIterator& cur, - BlueprintParserCore& parser, - Blueprint& output) - { - - // Mandatory name check - ParseSectionResult result = std::make_pair(Result(), cur); - if (IsFirstBlock(cur, section.bounds, output) && - !CheckBlueprintName(cur, output, parser, result.first)) - return result; - - // Parser resource group - ResourceGroup resourceGroup; - result = ResourceGroupParser::Parse(cur, - section.bounds.second, - section, - parser, - resourceGroup); - if (result.first.error.code != Error::OK) - return result; + static SectionTypes nestedSectionTypes() { + SectionTypes nested; + + // Resource Group & descendants + nested.push_back(ResourceGroupSectionType); + SectionTypes types = SectionProcessor<ResourceGroup>::nestedSectionTypes(); + nested.insert(nested.end(), types.begin(), types.end()); + + return nested; + } + + static void finalize(const MarkdownNodeIterator& node, + SectionParserData& pd, + Report& report, + Blueprint& out) { - Collection<ResourceGroup>::const_iterator duplicate = FindResourceGroup(parser.blueprint, resourceGroup); - if (duplicate != parser.blueprint.resourceGroups.end()) { + if (!out.name.empty()) + return; + + if (pd.options & RequireBlueprintNameOption) { + + // ERR: No API name specified + mdp::CharactersRangeSet sourceMap = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceData); + report.error = Error(ExpectedAPINameMessage, + BusinessError, + sourceMap); - // WARN: duplicate group - std::stringstream ss; - if (resourceGroup.name.empty()) { - ss << "anonymous group"; - } - else { - ss << "group '" << resourceGroup.name << "'"; - } - ss << " is already defined"; - - SourceCharactersBlock sourceBlock = CharacterMapForBlock(cur, section.bounds.second, section.bounds, parser.sourceData); - result.first.warnings.push_back(Warning(ss.str(), - DuplicateWarning, - sourceBlock)); } - - output.resourceGroups.push_back(resourceGroup); // FIXME: C++11 move - return result; + else if (!out.description.empty()) { + mdp::CharactersRangeSet sourceMap = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceData); + report.warnings.push_back(Warning(ExpectedAPINameMessage, + APINameWarning, + sourceMap)); + } } - - static ParseSectionResult ParseMetadataBlock(const BlockIterator& cur, - const SectionBounds& bounds, - BlueprintParserCore& parser, - Blueprint& output) { + static bool isUnexpectedNode(const MarkdownNodeIterator& node, + SectionType sectionType) { - typedef Collection<Metadata>::type MetadataCollection; - typedef Collection<Metadata>::iterator MetadataCollectionIterator; - MetadataCollection metadataCollection; - - ParseSectionResult result = std::make_pair(Result(), cur); - SourceData content = cur->content; + // Since Blueprint is currently top-level node any unprocessed node should be reported + return true; + } + + + static void parseMetadata(const MarkdownNodeIterator& node, + SectionParserData& pd, + Report& report, + MetadataCollection& out) { + + mdp::ByteBuffer content = node->text; TrimStringEnd(content); - std::vector<std::string> lines = Split(content, '\n'); - for (std::vector<std::string>::iterator line = lines.begin(); - line != lines.end(); - ++line) { - + + std::vector<mdp::ByteBuffer> lines = Split(content, '\n'); + + for (std::vector<mdp::ByteBuffer>::iterator it = lines.begin(); + it != lines.end(); + ++it) { + Metadata metadata; - if (KeyValueFromLine(*line, metadata)) - metadataCollection.push_back(metadata); + + if (CodeBlockUtility::keyValueFromLine(*it, metadata)) { + out.push_back(metadata); + } } - - if (lines.size() == metadataCollection.size()) { - + + if (lines.size() == out.size()) { + // Check duplicates - std::vector<std::string> duplicateKeys; - for (MetadataCollectionIterator it = metadataCollection.begin(); - it != metadataCollection.end(); + std::vector<mdp::ByteBuffer> duplicateKeys; + + for (MetadataCollectionIterator it = out.begin(); + it != out.end(); ++it) { - + MetadataCollectionIterator from = it; - if (++from == metadataCollection.end()) + if (++from == out.end()) break; - + MetadataCollectionIterator duplicate = std::find_if(from, - metadataCollection.end(), + out.end(), std::bind2nd(MatchFirsts<Metadata>(), *it)); - - if (duplicate != metadataCollection.end() && + + if (duplicate != out.end() && std::find(duplicateKeys.begin(), duplicateKeys.end(), it->first) == duplicateKeys.end()) { - + duplicateKeys.push_back(it->first); - - // WARN: duplicate metada definition + + // WARN: duplicate metadata definition std::stringstream ss; ss << "duplicate definition of '" << it->first << "'"; - - SourceCharactersBlock sourceBlock = CharacterMapForBlock(cur, bounds.second, bounds, parser.sourceData); - result.first.warnings.push_back(Warning(ss.str(), - DuplicateWarning, - sourceBlock)); + + mdp::CharactersRangeSet sourceMap = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceData); + report.warnings.push_back(Warning(ss.str(), + DuplicateWarning, + sourceMap)); } } - - // Insert parsed metadata into output - output.metadata.insert(output.metadata.end(), - metadataCollection.begin(), - metadataCollection.end()); - - ++result.second; } - else if (!metadataCollection.empty()) { + else if (!out.empty()) { + // WARN: malformed metadata block - SourceCharactersBlock sourceBlock = CharacterMapForBlock(cur, bounds.second, bounds, parser.sourceData); - result.first.warnings.push_back(Warning("ignoring possible metadata, expected" - " '<key> : <value>', one one per line", - FormattingWarning, - sourceBlock)); + mdp::CharactersRangeSet sourceMap = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceData); + report.warnings.push_back(Warning("ignoring possible metadata, expected '<key> : <value>', one one per line", + FormattingWarning, + sourceMap)); } - - return result; } - }; - - typedef BlockParser<Blueprint, SectionParser<Blueprint> > BlueprintParserInner; - - - // - // Blueprint Parser - // - class BlueprintParser { - public: - // Parse Markdown AST into API Blueprint AST - static void Parse(const SourceData& sourceData, - const MarkdownBlock::Stack& source, - BlueprintParserOptions options, - Result& result, - Blueprint& blueprint) { - - BlueprintParserCore parser(options, sourceData, blueprint); - BlueprintSection rootSection(std::make_pair(source.begin(), source.end())); - ParseSectionResult sectionResult = BlueprintParserInner::Parse(source.begin(), - source.end(), - rootSection, - parser, - blueprint); - result += sectionResult.first; - -#ifdef DEBUG - PrintSymbolTable(parser.symbolTable); -#endif - if (result.error.code != Error::OK) - return; - - PostParseCheck(sourceData, source, parser, result); + + /** Finds a resource group inside an resource groups collection */ + static ResourceGroupIterator findResourceGroup(const ResourceGroups& resourceGroups, + const ResourceGroup& resourceGroup) { + + return std::find_if(resourceGroups.begin(), + resourceGroups.end(), + std::bind2nd(MatchName<ResourceGroup>(), resourceGroup)); } - - /** - * Perform additional post-parsing result checks. - * Mainly to focused on running checking when top-level parser is not executed. - */ - static void PostParseCheck(const SourceData& sourceData, - const MarkdownBlock::Stack& source, - BlueprintParserCore& parser, - Result& result) { - - if ((parser.options & RequireBlueprintNameOption) && - parser.blueprint.name.empty()){ - - // ERR: No API name specified - result.error = Error(snowcrashconst::ExpectedAPINameMessage, - BusinessError, - MapSourceDataBlock(MakeSourceDataBlock(0, 0), parser.sourceData)); - } - } }; + + /** Blueprint Parser */ + typedef SectionParser<Blueprint, BlueprintSectionAdapter> BlueprintParser; } #endif