#nop vim: set filetype=tt:; /* 本文件属于 PaoTin++ 的一部分。 PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 */ #nop ############################ 系统工具-帮助文档和参数类型检查 #################################; #alias {xtt.PrintHelpDoc} { #local aliasList {%1}; #local funcList {%2}; #local what {%3}; #echo {%c%h} {light cyan}; #if { "$what" == "" } { #echo {%c%s} {light yellow} {别名:}; #foreach {$aliasList} {alias} { #if { "$alias" == "" } { #continue; }; #var __XTinTinAPIDoc__ {}; $alias 'XTinTinAPI; #if { "$__XTinTinAPIDoc__" == "" } { #echo { %c%-20s --- %s} {light cyan} {$alias} {(没有文档)}; }; #else { #echo { %c%-20s --- %s} {light cyan} {$alias} {$__XTinTinAPIDoc__[desc]}; }; }; #echo {%c%s} {light yellow} {函数:}; #local func {}; #foreach {$funcList} {func} { #if { "$func" == "" } { #continue; }; #var __XTinTinAPIDoc__ {}; #local {funcCall} {#local _ {@$func{'XTinTinAPI}}}; $funcCall; #if { "$__XTinTinAPIDoc__" == "" } { #echo { %c%-20s --- %s} {light cyan} {$func} {(没有文档)}; }; #else { #echo { %c%-20s --- %s} {light cyan} {$func} {$__XTinTinAPIDoc__[desc]}; }; }; }; #elseif { @slist.IndexOf{{$aliasList}; $what} > 0 } { #var __XTinTinAPIDoc__ {}; $what 'XTinTinAPI; #if { "$__XTinTinAPIDoc__" == "" } { #echo {%c%%s} {light cyan} {没有关于 $alias 的文档。}; }; #else { #local _ @xtt.GenHelpDoc{{$__XTinTinAPIDoc__}}; }; }; #elseif { @slist.IndexOf{{$funcList}; $what} > 0 } { #var __XTinTinAPIDoc__ {}; #local {funcCall} {#local _ {@$what{'XTinTinAPI}}}; $funcCall; #if { "$__XTinTinAPIDoc__" == "" } { #echo { %c%-20s --- %s} {light cyan} {$func} {(没有文档)}; }; #else { #local _ @xtt.GenHelpDoc{{$__XTinTinAPIDoc__}}; }; }; #echo {%c%h} {light cyan}; }; #func {xtt.GenHelpDoc} { #local args {%1}; #if { "$args[check]" == "'XTinTinAPI" } { #return { {name}{$args[name]} {type}{$args[type]} {desc}{$args[desc]} {args}{$args[args]} {check}{} }; }; #if { "$args[type]" == "alias" } { #echo {%c别名用法: %c%s 参数1 参数2...} {light cyan} {light yellow} {$args[name]}; }; #elseif { "$args[type]" == "function" } { #echo {%c函数用法: %c@%s{ {参数1}{值1} {参数2}{值2}... }} {light cyan} {light yellow} {$args[name]}; }; #else { #echo {%c不明类型的标识符: %c%s} {light red} {light yellow} {$args[name]}; #echo {%c%h} {light cyan}; #return; }; #echo {%c说明:%s} {light cyan} {$args[desc]}; #echo {%c参数列表(!必选,?可选):} {light cyan}; #foreach *args[args][] {idx} { #local key {}; #local desc {}; #nop 注意别名和函数收集参数名称的方法不同; #if { "$args[type]" == "alias" } { #list tmp create $args[args][$idx]; #format key {%s} {$tmp[1]}; #format desc {%s} {$tmp[2]}; }; #else { #format key {%s} {$idx}; #format desc {%s} $args[args][$idx]; }; #echo {%c %-16s -- %s} {light cyan} {$key} {$desc}; }; }; /* 用法:将下面这段代码作为模版,插入到想要生成文档的别名或者函数开头,就可以了。 #local ok @xtt.HelpDoc{{ {type}{function} {name}{helpDoc} {desc}{为函数或别名提供代码注释,生成帮助文档,进行参数检查} {check}{%0} {args}{ {!name}{函数或别名的名称} {!desc}{函数或别名的一句话说明} {!check}{需要检查的变量,别名用 %0,函数建议用 %1} {!args}{函数或别名的参数说明。} } }} #if { "$ok" != "true" } { #return "$ok" } 以上,截止到此处为止。 你可能已经注意到了,上面的代码调用了 xtt.HelpDoc 函数来检查参数并生成文档。 而 xtt.HelpDoc 本身就支持用以上语法来检查并生成文档。因此上面的代码本身也演示了如何调用函数。 注意第二个 args 有两种写法,如果是函数就用键值对,如果是别名就用列表(前面加{1}{2}{3})。 其中参数名用感叹号打头表示必选,问号打头表示可选,默认必选。 */ #func {xtt.HelpDoc} { #local args {%1}; #if { "$args[check]" == "'XTinTinAPI" } { #return @XTinTinGenHelpDoc{{$args}}; }; #if { "$XTinTin[debug]" != "true" } { #return {true}; }; #local ok {true}; #local realArgs {}; #list realArgs create {$args[check]}; #foreach {*args[args][]} {idx} { #nop 这个 key 必须要用 var,因为后面 regex 里面不支持 local; #var key {}; #nop 注意别名和函数收集参数名称的方法不同; #if { "$args[type]" == "alias" } { #list tmp create $args[args][$idx]; #format key {%s} {$tmp[1]}; }; #else { #format key {%s} {$idx}; }; #var optional {false}; #regex {$key} {{!|\?}%%2} { #format key {%s} {&2}; #if { "&1" == "?" } { #var optional {true}; }; }{}; #nop 注意别名和函数收集参数值的方法不同; #local value {}; #if { "$args[type]" == "alias" } { #format value {%s} {$realArgs[$idx]}; }; #else { #format value {%s} {$check[$key]}; }; #if { "$optional" == "false" && "$value" == "" } { #echo {%c函数或别名在调用时缺少必要的参数: %s} {light red} $key; #format ok {false}; }; }; #unvar key; #if { "$ok" != "true" } { #echo {%c%h} {light cyan}; #var _ @XTinTinGenHelpDoc{{$args}}; #echo {%c-----\n%s} {light cyan} {实际传递的参数信息: \n$args[check]}; #echo {%c%h} {light cyan}; }; #return $ok; }; #var xtt.module-doc {}; #var xtt.module-test {}; #alias {HELP} {xtt.Help %0; #buffer end}; #alias {xtt.Help} { #local keyword {%1}; #local len {@str.Len{$keyword}}; #if { $len == 0 } { warnLog 用法: HELP <关键字>; #return; }; #local maybe {}; #local module {}; #foreach {*xtt.module-doc[]} {module} { #if { "$module" == "%*$keyword%*" } { #local likely {@math.Eval{$len * 100 / @str.Len{$module}}}; #list maybe {add} {{ {likely} {$likely} {module} {$module} {keyword}{__MODULE__} }}; #continue; }; #local key {}; #foreach {*xtt.module-doc[$module][]} {key} { #if { "$key" == "__MODULE__" } { #continue; }; #if { "$key" == "%*$keyword%*" } { #local likely {@math.Eval{$len * 100 / @str.Len{$key}}}; #list maybe {add} {{ {likely} {$likely} {module} {$module} {keyword}{$key} }}; }; }; }; #local doc {}; #if { &maybe[] == 0 } { errLog 没有找到关键字「$keyword」; #return; }; #local doc {$maybe[1]}; #if { &maybe[] > 1 } { #list maybe {indexate} {likely}; #list maybe order; #local doc {$maybe[-1]}; #if { "$doc[module]/$doc[keyword]" == "%*/$keyword/" } { okLog 以下是模块 $doc[module] 的文档。; }; #elseif { $doc[likely] < 100 } { warnLog 找到 &maybe[] 个关键字,我猜你想看的是 <119>$doc[keyword]<129>@$doc[module]<299>; }; #else { okLog 以下是关键字 $doc[keyword] 的文档。; }; }; #local module {$doc[module]}; #local keyword {$doc[keyword]}; #local idx {}; #loop 1 {&xtt.module-doc[$module][$keyword][]} {idx} { #local line {$xtt.module-doc[$module][$keyword][$idx]}; #regex {$line} {{##|#@|#=} %S %*} { #if { "&1" == "##" } { #echo {<299>%s} {<128> ## 别名 <138>&2 <868>&3<299>}; }; #elseif { "&1" == "#@" } { #echo {<299>%s} {<128> ## 函数 <138>&2 <868>&3<299>}; }; #elseif { "&1" == "#=" } { #echo {<299>%s} {<128> ## 文档 <138>&2 <868>&3<299>}; }; } { #echo {<299>%s} {$line}; }; }; #if { &xtt.module-test[$module][$keyword][] > 0 } { #echo { 用法举例:\n}; #local case {}; #foreach {*xtt.module-test[$module][$keyword][]} {case} { #local {test} {test/$module/$keyword/case/$case}; #if { @xtt.genTestCase{$test;$module;$keyword;$case} } { #alias $test; #unalias $test; }; }; }; #if { &maybe[] > 1 } { warnLog 也许你还想看看:; #local idx {}; #loop {&maybe[]-1} {1} {idx} { #if { "$maybe[$idx][keyword]" == "__MODULE__" } { #echo {%s} {@mslp.Exec{xtt.Help $maybe[$idx][module];<129>$maybe[$idx][module] 模块<299>}}; }; #else { #echo {%s} {@mslp.Exec{xtt.Help $maybe[$idx][keyword];<129>$maybe[$idx][module] 模块的 <119>$maybe[$idx][keyword]<299>}}; }; }; }; }; #alias {///===} {xtt.Doc}; #alias {xtt.Doc} { #local doc {%1}; #replace {doc} {;} {\x3b}; #replace {doc} {\x7B} {\x7b}; #replace {doc} {\x7D} {\x7d}; #replace {doc} {///// } {;== }; #replace {doc} {/////;} {;== ;}; #replace {doc} {// } {;= }; #replace {doc} {//;} {;= ;}; #class xtt.doc.parse open; #var xtt-doc-keyword {}; #alias {xtt.doc.add.module-doc} { #local doc {%%1}; #regex {$doc} {== %*} { #list {xtt.module-doc[$MODULE][__MODULE__]} add {<269>&1<299>}; }; }; #alias {xtt.doc.add.keyword-doc} { #local doc {%%1}; #regex {$doc} {={([ ]{3,})- (\d\.\s*)?(\S+?)(: |:)| }%*} { #local text {}; #if { "&1" != " " } { #local text {<299>&2- <168>&3&4<278>&5&6}; }; #else { #local text {<299> &6}; }; #list {xtt.module-doc[$MODULE][__MODULE__]} add {$text}; #if { "$xtt-doc-keyword" != "" } { #list {xtt.module-doc[$MODULE][$xtt-doc-keyword]} add {$text}; }; }; }; #alias {xtt.doc.found.keyword} { #local doc {%%1}; #if { !@isEmpty{$xtt-doc-keyword} } { #LOCAL idx {}; #loop {&xtt.module-doc[$MODULE][$xtt-doc-keyword][]} {1} {idx} { #if @isEmpty{$xtt.module-doc[$MODULE][$xtt-doc-keyword][$idx]} { #list {xtt.module-doc[$MODULE][$xtt-doc-keyword]} delete {$idx}; }; }; }; #regex {$doc} {= {##|#@|#=} %S %*} { #var xtt-doc-keyword {&2}; xtt.doc.add.keyword-doc {= &1 &2 &3}; }; }; #alias {xtt.doc.new.example} { #list xtt.module-test[$MODULE][$xtt-doc-keyword] add {{}}; }; #alias {xtt.doc.add.example} { #local part {%%1}; #local line {%%2}; #replace line {^=%s} {}; #replace line {%+1..s$} {}; #local idx {&xtt.module-test[$MODULE][$xtt-doc-keyword][]}; #if { {$line} != {} } { #list xtt.module-test[$MODULE][$xtt-doc-keyword][$idx][$part] add {{$line}}; }; }; #class xtt.doc.parse close; #local state {doc}; #local line {}; #foreach {$doc} {line} { #switch {{$state/$xtt-doc-keyword/$line}} { #case {{%*/%*/= #@ %S %*}} { #local state {doc}; xtt.doc.found.keyword {$line}; }; #case {{%*/%*/= ## %S %*}} { #local state {doc}; xtt.doc.found.keyword {$line}; }; #case {{%*/%*/= #= %S %*}} { #local state {doc}; xtt.doc.found.keyword {$line}; }; #case {{%*/%*/= %sEXAMPLE:%*}} { #local state {test}; #replace line {%sEXAMPLE:} {}; xtt.doc.new.example; xtt.doc.add.example CODE {$line}; }; #case {{test/%*/= %sRESULT:%*}} { #local state {result}; #replace line {%sRESULT:} {}; xtt.doc.add.example RESULT {$line}; }; #case {{test/%*/= %sASSERT:%*}} { #local state {assert}; #replace line {%sASSERT:} {}; xtt.doc.add.example ASSERT {$line}; }; #case {{test/%*/= %sOUTPUT:%*}} { #local state {output}; #replace line {%sOUTPUT:} {}; xtt.doc.add.example OUTPUT {$line}; }; #case {{doc//}} {#0}; #case {{doc/%*/= %*}} {xtt.doc.add.keyword-doc {$line}}; #case {{test/%*/= %*}} {xtt.doc.add.example CODE {$line}}; #case {{result/%*/= %*}} {xtt.doc.add.example RESULT {$line}}; #case {{assert/%*/= %*}} {xtt.doc.add.example ASSERT {$line}}; #case {{output/%*/= %*}} {xtt.doc.add.example OUTPUT {$line}}; #default {xtt.doc.add.module-doc {$line}; #var xtt-doc-keyword {}}; }; }; #class xtt.doc.parse kill; }; #alias {OK} { #local module {%1}; #local keyword {%2}; #local case {%4}; #local msg {$module $keyword test case $case}; #echo {%s} {$msg @str.Repeat{@math.Eval{70 - @str.Width{$msg}};.} <129>OK<299>}; }; #alias {BAD} { #local module {%1}; #local keyword {%2}; #local case {%4}; #local msg {$module $keyword test case $case}; #echo {%s} {$msg @str.Repeat{@math.Eval{70 - @str.Width{$msg}};.} <119>BAD<299>}; }; #func {xtt.genTestCase} { #local name {%1}; #local module {%2}; #local keyword {%3}; #local case {@defaultNum{%4;1}}; #unalias {$name}; #if { &xtt.module-test[$module][$keyword][] <= 0 } { #return 0; }; #local call {@slist.Join{{@slist.FromList{$xtt.module-test[$module][$keyword][$case][CODE]}}}}; #replace call {^%*\\x3b$} {&1}; #local rslt {$xtt.module-test[$module][$keyword][$case][RESULT][1]}; #local assert {$xtt.module-test[$module][$keyword][$case][ASSERT][1]}; #local output {$xtt.module-test[$module][$keyword][$case][OUTPUT]}; #if { {$output} !== {} } { #local width {@math.Eval{&output[] / 10 + 1}}; #local lines {@slist.JoinAs{{@slist.FromList{@fp.Map{{$output};{第\@str.AlignRight{KEY;$width}行: VALUE}}}};{#nop %s\x3b}}}; #line sub {var;escapes} #alias {$name} {$call; #nop 预期得到多行文本:;$lines}; #unalias {$name/wrapper}; #line sub {var;escapes} #alias {$name/wrapper} { #local got {}; #line quiet #line capture got $name; #local got {@str.Plain{$got}}; xtt.Test.Assert {$module} {$keyword} {$case} 预期得到 {$output} 实际得到 {\$got}; }; #return 2; }; #if { {$assert} !== {} } { #local expect {}; #local got {}; #regex {$assert} {%* === %*} { #format got {%s} {&1}; #format expect {%s} {&2}; }; #line sub {var;escapes} #alias {$name} { $call; xtt.Test.Assert {$module} {$keyword} {$case} 预期得到 $expect 实际得到 $got; }; #return 1; }; #if { {$rslt} !== {} } { #line sub {var;escapes} #alias {$name} { #local result { $call }; xtt.Test.Assert {$module} {$keyword} {$case} 预期得到 $rslt 实际得到 {\$result}; }; #return 1; }; #if { {$call} !== {} } { #line sub {var;escapes} #alias {$name} { $call }; #return 3; }; }; #alias {xtt.Test.Assert} { #local module {%1}; #local keyword {%2}; #local case {%3}; #local expect {%5}; #local got {%7}; #if { {$got} === {$expect} } { OK $module $keyword case $case; }; #else { BAD $module $keyword case $case; errLog 断言失败:预期得到 {$expect} 实际得到 {$got}; }; }; ///=== { // ## xtt.Test [<模块名称> [<关键字名称>]] // 测试指定模块中的指定关键字所对应的全部测试用例。 // 如果省略关键字名称,则测试该模块中所有的关键字。 // 如果省略模块名称,则测试所有的模块。 // 你也可以通过 TEST 别名来使用本别名。 // }; #alias {TEST} {xtt.Test}; #alias {xtt.Test} { #local module {%1}; #local keyword {%2}; #if { "$module" == "" } { #foreach {*xtt.module-test[]} {module} { xtt.Test {$module}; }; #return; }; #if { "$keyword" == "" } { #foreach {*xtt.module-test[$module][]} {keyword} { xtt.Test {$module} {$keyword}; }; #return; }; #local case {}; #foreach {*xtt.module-test[$module][$keyword][]} {case} { #local {test} {test/$module/$keyword/case/$case}; #local code {@xtt.genTestCase{$test;$module;$keyword;$case}}; #switch {$code} { #case {1} {$test; #unalias $test}; #case {2} {$test/wrapper; #unalias $test; #unalias $test/wrapper}; }; }; }; #alias {test/{.*}/{[^/]*}/case/%d} {xtt.Test %1 %2} {9.000}; ///=== { ///// xtintin 是一个 TinTin++ 功能扩展集合,主要包含一些 MUD 无关的函数和别名,用于对 TinTin++ 的语法进行增强。 ///// 类似于其它语言的标准库。 ///// ///// 本文档采用 xtintin 的文档化注释系统生成。 ///// 有关 xtintin 的文档化注释,请查看 HELP xtt.Doc。 ///// // #= DataType // 打印 TinTin++ 和 xtintin 的<139>数据类型<299>说明文档。 // // TinTin++ 中存在几种不同的数据类型,xtintin 基于这些数据类型扩展了一些函数, // 以增强对它们的处理能力。下面简单予以介绍: // // - 字符串: 简单变量,可以包含拉丁字母、阿拉伯数字、标点符号、UTF-8 中文、表情符号。 // 理论上,所有其它类型的变量都有字符串表达形式,也都可以看作是字符串来操作。 // // 字符串支持变量内插,也就是说字符串当中出现的变量和复合变量表达式都会被替换 // 成相应的值,而函数调用则会被替换成调用结果。参见 #help SUBSTITUTIONS // // - 数值: TinTin++ 中并不严格区分字符串和数值,凡是内容长得像是数字一样的字符串,都 // 可以直接当作数值来使用,主要是进行算术运算。大部分场合下数字都像字符串一样。 // 只有少数几个场合才会被当成数值,请参考 #help math。 // // - 布尔值: TinTin++ 中的 bool 值可以用来进行布尔逻辑运算,或者作为条件判断的条件。 // 1 表示真,0 表示假。为了提高可读性,PaoTin++ 允许用 true 来表示真,用 false // 来表示假。 // // - 正则表达式: 正则表达式是具有特殊含义的字符串。仅在正则表达式上下文中生效。 // TinTin++ 中有两类正则表达式,一类是 TinTin++ 自身的语法,参见 #help regex, // 还有一类是 pcre 语法,参见 #help pcre。有关 pcre 的更多信息和高级用法,可以 // 参考 man pcre 和 man pcresyntax。 // // - 字符串列表: 字符串列表是由多个字符串构成的一个列表,多个字符串之间以分号隔开。 // 例如 {a;b;c;d}。如前所述,字符串列表本身也可以看作是一个字符串。但由于其 // 特殊的结构,可以视作是多个字符串构成的序列。 // // TinTin++ 原生对字符串列表的支持不多,仅可用来遍历,或者是作为 #list 命令的 // 参数。xtintin 对它进行了扩充,并基于字符串列表实现了集合运算。 // // 还有另一种字符串列表,那就是用花括号代替分号分隔的字符串序列,例如 // {a}{b}{c}{d},也被称为花括号列表,由于花括号列表在使用时存在一些限制, // 并且大部分情况下可以用分号分隔的字符串列表代替,所以不建议大家使用。 // 即使如此,在许多要求使用字符串列表的场合,仍可接受大括号列表。本文档中将 // 不再特别说明,请自行体会。 // // - 表格: 表格是指由多组键值对构成的一种高级结构。类似与其它编程语言的 map(C++)、 // hash(Perl)、dict(Python)、table(Lua)、Object(JSON)。例如: // // {a}{AAA}{b}{BBB}{c}{CCC} // // 习惯上一般把 a/b/c 称为 KEY(键),把 AAA/BBB/CCC 称为 VALUE(值)。键和值 // 都可以是任意字符串。但习惯上 KEY 一般是一个简单字符串,而 VALUE 则可以 // 包含复杂的数据结构(别忘了 TinTin++ 里任何数据结构都可以当作是字符串), // 因此表格实际上是可以嵌套的。TinTin++ 为嵌套的表格提供了专门的语法,可以 // 方便地访问其中的元素。 // // 请注意表格中的键值对通常被视作是没有顺序的。 // // - 列表: 列表实际上是一种特殊的表格,也就是说,这个表格的所有键都是数值, // 并且是按顺序排列的、从 1 开始的。参见 #help list。 // // 尽管如此,但列表在内存中的结构却和看上去并不相同。内存中的列表可以用 // #list 命令高效操作,花括号在内存中实际上并不存在,只是在代码中和输出 // 表达中为了区分键和值的边界而存在。 // // 和表格一样,列表的值可以存储任意字符串,从而达到嵌套的目的。 // // - 表格列表: 表格列表不是一种新的数据结构,只是说如果一个列表的所有值都是 // 同一种格式的表格,那么这个数据结构就可以被称为表格列表。TinTin++ 和 // xtintin 都为表格列表提供了更多的支持。参见 #help list。 // // - 嵌套数据结构: 上面已经提到,所有的数据结构都可以被当作是字符串。而字符串 // 又是 TinTin++ 中最基本的数据结构。所以这就为嵌套提供了语法基础。 // 嵌套的数据结构有许多用处,最常见的比如表格列表。此外还可以模拟面向对象 // 编程,或者是抽象数据类型。请自行体会。 // // 你可以通过 #help lists 来了解详细说明。或者是参考小乖乖的中文手册: // https://github.com/zixijian/tt/blob/master/Wiki.md#lists // ///// 下面是 xtintin 的全部内容: // }; #alias {xtt.Usage} { #local name {%1}; #local message {@default{%2;用法不对}}; errLog $message。正确用法如下:; xtt.Help $name; };