Top ↑
Jul 9, 2017 11:52:32 PM
作者: zozoh

顶级目录结构

zDoc 假想你的工作目录由如下结构组成

[imgs]       # 存放你所有的图片文件,zDoc 会全部 copy 到目标目录
[js]         # 存放你所有的脚本文件,zDoc 会全部 copy 到目标目录
[css]        # 存放你所有的样式表单,zDoc 会全部 copy 到目标目录
[_tmpl]      # 这里存放模板,一个网站可以有多个模板
[_libs]      # 这里存放代码片段
zdoc.conf    # 你的网站项目的总体配置文件
index.xml    # 【选】你的文档索引的顺序
index.html   # 所有的 HTML,markdown,zDoc 文件都会一视同仁进行转换
readme.md     
aboutus.zdoc

_tmpl 目录

这个目录就是给 ZDocTemplateFactory 的实现类使用的模板目录。 具体如何获取模板,由实现类决定,下面我就用默认提供的 FreemarkerTemplateFactory 来作为例子:

[_tmpl]                # 目录内任何一个 .ftl 文件就是一个Freemarker模板
    normal.ftl         # 这个模板的名字为 "normal"
    [black]             
        myblank.ftl    # 这个模板的名字为 "black.myblank"
        

无论你是什么模板引擎,你都会得到一个用 org.nutz.lang.util.NutMap 封装的一个上下文:

{
    siteTitle : "雨打沙滩点点坑",   // 整个站点的标题
    topTags   : [{key:'34..a9',text:'翻译',count:87},{...}],
    othersTag : {key:'others',text:'未归类',count:194},
    tags      : [{key:'ae..f0',text:'小说',count:87},{...}],
    tagPath   : 'tags',   // 要将所有的 tag 索引页输出到哪个路径
    // 每个页面模板将额外得到这个属性
    // 如果对应 tag 页面,只有 title, 和 bpath 有效
    doc : {
        author   : [{name:'zozoh',email:'zozoh@me.com'},{...}],
        verifier : [{name:'zozoh',email:'zozoh@me.com'},{...}],
        title    : '文档的标题',
        tags     : ['标签A', '标签B', '标签C'],
        lm       : Date(2014-12-09 12:34:21),
        rpath    : 'post/2014/001.md',   // 相对于根的路径
        bpath    : '../..',              // 回到根的路径
        conent   : '<div>.....</div>',   // 渲染出来的 HTML
        ... 剩下的是你在 doc 声明的对应属性 ...
    }
    // 每个标签列表页将额外得到这个属性
    tag  : {
        key   : '45..f2',
        text  : '翻译',
        count : 53,
        items : {
            // @see ZDocIndex.toMap()
        }
    }
}

因此在任何模板引擎里,如果要输出标题,应该支持类似如下写法:

...
<title>${doc.title}</title>
...

_libs 目录

_tmpl 目录基本一样 ...

zdoc.conf

放置这个文件的的目录将作为 zdoc 的工作目录,这个文件 ...

阅读全文>>
Jul 9, 2017 11:52:32 PM
作者: zozoh

世间任何文档,都是相似的

如何描述一个文档

抽象的看,任何一个文档都可以下列结构来描述

文档级属性 {         # ZDocMetas
    标题     
    作者
    子标题
    创建日期
    指定样式表
    …
}

标题                # ZDocNode.depth=0

    … 一块内容 …     # ZDocNode.depth=-1
    
    标题 A
        
        … 一块内容 …
        … 一块内容 …
        
  • 无论对于 HTML, markdown 我们都可以将其文档理解成这样的结构
  • 当然,我们可以用 zDoc 推荐的书写方式更加直白的写出这样的结构。

我们可以看出,所谓的 zDoc 文档结构,就是一个 ZDocNode 组成的文档结构树。所有的叶子节点 是段落内容,所有的中间节点则作为标题

关于 ZDocNode

任何一个节点, ...

阅读全文>>
Jul 9, 2017 11:52:32 PM
作者: zozoh

对于每个段落的解析,依靠自动机。本文为自动机的实现者提供一些参考

自动机堆栈

头 .................... 尾
[C][C][C]      # 字符缓冲栈
[T][T][T]      # 数据栈
[@][@]         # 操作栈
 1 ']'         # 指示堆栈,并联自动机当做退出字符,串联当做子机下标

四个堆栈的操作原则是:“谁压入谁弹出”

堆栈通过 {..} 来表示,可以构成级联

{..}
    {..}
        {..}
        {..}
    {..}

每个自动机都有方法
enter(AmStack, char):AmStatus  # 试图进入这个自动机,如果成功将设置堆栈
eat(AmStack, char):AmStatus    # 消费字符
done(AmStack):void             # 将字符缓冲的内容填充到数据栈

任何一个自动机每次被执行都会返回如下四个行为之一
DROP       # 丢弃当前堆栈
CONTINUE   # 继续,读取下一个字符,执行栈顶自动机的 run 方法
DONE       # 将弹出操作栈顶自动机,并执行它的 done 方法
DONE_BACK  # 执行 DONE 操作,并重新进入下一个可能的自动机

一个堆栈的数据结构

buffer  # 字符缓冲
objs    # 数据栈
ams     # 操作栈
qcs     # 退出字符 
sis     # 指示堆栈

i_obj
i_am
i_qc
i_si

candidates  # 候选堆栈

它应该支持的操作

enter(Am, char): bool       # 是否可以让这个自动机进入堆栈
eat(char)      : AmStatus   # 消费字符,返回 false 表示不能消费了
done           : void       # 调用栈顶自动机的 done
close          : T          # 将对象堆栈清空

pushAm(Am)
popAm   : Am
peekAm  : Am
pushObj(T)
popObj  : T
peekObj : T
pushQc(char)
popQc   : char
qc      : char
pushSi(int)
popSi   : int
si      : int


串联自动机

串联自动机有一个固定的进入字符

假设一个堆栈状态为:
[]       # 字符缓冲是干净的
[] ...   # 数据栈
[] ...   # 操作栈
   ...   # 指示堆栈
   
enter ...
#------------------------------------------------
如果符合进入字符

    []          # 字符缓冲内容由第一个子机决定
    [T] ...     # 准备一个对象
    [&] ...     # 压入自己
    -1  ...     # 指示字符为自己第一个子机

eat ...
#------------------------------------------------
如果发现当前子机下标小于 0,则表示子机未被执行 enter ,那么就
将下标变成正数,并调用对应子机的 enter 
(注意,这里的下标是 1 base,需要转换成 0 base 使用)

    []          #
    [T] ...     # 还是那个对象
    [&] ...     # 串联自动机
     1  ...     # 下标变成正数,并调用子机的 enter
         
如果当前子机 enter 未遂,或者 eat 返回 DROP 了,那么自己也返回 DROP
如果返回的状态是 DONE 或者 DONE_BACK, 
会调用当前子机的 done,并试图切换到下一个子机
    
    []          #
    [T] ...     # 还是那个对象
    [&] ...     # 串联自动机
    -2  ...     # 下标指向下一个子机,并调用子机的 enter

如果没有下一个子机了,则返回 DONE | DONE_BACK


done ...
#------------------------------------------------
如果为 done 时,堆栈应该为     

    [C][C]...   # 缓冲可能为空也可能有字符
    [T] ...     # 还是那个对象
    [&] ...     # 操作栈顶应该是最后一个子
     2  ...     # 下标指向最后一个子机

如果没有达到最后一个子机,那么调用当前子机的 done
然后将将自己退栈

    []
    [] ...   # 将 T 组合到之前的对象中
    [] ...   # 清除自己
       ...   # 清除了指示下标
     

并联自动机

如果发现有超过一个自动机都进入了堆栈,并联自动机会依次为其构建堆栈

假设一个堆栈状态为:
[]       # 字符缓冲是干净的
[] ...   # 数据栈
[] ...   # 操作栈
   ...   # 指示堆栈
enter ...
#------------------------------------------------
构建新堆栈:
[]
[]       # 不要准备对象
[@]      # 表示有子自动机进入了
']'      # 自己的退出字符

如果超过一个自动机进入了,那么将堆栈变成

    {候选堆栈A}         {候选堆栈B} -?- {母堆栈}
    {候选堆栈C} /
    ... 
那么就会将自身压入母堆栈,同时也要在母堆栈标识退出字符
[]
[] ...
[+]...  # 仅仅在当前堆栈压入自身
']'...  # 压入自己的退出字符
eat ...
#------------------------------------------------
如果没有候选堆栈,则本自动机将执行选择一个候选堆栈
如果有候选堆栈,那么它的状态应该为 :
[]
[] ...
[+]...
']'...
    [?]      # 子机设置
    [?]      # 子机设置
    [@]      # 某个子机
    ']'      # 自己的退出字符
每次 eat 如果某个候选堆栈 DROP , 就移除掉它
保证每次都给所有的候选堆栈消费字符,如果某个堆栈 DONE 了,
则将这个堆栈关闭后得到对象组合到母堆栈中。


done ...
#------------------------------------------------
如果没有候选堆栈,那么就什么也不做
如果有多个候选堆栈,并联自动机会首先调用候选A的栈顶自动机的 done

    {候选堆栈A}.close() => T
    {self}.mergeHead(T)
    {self}.pushQc({候选堆栈A}.popQc())
    
并将其压入自己所在堆栈,否则,就会调用自己堆栈栈顶自动机的 done,
让自己的的堆栈状态为:
[]          # 字符缓冲
[] ...      # 这个是自己的对象
[+] ...     # 头部就只有自己
']' ...     # 自己的退出字符在顶部

执行堆栈的真正弹出
[]
[] ...   # 将 T 组合到之前的对象中
[] ...   # 清除了自动机
   ...   # 清除了退出字符
阅读全文>>
Jul 9, 2017 11:52:32 PM
作者: zozoh

一个 zDoc 文档集合可以被输出成为任何介质

渲染整个文档集合

在 zDoc 的观点里,一次仅仅渲染一个文档是不够的。通常你会写很多文档, 然后放到一个文件夹下面,我们在 里声明了一个 zDoc工作目录的约定。 你解析出来这个目录的结构大约参见如下的对象结构

public class ZDocHome {

    protected ZDir src;
    protected ZCache<ZDocHtmlCacheItem> libs;
    protected ZCache<ZDocHtmlCacheItem> tmpl;
    protected List<ZDir> rss;
    protected Context vars;
    protected List<ZDocRule> rules;
    protected ZDocIndex index;
    ...

你的渲染将面对整个 ZDocHome

渲染时变量

大小写敏感
Name 说明
doc.content 文档被渲染过后的内容
doc.k ...

阅读全文>>