Compare commits

..

21 Commits
dev ... master

Author SHA1 Message Date
codeboss d37b3caaef 修改Qt版本 2025-05-18 20:12:55 +08:00
codeboss c58b022b21 修复空story崩溃问题 2025-05-17 22:40:43 +08:00
codeboss 5a4369d7cc 修正比较算法 2025-05-17 22:25:21 +08:00
codeboss 71d7de9f18 校正层级比较错误 2025-05-17 19:05:07 +08:00
codeboss 0290c8bf55 适配Qt5.12.9并添加fragment-layer-checker 2025-05-17 16:42:07 +08:00
codeboss 123cc2a05a 尝试添加层次校验 2025-05-17 01:23:29 +08:00
codeboss adb6e326d2 修复故事线独立情况下崩溃的bug 2025-05-11 15:55:40 +08:00
ws 0b68468679 update library-output-dir 2025-04-23 00:36:33 +08:00
codeboss 6705f057c1 update 2025-04-23 00:18:14 +08:00
ws c83b63a3bd update cmakelist 2025-04-22 23:14:35 +08:00
ws 990c1d202a update cmakelist 2025-04-21 23:15:03 +08:00
ws b3a969176e update cmakelists 2025-04-21 22:33:09 +08:00
ws 5e28c17115 update cmakelist 2025-04-21 22:26:07 +08:00
codeboss bcdd3b1922 update svg storyline 2025-04-14 07:38:13 +08:00
codeboss e18f45dba5 改进SVG的ToolTips 2025-04-13 00:15:23 +08:00
codeboss 06e1b5553a 开发路线记录 2025-04-12 16:59:00 +08:00
codeboss 2f0de3acdc 添加动作注释 2025-04-05 22:13:26 +08:00
codeboss 6936761994 修复了格式化输出的问题 2025-04-05 21:18:11 +08:00
codeboss 2d979ebfce 更新格式化代码 2025-04-05 11:13:51 +08:00
codeboss 5e58c924a8 构建格式化程序 2025-04-05 03:07:46 +08:00
codeboss 764e1bbb82 改进程序入口 2025-04-04 19:10:08 +08:00
26 changed files with 1001 additions and 116 deletions

7
.gitignore vendored
View File

@ -2,4 +2,9 @@
.vs/*
x64/*
*/bin/*
*/obj/*
*/obj/*
.fake
.DS_Store
bin/*
build/*
out/*

View File

@ -0,0 +1,27 @@
{
"configurations": [
{
"name": "Mac",
"defines": [],
"compilerPath": "/usr/bin/clang",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "macos-clang-x64",
"includePath": [
"/"
]
},
{
"name": "mloc",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [],
"compilerPath": "/usr/bin/clang",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "macos-clang-x64"
}
],
"version": 4
}

22
ArgsParser/CMakeLists.txt Normal file
View File

@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.10)
project(ArgsParser CXX)
set(CMAKE_CXX_STANDARD 20)
find_package(Qt5 COMPONENTS Core REQUIRED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
add_library(ArgsParser SHARED
argsparser.cpp
)
message("ArgsParser::RUNTIME_PATH=${RUNTIME_PATH}")
set_target_properties(ArgsParser PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${RUNTIME_PATH}")
set_target_properties(ArgsParser PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${RUNTIME_PATH}")
set_target_properties(ArgsParser PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${RUNTIME_PATH}")
set_target_properties(ArgsParser PROPERTIES OUTPUT_NAME "ArgsParser.${BUILD_TYPE_MARK}")
target_compile_definitions(ArgsParser PUBLIC ARGSPARSER_LIB)
target_link_libraries(ArgsParser Qt5::Core)

View File

@ -107,10 +107,18 @@ bool FloatOption::parse(const QList<QString> args) {
MatchMode::MatchMode(int mode_code, const QString& mode)
:_means_explain(mode), code_store(mode_code) { }
/**
* @brief
* @return
*/
#include <QtCore/QByteArray>
class inner_exception : public std::exception {
private:
QByteArray _message;
public:
inner_exception(const QString &msg)
:_message(msg.toUtf8()){}
virtual const char* what() const noexcept{
return _message.data();
}
};
MatchMode& MatchMode::operator<<(std::shared_ptr<IArgvPack> unit) {
if(unit->paramType() == ParamType::FloatParam){
@ -119,7 +127,7 @@ MatchMode& MatchMode::operator<<(std::shared_ptr<IArgvPack> unit) {
if (u_exist->paramType() == ParamType::FloatParam) {
auto u_cast = std::dynamic_pointer_cast<__FloatArgvImpls>(u_exist);
if (u_cast->bindKey() == unit_in->bindKey())
throw new std::exception("重复设置选项");
throw new inner_exception("重复设置选项");
}
}
}

View File

@ -8,6 +8,7 @@
<ItemGroup>
<Compile Include="AstImport.fs" />
<Compile Include="HtmlStruct.fs" />
<Compile Include="FmtStruct.fs" />
<Compile Include="Program.fs" />
</ItemGroup>

View File

@ -5,11 +5,19 @@ open System.Linq
module AstImport =
///
type AstObject() =
type AstObject(bind: XmlElement) =
class
member this.astXML() = bind
member this.rows() =
let xmle = bind.SelectSingleNode("tokens/token") :?> XmlElement
int(xmle.GetAttribute "row")
abstract member address:unit -> string
default this.address():string = "mem_" + string(this.GetHashCode ())
abstract member filePath:unit -> string
default this.filePath():string = ""
abstract member members:unit -> AstObject list
default this.members(): AstObject list = []
end
@ -34,7 +42,7 @@ open System.Linq
/// TextWord
type TextItem(bind: XmlElement) =
class
inherit AstObject()
inherit AstObject(bind)
member this.content():string =
bind.GetAttribute "text"
@ -54,7 +62,7 @@ open System.Linq
///
type FragmentRef(bind: XmlElement, texts: TextItem list) =
class
inherit AstObject()
inherit AstObject(bind)
new(bind: XmlElement) = FragmentRef(bind, [])
member this.sliceRef() =
@ -89,7 +97,7 @@ open System.Linq
///
type FragmentSlice(bind: XmlElement, objs: AstObject list) =
class
inherit AstObject()
inherit AstObject(bind)
new(bind: XmlElement) = FragmentSlice(bind, [])
member this.name() =
@ -103,6 +111,8 @@ open System.Linq
override this.members(): AstObject list = objs
override this.address (): string =
bind.GetAttribute "address"
override this.filePath (): string =
bind.GetAttribute "file-path"
static member GenerateFromChildSibling(child: XmlNode): SliceChildType list =
match child with
@ -134,7 +144,7 @@ open System.Linq
///
type StoryDef(bind: XmlElement, objs: AstObject list) =
class
inherit AstObject()
inherit AstObject(bind)
new(bind: XmlElement) = StoryDef(bind, [])
member this.name() =
@ -150,6 +160,8 @@ open System.Linq
override this.members(): AstObject list = objs
override this.address (): string =
bind.GetAttribute "address"
override this.filePath (): string =
bind.GetAttribute "file-path"
static member GenerateFromChildSibling(child: XmlNode): StoryChildType list =
match child with
@ -181,7 +193,7 @@ open System.Linq
///
type ArticleDef(bind: XmlElement, objs: AstObject list) =
class
inherit AstObject()
inherit AstObject(bind)
new(bind: XmlElement) = ArticleDef(bind, [])
member this.name() =
@ -226,7 +238,7 @@ open System.Linq
///
type VolumeDef(bind: XmlElement, objs: AstObject list) =
class
inherit AstObject()
inherit AstObject(bind)
new(bind: XmlElement) = VolumeDef(bind, [])
member this.name() =
@ -244,6 +256,8 @@ open System.Linq
override this.members(): AstObject list = objs
override this.address (): string =
bind.GetAttribute "address"
override this.filePath (): string =
bind.GetAttribute "file-path"
static member GenerateFromChildSibling(child: XmlNode): VolumeChildType list =
match child with
@ -270,13 +284,26 @@ open System.Linq
end
type ProgramChildType = |Volume of VolumeDef |Story of StoryDef
type RankDef(bind: XmlElement) =
class
inherit AstObject(bind)
member this.sort():int32 =
int32(bind.GetAttribute "rank")
override this.filePath (): string =
bind.GetAttribute "doc-path"
end
type ProgramChildType = |Volume of VolumeDef |Story of StoryDef |Rank of RankDef
///
type Program(bind: XmlElement, objs: AstObject list) =
class
inherit AstObject()
inherit AstObject(bind)
new(bind: XmlElement) = Program(bind, [])
member this.dirPath() =
bind.GetAttribute "dir_src"
member this.time() =
bind.GetAttribute "time"
member this.children() = objs
@ -285,6 +312,7 @@ open System.Linq
match it with
|Story s -> s :> AstObject
|Volume v -> v :> AstObject
|Rank r -> r:> AstObject
)
Program(bind, objs @items)
@ -297,6 +325,7 @@ open System.Linq
let d = match child.Name with
| "volume" -> Some(Volume(VolumeDef.GenerateVolumeDef(child).Value))
| "story" -> Some(Story(StoryDef.GenerateStoryDef(child).Value))
| "rank" -> Some(Rank(RankDef(child :?> XmlElement)))
| _ -> None
match d with
|Some value -> value::Program.GenerateFromChildSibling(child.NextSibling)
@ -309,6 +338,7 @@ open System.Linq
let conv data = match data with
|Volume d -> d:>AstObject
|Story d -> d :> AstObject
|Rank d -> d:> AstObject
Program(ast, mbrs |> List.map conv)
end

226
AstConv/FmtStruct.fs Normal file
View File

@ -0,0 +1,226 @@
namespace FmtStruct
open System.IO
open AstAccess
open HtmlStruct
module FmtEntry =
// rank
let rank_fmt(node: AstImport.AstObject): string =
let domx = node :?> AstImport.RankDef
$"#排序 {domx.sort()}\n"
//
let rec same_collect<'T when 'T:>Present.IDomUnit>
(nodes: Present.IDomUnit list): Present.IDomUnit list * Present.IDomUnit list =
if nodes.Length = 0 then
[], []
else
match nodes.Head with
| :? 'T ->
let tn, rest = same_collect<'T>(nodes.Tail)
nodes.Head::tn, rest
| _ -> [], nodes
//
let rec same_fold<'T1,'T2 when 'T1:> Present.IDomUnit and 'T2:> Present.IDomUnit>
(nodes: Present.IDomUnit list): Present.IDomUnit list list =
let first_list, rest_0 = same_collect<'T1>(nodes)
let second_list, rest_1 = same_collect<'T2>(rest_0)
let list = match rest_1 with
|[] -> first_list::[second_list]
| _ -> first_list::[second_list]@same_fold<'T1, 'T2>(rest_1)
list |> List.filter(fun slist -> slist.Length > 0)
//
let rec text_fmt(prefix:string)(line_prev: int32)(nodes: Content.TextContent list): string =
match nodes with
| [] -> ""
| _ ->
let obj_text = (nodes.Head :> Present.IDomUnit).object()
let this_text = (obj_text :?> AstImport.TextItem).content()
if obj_text.rows() = line_prev then
" " + this_text + text_fmt prefix line_prev nodes.Tail
else
$"\n{prefix}" + this_text + text_fmt prefix (obj_text.rows()) nodes.Tail
// Refer:Node
let slice_ref_fmt(prefix:string)(node: Content.FragmentRefer): string =
let objref = (node:> Present.IDomUnit).object():?> AstImport.FragmentRef
let childs = (node:> Present.IContainer).children()
let text_childs, rest = same_collect<Content.TextContent>(childs)
assert(rest.Length = 0)
let text_type_childs = text_childs|> List.map(fun n-> n:?> Content.TextContent)
let sections = text_fmt (prefix+" ") (objref.rows()) text_type_childs
$"\n{prefix}{{@情节 {objref.storyRef()}&{objref.sliceRef()}{sections}}}"
// Slice:Node
let slice_def_fmt(prefix:string)(node: Content.FragmentSlice): string =
let obj_def = (node:> Present.IDomUnit).object():?> AstImport.FragmentSlice
let childs = (node:> Present.IContainer).children()
let same_groups_set = same_fold<Content.TextContent, Content.FragmentRefer>(childs)
let contents = same_groups_set |> List.map(fun same_set ->
if same_set.Length = 0 then
""
else
match same_set.Head with
| :? Content.FragmentRefer ->
let strx = same_set|> List.map(fun n ->
slice_ref_fmt (prefix+" ") (n:?> Content.FragmentRefer)
)
strx |> List.reduce(fun a b -> a + "\n" + b)
| :? Content.TextContent ->
let typed_set = same_set|>List.map(fun fn->fn:?>Content.TextContent)
text_fmt (prefix+" ") (obj_def.rows()) typed_set
| _ -> failwith "mismatch"
)
let subs = match contents with
| [] -> ""
| _ -> contents|> List.reduce(fun a b -> a + "\n" + b)
$"\n{prefix}{{情节 {obj_def.name()}{subs}\n{prefix}}}"
// Story:Node
let story_def_fmt(node: Content.StoryDefine): string =
let obj_story = (node:> Present.IDomUnit).object():?> AstImport.StoryDef
let childs = (node:> Present.IContainer).children()
let same_groups_set = same_fold<Content.TextContent, Content.FragmentSlice>(childs)
let contents = same_groups_set |> List.map(fun same_set ->
if same_set.Length = 0 then
""
else
match same_set.Head with
| :? Content.FragmentSlice ->
let strx = same_set|> List.map(fun n ->
slice_def_fmt " " (n:?> Content.FragmentSlice)
)
strx |> List.reduce(fun a b -> a + "\n" + b)
| :? Content.TextContent ->
let typed_set = same_set|>List.map(fun fn->fn:?>Content.TextContent)
text_fmt " " (obj_story.rows()) typed_set
| _ -> failwith "mismatch-storydef"
)
let subs = match contents with
| [] -> ""
| _ -> contents|> List.reduce(fun a b -> a + "\n" + b)
$"\n{{故事 {obj_story.name()}{subs}\n}}"
// ArticleNode
let article_def_fmt(prefix:string)(node: Content.ArticleDefine): string =
let obj_article = (node:> Present.IDomUnit).object():?> AstImport.ArticleDef
let childs = (node:> Present.IContainer).children()
let same_groups_set = same_fold<Content.TextContent, Content.FragmentRefer>(childs)
let contents = same_groups_set |> List.map(fun same_set ->
if same_set.Length = 0 then
""
else
match same_set.Head with
| :? Content.FragmentRefer ->
let strx = same_set|> List.map(fun n ->
slice_ref_fmt (prefix+" ") (n:?> Content.FragmentRefer)
)
strx |> List.reduce(fun a b -> a + "\n" + b)
| :? Content.TextContent ->
let typed_set = same_set|>List.map(fun fn->fn:?>Content.TextContent)
text_fmt (prefix+" ") (obj_article.rows()) typed_set
| _ -> failwith "mismatch"
)
let subs = match contents with
| [] -> ""
| _ -> contents|> List.reduce(fun a b -> a + "\n" + b)
$"\n{prefix}{{章节 {obj_article.name()}{subs}\n{prefix}}}"
// VolumeNode
let volume_def_fmt(node: Content.VolumeDefine): string =
let obj_volume = (node:> Present.IDomUnit).object():?> AstImport.VolumeDef
let childs = (node:> Present.IContainer).children()
let same_groups_set = same_fold<Content.TextContent, Content.ArticleDefine>(childs)
let contents = same_groups_set |> List.map(fun same_set ->
if same_set.Length = 0 then
""
else
match same_set.Head with
| :? Content.ArticleDefine ->
let strx = same_set|> List.map(fun n ->
article_def_fmt " " (n:?> Content.ArticleDefine)
)
strx |> List.reduce(fun a b -> a + "\n" + b)
| :? Content.TextContent ->
let typed_set = same_set |> List.map(fun fn->fn:?>Content.TextContent)
text_fmt " " (obj_volume.rows()) typed_set
| _ -> failwith "mismatch-volumedef"
)
let subs = match contents with
| [] -> ""
| _ -> contents|> List.reduce(fun a b -> a + "\n" + b)
$"\n{{分卷 {obj_volume.name()}{subs}\n}}"
let document_def_fmt(path:string)(nodes: Present.IDomUnit list): unit =
let head_str, rest_nodes=match nodes.Head with
| :? Content.RankDefine ->
rank_fmt(nodes.Head.object()), nodes.Tail
| _ -> "", nodes
let sorted_nodes = rest_nodes|> List.sortBy (fun a->a.object().rows())
let nodes_str = sorted_nodes|> List.map(fun nx ->
match nx with
| :? Content.VolumeDefine as vdef ->
volume_def_fmt vdef
| :? Content.StoryDefine as sdef->
story_def_fmt sdef
| _ -> failwith "error-doc-child"
)
let xcontent = head_str + (nodes_str|> List.reduce(fun a b -> a + "\n" + b))
use wr = new StreamWriter(path,false)
wr.Write(xcontent)
printfn $"- {path}"
let program_def_fmt(dir:string)(nodes: Present.IDomUnit list): unit =
let ranks = nodes|> List.filter(fun n -> n:?Content.RankDefine)
ranks|> List.iter(fun rankn ->
let rank_o = rankn.object() :?> AstImport.RankDef
let file_path = rank_o.filePath()
let nodes_within = nodes|> List.filter(fun n ->
match n with
| :? Content.StoryDefine ->
n.object().filePath() = file_path
| :? Content.VolumeDefine ->
n.object().filePath() = file_path
| _ -> false)
document_def_fmt (Path.Combine(dir, file_path)) (rankn::nodes_within)
)
let nodes_hangout = nodes|> List.filter(fun n ->
let rank_paths = ranks|> List.map(fun n -> n.object().filePath())
match n with
| :? Content.VolumeDefine ->
not(List.contains (n.object().filePath()) rank_paths)
| _ -> false
)
let path_hangout = nodes_hangout|> List.map(fun n -> n.object().filePath())
|> List.distinct
path_hangout |> List.iter(fun path ->
let nodes_within = nodes_hangout|> List.filter(fun n -> n.object().filePath() = path)
document_def_fmt (Path.Combine(dir, path)) nodes_within
)

View File

@ -269,6 +269,13 @@ open System.IO
)
VolumeDefine(defs, childs)
type RankDefine(defs: AstImport.RankDef) =
class
interface Present.IDomUnit with
member this.name(): string =
raise (System.NotImplementedException())
member this.object(): AstImport.AstObject = defs
end
type UnitGenerate(root: AstImport.AstObject) =
class
@ -296,6 +303,8 @@ open System.IO
result_nodes@[(refs_depth, ArticleDefine(defs, childs))]
| :? AstImport.VolumeDef as defs ->
result_nodes@[(refs_depth, VolumeDefine(defs, childs))]
| :? AstImport.RankDef as defs ->
result_nodes@[refs_depth, RankDefine(defs)]
| :? AstImport.Program -> result_nodes
| _ -> failwith "match error"
@ -942,11 +951,11 @@ open System.IO
// 线
let story_decl = story_lines|> List.map(fun story ->
$"""node_{story.address()}[label="{story.name()}" shape="cds"]
$"""node_{story.address()}[label="{story.name()}" shape="cds" tooltip="{story.filePath()}"]
""")|> List.reduce(fun a b -> a + b)
//
let points_decl = fragment_defs |> List.map(fun (_, node_def) ->
$"""node_{node_def.address()}[label="{node_def.name()}" shape="rect"]
$"""node_{node_def.address()}[label="{node_def.name()}" shape="rect" tooltip="{node_def.filePath()}"]
""")|> List.reduce(fun a b -> a + b)
//
let nodes_decl = story_decl + points_decl
@ -959,7 +968,7 @@ open System.IO
let vhead = nexts.Head
match vhead with
| :? AstImport.FragmentSlice as slice ->
$"""node_{node_curr.address()}->node_{vhead.address()}[label="{story_nm}"]
$"""node_{node_curr.address()}->node_{vhead.address()}[label="{story_nm}" tooltip="{story_nm}" arrowhead="diamond"]
"""::node_chains story_nm slice nexts.Tail
| _ -> node_chains story_nm node_curr nexts.Tail
let arrows_decl = story_lines|> List.map(fun story -> node_chains (story.name()) story (story.children()))
@ -971,22 +980,26 @@ open System.IO
$"{a.Value.name()}&{b.name()}", b.address())
// 线线
let towards_arrs = fragment_defs|> List.map(fun (_, defn) ->
let towards_prim = fragment_defs|> List.map(fun (_, defn) ->
let node_refs = defn.children() |> List.filter(fun v -> v:? AstImport.FragmentRef)
|> List.map(fun v -> v:?> AstImport.FragmentRef)
node_refs|> List.map(fun nref ->
let node_name = $"""{nref.storyRef()}&{nref.sliceRef()}"""
let _, address_target = node_map|> List.filter(fun (a,_)-> a = node_name)|> List.item 0
$"""node_{defn.address()}->node_{address_target}[style="dotted"]
$"""node_{defn.address()}->node_{address_target}[style="dotted" arrowhead="open"]
"""
)
)
|> List.reduce(fun al bl-> al @ bl)
|> List.reduce(fun a b -> a + b)
let all_arrows = arrows_decl + towards_arrs
let all_arrows =
if towards_prim.IsEmpty then
arrows_decl
else
let towards_arrs = towards_prim |> List.reduce(fun a b -> a + b)
arrows_decl + towards_arrs
$"""digraph node_relates{{
rankdir=LR
label="{gname}"
@ -1078,17 +1091,25 @@ open System.IO
|> List.map(fun v -> v, (v.children()|> List.filter(fun v-> v:? AstImport.FragmentRef))) // 线
|> List.filter(fun (_, al)-> al.Length > 0)
let refers = rst|> List.map(fun (a, refsa) ->
refsa|> List.map(fun refn ->
let refx = refn :?> AstImport.FragmentRef
let target_name = $"""{refx.storyRef()}&{refx.sliceRef()}"""
let _, target_address = node_map|> List.filter(fun (a, _) -> a = target_name)|> List.item 0
$"""node_{a.address()}--node_{target_address}
"""
)
)
|> List.reduce (fun a b -> a @ b)
|> List.reduce (fun a b -> a + b)
let refers_prim=
if not rst.IsEmpty then
rst |> List.map(fun (a, refsa) ->
refsa |> List.map(fun refn ->
let refx = refn :?> AstImport.FragmentRef
let target_name = $"""{refx.storyRef()}&{refx.sliceRef()}"""
let _, target_address = node_map|> List.filter(fun (a, _) -> a = target_name)|> List.item 0
$"""node_{a.address()}--node_{target_address}
"""
)
)
|> List.reduce (fun a b -> a @ b)
else []
let refers =
if refers_prim.IsEmpty then
""
else
refers_prim |> List.reduce (fun a b -> a + b)
$"""graph{{ label="{gname}" {clusters_desc} {refers} }}"""

View File

@ -2,78 +2,110 @@
open HtmlStruct.Content
open System.Xml
open HtmlStruct.Assemble
open HtmlStruct.Content
open HtmlStruct.Present
open System.IO
open System.Text
open System
open System.Diagnostics
open FmtStruct.FmtEntry
let html_generate (file_in:FileInfo) (dir_o:DirectoryInfo): unit =
let out_dir = Uri(dir_o.FullName)
let doc = XmlDocument()
doc.Load(file_in.FullName)
let prog = Program.GenerateFrom(doc)
let entry = AstVisitEntry(prog)
let visitor = UnitGenerate(prog)
entry.visitWith(visitor) |> ignore
let volume_pages = volume_page_assemble(visitor.contents()) |> List.map<VolumePage, IDomUnit> (fun x->x)
let story_pages = story_page_assemble(visitor.contents()) |> List.map<StoryPage, IDomUnit> (fun x->x)
let point_pages = slice_page_assemble(volume_pages @ story_pages)
volume_pages @ story_pages @ point_pages |> List.iter (fun it -> (it:?>PageAccess).setPageRoot(out_dir))
let makers = volume_pages @ story_pages @ point_pages
|> List.map(fun page_unit ->
match page_unit with
| :? SlicePage as point ->
PageMaker(point) :> PageText
| :? VolumePage as vol ->
PageMaker(vol)
| :? StoryPage as story ->
PageMaker(story)
| _ -> failwith ""
)
let index_page = IndexPage("汇总页面", volume_pages @ story_pages @ point_pages)
index_page.setPageRoot(out_dir)
let content = (index_page :> PageText).getHtmlText()
let href = index_page.pageURL()
File.WriteAllLines(href, [content])
for refs in makers do
let file_path = (refs.bindPage() :?> PageAccess).pageURL()
File.WriteAllLines(file_path, [refs.getHtmlText()])
let graph = StorylineGraphMake("故事线网络", story_pages|> List.map(fun x -> x :?> PageAccess))
let graph_code = graph.getGraphCode()
let stream = new StreamWriter(Path.Combine(dir_o.FullName, "storys_display.dot"), false)
stream.Write(graph_code)
stream.Flush()
Process.Start("dot", $"""-Tsvg -o{Path.Combine(dir_o.FullName, "storys_display.svg")} {Path.Combine(dir_o.FullName, "storys_display.dot")}""") |> ignore
let graph2 = VolumeGraphMake("卷章引用网络", (volume_pages@story_pages)|> List.map(fun d -> d :?> PageAccess))
let graph2_code = graph2.getGraphCode()
let stream2 = new StreamWriter(Path.Combine(dir_o.FullName, "volume_display.dot"), false)
stream2.Write(graph2_code)
stream2.Flush()
Process.Start("dot", $"""-Tsvg -o{Path.Combine(dir_o.FullName, "volume_display.svg")} {Path.Combine(dir_o.FullName, "volume_display.dot")}""") |> ignore
let source_format (file_in:FileInfo): unit =
let doc = XmlDocument()
doc.Load(file_in.FullName)
let prog = Program.GenerateFrom(doc)
let entry = AstVisitEntry(prog)
let visitor = UnitGenerate(prog)
entry.visitWith(visitor) |> ignore
//
let nodes = visitor.contents()
program_def_fmt (prog.dirPath()) nodes
let args = System.Environment.GetCommandLineArgs()|> Array.toList
if args.Length = 2 && args.Item(1) = "--help" then
System.Console.WriteLine("AstConv.exe -file xast文件路径 -odir ")
//
match args.Length with
| 2 when args.Item(1) = "--help" ->
printfn "AstConv.exe --file xast文件路径 --html \n\tHTML"
printfn "AstConv.exe --file xast文件路径 --format\n\t"
exit 0
if args.Length <> 5 then
failwith "程序调用参数错误AstConv.exe -file xast文件路径 -odir "
//
| 4 when args.Item(1) = "--file" && args.Item(3) = "--format" ->
if not(FileInfo(args.Item 2).Exists) then
printfn "Arguments-Error:xast"
exit 0
source_format(FileInfo(args.Item 2)) |> ignore
let file_in, dir_o = match args.Item(1), args.Item(3) with
| "-file","-odir" ->
if not(FileInfo(args.Item 2).Exists) then
failwith "xast"
if not(DirectoryInfo(args.Item 4).Exists) then
failwith ""
FileInfo(args.Item 2), DirectoryInfo(args.Item 4)
| _ -> failwith "程序调用参数错误AstConv.exe -file xast文件路径 -odir "
// html
| 5 when args.Item(1) = "--file" && args.Item(3) = "--html" ->
if not(FileInfo(args.Item 2).Exists) then
printfn "Arguments-Error:xast"
exit 0
if not(DirectoryInfo(args.Item 4).Exists) then
printfn "Arguments-Error:"
exit 0
let out_dir = Uri(dir_o.FullName)
let xfile, xdir = FileInfo(args.Item 2), DirectoryInfo(args.Item 4)
// html
html_generate xfile xdir
exit 0
let doc = XmlDocument()
doc.Load(file_in.FullName)
let prog = Program.GenerateFrom(doc)
let entry = AstVisitEntry(prog)
let visitor = UnitGenerate(prog)
entry.visitWith(visitor) |> ignore
let volume_pages = volume_page_assemble(visitor.contents()) |> List.map<VolumePage, IDomUnit> (fun x->x)
let story_pages = story_page_assemble(visitor.contents()) |> List.map<StoryPage, IDomUnit> (fun x->x)
let point_pages = slice_page_assemble(volume_pages @ story_pages)
volume_pages @ story_pages @ point_pages |> List.iter (fun it -> (it:?>PageAccess).setPageRoot(out_dir))
let makers = volume_pages @ story_pages @ point_pages
|> List.map(fun page_unit ->
match page_unit with
| :? SlicePage as point ->
PageMaker(point) :> PageText
| :? VolumePage as vol ->
PageMaker(vol)
| :? StoryPage as story ->
PageMaker(story)
| _ -> failwith ""
)
let index_page = IndexPage("汇总页面", volume_pages @ story_pages @ point_pages)
index_page.setPageRoot(out_dir)
let content = (index_page :> PageText).getHtmlText()
let href = index_page.pageURL()
File.WriteAllLines(href, [content])
for refs in makers do
let file_path = (refs.bindPage() :?> PageAccess).pageURL()
File.WriteAllLines(file_path, [refs.getHtmlText()])
let graph = StorylineGraphMake("故事线网络", story_pages|> List.map(fun x -> x :?> PageAccess))
let graph_code = graph.getGraphCode()
let stream = new StreamWriter(Path.Combine(dir_o.FullName, "storys_display.dot"), false)
stream.Write(graph_code)
stream.Flush()
Process.Start("dot", $"""-Tsvg -o{Path.Combine(dir_o.FullName, "storys_display.svg")} {Path.Combine(dir_o.FullName, "storys_display.dot")}""") |> ignore
let graph2 = VolumeGraphMake("卷章引用网络", (volume_pages@story_pages)|> List.map(fun d -> d :?> PageAccess))
let graph2_code = graph2.getGraphCode()
let stream2 = new StreamWriter(Path.Combine(dir_o.FullName, "volume_display.dot"), false)
stream2.Write(graph2_code)
stream2.Flush()
Process.Start("dot", $"""-Tsvg -o{Path.Combine(dir_o.FullName, "volume_display.svg")} {Path.Combine(dir_o.FullName, "volume_display.dot")}""") |> ignore
| _ ->
printfn ""
printfn "AstConv.exe --file xast文件路径 --html \n\tHTML"
printfn "AstConv.exe --file xast文件路径 --format\n\t"
exit 0

View File

@ -2,7 +2,7 @@
"profiles": {
"AstConv": {
"commandName": "Project",
"commandLineArgs": "-file \"E:/storyline.xast\" -odir \"E:/\""
"commandLineArgs": "--file \"E:/storyline.xast\" --html \"E:/html\""
}
}
}

17
CMakeLists.txt Normal file
View File

@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.10)
project(WsNovelParser)
set(CMAKE_PREFIX_PATH "/Users/ws/Qt5.12.4/5.12.4/clang_64/lib/cmake")
# ======================================
set(BUILD_TYPE_MARK "$<IF:$<CONFIG:Debug>,0,1>")
set(SOLUTION_ROOT "${CMAKE_CURRENT_LIST_DIR}")
set(RUNTIME_PATH "$<IF:$<CONFIG:Debug>,${CMAKE_CURRENT_LIST_DIR}/out,${CMAKE_CURRENT_LIST_DIR}/out>")
# ======================================
find_package(Qt5 COMPONENTS Core Xml REQUIRED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
add_subdirectory(./ArgsParser)

20
TestProject/Program.fs Normal file
View File

@ -0,0 +1,20 @@
// For more information see https://aka.ms/fsharp-console-apps
type MarkX() =
class
abstract member some:unit-> unit
default this.some() =
printfn "Hello World!"
end
type SubMark() =
class
inherit MarkX()
override this.some (): unit =
printfn "Substract"
end
let code = SubMark()
code.some()

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
</Project>

View File

@ -40,6 +40,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CoreTest", "CoreTest\CoreTe
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "AstConv", "AstConv\AstConv.fsproj", "{0C77216C-6484-4C94-BE06-D5D9FF18EA81}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TestProject", "TestProject\TestProject.fsproj", "{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -134,6 +136,18 @@ Global
{0C77216C-6484-4C94-BE06-D5D9FF18EA81}.Release|x64.Build.0 = Release|Any CPU
{0C77216C-6484-4C94-BE06-D5D9FF18EA81}.Release|x86.ActiveCfg = Release|Any CPU
{0C77216C-6484-4C94-BE06-D5D9FF18EA81}.Release|x86.Build.0 = Release|Any CPU
{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}.Debug|Any CPU.Build.0 = Debug|Any CPU
{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}.Debug|x64.ActiveCfg = Debug|Any CPU
{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}.Debug|x64.Build.0 = Debug|Any CPU
{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}.Debug|x86.ActiveCfg = Debug|Any CPU
{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}.Debug|x86.Build.0 = Debug|Any CPU
{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}.Release|Any CPU.ActiveCfg = Release|Any CPU
{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}.Release|Any CPU.Build.0 = Release|Any CPU
{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}.Release|x64.ActiveCfg = Release|Any CPU
{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}.Release|x64.Build.0 = Release|Any CPU
{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}.Release|x86.ActiveCfg = Release|Any CPU
{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -113,12 +113,12 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="htmlprint.cpp" />
<ClCompile Include="astprint.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="novelparser.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="htmlprint.h" />
<ClInclude Include="astprint.h" />
<ClInclude Include="novelparser.h" />
</ItemGroup>
<ItemGroup>

View File

@ -29,7 +29,7 @@
<ClCompile Include="novelparser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="htmlprint.cpp">
<ClCompile Include="astprint.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
@ -37,7 +37,7 @@
<ClInclude Include="novelparser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="htmlprint.h">
<ClInclude Include="astprint.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>

View File

@ -3,7 +3,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerWorkingDirectory>$(SolutionDir)$(Platform)\$(Configuration)\</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerCommandArguments>--path "D:/Projects/Cpp/WsNovelParser/x64/test_file/" --dest E:\</LocalDebuggerCommandArguments>
<LocalDebuggerCommandArguments>--path "E:\CustomNovels\科学+修仙+创造世界" --dest F:\novelout</LocalDebuggerCommandArguments>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerCommandArguments>--path "D:\Projects\Cpp\WsNovelParser\x64\test_file" --dest E:\</LocalDebuggerCommandArguments>

View File

@ -1,4 +1,4 @@
#include "htmlprint.h"
#include "astprint.h"
#include <QDir>
#include <QDomElement>
@ -86,6 +86,9 @@ bool printer::AstGenerate::visit(std::shared_ptr<const ast_gen::ElementAccess> s
dom_slice.setAttribute("name", slice_node->name());
dom_slice.setAttribute("address", (qulonglong) slice_node.get());
dom_slice.setAttribute("file-path", src_root.relativeFilePath(slice_node->filePath()));
append_tokens(dom_slice, slice_node);
}break;
case NovelNode::TextSection:
{

View File

@ -14,7 +14,7 @@
#include <argsparser.h>
#include "novelparser.h"
#include "htmlprint.h"
#include "astprint.h"
using namespace example_novel;
using namespace std;

View File

@ -13,6 +13,7 @@ NovelParser::NovelParser() {
checker_list << std::make_shared<FragmentExistsCheck>();
checker_list << std::make_shared<StoryOrderCheck>();
checker_list << std::make_shared<PointGraphCheck>();
checker_list << std::make_shared<FragmentLayerCheck>();
analyzer_ref = std::make_shared<Analyzer>(checker_list);
}

View File

@ -31,7 +31,7 @@ std::shared_ptr<const SyntaxElement> ElementsCache::getNamedNodeBy(int paramType
return node_cache[mixed_key];
}
void example_novel::FragmentExistsCheck::nodes_regist(std::shared_ptr<ElementsCache> cache, std::shared_ptr<const ast_gen::ElementAccess> target) {
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());
@ -53,8 +53,9 @@ void FragmentExistsCheck::exists_check(std::shared_ptr<ElementsCache> root, std:
}
}
example_novel::FragmentExistsCheck::FragmentExistsCheck()
:_nodes_cache(std::make_shared<ElementsCache>()) { }
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);
@ -200,11 +201,13 @@ void PointGraphCheck::validCheck(std::shared_ptr<const ElementAccess> root) cons
// 获取绑定节点名称
auto get_name = [](std::shared_ptr<const ElementAccess> node)->QString {
switch (node->element()->typeMark()) {
case (int) NovelNode::FragmentSlice: {
case (int) NovelNode::FragmentSlice:
{
auto def_node = std::dynamic_pointer_cast<const FragmentSlice>(node->element());
return def_node->signature();
}break;
case (int) NovelNode::FragmentRefers: {
case (int) NovelNode::FragmentRefers:
{
auto ref_node = std::dynamic_pointer_cast<const FragmentRefers>(node->element());
return ref_node->referSignature();
}break;
@ -227,7 +230,7 @@ void PointGraphCheck::validCheck(std::shared_ptr<const ElementAccess> root) cons
}
// 构建完整图结构:串联引用节点的顺序
for(auto point : point_items){
for (auto point : point_items) {
auto first_name = get_name(point);
auto first_helper = getElement(first_name);
@ -272,10 +275,11 @@ void PointGraphCheck::validCheck(std::shared_ptr<const ElementAccess> root) cons
}
QString PointGraphCheck::name() const {
return "情节网络有效性检查器";
return "情节网络引用检查器";
}
PointGraphHelper::PointGraphHelper(std::shared_ptr<const FragmentSlice> node) : node_peer(node) { }
PointGraphHelper::PointGraphHelper(std::shared_ptr<const FragmentSlice> node) : node_peer(node) {
}
std::shared_ptr<const FragmentSlice> PointGraphHelper::nodePeer() const {
return this->node_peer;
@ -298,14 +302,16 @@ QList<std::shared_ptr<const ElementAccess>> StoryOrderCheck::valid_docs_peak(std
auto type_code = pnode->element()->typeMark();
switch ((NovelNode) type_code) {
case NovelNode::GlobalElement: {
case NovelNode::GlobalElement:
{
auto children = pnode->children();
for (auto& cinst : children) {
values.append(valid_docs_peak(cinst));
}
}break;
case NovelNode::Document: {
case NovelNode::Document:
{
auto elms_set = pnode->children();
decltype(elms_set) story_set;
@ -371,3 +377,162 @@ void StoryOrderCheck::validCheck(std::shared_ptr<const ElementAccess> root) cons
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;
}

View File

@ -108,4 +108,59 @@ namespace example_novel {
QString name() const override;
void validCheck(std::shared_ptr<const ast_gen::ElementAccess> root) const override;
};
class FragmentNode : public std::enable_shared_from_this<FragmentNode> {
private:
std::shared_ptr<const ast_gen::ElementAccess> _src_fragm = nullptr;
QList<std::shared_ptr<FragmentNode>> _next_fragms;
int _layer_number = -1;
public:
FragmentNode(std::shared_ptr<const ast_gen::ElementAccess> bind);
std::shared_ptr<const ast_gen::ElementAccess> bind() const;
void setLayer(int number);
int layerNumber() const;
QList<std::shared_ptr<FragmentNode>> referNodes() const;
void appendNext(std::shared_ptr<FragmentNode> ins);
};
/**
* @brief
*/
class LIBPARSE_EXPORT FragmentLayerCheck : public lib_parse::CheckProvider {
private:
QHash<QString, std::shared_ptr<FragmentNode>> _node_set;
QList<std::shared_ptr<FragmentNode>> _story_start;
/**
* @brief 线
*/
std::shared_ptr<FragmentNode> _sets_fill(std::shared_ptr<const ast_gen::ElementAccess> node);
/// <summary>
/// 引用网络重构
/// </summary>
/// <param name="node">传入情节定义结点</param>
void _refers_rebuild(std::shared_ptr<const FragmentNode> node);
/// <summary>
/// 节点层级重排
/// </summary>
/// <param name="node">目标结点</param>
/// <param name="curr">目标层级</param>
void node_relayer(std::shared_ptr<FragmentNode> node, int curr = 0);
/// <summary>
/// 层级确认
/// </summary>
/// <param name="node">故事线</param>
void layer_check(std::shared_ptr<const ast_gen::ElementAccess> node);
void story_layer_check(std::shared_ptr<const ast_gen::ElementAccess> story);
void sibling_element_compair(const QString& story, std::shared_ptr<const FragmentNode> prev, std::shared_ptr<const FragmentNode> curr);
public:
// 通过 CheckProvider 继承
QString name() const override;
void validCheck(std::shared_ptr<const ast_gen::ElementAccess> root) const override;
};
} // namespace example_novel

23
libSyntax/CMakeLists.txt Normal file
View File

@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.10)
project(libSyntax)
set(CMAKE_PREFIX_PATH "~/Qt5.12.4/5.12.4/clang_64/lib/cmake")
find_package(Qt5 COMPONENTS Core REQUIRED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_library(libSyntax
libsyntax.cpp
ast_basic.cpp
ast_gen.cpp
ast_novel.cpp
libtokens.cpp
syntax_novel.cpp
tokens_novel.cpp
)
set(CMAKE_CXX_STANDARD 20)
target_link_libraries(libSyntax Qt5::Core)

16
libWords/CMakeLists.txt Normal file
View File

@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.10)
project(libWords)
set(CMAKE_PREFIX_PATH "~/Qt5.12.4/5.12.4/clang_64/lib/cmake")
find_package(Qt5 COMPONENTS Core REQUIRED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_library(libWords libwords.cpp)
set(CMAKE_CXX_STANDARD 20)
target_link_libraries(libWords Qt5::Core)

187
语法进化路线.md Normal file
View File

@ -0,0 +1,187 @@
# 语法进化路线
## v1 软件设计
### 实现目标
1. 构建UI编辑软件
2. 故事线展示
3. 悬空节点筛选
4. 可用节点展示
5. 基于数据库
## v2 软件设计
### 语法实例
```
#大纲 作品名称
作品描述
#分卷 分卷名称
分卷描述
#脉络 故事名称
故事叙述
#节点 节点名称
节点叙述
@节点 故事名称&节点名称
引用叙述
#概念 分类名称
分类叙述
#节点 种类名称
种类描述
```
### 实现目标
1. 基于QTextDocument和行编辑基于GUI
2. 行格式与语义绑定
## v3 软件设计
### 语法实例
```
#故事 1 故事名称{
故事叙述
#节点 节点名称{
节点叙述
}
@节点 故事名称&节点名称{
引用叙述
}
}
#分卷 分卷名称{
分卷叙述
#章节 章节名称{
@节点 故事名称&节点名称{
引用叙述
}
}
}
```
### 实现目标
1. 手动展开ANY/SEQUENCE/REPEAT语法逻辑构建语法分析类库
2. 分析类库集成入软件部件,非独立软件
## v4 软件设计
### 语法实例
```
{故事 1 故事名称
故事叙述
{节点 节点名称
节点叙述
}
{@节点 故事名称&节点名称
引用叙述
}
}
{分卷 分卷名称
分卷叙述
{章节 章节名称
章节叙述
{@节点 故事名称&节点名称
引用叙述
}
}
}
```
### 实现目标
1. 软件独立化
1. 使用宏和ANY/SEQUENCE/REPEAT结构编织语法构建语法分析前端
## v5 软件设计
### 语法实例
```
#排序 1
{故事 故事名称
故事叙述
{节点 节点名称
节点叙述
}
{@节点 故事名称&节点名称
引用叙述
}
}
{分卷 分卷名称
分卷叙述
{章节 章节名称
章节叙述
{@节点 故事名称&节点名称
引用叙述
}
}
}
```
### 实现目标
#### v5.01
1. 简化语法
2. 编译直出HTML
#### v5.02
1. 编译直出XAST
2. 另外构建软件负责XAST->HTML
#### v5.03
1. 使用模板语法替代宏
## v6 软件设计
### 语法实例
```
#排序 1
{故事 故事名称
故事叙述
{剧情 剧情名称
剧情叙述
{节点 节点名称
节点叙述
}
{@节点 故事名称&剧情名称&节点名称
引用叙述
}
}
}
{分卷 分卷名称
分卷叙述
{章节 章节名称
章节叙述
{@节点 故事名称&剧情名称&节点名称
引用叙述
}
}
}
```
### 实现目标
1. 改进XAST->HTML
2. 改进图示格式
## v7 软件设计
### 语法实例
```
#排序 1
{故事 故事名称
故事叙述
{节点 节点名称
节点叙述
{@节点 故事名称&节点名称
引用叙述
}
{@节点 故事名称&节点名称
引用叙述
}
}
}
{分卷 分卷名称
分卷叙述
{章节 章节名称
章节叙述
{@节点 故事名称&节点名称
引用叙述
}
{@节点 故事名称&节点名称
引用叙述
}
}
}
```
### 实现目标
1. 改进XAST->HTML
2. 改进图示格式