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

5
.gitignore vendored
View File

@ -3,3 +3,8 @@
x64/* x64/*
*/bin/* */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) MatchMode::MatchMode(int mode_code, const QString& mode)
:_means_explain(mode), code_store(mode_code) { } :_means_explain(mode), code_store(mode_code) { }
/** #include <QtCore/QByteArray>
* @brief class inner_exception : public std::exception {
* @return 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) { MatchMode& MatchMode::operator<<(std::shared_ptr<IArgvPack> unit) {
if(unit->paramType() == ParamType::FloatParam){ if(unit->paramType() == ParamType::FloatParam){
@ -119,7 +127,7 @@ MatchMode& MatchMode::operator<<(std::shared_ptr<IArgvPack> unit) {
if (u_exist->paramType() == ParamType::FloatParam) { if (u_exist->paramType() == ParamType::FloatParam) {
auto u_cast = std::dynamic_pointer_cast<__FloatArgvImpls>(u_exist); auto u_cast = std::dynamic_pointer_cast<__FloatArgvImpls>(u_exist);
if (u_cast->bindKey() == unit_in->bindKey()) if (u_cast->bindKey() == unit_in->bindKey())
throw new std::exception("重复设置选项"); throw new inner_exception("重复设置选项");
} }
} }
} }

View File

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

View File

@ -5,11 +5,19 @@ open System.Linq
module AstImport = module AstImport =
/// ///
type AstObject() = type AstObject(bind: XmlElement) =
class class
member this.astXML() = bind
member this.rows() =
let xmle = bind.SelectSingleNode("tokens/token") :?> XmlElement
int(xmle.GetAttribute "row")
abstract member address:unit -> string abstract member address:unit -> string
default this.address():string = "mem_" + string(this.GetHashCode ()) default this.address():string = "mem_" + string(this.GetHashCode ())
abstract member filePath:unit -> string
default this.filePath():string = ""
abstract member members:unit -> AstObject list abstract member members:unit -> AstObject list
default this.members(): AstObject list = [] default this.members(): AstObject list = []
end end
@ -34,7 +42,7 @@ open System.Linq
/// TextWord /// TextWord
type TextItem(bind: XmlElement) = type TextItem(bind: XmlElement) =
class class
inherit AstObject() inherit AstObject(bind)
member this.content():string = member this.content():string =
bind.GetAttribute "text" bind.GetAttribute "text"
@ -54,7 +62,7 @@ open System.Linq
/// ///
type FragmentRef(bind: XmlElement, texts: TextItem list) = type FragmentRef(bind: XmlElement, texts: TextItem list) =
class class
inherit AstObject() inherit AstObject(bind)
new(bind: XmlElement) = FragmentRef(bind, []) new(bind: XmlElement) = FragmentRef(bind, [])
member this.sliceRef() = member this.sliceRef() =
@ -89,7 +97,7 @@ open System.Linq
/// ///
type FragmentSlice(bind: XmlElement, objs: AstObject list) = type FragmentSlice(bind: XmlElement, objs: AstObject list) =
class class
inherit AstObject() inherit AstObject(bind)
new(bind: XmlElement) = FragmentSlice(bind, []) new(bind: XmlElement) = FragmentSlice(bind, [])
member this.name() = member this.name() =
@ -103,6 +111,8 @@ open System.Linq
override this.members(): AstObject list = objs override this.members(): AstObject list = objs
override this.address (): string = override this.address (): string =
bind.GetAttribute "address" bind.GetAttribute "address"
override this.filePath (): string =
bind.GetAttribute "file-path"
static member GenerateFromChildSibling(child: XmlNode): SliceChildType list = static member GenerateFromChildSibling(child: XmlNode): SliceChildType list =
match child with match child with
@ -134,7 +144,7 @@ open System.Linq
/// ///
type StoryDef(bind: XmlElement, objs: AstObject list) = type StoryDef(bind: XmlElement, objs: AstObject list) =
class class
inherit AstObject() inherit AstObject(bind)
new(bind: XmlElement) = StoryDef(bind, []) new(bind: XmlElement) = StoryDef(bind, [])
member this.name() = member this.name() =
@ -150,6 +160,8 @@ open System.Linq
override this.members(): AstObject list = objs override this.members(): AstObject list = objs
override this.address (): string = override this.address (): string =
bind.GetAttribute "address" bind.GetAttribute "address"
override this.filePath (): string =
bind.GetAttribute "file-path"
static member GenerateFromChildSibling(child: XmlNode): StoryChildType list = static member GenerateFromChildSibling(child: XmlNode): StoryChildType list =
match child with match child with
@ -181,7 +193,7 @@ open System.Linq
/// ///
type ArticleDef(bind: XmlElement, objs: AstObject list) = type ArticleDef(bind: XmlElement, objs: AstObject list) =
class class
inherit AstObject() inherit AstObject(bind)
new(bind: XmlElement) = ArticleDef(bind, []) new(bind: XmlElement) = ArticleDef(bind, [])
member this.name() = member this.name() =
@ -226,7 +238,7 @@ open System.Linq
/// ///
type VolumeDef(bind: XmlElement, objs: AstObject list) = type VolumeDef(bind: XmlElement, objs: AstObject list) =
class class
inherit AstObject() inherit AstObject(bind)
new(bind: XmlElement) = VolumeDef(bind, []) new(bind: XmlElement) = VolumeDef(bind, [])
member this.name() = member this.name() =
@ -244,6 +256,8 @@ open System.Linq
override this.members(): AstObject list = objs override this.members(): AstObject list = objs
override this.address (): string = override this.address (): string =
bind.GetAttribute "address" bind.GetAttribute "address"
override this.filePath (): string =
bind.GetAttribute "file-path"
static member GenerateFromChildSibling(child: XmlNode): VolumeChildType list = static member GenerateFromChildSibling(child: XmlNode): VolumeChildType list =
match child with match child with
@ -270,13 +284,26 @@ open System.Linq
end 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) = type Program(bind: XmlElement, objs: AstObject list) =
class class
inherit AstObject() inherit AstObject(bind)
new(bind: XmlElement) = Program(bind, []) new(bind: XmlElement) = Program(bind, [])
member this.dirPath() =
bind.GetAttribute "dir_src"
member this.time() = member this.time() =
bind.GetAttribute "time" bind.GetAttribute "time"
member this.children() = objs member this.children() = objs
@ -285,6 +312,7 @@ open System.Linq
match it with match it with
|Story s -> s :> AstObject |Story s -> s :> AstObject
|Volume v -> v :> AstObject |Volume v -> v :> AstObject
|Rank r -> r:> AstObject
) )
Program(bind, objs @items) Program(bind, objs @items)
@ -297,6 +325,7 @@ open System.Linq
let d = match child.Name with let d = match child.Name with
| "volume" -> Some(Volume(VolumeDef.GenerateVolumeDef(child).Value)) | "volume" -> Some(Volume(VolumeDef.GenerateVolumeDef(child).Value))
| "story" -> Some(Story(StoryDef.GenerateStoryDef(child).Value)) | "story" -> Some(Story(StoryDef.GenerateStoryDef(child).Value))
| "rank" -> Some(Rank(RankDef(child :?> XmlElement)))
| _ -> None | _ -> None
match d with match d with
|Some value -> value::Program.GenerateFromChildSibling(child.NextSibling) |Some value -> value::Program.GenerateFromChildSibling(child.NextSibling)
@ -309,6 +338,7 @@ open System.Linq
let conv data = match data with let conv data = match data with
|Volume d -> d:>AstObject |Volume d -> d:>AstObject
|Story d -> d :> AstObject |Story d -> d :> AstObject
|Rank d -> d:> AstObject
Program(ast, mbrs |> List.map conv) Program(ast, mbrs |> List.map conv)
end 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) 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) = type UnitGenerate(root: AstImport.AstObject) =
class class
@ -296,6 +303,8 @@ open System.IO
result_nodes@[(refs_depth, ArticleDefine(defs, childs))] result_nodes@[(refs_depth, ArticleDefine(defs, childs))]
| :? AstImport.VolumeDef as defs -> | :? AstImport.VolumeDef as defs ->
result_nodes@[(refs_depth, VolumeDefine(defs, childs))] result_nodes@[(refs_depth, VolumeDefine(defs, childs))]
| :? AstImport.RankDef as defs ->
result_nodes@[refs_depth, RankDefine(defs)]
| :? AstImport.Program -> result_nodes | :? AstImport.Program -> result_nodes
| _ -> failwith "match error" | _ -> failwith "match error"
@ -942,11 +951,11 @@ open System.IO
// 线 // 线
let story_decl = story_lines|> List.map(fun story -> 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) """)|> List.reduce(fun a b -> a + b)
// //
let points_decl = fragment_defs |> List.map(fun (_, node_def) -> 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) """)|> List.reduce(fun a b -> a + b)
// //
let nodes_decl = story_decl + points_decl let nodes_decl = story_decl + points_decl
@ -959,7 +968,7 @@ open System.IO
let vhead = nexts.Head let vhead = nexts.Head
match vhead with match vhead with
| :? AstImport.FragmentSlice as slice -> | :? 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 slice nexts.Tail
| _ -> node_chains story_nm node_curr 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())) let arrows_decl = story_lines|> List.map(fun story -> node_chains (story.name()) story (story.children()))
@ -971,21 +980,25 @@ open System.IO
$"{a.Value.name()}&{b.name()}", b.address()) $"{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) let node_refs = defn.children() |> List.filter(fun v -> v:? AstImport.FragmentRef)
|> List.map(fun v -> v:?> AstImport.FragmentRef) |> List.map(fun v -> v:?> AstImport.FragmentRef)
node_refs|> List.map(fun nref -> node_refs|> List.map(fun nref ->
let node_name = $"""{nref.storyRef()}&{nref.sliceRef()}""" let node_name = $"""{nref.storyRef()}&{nref.sliceRef()}"""
let _, address_target = node_map|> List.filter(fun (a,_)-> a = node_name)|> List.item 0 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 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{{ $"""digraph node_relates{{
rankdir=LR rankdir=LR
@ -1078,8 +1091,10 @@ open System.IO
|> List.map(fun v -> v, (v.children()|> List.filter(fun v-> v:? AstImport.FragmentRef))) // 线 |> List.map(fun v -> v, (v.children()|> List.filter(fun v-> v:? AstImport.FragmentRef))) // 线
|> List.filter(fun (_, al)-> al.Length > 0) |> List.filter(fun (_, al)-> al.Length > 0)
let refers = rst|> List.map(fun (a, refsa) -> let refers_prim=
refsa|> List.map(fun refn -> if not rst.IsEmpty then
rst |> List.map(fun (a, refsa) ->
refsa |> List.map(fun refn ->
let refx = refn :?> AstImport.FragmentRef let refx = refn :?> AstImport.FragmentRef
let target_name = $"""{refx.storyRef()}&{refx.sliceRef()}""" let target_name = $"""{refx.storyRef()}&{refx.sliceRef()}"""
let _, target_address = node_map|> List.filter(fun (a, _) -> a = target_name)|> List.item 0 let _, target_address = node_map|> List.filter(fun (a, _) -> a = target_name)|> List.item 0
@ -1088,7 +1103,13 @@ open System.IO
) )
) )
|> List.reduce (fun a b -> a @ b) |> List.reduce (fun a b -> a @ b)
|> 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} }}""" $"""graph{{ label="{gname}" {clusters_desc} {refers} }}"""

View File

@ -2,47 +2,29 @@
open HtmlStruct.Content open HtmlStruct.Content
open System.Xml open System.Xml
open HtmlStruct.Assemble open HtmlStruct.Assemble
open HtmlStruct.Content
open HtmlStruct.Present open HtmlStruct.Present
open System.IO open System.IO
open System.Text
open System open System
open System.Diagnostics open System.Diagnostics
open FmtStruct.FmtEntry
let args = System.Environment.GetCommandLineArgs()|> Array.toList let html_generate (file_in:FileInfo) (dir_o:DirectoryInfo): unit =
let out_dir = Uri(dir_o.FullName)
if args.Length = 2 && args.Item(1) = "--help" then let doc = XmlDocument()
System.Console.WriteLine("AstConv.exe -file xast文件路径 -odir ") doc.Load(file_in.FullName)
exit 0
if args.Length <> 5 then let prog = Program.GenerateFrom(doc)
failwith "程序调用参数错误AstConv.exe -file xast文件路径 -odir " let entry = AstVisitEntry(prog)
let visitor = UnitGenerate(prog)
entry.visitWith(visitor) |> ignore
let file_in, dir_o = match args.Item(1), args.Item(3) with let volume_pages = volume_page_assemble(visitor.contents()) |> List.map<VolumePage, IDomUnit> (fun x->x)
| "-file","-odir" -> let story_pages = story_page_assemble(visitor.contents()) |> List.map<StoryPage, IDomUnit> (fun x->x)
if not(FileInfo(args.Item 2).Exists) then let point_pages = slice_page_assemble(volume_pages @ story_pages)
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 "
let out_dir = Uri(dir_o.FullName) volume_pages @ story_pages @ point_pages |> List.iter (fun it -> (it:?>PageAccess).setPageRoot(out_dir))
let makers = volume_pages @ story_pages @ point_pages
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 -> |> List.map(fun page_unit ->
match page_unit with match page_unit with
| :? SlicePage as point -> | :? SlicePage as point ->
@ -54,26 +36,76 @@ let makers = volume_pages @ story_pages @ point_pages
| _ -> failwith "" | _ -> failwith ""
) )
let index_page = IndexPage("汇总页面", volume_pages @ story_pages @ point_pages) let index_page = IndexPage("汇总页面", volume_pages @ story_pages @ point_pages)
index_page.setPageRoot(out_dir) index_page.setPageRoot(out_dir)
let content = (index_page :> PageText).getHtmlText() let content = (index_page :> PageText).getHtmlText()
let href = index_page.pageURL() let href = index_page.pageURL()
File.WriteAllLines(href, [content]) File.WriteAllLines(href, [content])
for refs in makers do for refs in makers do
let file_path = (refs.bindPage() :?> PageAccess).pageURL() let file_path = (refs.bindPage() :?> PageAccess).pageURL()
File.WriteAllLines(file_path, [refs.getHtmlText()]) File.WriteAllLines(file_path, [refs.getHtmlText()])
let graph = StorylineGraphMake("故事线网络", story_pages|> List.map(fun x -> x :?> PageAccess)) let graph = StorylineGraphMake("故事线网络", story_pages|> List.map(fun x -> x :?> PageAccess))
let graph_code = graph.getGraphCode() let graph_code = graph.getGraphCode()
let stream = new StreamWriter(Path.Combine(dir_o.FullName, "storys_display.dot"), false) let stream = new StreamWriter(Path.Combine(dir_o.FullName, "storys_display.dot"), false)
stream.Write(graph_code) stream.Write(graph_code)
stream.Flush() stream.Flush()
Process.Start("dot", $"""-Tsvg -o{Path.Combine(dir_o.FullName, "storys_display.svg")} {Path.Combine(dir_o.FullName, "storys_display.dot")}""") |> ignore 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 = VolumeGraphMake("卷章引用网络", (volume_pages@story_pages)|> List.map(fun d -> d :?> PageAccess))
let graph2_code = graph2.getGraphCode() let graph2_code = graph2.getGraphCode()
let stream2 = new StreamWriter(Path.Combine(dir_o.FullName, "volume_display.dot"), false) let stream2 = new StreamWriter(Path.Combine(dir_o.FullName, "volume_display.dot"), false)
stream2.Write(graph2_code) stream2.Write(graph2_code)
stream2.Flush() stream2.Flush()
Process.Start("dot", $"""-Tsvg -o{Path.Combine(dir_o.FullName, "volume_display.svg")} {Path.Combine(dir_o.FullName, "volume_display.dot")}""") |> ignore 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
//
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
//
| 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
// 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 xfile, xdir = FileInfo(args.Item 2), DirectoryInfo(args.Item 4)
// html
html_generate xfile xdir
exit 0
| _ ->
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": { "profiles": {
"AstConv": { "AstConv": {
"commandName": "Project", "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 EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "AstConv", "AstConv\AstConv.fsproj", "{0C77216C-6484-4C94-BE06-D5D9FF18EA81}" Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "AstConv", "AstConv\AstConv.fsproj", "{0C77216C-6484-4C94-BE06-D5D9FF18EA81}"
EndProject EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TestProject", "TestProject\TestProject.fsproj", "{08F9F7F7-CDB4-4644-92A3-99C41A77CD90}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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|x64.Build.0 = Release|Any CPU
{0C77216C-6484-4C94-BE06-D5D9FF18EA81}.Release|x86.ActiveCfg = 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 {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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

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

View File

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

View File

@ -3,7 +3,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerWorkingDirectory>$(SolutionDir)$(Platform)\$(Configuration)\</LocalDebuggerWorkingDirectory> <LocalDebuggerWorkingDirectory>$(SolutionDir)$(Platform)\$(Configuration)\</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor> <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>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerCommandArguments>--path "D:\Projects\Cpp\WsNovelParser\x64\test_file" --dest E:\</LocalDebuggerCommandArguments> <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 <QDir>
#include <QDomElement> #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("name", slice_node->name());
dom_slice.setAttribute("address", (qulonglong) slice_node.get()); 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; }break;
case NovelNode::TextSection: case NovelNode::TextSection:
{ {

View File

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

View File

@ -13,6 +13,7 @@ NovelParser::NovelParser() {
checker_list << std::make_shared<FragmentExistsCheck>(); checker_list << std::make_shared<FragmentExistsCheck>();
checker_list << std::make_shared<StoryOrderCheck>(); checker_list << std::make_shared<StoryOrderCheck>();
checker_list << std::make_shared<PointGraphCheck>(); checker_list << std::make_shared<PointGraphCheck>();
checker_list << std::make_shared<FragmentLayerCheck>();
analyzer_ref = std::make_shared<Analyzer>(checker_list); 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]; 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()) if (!target->element()->isAnonymous())
cache->appendToCache(target->element()); cache->appendToCache(target->element());
@ -53,8 +53,9 @@ void FragmentExistsCheck::exists_check(std::shared_ptr<ElementsCache> root, std:
} }
} }
example_novel::FragmentExistsCheck::FragmentExistsCheck() FragmentExistsCheck::FragmentExistsCheck()
:_nodes_cache(std::make_shared<ElementsCache>()) { } :_nodes_cache(std::make_shared<ElementsCache>()) {
}
void FragmentExistsCheck::validCheck(std::shared_ptr<const ElementAccess> root) const { void FragmentExistsCheck::validCheck(std::shared_ptr<const ElementAccess> root) const {
const_cast<FragmentExistsCheck*>(this)->nodes_regist(this->_nodes_cache, root); 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 { auto get_name = [](std::shared_ptr<const ElementAccess> node)->QString {
switch (node->element()->typeMark()) { switch (node->element()->typeMark()) {
case (int) NovelNode::FragmentSlice: { case (int) NovelNode::FragmentSlice:
{
auto def_node = std::dynamic_pointer_cast<const FragmentSlice>(node->element()); auto def_node = std::dynamic_pointer_cast<const FragmentSlice>(node->element());
return def_node->signature(); return def_node->signature();
}break; }break;
case (int) NovelNode::FragmentRefers: { case (int) NovelNode::FragmentRefers:
{
auto ref_node = std::dynamic_pointer_cast<const FragmentRefers>(node->element()); auto ref_node = std::dynamic_pointer_cast<const FragmentRefers>(node->element());
return ref_node->referSignature(); return ref_node->referSignature();
}break; }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_name = get_name(point);
auto first_helper = getElement(first_name); auto first_helper = getElement(first_name);
@ -272,10 +275,11 @@ void PointGraphCheck::validCheck(std::shared_ptr<const ElementAccess> root) cons
} }
QString PointGraphCheck::name() const { 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 { std::shared_ptr<const FragmentSlice> PointGraphHelper::nodePeer() const {
return this->node_peer; 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(); auto type_code = pnode->element()->typeMark();
switch ((NovelNode) type_code) { switch ((NovelNode) type_code) {
case NovelNode::GlobalElement: { case NovelNode::GlobalElement:
{
auto children = pnode->children(); auto children = pnode->children();
for (auto& cinst : children) { for (auto& cinst : children) {
values.append(valid_docs_peak(cinst)); values.append(valid_docs_peak(cinst));
} }
}break; }break;
case NovelNode::Document: { case NovelNode::Document:
{
auto elms_set = pnode->children(); auto elms_set = pnode->children();
decltype(elms_set) story_set; decltype(elms_set) story_set;
@ -371,3 +377,162 @@ void StoryOrderCheck::validCheck(std::shared_ptr<const ElementAccess> root) cons
for (auto& story : story_docs) for (auto& story : story_docs)
ranks_number = story_node_sort(story, ranks_number); 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; QString name() const override;
void validCheck(std::shared_ptr<const ast_gen::ElementAccess> root) 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 } // 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. 改进图示格式