Differences between revisions 3 and 5 (spanning 2 versions)
Revision 3 as of 2004-08-29 03:21:37
Size: 6379
Editor: 218
Comment: discuss
Revision 5 as of 2004-08-29 06:31:09
Size: 14725
Editor: limodou
Comment:
Deletions are marked like this. Additions are marked like this.
Line 66: Line 66:
import StringIO
Line 102: Line 103:
    print template.value('*', {'*':{}})     print template.value('*')
Line 153: Line 154:
关于load()的要工作集中在自定义的preprocess上,因此只是一个测试,因此在这个测试中不作处理了。
value的修改已经完成,并进行了更新。可以下载最新的[http://wiki.woodpecker.org.cn/moin.cgi/Meteor?action=AttachFile&do=get&target=meteor0.03.zip meteor0.03.zip]
Line 164: Line 168:

我把两个python程序合成了一个。如下:

{{{
# -*- coding: mbcs -*-
import re
import sets
import copy
import types

class T:
    def __init__(self, string):
        self.text = string

    def getText(self):
        return self.text

#预处理器基类
class PreprocessBase:
    #ptype 为处理器的名字,如'py'
    def __init__(self, ptype, beginchars='<#', endchars='#>'):
        self.ptype = ptype
        self.beginchars = beginchars #define template var's left delimeter chars
        self.endchars = endchars #define template var's right delimeter chars

    #进行模板分析,应返回T对象的一个字典和相应的相关集
    def process(self, filename):
        return {}, {}

    def getPattern(self):
        return r'%s(\w+)%s' % (self.beginchars, self.endchars)

class PyPreprocess(PreprocessBase):
    def process(self, filename):
        mod = __import__(filename)
        components = filename.split('.')
        for comp in components[1:]:
            mod = getattr(mod, comp)

        vars = {}
        nodes = {}
        for vn in dir(mod):
            v = getattr(mod, vn)
            if hasattr(v, '__class__') and v.__class__.__name__ == 'T':
                vars[vn] = v
                nodes[vn] = self._get_rely_on_node(v.getText())

        return vars, nodes

    #取模板元素的相关集
    def _get_rely_on_node(self, s): #search for %(name)s format, make a dict
        re_node = re.compile(self.getPattern())

        return list(sets.Set(re_node.findall(s)))

class NoPreprocess(Exception): pass
class CircleFound(Exception): pass

#定义模板处理类
class Template:

    preprocess ={}

    def __init__(self):
        self.vars = {}
        self.nodes = {}

    #装入模板
    def load(self, tplfile, tpltype='py'):
        self.pre = self.preprocess.get(tpltype, None)
        if not self.pre:
            raise NoPreprocess(), 'No proper preprocess'

        vars, nodes = self.pre.process(tplfile)
        self.vars.update(vars)
        self.nodes.update(nodes)

    #生成模板值
    #values应为字典的字典。即每一个模板元素如果引用有外部的变量,那么在values中应有此模板元素的一个键。
    #同时它的值应为所有外部变量的一个字典
    def value(self, target, values):
        self.global_values = values[target]
        self.target = target
        return self._value(target, values[target])

    def _value(self, target, values=None):

        text = self.OnReplace(target, values)
        if text is not None:
            return text

        nodes = self.nodes[target]

        if not isinstance(values, types.ListType):
            values = [values]

        s = []
        for v in values:
            vals = {}
            for node in nodes:
                if not v.has_key(node):
                    if node in self.vars.keys():
                        vals[node] = self._value(node, self.global_values.get(node, {}))
                else:
                    if node in self.vars.keys(): #如果node是一个模板变量,则继续替换
                        vals[node] = self._value(node, v[node])
                    else: #说明为一个外部变量
                        vals[node] = v[node]
            s.append(self._replace(target, self.vars[target].getText(), vals))

        return ''.join(s)

    #可以自已写处理函数
    #name为模板元素的名字
    #text为要替换的引用变量的名字
    def OnReplace(self, name, values):
        return None

    #把一段文件本的可替换信息使用values中的变量进行替换
    #text是段字符串
    #values是一个对应替换信息的字典
    def _replace(self, name, text, values):
        def dosup(matchobj, name=name, text=text, values=values):
            if values:
                result = values.get(matchobj.groups()[0], matchobj.group())
            else:
                result = matchobj.group()
            return result

        if not text:
            return text
        return re.sub(self.pre.getPattern(), dosup, text)

#注册预处理器函数,是一个处理器实例
def register(preprocess):
    Template.preprocess[preprocess.ptype] = preprocess
"""

register(PyPreprocess('py'))

if __name__ == '__main__':
    vars = {
        'program':{
            'hello':[
                {'var' :'var1'},
                {'var' :'var2'},
                {'var' :'var3'},
            ],
        },
    }
    template = Template()
    template.load('tmp2')
    print template.value('program', vars)

"""

class NowebPreprocess(PreprocessBase):
    def __init__(self):
        PreprocessBase.__init__(self, 'noweb', '<<', '>>')

    def process(self, filename):
        f = file(filename)
        re_p = re.compile('^<<(.*)>>=$', re.M)
        vars = {}
        nodes = {}
        line = f.readline()
        while line:
            b = re_p.search(line)
            if b:
                name = b.groups()[0]
                line = f.readline()
                s = []
                while line.strip():
                    s.append(line)
                    line = f.readline()
                s[-1] = s[-1].rstrip()
                vars[name] = T(''.join(s))
                nodes[name] = self._get_rely_on_node(vars[name].getText())
            line = f.readline()
        return vars, nodes

    def _get_rely_on_node(self, s): #search for %(name)s format, make a dict
        re_node = re.compile(self.getPattern())

        return list(sets.Set(re_node.findall(s)))

register(NowebPreprocess())

if __name__ == '__main__':
    template = Template()
    template.load('noweb.txt', 'noweb')
    print template.value('*', {'*':{}})

}}}

=== 与Tomz讨论 ===

遇到冒号就进行缩近则只能限定在Python程序中,但这种限定必然同时会使得对noweb模板的使用受限。当然还是可以扩展NowebPreprocess类,以接收一个输出格式串,当为'python'时就对冒号后面进行缩近。不过,这种支持仍然是有限的,因为何时不用缩近却没有一个明显的符号来限定。这时可能还需要手工设置。

关于模板元素的分割,因为只是一个测试,因此暂时不改了,你需要我把它实用化吗?如果需要我就改一下,如果只是测试的话就这样了。

面向方面的编程只是一般地了解。据我所知,它一般的作法是自动在源程序中插入一些代码,这种插入应该不需要人为作特别的努力。但因此我也不清楚到底有什么用,我只是知道面向方面的编程一般是用于记日志、作分析使用。因为没见过,实在不知道在具体的应用中会对我们平时的编程和使用有多大的帮助。如果只是记日志、作分析的话,那只是对调试,分析效率有帮助。需要多了解一些。不过,这方面的东西已经超出meteor的能力了。Python虽然也有面向方面编程的东西,不过这些东西没时间研究,而且可能还比较抽象。

另外不知为什么要把两个程序合在一起。Meteor原本就是一个通用的类,可以被别的类所继承。合在一起可能只是方便你的使用。不过,我已经提供了包含Otter和noweb测试的完整代码放在meteor的下载页面中了。

leo我没有用过,也没有体会过它有什么好处。我也对这种形式上的清晰对实际的编程的帮助不是很清楚。因为这种形式完全可以通过一些别的方法来实现:

 * 文档
 * 在主控代码中显示程序的控制逻辑,每个控制点都是一个函数或过程,可以展开
 * 加注释

另外,它如何显示控制方面的信息呢?比如:循环,判断等。不过这些也与Meteor无关了,倒是一些题外话了。呵呵。

含有章节索引的中文 文章模板

-- limodou [DateTime(2004-08-28T22:19:58Z)] TableOfContents

Tomz 所提的noweb模板测试

由Tomz所提的文学模板,虽然不太明白,不过还是进行了简单地测试,仍然发现了meteor存在的不足,以后会改进

Noweb模板原型

由Tomz所提,没有听说过的一个东西,原型:

<<*>>=
<<def>>
<<print>>
<<defend>>
<<run>>

<<def>>=
def myprint():

<<print>>
print "test"

<<run>>=
myprint()

输出结果是:

def mypirnt():
print "test"
myprint()

可以看出文学编程不支持python的缩进。 这个是不是比meteor的调用方式更好呢? 另外,文学编程能集成面向方面编程就更好了。

by Tomz

Noweb模板原型分析

从上面的noweb模板原型我们可以看出,一个模板元素的定义为:

<<name>>=换行符
多行内容
空行

因为没有更多关于noweb模板的资料因此从原型可以这样规定。其中多行的内容可以包括其它的模板元素。

再从模板引用的示例与输出结果的对比我们可以看出,在<<*>>根元素中,对每个模板元素的引用后面都有一个回车,同时每个模板元素的定义中都是以空行结尾的,这样引出模板元素如果原样处理的话,必然带一个回车符。这样加上根元素中的模板引用中的回车,会生成两个回车。这一点在我没注意前的运行结果的确就是这样。于是我做了一个特殊处理,将每个元素最后的回车符去掉,输出结果与原型的结果就相同的。当然,实际上这种引用方式过于简单。因为一个模板元素完全可以是嵌入到一段文字中的,并不一定是独立为一行。不过,在这个简单的例子中我只关心能否生成相同的结果,而并不关心实际的情况。不过原型中可能存在错误,如果细心点可以看出,<<*>>中引用的模板有一个<<defend>>,但这个模板元素其实没有定义。而且输出结果中也少了一项。不知道是写错了,还是故意的。在测试中我将其去掉了。

因为noweb的模板替换(这个例子非常简单)没有超出Meteor的处理范围,只是模板定义形式不是python的定义格式,因此需要从预处理类中派生可以生成处理noweb的预处理类,然后在模板替换时指定这个新的处理器就可以了。

Meteor的定制模板程序

   1 from Template import *
   2 import re
   3 import StringIO
   4 
   5 class NowebPreprocess(PreprocessBase):
   6     def __init__(self):
   7         PreprocessBase.__init__(self, 'noweb', '<<', '>>')
   8         
   9     def process(self, filename):
  10         f = file(filename)
  11         re_p = re.compile('^<<(.*)>>=$', re.M)
  12         vars = {}
  13         nodes = {}
  14         line = f.readline()
  15         while line:
  16             b = re_p.search(line)
  17             if b:
  18                 name = b.groups()[0]
  19                 line = f.readline()
  20                 s = []
  21                 while line.strip():
  22                     s.append(line)
  23                     line = f.readline()
  24                 s[-1] = s[-1].rstrip()
  25                 vars[name] = T(''.join(s))
  26                 nodes[name] = self._get_rely_on_node(vars[name].getText())
  27             line = f.readline()
  28         return vars, nodes
  29 
  30     def _get_rely_on_node(self, s): #search for %(name)s format, make a dict
  31         re_node = re.compile(self.getPattern())
  32 
  33         return list(sets.Set(re_node.findall(s)))
  34 
  35 register(NowebPreprocess())
  36 
  37 if __name__ == '__main__':
  38     template = Template()
  39     template.load('noweb.txt', 'noweb')
  40     print template.value('*')

可以看出,实际对noweb的处理只是对PreprocessBase进行派生,生成了新的NowebPreprocess的处理器类,通过register将其注册到Template中。测试就很简单了:

  • 生成模板实例
  • 装入模板数据,指定'noweb'预处理器
  • 进行模板替换,提供对根元素的一个空字典

模板测试样例

这是一个测试样例:

<<*>>=
<<def>>
<<print>>
<<run>>

<<def>>=
def myprint():

<<print>>=
    print "test"

<<run>>=
    myprint()

这与原型有区别的就是:

  • 将<<defend>去掉

  • <<print>><<run>>模板中加入了缩近空格

输出结果为:

def myprint():
    print "test"
    myprint()

结果与预期的差不多。由于加入了缩近,因此就是符合Python要求的代码了。因此象Tomz所提不支持缩近应该完全是模板处理系统的原因。meteor不会随意对空格进行处理的。

讨论

Meteor是一个Python模板处理系统,但它提供了相应的接口可以通过派生,重载等方法来实现一个新的模板处理。当然能过这次测试我发现Meteor还有可以改进的地方:

  • load()方法应该可以支持文件名,文件对象,字符串等多种方式。而现在只能处理文件名。
  • value()方法中提供的字典数据,在没有数据时应可以省略或只是简单地提供一个{}就可以。而现在是至少有一个根元素的字典。

关于load()的要工作集中在自定义的preprocess上,因此只是一个测试,因此在这个测试中不作处理了。 value的修改已经完成,并进行了更新。可以下载最新的[http://wiki.woodpecker.org.cn/moin.cgi/Meteor?action=AttachFile&do=get&target=meteor0.03.zip meteor0.03.zip]

已经非常不错了 by tomz

我说的支持python的意思就是能够自动遇到冒号时在下一行开始缩进。自动和上面的缩进对齐。因为一般的语言对缩进没有要求,所以没有缩进的处理要求。如果用其它语言编程就会非常方便。自己处理缩进也凑合能用。我试了试,感觉很不错。就不用noweb软件了。

这种格式不是遇到空行分割,而是遇到下一个变量<<某某: invalid macro name>>分割。

这种noweb格式,dohao.org上推荐的leo编辑器支持。能够自动形成outline视图。

不知道limodou是不是能够体会到这种编程方式的好处。这是一种自顶到下的编程方式。从构思程序开始就能写程序。写出来的程序更容易懂。在这里还能加入面向方面编程的功能,就棒透了。不需要具体的实现就能够表达编程的思想。只是没有适应UML方式。不适应面向对象编程。但已经不错了。

非常感谢limodou的工作。这样,limodou也能有一个把meteor加入newedit的理由了。就是用newedit实现文学编程功能。

我把两个python程序合成了一个。如下:

# -*- coding: mbcs -*-
import re
import sets
import copy
import types

class T:
    def __init__(self, string):
        self.text = string

    def getText(self):
        return self.text

#预处理器基类
class PreprocessBase:
    #ptype 为处理器的名字,如'py'
    def __init__(self, ptype, beginchars='<#', endchars='#>'):
        self.ptype = ptype
        self.beginchars = beginchars        #define template var's left delimeter chars
        self.endchars = endchars            #define template var's right delimeter chars

    #进行模板分析,应返回T对象的一个字典和相应的相关集
    def process(self, filename):
        return {}, {}

    def getPattern(self):
        return r'%s(\w+)%s' % (self.beginchars, self.endchars)

class PyPreprocess(PreprocessBase):
    def process(self, filename):
        mod = __import__(filename)
        components = filename.split('.')
        for comp in components[1:]:
            mod = getattr(mod, comp)

        vars = {}
        nodes = {}
        for vn in dir(mod):
            v = getattr(mod, vn)
            if hasattr(v, '__class__') and v.__class__.__name__ == 'T':
                vars[vn] = v
                nodes[vn] = self._get_rely_on_node(v.getText())

        return vars, nodes

    #取模板元素的相关集
    def _get_rely_on_node(self, s): #search for %(name)s format, make a dict
        re_node = re.compile(self.getPattern())

        return list(sets.Set(re_node.findall(s)))

class NoPreprocess(Exception): pass
class CircleFound(Exception): pass

#定义模板处理类
class Template:

    preprocess ={}

    def __init__(self):
        self.vars = {}
        self.nodes = {}

    #装入模板
    def load(self, tplfile, tpltype='py'):
        self.pre = self.preprocess.get(tpltype, None)
        if not self.pre:
            raise NoPreprocess(), 'No proper preprocess'

        vars, nodes = self.pre.process(tplfile)
        self.vars.update(vars)
        self.nodes.update(nodes)

    #生成模板值
    #values应为字典的字典。即每一个模板元素如果引用有外部的变量,那么在values中应有此模板元素的一个键。
    #同时它的值应为所有外部变量的一个字典
    def value(self, target, values):
        self.global_values = values[target]
        self.target = target
        return self._value(target, values[target])

    def _value(self, target, values=None):

        text = self.OnReplace(target, values)
        if text is not None:
            return text

        nodes = self.nodes[target]

        if not isinstance(values, types.ListType):
            values = [values]

        s = []
        for v in values:
            vals = {}
            for node in nodes:
                if not v.has_key(node):
                    if node in self.vars.keys():
                        vals[node] = self._value(node, self.global_values.get(node, {}))
                else:
                    if node in self.vars.keys():    #如果node是一个模板变量,则继续替换
                        vals[node] = self._value(node, v[node])
                    else:       #说明为一个外部变量
                        vals[node] = v[node]
            s.append(self._replace(target, self.vars[target].getText(), vals))

        return ''.join(s)

    #可以自已写处理函数
    #name为模板元素的名字
    #text为要替换的引用变量的名字
    def OnReplace(self, name, values):
        return None

    #把一段文件本的可替换信息使用values中的变量进行替换
    #text是段字符串
    #values是一个对应替换信息的字典
    def _replace(self, name, text, values):
        def dosup(matchobj, name=name, text=text, values=values):
            if values:
                result = values.get(matchobj.groups()[0], matchobj.group())
            else:
                result = matchobj.group()
            return result

        if not text:
            return text
        return re.sub(self.pre.getPattern(), dosup, text)

#注册预处理器函数,是一个处理器实例
def register(preprocess):
    Template.preprocess[preprocess.ptype] = preprocess
"""

register(PyPreprocess('py'))

if __name__ == '__main__':
    vars = {
        'program':{
            'hello':[
                {'var'  :'var1'},
                {'var'  :'var2'},
                {'var'  :'var3'},
            ],
        },
    }
    template = Template()
    template.load('tmp2')
    print template.value('program', vars)

"""

class NowebPreprocess(PreprocessBase):
    def __init__(self):
        PreprocessBase.__init__(self, 'noweb', '<<', '>>')

    def process(self, filename):
        f = file(filename)
        re_p = re.compile('^<<(.*)>>=$', re.M)
        vars = {}
        nodes = {}
        line = f.readline()
        while line:
            b = re_p.search(line)
            if b:
                name = b.groups()[0]
                line = f.readline()
                s = []
                while line.strip():
                    s.append(line)
                    line = f.readline()
                s[-1] = s[-1].rstrip()
                vars[name] = T(''.join(s))
                nodes[name] = self._get_rely_on_node(vars[name].getText())
            line = f.readline()
        return vars, nodes

    def _get_rely_on_node(self, s): #search for %(name)s format, make a dict
        re_node = re.compile(self.getPattern())

        return list(sets.Set(re_node.findall(s)))

register(NowebPreprocess())

if __name__ == '__main__':
    template = Template()
    template.load('noweb.txt', 'noweb')
    print template.value('*', {'*':{}})

与Tomz讨论

遇到冒号就进行缩近则只能限定在Python程序中,但这种限定必然同时会使得对noweb模板的使用受限。当然还是可以扩展NowebPreprocess类,以接收一个输出格式串,当为'python'时就对冒号后面进行缩近。不过,这种支持仍然是有限的,因为何时不用缩近却没有一个明显的符号来限定。这时可能还需要手工设置。

关于模板元素的分割,因为只是一个测试,因此暂时不改了,你需要我把它实用化吗?如果需要我就改一下,如果只是测试的话就这样了。

面向方面的编程只是一般地了解。据我所知,它一般的作法是自动在源程序中插入一些代码,这种插入应该不需要人为作特别的努力。但因此我也不清楚到底有什么用,我只是知道面向方面的编程一般是用于记日志、作分析使用。因为没见过,实在不知道在具体的应用中会对我们平时的编程和使用有多大的帮助。如果只是记日志、作分析的话,那只是对调试,分析效率有帮助。需要多了解一些。不过,这方面的东西已经超出meteor的能力了。Python虽然也有面向方面编程的东西,不过这些东西没时间研究,而且可能还比较抽象。

另外不知为什么要把两个程序合在一起。Meteor原本就是一个通用的类,可以被别的类所继承。合在一起可能只是方便你的使用。不过,我已经提供了包含Otter和noweb测试的完整代码放在meteor的下载页面中了。

leo我没有用过,也没有体会过它有什么好处。我也对这种形式上的清晰对实际的编程的帮助不是很清楚。因为这种形式完全可以通过一些别的方法来实现:

  • 文档
  • 在主控代码中显示程序的控制逻辑,每个控制点都是一个函数或过程,可以展开
  • 加注释

另外,它如何显示控制方面的信息呢?比如:循环,判断等。不过这些也与Meteor无关了,倒是一些题外话了。呵呵。

Tomz_所提的noweb模板处理 (last edited 2009-12-25 07:17:17 by localhost)