File: rpn.py

package info (click to toggle)
python-tubes 0.2.1-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 740 kB
  • sloc: python: 3,215; makefile: 149
file content (107 lines) | stat: -rw-r--r-- 2,815 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
from tubes.itube import IFrame, ISegment
from tubes.tube import tube, receiver
from tubes.listening import Listener

from twisted.internet.endpoints import serverFromString
from twisted.internet.defer import Deferred, inlineCallbacks
from tubes.protocol import flowFountFromEndpoint

class Calculator(object):
    def __init__(self):
        self.stack = []

    def push(self, number):
        self.stack.append(number)

    def do(self, operator):
        if len(self.stack) < 2:
            return "UNDERFLOW"
        left = self.stack.pop()
        right = self.stack.pop()
        result = operator(left, right)
        self.push(result)
        return result

err = object()

@receiver(inputType=IFrame)
def linesToNumbersOrOperators(line):
    from operator import add, mul
    try:
        yield int(line)
    except ValueError:
        if line == b'+':
            yield add
        elif line == b'*':
            yield mul
        else:
            yield err

@tube
class CalculatingTube(object):
    def __init__(self, calculator):
        self.calculator = calculator

    def received(self, value):
        if isinstance(value, int):
            self.calculator.push(value)
        elif value is err:
            yield "SYNTAX"
        else:
            yield self.calculator.do(value)

@receiver()
def numbersToLines(value):
    yield str(value).encode("ascii")

@tube
class Prompter(object):
    outputType = ISegment
    def started(self):
        yield b"> "
    def received(self, item):
        yield b"> "
    def stopped(self, failure):
        yield b"BYE"

def promptingCalculatorSeries():
    from tubes.fan import Thru
    from tubes.tube import series
    from tubes.framing import bytesToLines, linesToBytes

    full = series(bytesToLines(),
                  Thru([series(linesToNumbersOrOperators,
                               CalculatingTube(Calculator()),
                               numbersToLines,
                               linesToBytes()),
                        series(Prompter())]))
    return full

def calculatorSeries():
    from tubes.tube import series
    from tubes.framing import bytesToLines, linesToBytes

    return series(
        bytesToLines(),
        linesToNumbersOrOperators,
        CalculatingTube(Calculator()),
        numbersToLines,
        linesToBytes()
    )

def mathFlow(flow):
    processor = promptingCalculatorSeries()
    nextDrain = flow.fount.flowTo(processor)
    nextDrain.flowTo(flow.drain)

@inlineCallbacks
def main(reactor, port="stdio:"):
    endpoint = serverFromString(reactor, port)
    flowFount = yield flowFountFromEndpoint(endpoint)
    flowFount.flowTo(Listener(mathFlow))
    yield Deferred()

if __name__ == '__main__':
    from twisted.internet.task import react
    from sys import argv
    react(main, argv[1:])