栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

PyParsec 升级至 0.7.3

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

PyParsec 升级至 0.7.3

PyParsec 是 Python 3 的 parsec 移植。这个项目原本是一个非常微型的练手作品,是有一次 archmmit 会场里,我和老友赖勇浩聊天的时候,随手写出来的。因为 Python 是动态类型语言,很多 Haskell 里基于静态类型构建的类型约束也就失去了作用——当然,另一方面说,动态语言项目,很多静态语言的技术要点也不用考虑。
后面有很长一段时间,我都没太去想这个项目,对于我来说,验证了 python 可以作类似的东西,也就达成了目的。但是近期我加入到 csdn 的 ai 团队,有一些开发工作需要编写一些微型的解释器,或者复杂文本的分析程序。于是,我就将它又翻了出来。
除了缺少一些我在工作中用到的算子,pyparsec 还有一些比较突出的不足,比如算子都是很朴素的函数或者可调用对象,这导致复杂的算子写起来可读性会受影响,例如这个不太复杂的例子:

p = many(ch('i'))

显然写成

p = ch('i').many


更友善一些。
没有第一时间这样做,是因为 many 是一个组合子,它是基于 parsec 规范定义的算子,不应该成为一个地位超然的东西。基本法定下来,大家都要遵守,要么将来有办法给Parsec算子任意挂载新的扩展方法,要么大家都不能直接出现在 Parsec 的方法列表里,都不要有钦点的意思。
最终,在 scala 版本中,这些组合子以 typeclass 的风格成为了算子的扩展方法,它们可以通过 import 对应的隐式类型(在 Scala 3中是 extension)而任意扩展,外部扩展和内置算子地位一致,并不会破坏 parsec 的设计。
而在 python 中,我做了一些折衷。首先,为了解环形引用,也为了将来用于自己实现新的扩展方法方便,我定义了一个 combinator 类型:

class Combinator:
    def __init__(self, parsec):
        self.parsec = parsec

    def __call__(self, st):
        return self.parsec(st)

    def bind(self, continuation):
        return self.parsec.bind(continuation)

    def then(self, p):
        return self.parsec.then(p)

    def over(self, p):
        return self.parsec.over(p)


需要注意的是这个类型虽然接口与 Parsec 类型一致,但是实现并不相同。
然后再实现一个内置组合子的修饰器类型:

class BuiltIn(Combinator):
    def __init__(self, parsec):
        super().__init__(parsec)
        self.parsec = parsec

    @property
    def head(self):
        from .combinator import ahead
        return ahead(self.parsec)

    @property
    def attempt(self):
        from .combinator import attempt
        return attempt(self.parsec)

    @property
    def many(self):
        from .combinator import many
        return many(self.parsec)

    @property
    def many1(self):
        from .combinator import many1
        return many1(self.parsec)

    @property
    def skip(self):
        from .combinator import skip
        return skip(self.parsec)

    @property
    def skip1(self):
        from .combinator import skip1
        return skip1(self.parsec)

    def sepBy(self, by):
        from .combinator import sep
        return sep(self.parsec, by)

    def sepBy1(self, by):
        from .combinator import sep1
        return sep1(self.parsec, by)

    def otherwise(self, other):
        from .combinator import choice
        return choice(self.parsec.attempt, other)

    def manyTill(self, end):
        from .combinator import manyTill
        return manyTill(self.parsec, end)

这个类型可以直接挂在其它类型或函数的定义上,成为一个修饰器(decorator),而这里还是给了它一点特权,Parsec 类型其实是继承自 Builtin:

#!/usr/bin/env python3
# coding:utf-8

from .builtin import BuiltIn


class Parsec(BuiltIn):
    def __init__(self, parsec):
        super().__init__(parsec)
        self.parsec = BuiltIn(parsec)

    def __call__(self, st):
        return self.parsec(st)

    def bind(self, continuation):
        def bind(st):
            binder = continuation(self.parsec(st))
            return binder(st)

        return Parsec(bind)

    def then(self, p):
        def then(st):
            self.parsec(st)
            return p(st)

        return Parsec(then)

    def over(self, p):
        def over(st):
            re = self.parsec(st)
            p(st)
            return re

        return Parsec(over)

虽然我花了一点儿时间想办法解决了环形引用,但是这样只需要一个修饰器,就自动拥有了 builtin 方法,如同 python 的 builtins ,也不需要额外 import 。
而外部定义的修饰器扩展,需要和 Parsec 修饰器一起使用,组合修饰在算子定义上。
当然,更理想的办法是让任意的扩展方法可以自动挂载,但是 Python 既没有 typclass,也没有很方便的reopen能力,meta class也非常容易被破坏。修饰器是一个不够简洁,但是相对简单的方案。最终 pyparsec 选择了这个路线。 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/360211.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号