自作PEGパーサーコンビネータでCalcの実装

自作のPEGパーサーコンビネータで、よくあるCalcのサンプルを作ってみた。
左再起のサポートとか文法の拡張とかはなく、PEGの基本的なオペレータしか実装していない。しかし、マッチしなかった場合もNoneや空リストを返してASTのノードの位置を極力固定するという仕様によって、セマンティックアクションは比較的シンプルに実装できるんじゃないかと思う。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import unicode_literals
from peg import GrammarBuilder, Parser


def calcgrammar():
    g = GrammarBuilder()

    def number():
        return g.regexp(r'\d+')

    def factor():
        return [('(', expression, ')'), number]

    def term():
        return factor, g.repeat0(['*', '/'], factor)

    def expression():
        return term, g.repeat0(['+', '-'], term)

    def program():
        return expression

    return g.build(program)


class Calc(object):

    def number(self, t):
        return int(t)

    def factor(self, t):
        if isinstance(t, list):
            return t[1]
        else:
            return t

    def term(self, t):
        x = t[0]
        for op, r in t[1]:
            if op == '*':
                x *= r
            else:
                x /= r
        return x

    def expression(self, t):
        x = t[0]
        for op, r in t[1]:
            if op == '+':
                x += r
            else:
                x -= r
        return x


p = calcgrammar()
s = '3 - 2 - (1 + 3 * 4)'
accepted, t = p.parse(Parser(s, semantics=Calc()))
print(t)