StoryCheckTools/parse/ast_load.py

273 lines
7.8 KiB
Python

from lxml import etree
from typing import List, Dict
global_ast_path = r"E:\storyline.xast"
def items_filter(items, proc):
values = []
for it in items:
if proc(it):
values.append(it)
pass
pass
return values
def items_map(items: List['StoryLine']) -> Dict[str, 'StoryLine']:
map: Dict[str, 'StoryLine'] = {}
for inst in items:
map[inst.signature] = inst
pass
return map
class FragmentSlice():
"""
故事情节片段
"""
def __init__(self, signature: str, file_path: str):
self.signature = signature
self.file_path = file_path
self.text_sections = []
self.refer_items: 'FragmentSlice' = []
pass
def append_text_section(self, section: str):
"""
添加描述文本段落
:param section: 文本段落
:return: None
"""
self.text_sections.append(section)
pass
def append_refer_slice(self, refer:'FragmentSlice'):
"""
添加引用描述段落内容
:param refer: 引用段落
:return: None
"""
self.refer_items.append(refer)
pass
def content_equal(self, other: 'FragmentSlice') -> bool:
"""
内容比较
:param other: 其他片段
:return: 比较结果
"""
if self.signature != other.signature:
return False
if len(self.text_sections) != len(other.text_sections):
return False
for line0 in self.text_sections:
if not line0 in other.text_sections:
return False
pass
if len(self.refer_items) != len(other.refer_items):
return False
for slice in self.refer_items:
find: bool = False
for slice_1 in other.refer_items:
if slice.content_equal(slice_1):
find = True
pass
pass
if not find:
return False
pass
return True
class StoryLine():
"""
故事线结果
"""
def __init__(self, signature: str, file_path: str):
self.signature = signature
self.file_path = file_path
self.text_sections = []
self.fragment_slices = []
pass
def append_text_section(self, section: str):
"""
添加文本描述
:param section: 文本段落
:return: None
"""
self.text_sections.append(section)
pass
def append_fragment_slice(self, slice: FragmentSlice):
"""
添加情节
:param slice: 情节实例
:return: None
"""
self.fragment_slices.append(slice)
pass
def content_equal(self, other: "StoryLine") -> bool:
"""
内容比较
:param other: 其他实例
:return: 比较结果
"""
if self.signature != other.signature:
return False
if len(self.text_sections) != len(other.text_sections):
return False
for line in self.text_sections:
if not line in other.text_sections:
return False
pass
if len(self.fragment_slices) != len(other.fragment_slices):
return False
for slice0 in self.fragment_slices:
find: bool = False
for slice1 in other.fragment_slices:
if slice0.content_equal(slice1):
find = True
pass
pass
if not find:
return False
pass
return True
class AstParse():
"""
Ast解析器
"""
def __init__(self, ast_path: str):
self.ast_path = ast_path
ast_file = open(ast_path, "rb")
ast_text = ast_file.read()
self.ast_inst = etree.XML(ast_text)
pass
def generate_time(self) -> str:
"""
获取生成时间
:return: 生成时间字符串
"""
attr_time = self.ast_inst.xpath("/ast[1]/@time")[0]
return str(attr_time)
def peak_storylines(self) -> List[StoryLine]:
"""
提取所有故事线
:return: 故事线列表
"""
story_list = self.ast_inst.xpath("/ast/story") # 获取所有故事节点
story_items = []
fragm_map: Dict[str, FragmentSlice] = {}
for story in story_list:
story_name = story.xpath("@name")[0]
story_file = story.xpath("@file-path")[0]
story_slice = StoryLine("story:" + story_name, story_file)
story_items.append(story_slice)
story_text_lines = story.xpath("text-section/@text") #获取所有描述
for line in story_text_lines:
story_slice.append_text_section(line)
pass
fragment_items = story.xpath("fragment") #获取所有情节定义
for fragm in fragment_items:
fragment_name = fragm.xpath("@name")[0]
fragm_slice = FragmentSlice(f"fragment:{story_name}#{fragment_name}", story_file)
story_slice.append_fragment_slice(fragm_slice)
fragm_map[fragm_slice.signature] = fragm_slice
fragm_text_lines = fragm.xpath("text-section/@text")
for line in fragm_text_lines:
fragm_slice.append_text_section(line)
pass
pass
pass
fragm_refer_items = self.ast_inst.xpath("//refer")
for refer_item in fragm_refer_items:
story_name_refer = refer_item.xpath("@story")[0]
fragm_name_refer = refer_item.xpath("@fragment")[0]
fragm_file = refer_item.xpath("@file-path")[0]
fragm_signature = f"fragment:{story_name_refer}#{fragm_name_refer}"
fragment_target = fragm_map[fragm_signature]
fragm_pnode = refer_item.xpath("..")[0]
if fragm_pnode.xpath("name()") == "story":
this_story_name = fragm_pnode.xpath("@name")[0]
refer_slice = FragmentSlice(f"refer:<{this_story_name}>{story_name_refer}#{fragm_name_refer}", fragm_file)
fragment_target.append_refer_slice(refer_slice)
refer_lines = refer_item.xpath("text-section/@text")
for line in refer_lines:
refer_slice.append_text_section(line)
pass
pass
if fragm_pnode.xpath("name()") == "article":
this_article_name = fragm_pnode.xpath("@name")[0]
this_volume_node = fragm_pnode.xpath("..")[0]
this_volume_name = this_volume_node.xpath("@name")[0]
refer_slice = FragmentSlice(f"refer-article<{this_volume_name}#{this_article_name}>{story_name_refer}#{fragm_name_refer}", fragm_file)
fragment_target.append_refer_slice(refer_slice)
refer_lines = refer_item.xpath("text-section/@text")
for line in refer_lines:
refer_slice.append_text_section(line)
pass
pass
return story_items
def storylines_compare(self, base_list: List[StoryLine], other_list: List[StoryLine]) -> List[str]:
changed_list: List[str] = []
base_map = items_map(base_list)
for other_item in other_list:
if not other_item.signature in base_map:
changed_list.append(other_item.signature)
continue
base_item = base_map[other_item.signature]
if not other_item.content_equal(base_item):
changed_list.append(other_item.signature)
pass
base_map.pop(other_item.signature)
pass
for addit in base_map.values():
changed_list.append(addit.signature)
pass
return changed_list