#include "parse_novel.h" #include #include #include using namespace lib_parse; using namespace example_novel; using namespace ast_basic; using namespace ast_gen; using namespace lib_syntax; using namespace example_novel; void ElementsCache::clearCache() { node_cache.clear(); } std::shared_ptr ElementsCache::appendToCache(std::shared_ptr named_node) { auto mixed_key = QString("%1<%2>").arg(named_node->signature()).arg(named_node->typeMark()); if (node_cache.contains(mixed_key)) throw new CheckException(QString("节点命名重复:%1!").arg(mixed_key)); node_cache[mixed_key] = named_node; return nullptr; } std::shared_ptr ElementsCache::getNamedNodeBy(int paramType, const QString& signature) const { auto mixed_key = QString("%1<%2>").arg(signature).arg(paramType); if (!node_cache.contains(mixed_key)) return nullptr; return node_cache[mixed_key]; } void FragmentExistsCheck::nodes_regist(std::shared_ptr cache, std::shared_ptr target) { if (!target->element()->isAnonymous()) cache->appendToCache(target->element()); for (auto item : target->children()) nodes_regist(cache, item); } void FragmentExistsCheck::exists_check(std::shared_ptr root, std::shared_ptr target) const { if (target->element()->typeMark() == (int) NovelNode::FragmentRefers) { auto refer = std::dynamic_pointer_cast(target->element()); auto signature = refer->storyRefer() + "&" + refer->sliceRefer(); if (!root->getNamedNodeBy((int) NovelNode::FragmentSlice, signature)) throw new SyntaxException(QString("CheckError[0x0005]系统中不包含指定签名的节点:%1{%3:(%4)}") .arg(signature).arg((int) NovelNode::FragmentRefers).arg(refer->signature()).arg(refer->filePath())); } for (auto& xit : target->children()) { exists_check(root, xit); } } FragmentExistsCheck::FragmentExistsCheck() :_nodes_cache(std::make_shared()) { } void FragmentExistsCheck::validCheck(std::shared_ptr root) const { const_cast(this)->nodes_regist(this->_nodes_cache, root); this->exists_check(this->_nodes_cache, root); } QString FragmentExistsCheck::name() const { return "情节引用有效性检查器"; } QList> PointGraphCheck::refers_cycle_check( std::shared_ptr item, QList> prevs) const { if (prevs.contains(item)) { return prevs << item; } auto next_list = item->nextList(); if (next_list.size()) { prevs << item; for (auto next : next_list) { auto ref_link = refers_cycle_check(next, prevs); if (ref_link.size()) return ref_link; } } return QList>(); } void PointGraphCheck::setElement(std::shared_ptr inst) { elements_store[inst->nodePeer()->signature()] = inst; } QList> PointGraphCheck::pointsSequence() const { return fragments_sort_list; } std::shared_ptr PointGraphCheck::getElement(const QString& signature) const { return elements_store[signature]; } QList> PointGraphCheck::getHangoutNodes() { QList> values; for (auto node_item : elements_store) { if (!node_item->inDegree()) values.append(node_item); } for (auto inst : values) { auto name = inst->nodePeer()->signature(); elements_store.remove(name); } return values; } bool PointGraphCheck::nodeDismantle(std::shared_ptr inst) { bool flag = false; for (auto item : inst->nextList()) { item->inDegree()--; flag = true; } return flag; } void PointGraphCheck::validCheck(std::shared_ptr root) const { const_cast(this)->fragments_sort_list.clear(); auto self = std::const_pointer_cast(this->shared_from_this()); // 提取所有故事线 std::function>(std::shared_ptr)> story_peak = [&](std::shared_ptr root)->QList> { QList> return_temp; auto type_mark = (NovelNode) root->element()->typeMark(); if (type_mark == NovelNode::StoryDefine) { return_temp << root; } for (auto child : root->children()) { return_temp.append(story_peak(child)); } return return_temp; }; // 提取所有节点定义 std::function>(std::shared_ptr)> points_extract = [&](std::shared_ptr story) { QList> return_temp; auto story_children = story->children(); for (auto ins : story_children) { switch (ins->element()->typeMark()) { case (int) NovelNode::FragmentSlice: return_temp << ins; break; default: return_temp << points_extract(ins); break; } } return return_temp; }; // 提取节点内包含的引用节点 std::function>(std::shared_ptr)> refers_within = [&](std::shared_ptr defs) { QList> return_temp; auto x_children = defs->children(); for (auto ins : x_children) { switch (ins->element()->typeMark()) { case (int) NovelNode::FragmentRefers: return_temp << ins; break; default: return_temp << refers_within(ins); break; } } return return_temp; }; // 获取所有故事线 auto all_story = story_peak(root); // 注册图节点 for (auto story : all_story) { auto point_items = points_extract(story); // 构建情节节点列表 for (auto point_primitive : point_items) { auto target_node = std::dynamic_pointer_cast(point_primitive->element()); auto finst = std::make_shared(target_node); self->setElement(finst); } } // 获取绑定节点名称 auto get_name = [](std::shared_ptr node)->QString { switch (node->element()->typeMark()) { case (int) NovelNode::FragmentSlice: { auto def_node = std::dynamic_pointer_cast(node->element()); return def_node->signature(); }break; case (int) NovelNode::FragmentRefers: { auto ref_node = std::dynamic_pointer_cast(node->element()); return ref_node->referSignature(); }break; } return QString(); }; // 构建图连接 for (auto story : all_story) { // 过滤获取情节节点 auto point_items = points_extract(story); // 构建完整图结构:串联定义节点的顺序 for (auto fidx = 1; fidx < point_items.size(); fidx++) { auto tail_name = get_name(point_items[fidx - 1]); auto head_name = get_name(point_items[fidx]); auto tail_helper = getElement(tail_name); auto head_helper = getElement(head_name); tail_helper->appendNext(head_helper); head_helper->inDegree()++; } // 构建完整图结构:串联引用节点的顺序 for (auto point : point_items) { auto first_name = get_name(point); auto first_helper = getElement(first_name); auto refers_nodes = refers_within(point); for (auto refer : refers_nodes) { auto second_name = get_name(refer); auto second_helper = getElement(second_name); first_helper->appendNext(second_helper); second_helper->inDegree()++; } } } // 获取拓扑排序序列 auto values = self->getHangoutNodes(); for (auto idx = 0; idx < values.size(); ++idx) { auto target = values[idx]; self->nodeDismantle(target); auto x_values = self->getHangoutNodes(); values.append(x_values); } // 理论上不应该有残余 if (elements_store.size()) { for (auto node : elements_store.values()) { auto cycle_link = refers_cycle_check(node); QString error_msg = "CheckError[0x0006]情节引用存在环形结构:\n"; for (auto n : cycle_link) { error_msg += QString("%1->").arg(n->nodePeer()->signature()); } if (cycle_link.size()) throw new CheckException(error_msg); } } for (auto& inst : values) const_cast(this)->fragments_sort_list.append(inst); } QString PointGraphCheck::name() const { return "情节网络引用检查器"; } PointGraphHelper::PointGraphHelper(std::shared_ptr node) : node_peer(node) { } std::shared_ptr PointGraphHelper::nodePeer() const { return this->node_peer; } void PointGraphHelper::appendNext(std::shared_ptr node) { this->next_nodes.append(node); } QList> PointGraphHelper::nextList() const { return next_nodes; } uint& PointGraphHelper::inDegree() { return this->indegree; } QList> StoryOrderCheck::valid_docs_peak(std::shared_ptr pnode) const { QList> values; auto type_code = pnode->element()->typeMark(); switch ((NovelNode) type_code) { case NovelNode::GlobalElement: { auto children = pnode->children(); for (auto& cinst : children) { values.append(valid_docs_peak(cinst)); } }break; case NovelNode::Document: { auto elms_set = pnode->children(); decltype(elms_set) story_set; std::copy_if(elms_set.begin(), elms_set.end(), std::back_inserter(story_set), [](std::shared_ptr e) { return e->element()->typeMark() == (int) NovelNode::StoryDefine; }); if (story_set.size()) { values.append(pnode); auto first_elm = elms_set.at(0); if (first_elm->element()->typeMark() != (int) NovelNode::RankDeclaration) throw new CheckException(QString("CheckError[0x0007]具有故事节点的文档必须在第一行指定排序(%1)").arg(pnode->element()->path())); } }break; default: break; } return values; } QString StoryOrderCheck::name() const { return "故事序列有效性检查器"; } void StoryOrderCheck::validCheck(std::shared_ptr root) const { const_cast(this)->sort_index = 1; auto story_docs = valid_docs_peak(root); std::sort(story_docs.begin(), story_docs.end(), [](std::shared_ptr adoc, std::shared_ptr bdoc) { auto elm_xa = std::dynamic_pointer_cast(adoc->children().first()->element()); auto elm_xb = std::dynamic_pointer_cast(bdoc->children().first()->element()); return elm_xa->rankNumber() < elm_xb->rankNumber(); }); // page_rank valid int page_rank = 0; for (auto& item : story_docs) { auto elm_xa = std::dynamic_pointer_cast(item->children().first()->element()); if (page_rank >= elm_xa->rankNumber()) throw new CheckException(QString("CheckError[0x0009]文档排序声明数字必须大于0,不同文档的排序不能重复{%1}").arg(elm_xa->path())); page_rank = elm_xa->rankNumber(); } // 故事节点排序 auto story_node_sort = [](std::shared_ptr doc_node, int start_index) -> int { auto childs = doc_node->children(); for (auto& inst : childs) { if (inst->element()->typeMark() == (int) NovelNode::StoryDefine) { auto cast_inst = std::dynamic_pointer_cast(inst->element()); std::const_pointer_cast(cast_inst)->setSort(start_index++); } } return start_index; }; int ranks_number = 1; for (auto& story : story_docs) ranks_number = story_node_sort(story, ranks_number); } std::shared_ptr FragmentLayerCheck::_sets_fill(std::shared_ptr node) { switch ((NovelNode) node->element()->typeMark()) { case NovelNode::FragmentSlice: { auto wins = std::make_shared(node); this->_node_set[node->element()->signature()] = wins; return wins; }break; case NovelNode::StoryDefine: { QList> list; for (auto nis : node->children()) { auto ptr = _sets_fill(nis); if (ptr) list << ptr; } for (auto node = ++list.begin(); node != list.end(); ++node) { auto prev = *(node - 1); auto curr = *node; prev->appendNext(curr); } if (list.size()) this->_story_start << list.first(); } break; default: for (auto nis : node->children()) _sets_fill(nis); break; } return nullptr; } void FragmentLayerCheck::_refers_rebuild(std::shared_ptr node) { auto fragm = std::static_pointer_cast(node->bind()->element()); for (auto node_t : fragm->children()) { auto refn = std::dynamic_pointer_cast(node_t); if(refn){ auto target_node = _node_set[refn->referSignature()]; std::const_pointer_cast(node)->appendNext(target_node); } } } void FragmentLayerCheck::node_relayer(std::shared_ptr node, int curr_num) { if (node->layerNumber() < curr_num) { node->setLayer(curr_num); for (auto nis : node->referNodes()) node_relayer(nis, curr_num + 1); } } /// /// 层级确认 /// /// 故事线 void FragmentLayerCheck::layer_check(std::shared_ptr node) { switch ((NovelNode) node->element()->typeMark()) { case NovelNode::StoryDefine: story_layer_check(node); break; default: for(auto ins : node->children()) layer_check(ins); break; } } void FragmentLayerCheck::story_layer_check(std::shared_ptr story) { auto fragms_list = story->children(); decltype(fragms_list) real_elms; std::copy_if(fragms_list.begin(), fragms_list.end(), std::back_inserter(real_elms), [](std::shared_ptr ins) { return ins->element()->typeMark() == (int) NovelNode::FragmentSlice; }); for (auto curr = ++real_elms.begin(); curr != real_elms.end(); ++curr) { auto prev = curr - 1; auto curr_sign = (*curr)->element()->signature(); auto prev_sign = (*prev)->element()->signature(); sibling_element_compair(_node_set[prev_sign], _node_set[curr_sign]); } } void FragmentLayerCheck::sibling_element_compair(std::shared_ptr prev, std::shared_ptr curr) { auto prev_childs = prev->referNodes(); auto curr_childs = curr->referNodes(); QList prev_layers; std::transform(prev_childs.begin(), prev_childs.end(), std::back_inserter(prev_layers), [](std::shared_ptr ins) { return ins->layerNumber(); }); QList next_layers; std::transform(curr_childs.begin(), curr_childs.end(), std::back_inserter(next_layers), [](std::shared_ptr ins) { return ins->layerNumber(); }); auto min0 = *std::min_element(prev_layers.begin(), prev_layers.end()); auto max1 = *std::max_element(next_layers.begin(), next_layers.end()); if (next_layers.size() && min0 > max1) { decltype(prev_childs) tempx; std::copy_if(curr_childs.begin(), curr_childs.end(), std::back_inserter(tempx), [=](std::shared_ptr ins) { return ins->layerNumber() >= min0; }); QStringList names; std::transform(tempx.begin(), tempx.end(), std::back_inserter(names), [=](std::shared_ptr ins) { return ins->bind()->element()->signature(); }); throw new CheckException(QString("CheckError[0x0009]情节时间序错误!%1->%2\n\t\t%3->{%4}") .arg(prev->bind()->element()->signature()).arg(curr->bind()->element()->signature()) .arg(curr->bind()->element()->signature(), names.join(","))); } } QString FragmentLayerCheck::name() const { return "情节时间层级校验器"; } void FragmentLayerCheck::validCheck(std::shared_ptr root) const { auto chk = const_cast(this); chk->_sets_fill(root); for (auto node : chk->_node_set) chk->_refers_rebuild(node); for (auto node : chk->_story_start) chk->node_relayer(node); chk->layer_check(root); } FragmentNode::FragmentNode(std::shared_ptr bind) :_src_fragm(bind) { } std::shared_ptr FragmentNode::bind() const { return this->_src_fragm; } void FragmentNode::setLayer(int number) { this->_layer_number = number; } int FragmentNode::layerNumber() const { return this->_layer_number; } QList> FragmentNode::referNodes() const { return _next_fragms; } void FragmentNode::appendNext(std::shared_ptr ins) { this->_next_fragms << ins; }