WsParser_VS/libParse/parse_novel.cpp

366 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "parse_novel.h"
#include <ast_novel.h>
#include <iterator>
#include <QTime>
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<const SyntaxElement> ElementsCache::appendToCache(std::shared_ptr<const SyntaxElement> named_node) {
auto mixed_key = QString("%1<%2>").arg(named_node->signature()).arg(named_node->typeMark());
if (node_cache.contains(mixed_key))
return node_cache[mixed_key];
node_cache[mixed_key] = named_node;
return nullptr;
}
std::shared_ptr<const SyntaxElement> 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 example_novel::FragmentExistsCheck::nodes_regist(std::shared_ptr<ElementsCache> cache, std::shared_ptr<const ast_gen::ElementAccess> 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<ElementsCache> root, std::shared_ptr<const ElementAccess> target) const {
if (target->element()->typeMark() == (int) NovelNode::PointRefers) {
auto refer = std::dynamic_pointer_cast<const PointRefers>(target->element());
auto signature = refer->storyRefer() + "&" + refer->sliceRefer() + "&" + refer->pointRefer();
if (!root->getNamedNodeBy((int) NovelNode::PointDefines, signature))
throw new SyntaxException(QString("CheckError[0x0005]系统中不包含指定签名的节点:%1<type%2>{%3:(%4)}")
.arg(signature).arg((int) NovelNode::PointDefines).arg(refer->signature()).arg(refer->filePath()));
}
for (auto& xit : target->children()) {
exists_check(root, xit);
}
}
void FragmentExistsCheck::validCheck(std::shared_ptr<const ElementAccess> root) const {
const_cast<FragmentExistsCheck*>(this)->nodes_regist(this->_nodes_cache, root);
this->exists_check(this->_nodes_cache, root);
}
QString FragmentExistsCheck::name() const {
return "情节引用有效性检查器";
}
QList<std::shared_ptr<PointGraphHelper>> FragmentGraphCheck::refers_cycle_check(
std::shared_ptr<PointGraphHelper> item, QList<std::shared_ptr<PointGraphHelper>> 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<std::shared_ptr<PointGraphHelper>>();
}
void FragmentGraphCheck::setElement(std::shared_ptr<PointGraphHelper> inst) {
elements_store[inst->nodePeer()->signature()] = inst;
}
QList<std::shared_ptr<const PointGraphHelper>> FragmentGraphCheck::fragmentsSequence() const {
return fragments_sort_list;
}
std::shared_ptr<PointGraphHelper> FragmentGraphCheck::getElement(const QString& signature) const {
return elements_store[signature];
}
QList<std::shared_ptr<PointGraphHelper>> FragmentGraphCheck::getHangoutNodes() {
QList<std::shared_ptr<PointGraphHelper>> 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 FragmentGraphCheck::nodeDismantle(std::shared_ptr<PointGraphHelper> inst) {
bool flag = false;
for (auto item : inst->nextList()) {
item->inDegree()--;
flag = true;
}
return flag;
}
void FragmentGraphCheck::validCheck(std::shared_ptr<const ElementAccess> root) const {
const_cast<FragmentGraphCheck*>(this)->fragments_sort_list.clear();
auto self = std::const_pointer_cast<FragmentGraphCheck>(this->shared_from_this());
std::function<QList<std::shared_ptr<const ElementAccess>>(std::shared_ptr<const ElementAccess>)> story_peak
= [&](std::shared_ptr<const ElementAccess> root)->QList<std::shared_ptr<const ElementAccess>> {
QList<std::shared_ptr<const ElementAccess>> 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<QList<std::shared_ptr<const ElementAccess>>(std::shared_ptr<const ElementAccess>)>
points_extract = [](std::shared_ptr<const ElementAccess> story) {
QList<std::shared_ptr<const ElementAccess>> return_temp;
auto story_children = story->children();
QList<std::shared_ptr<const ElementAccess>> slice_list;
std::copy_if(story_children.begin(), story_children.end(),
std::back_inserter(slice_list), [](std::shared_ptr<const ElementAccess> ins) {
return ins->element()->typeMark() == (int) NovelNode::FragmentSlice;
});
decltype(slice_list) slice_children;
for (auto slice : slice_list)
slice_children.append(slice->children());
std::copy_if(slice_children.begin(), slice_children.end(),
std::back_inserter(return_temp), [](std::shared_ptr<const ElementAccess> ins) {
return ins->element()->typeMark() == (int) NovelNode::PointDefines &&
ins->element()->typeMark() == (int) NovelNode::PointRefers;
});
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) {
switch (point_primitive->element()->typeMark()) {
case (int) NovelNode::PointDefines:
{
auto target_node = std::dynamic_pointer_cast<const PointDefines>(point_primitive->element());
auto finst = std::make_shared<PointGraphHelper>(target_node);
self->setElement(finst);
}break;
default: break;
}
}
}
// 构建图连接
for (auto story : all_story) {
auto point_items = points_extract(story);
// 过滤获取情节节点
for (auto idx = 0; idx < point_items.size(); idx++) {
auto point_inst = point_items[idx];
switch (point_inst->element()->typeMark()) {
case (int) NovelNode::PointDefines:
case (int) NovelNode::PointRefers:
break;
default:
point_items.removeAt(idx--);
break;
}
}
auto get_name = [](std::shared_ptr<const ElementAccess> node)->QString {
switch (node->element()->typeMark()) {
case (int) NovelNode::PointDefines:
{
auto def_node = std::dynamic_pointer_cast<const PointDefines>(node->element());
return def_node->signature();
}break;
case (int) NovelNode::PointRefers:
{
auto ref_node = std::dynamic_pointer_cast<const PointRefers>(node->element());
return ref_node->referSignature();
}break;
}
return QString();
};
// 构建完整图结构
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()++;
}
}
// 获取拓扑排序序列
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<FragmentGraphCheck*>(this)->fragments_sort_list.append(inst);
}
QString FragmentGraphCheck::name() const {
return "情节网络有效性检查器";
}
PointGraphHelper::PointGraphHelper(std::shared_ptr<const PointDefines> node) : node_peer(node) {
}
std::shared_ptr<const PointDefines> PointGraphHelper::nodePeer() const {
return this->node_peer;
}
void PointGraphHelper::appendNext(std::shared_ptr<PointGraphHelper> node) {
this->next_nodes.append(node);
}
QList<std::shared_ptr<PointGraphHelper>> PointGraphHelper::nextList() const {
return next_nodes;
}
uint& PointGraphHelper::inDegree() {
return this->indegree;
}
QList<std::shared_ptr<const ElementAccess>> StoryOrderCheck::valid_docs_peak(std::shared_ptr<const ElementAccess> pnode) const {
QList<std::shared_ptr<const ElementAccess>> 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<const ElementAccess> 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<const ElementAccess> root) const {
const_cast<StoryOrderCheck*>(this)->sort_index = 1;
auto story_docs = valid_docs_peak(root);
std::sort(story_docs.begin(), story_docs.end(),
[](std::shared_ptr<const ElementAccess> adoc, std::shared_ptr<const ElementAccess> bdoc) {
auto elm_xa = std::dynamic_pointer_cast<const RankDeclare>(adoc->children().first()->element());
auto elm_xb = std::dynamic_pointer_cast<const RankDeclare>(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<const RankDeclare>(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<const ElementAccess> 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<const StoryDefine>(inst->element());
std::const_pointer_cast<StoryDefine>(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);
}