高级篇

走到这里,词法分析基本上在你的正则表达式的应用应该非常的熟练了。走到这里应该学习词法分析。并且能够一些AST以及巴斯特范式等等。 当然这也不是一个新话题了,所以已经大的成熟稳定的工具与库可以用了。

以及各种格式的之间的相互转换,主要是通过AST与进行翻译。并且大部分的标准语法树的转换都已经有现成的实现,能够看懂拿来用

其实转换起来很简单,只要语法树了建立了,也就是一个

for node in nodelist:
    switch node_type:
         doing something.

巴斯特范式

从SGML 到HTML再到 XML,再到 YAML以及后面的Markdown语法

troff -> tex -> 可计算文档

digraph  flow {

"make tree" -> "parse the Tree" -> "filter";

}

格式转换

一般情况一下,类似格式的转换相对比较容易,可以直接用词对词的转换,并且两都是以行为主要逻辑单位就比较容易。

例如 twiki 到 rst 的转换。

---++++ Header3

Header3
=======

可以用

:g/---+++ /s/---+++ // |t. |s/.*/=repeat("=",len(submatch(0))). "r"/

对于列表的转换最简单 把 ` *` 替换成 #. 直接

%s/ * /#. /

对于复杂一点那就是 link 的格式的 替换

[[baidu][wwww.baidu.com]]`baidu <wwww.baidu.com>`_

可以这一条搞定

%s/\[\[\(.\{-}\)]\[\(.\{-}\)]]/`\2 <\1>`_/g

对于 dot graphviz 插件就有点复杂了

digraph G {
   a->b;
}

来转换成 rst 格式。要就三条语句了

g/<dot>/,/<\/dot>/s/.*/   &/
g/<dot>/,/<\/dot>/s/.*<dot>/.. graphviz::\r
%s/<\/dot>/\r/g

这里是整个 vim脚本 直接 :so twikifilter.vim 就可了。

.. include:: /Stage_3/twikifiler.vim

但是对复杂的非行为单位,以及table等等简单的替换已经不行了。 这样的直接搞不定了, 就是需要更一点点的语法分析了来搞定了。

sphinx

文档的编写,是一个基础工作,从最初用的txt->sgml->docbook->markdown->restructureText,到后来的word,再到后来的wiki再到markdown,由于markdown的语法太简单,不能支持复杂的,并且通用性不强,见

格式要示。也就出现这三大工具:

  1. Creole 把各种wiki通用的语法总结成一个致语法,并且形成标准并制作相应的工具。
  2. SGML and XML docbook的语法太啰嗦。
  3. asciidoc a2x 相应的一堆转换工具类似rst.
  4. sphinx-doc 的rst.
_images/docutils_framework.png
def publish(self,argv=None,usage=None,description=None,
            settings_spec=None,settings_overrides=None,
            config_section=None,enable_exit_status=False):
    ...
    self.document = self.reader.read(self.source,self.parser,self.settings)
    self.apply_transforms()
    output = self.writer.write(self.document,self.destination)

publisher 的详细说明见 http://docutils.sourceforge.net/docs/api/publisher.html

parser 的原理

采用可以重入的状态机机制,其实也就是递归模式来进行解析,每一个node 都有startag,endtag.

  1. 从一个文档的开始定义一个document root.
  2. 从上到下进行逐行解析,同一级的每一个节点都是root的子节点顺序排列。
  3. 遇到子节点,进行递归进入当前节点的子节点。
root,
root.children=[.....]
     chidren1
        chidren[....]
     chidren2
     ....

最后template rendering时,也是流式不断写文件是一样用content,或者body不断append内容。

template

sphinx 的模板用的是jinjia,类似于perl template toolkit,主要上下左右几部分,每一部分二次二次的代理,

例如sphinx 对整个分成了五部分

  1. doctype
  2. linktags
  3. extrahead 如果想独立的html可以把style 放在这里。
  4. rootrellink
  5. document 对于 body同时还一个page.html的模板。定义一下就个树了。其实也很简单。

例如就是封面,章,节等等。

{% extends "!layout.html" %}
{% set reldelim1 = '&gt;' %}
{% block footer %} {% endblock %}

sphinx 能够实现的只用一个template engine都很容易实现。只要稍微把模板灵活一些,其实就搞定了。

各种template engine的对比 见

一个template engine应该具有的功能

  1. Variables
  2. Functions
  3. includes
  4. Condition inclusion
  5. Looping
  6. Evaluation
  7. Assignment
  8. Errors and exceptions
  9. il8n
  10. Natural templates
  11. Inheritance
class doc:
      foreword = ""
      chapters =[]
      end=""
class chapter:
      sections = []
      titile = ""

foreach print anyformat

现在sphinx 已经把文档结构给解析好了。只需要实现reader 就行了。

而sphinx采用的是node 的机制。 http://sphinx-doc.org/latest/extdev/nodes.html 采用vistor 模式。这样也就是每节点类型,都会有一个自己的解析函数,每一个vistor 要实现自己需要处理的node. http://zh.wikipedia.org/wiki/%E8%AE%BF%E9%97%AE%E8%80%85%E6%A8%A1%E5%BC%8F

所以写builder也就容易了,reader已经把结构给读好了,我只要这些vistor方法实现一遍即可。 而在Visoter用的是 dispatch_vist(),walkabout 采用getattr的方法来判定这里是有由

默认的方法是原样copy,当然也可以skipNode. 可以这样的丰富。

vist_... 的方法, 在writers/html.py里面是一目了然。writer 就像包管理中的dpkg,而builder 就像是apt-get. 中间的连接用的是 publish pattern, pub.reader,以及pub.writer的方法。 http://docutils.sf.net/docs/api/publisher.html 从这里就可以看publish_parts 就看协议包的构造了,如何来构造了。并且每一块对应关系就有了。只要把流程搞明白了,如何实现采用什么样的pattern是自然而然的事情。 http://docutils.sourceforge.net/docs/peps/pep-0258.html 而整个sphinx 是建立在docutils 的基础上的。 并且http://www.arnebrodowski.de/blog/write-your-own-restructuredtext-writer.html 哈哈,原来那些unit 测试都是采用这样的方法的设计的,采用vistor模式。 那些继承是为了正方便修改。只改需要修改的问题。

doctree 本身的扩展有三种: #. role 一种简单的inline element. #. directive 这种类类似于 graphviz 插件。 #. transform 也就是添加过滤器。有点类似于LLVM 的Pass. #. output本身的格式化,例如html,还有template + css 可以用。

digraph doctree_flow {

  "markdown"-> nodes_tree->transform_pass->nodes_tree;
}

设计方法很简单

import test
clslist = getclstst(test)
for cls in clslist:
       name = cls.__class_.__name__
       setup = getattr(cls,"setup__")
       run = getattr(cls,"run__")
       close = getattr(cls,"close__")
       print "begin testing {}" %(name)
       print "  setup: {} " %(name)
       setup()
       print "  run:{} " %(name)
       run()
       close()
       print "  finish{} " %(name)

而sphinx 的内部结构可以http://docutils.sourceforge.net/docs/ref/docutils.dtd 这时得到,同时对应,docutils/nodes.py就可以了。

其被背后用是https://docs.python.org/2/library/xml.dom.minidom.html,其实起来了也很简单。 其整个的实现方法,那就在nodes.py里,每一个节点就是一个tag,并且都会指定自己的accept方法。

都每个都有startag,以及endtag. http://docutils.sourceforge.net/docs/ref/doctree.html

transform

基本上是进来node列表,出来一个node列表。

  1. transform api
  2. using-rest-restructuredtext-to-create-html-snippet
  3. sphinx appapi
  4. example of transform
for ref in self.document.traverse(nodes.substuition_reference):
    refname = ref['refname']
    """
    do something
    """
    ref.replace_self(nodes.Text(text,text))

doctree

可以参考 sphinx/versioning.py 来处理。这里面有两个函数,merge_doctrees

以及 add_uids 来实现。

http://docutils.sourceforge.net/docs/ref/doctree.html

toc tree

直接用 toc来得到这个tree,同时可以用node.pformat,node.asdom().toxml()就可以生成生了。

singlehtml

对于sphinx现在没有办法方便做到,singlehtml重新写一个新的builder,不过也应该不是很难。 一个简单的办法,利用模板 直接把需要东东都放在 header. 例如把样式表放在前面。 然后所有需要放东东都放在style.css中就行了。

http://www.sphinx-doc.org/en/stable/extdev/nodes.html 可以这些api.

{% extends "!layout.html" %}
{% set reldelim1 = '&gt;' %}
{% block extraheader %}
{% include "../_static/sytle.css %}
{% endblock %}

生成pdf

默认的documentclass只有两种 howto,manual, 或者自定义,没有也行 但如果title 指定了中文,同时指定了manual就报错了,应该是manual对中文的支持不够。

添加对文献的支持用 bibtex 参考 http://sphinxcontrib-bibtex.readthedocs.org/en/latest/usage.html。 现在的问题是添加文献,没有索引号,但是第一版是可以的,看看是哪里配置做了。

sphinx 进行二次开发

http://sphinx-doc.org/extdev/tutorial.html#exttut http://docutils.sourceforge.net/docs/ref/doctree.html

http://sphinx-doc.org/extdev/nodes.html#nodes

内部结构的存储,用node的链表

如何添加一个role

可以参考这个 https://doughellmann.com/blog/2010/05/09/defining-custom-roles-in-sphinx/

主要是两步

  1. 注册你的role

    def setup(app):
        """Install the plugin.
        :param app: Sphinx applicaton context.
        """
        app.add_role("fb",fb_role)
        return
    
  2. 写你自己的回调函数

    def fb_role(name,rawtext,text,lineno,inliner,options,content):
        node = make_a_node(...)
        return [node], []
    

更进一步的定制可以参考 Docutils Hacker’s Guide

rst 本身的解析采用的是状态机来实现的,具体的实现可以参考 /usr/local/lib/python2.7/dist-packages/docutils/parsers/rst/states.py

单个文本的转换,可以用http://docutils.sourceforge.net/docs/api/cmdline-tool.html 这些实现,当然也可以采用pandoc来实现。

标题的层级

sphinx本身没有特殊符号要求,默认遇到的第一个是就一级level. 你只要保证使用的一致就行了。 但是当文件太大,使用了各种include在一起的时候,就可能会有各种不一致。

如何在readthedoc上添加一个留言区

可以参考 https://github.com/moorepants/dissertation/blob/master/_templates/page.html 原理就是在模板中加入的`Disqus <https://disqus.com/>`_ ,或者自己搭一个`isso <https://github.com/posativ/isso>`_ 的comments server. 并且添加额外的javascript 来实现它,如何添加额外的js可以参考 how-to-load-external-javascript-in-sphinx-doc

以及如何用sphinx来写论文,可以参考 https://github.com/moorepants/dissertation 以及如何用sphinx来写书,可以参考http://hyry.dip.jp/tech/book/page.html/sphinx/index.html, Python科学计算 就是用 Sphinx来写的。

自定义css

都可以用 template + theme 来实现。

或者使用编程接口,app.addstylefile() 来实现。

条件包含

可以采用 https://stackoverflow.com/questions/15001888/conditional-toctree-in-sphinx,也可以在 conf.py里添加代码直接发生成 或者可以事件生成部分内容,然后把加进正式的编译中。 Including content based on tags 这个类似于C的#IFDEFINE

当然sphinx也是支持tag来实现条件包含编译的。 https://stackoverflow.com/questions/16863444/conditionally-include-extensions

用代码生成文档

https://stackoverflow.com/questions/7250659/python-code-to-generate-part-of-sphinx-documentation-is-it-possible 用上面的方法也行,也可以用ptyhon 相应的 tabulate , rstcloth 当然可以用插件transform来完成。

各种rst的工具

https://stackoverflow.com/questions/2746692/restructuredtext-tool-support/2747041#2747041

最基本的工具:

rst2html
from reStructuredText to HTML
rst2xml
from reStructuredText to XML
rst2latex
from reStructuredText to LaTeX
rst2odt
from reStructuredText to ODF Text (word processor) document.
rst2s5
from reStructuredText to S5, a Simple Standards-based Slide Show System
rst2man
from reStructuredText to Man page

宏替换功能

可以由 .. |name| replace:: xxxxxxx 就可以了后面用 |name| 来引用替换了。 是用transform来实现的,可以参考 sphinx/transforms/__init__.py:124 或者参考 sphinx/versioning.py

class DefaultSubstitutions

这个来实现的。

对于一些特殊的语法

列如下划线,可以用role,再加样式表来实现。https://stackoverflow.com/questions/6518788/rest-strikethrough

..

对于如何实现一个自定义的builder

一个是可以参考 blog1 那就是参考 sphinxcontrib.spelling 插件的实现.

Pandoc

Pandoc 现在支持常见的各种文档的格式。并且基本格式之间相互转换。

digraph flow {
   reader -> ast -> writer;
}

可以通过filter来实现对插件语法的支持。但是filter只是在reader阶段的,如果还需要writer阶段的支持话就还需要template的支持了。

所以输入与输出是独立的,但是中间这个AST 就是异常通用兼容。

  1. 初步试验

    pandoc -f twiki abc.txt -t rst -o abc.rst

直接生成中文的pdf

pandoc demo.rst -o demo.pdf --latex-engine=xelatex -V mainfont="WenQuanYi Micro Hei"

生成 word

pandoc demo.rst -o demo.docx -V mainfont="WenQuanYi Micro Hei"

how-to-set-up-sublime-text-for-markdown-export-to-word

Pandoc 的template

它的模板是存放在 /usr/share/pandoc-1.9.2/templates , 同时可以用 pandoc -D html 输出 其template 的定义。

Pandoc的 filter

可以直接用haskell 来写,也可以用其他语言来写。 例如 python, 这里有 pandocfilters

可以通过管道或也可以 --filter filter.py 来用。

如看其pandoc 的内部格式

pandoc -t native test.txt

其内部结构定义 可以考能 pandoc type definiton

内部采用 JSON 来做为语法的。

分两部分,可以直接从pandocfilters.py中得到。

Block
Plain = elt(‘Plain’
Para = elt(‘Para’
CodeBlock = elt(‘CodeBlock’
RawBlock = elt(‘RawBlock’
BlockQuote = elt(‘BlockQuote’
OrderedList = elt(‘OrderedList’
BulletList = elt(‘BulletList’
DefinitionList = elt(‘DefinitionList’
Header = elt(‘Header’
HorizontalRule = elt(‘HorizontalRule’
Table = elt(‘Table’
Div = elt(‘Div’
Null = elt(‘Null’
inline elements
Str = elt(‘Str’
Emph = elt(‘Emph’
Strong = elt(‘Strong’
Strikeout = elt(‘Strikeout’
Superscript = elt(‘Superscript’
Subscript = elt(‘Subscript’
SmallCaps = elt(‘SmallCaps’
Quoted = elt(‘Quoted’
Cite = elt(‘Cite’
Code = elt(‘Code’
Space = elt(‘Space’
LineBreak = elt(‘LineBreak’
Math = elt(‘Math’
RawInline = elt(‘RawInline’
Link = elt(‘Link’
Image = elt(‘Image’
Note = elt(‘Note’
Span = elt(‘Span’

http://www.cnblogs.com/ouxiaogu/archive/2013/09/16/3323507.html

pandoc 手工编译

从官网下载代码,或者直接从github上clone, 然后解压后,直接 make 就可以了。

用pandoc转换从twiki到rst

对于它的应用,不仅要知道其reader的处理,还要知道 writer处理,然后才可以真正掌握灵活的处理。

例如pandoc是对于 graphviz 的本身结构识别就太格式,并且reader 的结构,还是不很清楚。 用来起还是比较麻烦。 没有直接处理来的快速。

requiremnt

  1. 基本格式都可以转换。
  2. graphviz 支持
  3. 对于latex的支持。
  4. verbatim 的支持。
  5. 其他。。

生成一个简单的dailyTodo

这个使用pandoc 生成一个单页的html还是非常的方便。

pandoc –self-contained Today.rst -o ToDo.html -H default.css mutt -e “set content_type=text/html” -s “DailyToDo” email@address < ToDo.html

有点不爽的那就是pandoc对于rst 的指令并不是那么完全。 对于 table并没有支持 csv-table等等, 只支持了简单的 ASCII table. 生成 ascii table也很容易自己实现或者直接使用 python tabulate package. 就行了。再加 rstcloth可以直接生成rst, 然后再pandoc 来生成各种格式。自己需要写一种markdown就够了。

但是对于一个dailyToDo来说应该是足够了。 现在想想完全用自己买一个todo应该不麻烦。

是日常工作中,经常会需要对各种模式之间转换,当然pandoc也不是一个万能的。 还需要一些专用工具,例如

html2pdf wkhtmltopdf
pdf2html ` pdf2htmlEX <https://github.com/coolwanglu/pdf2htmlEX/wiki/Download>`_
digraph flow {
    Input->lexer->token-> ASTree-> walk the three;
}