366 lines
11 KiB
C++
366 lines
11 KiB
C++
#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);
|
||
}
|