WsParser_VS/libParse/parse_novel.cpp

539 lines
17 KiB
C++
Raw Permalink 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))
throw new CheckException(QString("节点命名重复:%1").arg(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 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::FragmentRefers) {
auto refer = std::dynamic_pointer_cast<const FragmentRefers>(target->element());
auto signature = refer->storyRefer() + "&" + refer->sliceRefer();
if (!root->getNamedNodeBy((int) NovelNode::FragmentSlice, signature))
throw new SyntaxException(QString("CheckError[0x0005]系统中不包含指定签名的节点:%1<type%2>{%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<ElementsCache>()) {
}
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>> PointGraphCheck::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 PointGraphCheck::setElement(std::shared_ptr<PointGraphHelper> inst) {
elements_store[inst->nodePeer()->signature()] = inst;
}
QList<std::shared_ptr<const PointGraphHelper>> PointGraphCheck::pointsSequence() const {
return fragments_sort_list;
}
std::shared_ptr<PointGraphHelper> PointGraphCheck::getElement(const QString& signature) const {
return elements_store[signature];
}
QList<std::shared_ptr<PointGraphHelper>> PointGraphCheck::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 PointGraphCheck::nodeDismantle(std::shared_ptr<PointGraphHelper> inst) {
bool flag = false;
for (auto item : inst->nextList()) {
item->inDegree()--;
flag = true;
}
return flag;
}
void PointGraphCheck::validCheck(std::shared_ptr<const ElementAccess> root) const {
const_cast<PointGraphCheck*>(this)->fragments_sort_list.clear();
auto self = std::const_pointer_cast<PointGraphCheck>(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();
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<QList<std::shared_ptr<const ElementAccess>>(std::shared_ptr<const ElementAccess>)> refers_within
= [&](std::shared_ptr<const ElementAccess> defs) {
QList<std::shared_ptr<const ElementAccess>> 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<const FragmentSlice>(point_primitive->element());
auto finst = std::make_shared<PointGraphHelper>(target_node);
self->setElement(finst);
}
}
// 获取绑定节点名称
auto get_name = [](std::shared_ptr<const ElementAccess> node)->QString {
switch (node->element()->typeMark()) {
case (int) NovelNode::FragmentSlice:
{
auto def_node = std::dynamic_pointer_cast<const FragmentSlice>(node->element());
return def_node->signature();
}break;
case (int) NovelNode::FragmentRefers:
{
auto ref_node = std::dynamic_pointer_cast<const FragmentRefers>(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<PointGraphCheck*>(this)->fragments_sort_list.append(inst);
}
QString PointGraphCheck::name() const {
return "情节网络引用检查器";
}
PointGraphHelper::PointGraphHelper(std::shared_ptr<const FragmentSlice> node) : node_peer(node) {
}
std::shared_ptr<const FragmentSlice> 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);
}
std::shared_ptr<FragmentNode> FragmentLayerCheck::_sets_fill(std::shared_ptr<const ElementAccess> node) {
switch ((NovelNode) node->element()->typeMark()) {
case NovelNode::FragmentSlice:
{
auto wins = std::make_shared<FragmentNode>(node);
this->_node_set[node->element()->signature()] = wins;
return wins;
}break;
case NovelNode::StoryDefine:
{
QList<std::shared_ptr<FragmentNode>> list;
for (auto nis : node->children()) {
auto ptr = _sets_fill(nis);
if (ptr) list << ptr;
}
if (list.size()) {
for (auto nitr = ++list.begin(); nitr != list.end(); nitr++) {
auto prev = *(nitr - 1);
prev->appendNext(*nitr);
}
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<const FragmentNode> node) {
auto fragm = std::static_pointer_cast<const FragmentSlice>(node->bind()->element());
for (auto node_t : fragm->children()) {
auto refn = std::dynamic_pointer_cast<const FragmentRefers>(node_t);
if (refn) {
auto target_node = _node_set[refn->referSignature()];
std::const_pointer_cast<FragmentNode>(node)->appendNext(target_node);
}
}
}
void FragmentLayerCheck::node_relayer(std::shared_ptr<FragmentNode> node, int curr_num) {
if (node->layerNumber() < curr_num) {
node->setLayer(curr_num);
for (auto nis : node->referNodes())
node_relayer(nis, curr_num + 1);
}
}
/// <summary>
/// 层级确认
/// </summary>
/// <param name="node">故事线</param>
void FragmentLayerCheck::layer_check(std::shared_ptr<const ElementAccess> 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<const ast_gen::ElementAccess> story) {
auto fragms_list = story->children();
auto story_nm = story->element()->signature();
decltype(fragms_list) real_elms;
std::copy_if(fragms_list.begin(), fragms_list.end(), std::back_inserter(real_elms),
[](std::shared_ptr<const ast_gen::ElementAccess> ins) { return ins->element()->typeMark() == (int) NovelNode::FragmentSlice; });
if (real_elms.size())
for (auto curr = ++real_elms.begin(); curr != real_elms.end(); ++curr) {
auto prev_it = curr - 1;
auto curr_it = curr;
for (; curr_it != real_elms.end(); ++curr_it) {
auto curr_sign = (*curr_it)->element()->signature();
auto prev_sign = (*prev_it)->element()->signature();
sibling_element_compair(story_nm, _node_set[prev_sign], _node_set[curr_sign]);
}
}
}
void FragmentLayerCheck::sibling_element_compair(const QString& story, std::shared_ptr<const FragmentNode> prev, std::shared_ptr<const FragmentNode> curr) {
auto prev_childs_o = prev->referNodes();
auto curr_childs_o = curr->referNodes();
auto vpeak = [=](std::shared_ptr<const FragmentNode> ins) { return !ins->bind()->element()->signature().startsWith(story); };
decltype(prev_childs_o) prev_childs;
std::copy_if(prev_childs_o.begin(), prev_childs_o.end(), std::back_inserter(prev_childs), vpeak);
decltype(curr_childs_o) curr_childs;
std::copy_if(curr_childs_o.begin(), curr_childs_o.end(), std::back_inserter(curr_childs), vpeak);
if (!curr_childs.size() || !prev_childs.size())
return;
auto max_e0 = *std::max_element(prev_childs.begin(), prev_childs.end(),
[](std::shared_ptr<const FragmentNode> a, std::shared_ptr<const FragmentNode> b) {
return a->layerNumber() < b->layerNumber();
});
auto min_e1 = *std::min_element(curr_childs.begin(), curr_childs.end(),
[](std::shared_ptr<const FragmentNode> a, std::shared_ptr<const FragmentNode> b) {
return a->layerNumber() < b->layerNumber();
});
if (curr_childs.size() && max_e0->layerNumber() > min_e1->layerNumber()) {
throw new CheckException(QString("CheckError[0x0009]情节时间序错误!%1 && %2\n\t\t%3->%4@%5\n\t\t%6->%7@%8")
.arg(prev->bind()->element()->signature()).arg(curr->bind()->element()->signature())
.arg(prev->bind()->element()->signature(), max_e0->bind()->element()->signature(), QString("%1").arg(max_e0->layerNumber()))
.arg(curr->bind()->element()->signature(), min_e1->bind()->element()->signature(), QString("%1").arg(min_e1->layerNumber())));
}
}
QString FragmentLayerCheck::name() const {
return "情节时间层级校验器";
}
void FragmentLayerCheck::validCheck(std::shared_ptr<const ast_gen::ElementAccess> root) const {
auto chk = const_cast<FragmentLayerCheck*>(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<const ElementAccess> bind) :_src_fragm(bind) {
}
std::shared_ptr<const ElementAccess> 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<std::shared_ptr<FragmentNode>> FragmentNode::referNodes() const {
return _next_fragms;
}
void FragmentNode::appendNext(std::shared_ptr<FragmentNode> ins) {
this->_next_fragms << ins;
}