Skip to content

Commit 0e5d2fe

Browse files
committed
2013 samples
1 parent 863e71a commit 0e5d2fe

File tree

3 files changed

+301
-0
lines changed

3 files changed

+301
-0
lines changed

2012/rd_infix_precedence.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#-----------------------------------------------
2+
# Precedence climbing expression parser.
3+
#
4+
# Eli Bendersky ([email protected])
5+
# License: this code is in the public domain
6+
# Last modified: July 2012
7+
#-----------------------------------------------
8+
from collections import namedtuple
9+
import re
10+
11+
12+
Tok = namedtuple('Tok', 'name value')
13+
14+
15+
class Tokenizer(object):
16+
""" Simple tokenizer object. The cur_token attribute holds the current
17+
token (Tok). Call get_next_token() to advance to the
18+
next token. cur_token is None before the first token is
19+
taken and after the source ends.
20+
"""
21+
TOKPATTERN = re.compile("\s*(?:(\d+)|(.))")
22+
23+
def __init__(self, source):
24+
self._tokgen = self._gen_tokens(source)
25+
self.cur_token = None
26+
27+
def get_next_token(self):
28+
""" Advance to the next token, and return it.
29+
"""
30+
try:
31+
self.cur_token = self._tokgen.next()
32+
except StopIteration:
33+
self.cur_token = None
34+
return self.cur_token
35+
36+
def _gen_tokens(self, source):
37+
for number, operator in self.TOKPATTERN.findall(source):
38+
if number:
39+
yield Tok('NUMBER', number)
40+
elif operator == '(':
41+
yield Tok('LEFTPAREN', '(')
42+
elif operator == ')':
43+
yield Tok('RIGHTPAREN', ')')
44+
else:
45+
yield Tok('BINOP', operator)
46+
47+
def __repr__(self):
48+
return 'Tokenizer(cur_token=%s)' % str(self.cur_token)
49+
50+
51+
52+
# For each operator, a (precedence, associativity) pair.
53+
OpInfo = namedtuple('OpInfo', 'prec assoc')
54+
55+
OPINFO_MAP = {
56+
'+': OpInfo(1, 'LEFT'),
57+
'-': OpInfo(1, 'LEFT'),
58+
'*': OpInfo(2, 'LEFT'),
59+
'/': OpInfo(2, 'LEFT'),
60+
'^': OpInfo(3, 'RIGHT'),
61+
}
62+
63+
64+
def parse_error(msg):
65+
raise RuntimeError(msg)
66+
67+
68+
from eblib.tracer import TraceCalls
69+
70+
@TraceCalls(show_ret=True)
71+
def compute_atom(tokenizer):
72+
tok = tokenizer.cur_token
73+
if tok.name == 'LEFTPAREN':
74+
tokenizer.get_next_token()
75+
val = compute_expr(tokenizer, 1)
76+
if tokenizer.cur_token.name != 'RIGHTPAREN':
77+
parse_error('unmatched "("')
78+
tokenizer.get_next_token()
79+
return val
80+
elif tok is None:
81+
parse_error('source ended unexpectedly')
82+
elif tok.name == 'BINOP':
83+
parse_error('expected an atom, not an operator "%s"' % tok.value)
84+
else:
85+
assert tok.name == 'NUMBER'
86+
tokenizer.get_next_token()
87+
return int(tok.value)
88+
89+
90+
@TraceCalls(show_ret=True)
91+
def compute_expr(tokenizer, min_prec):
92+
atom_lhs = compute_atom(tokenizer)
93+
94+
while True:
95+
cur = tokenizer.cur_token
96+
if (cur is None or cur.name != 'BINOP'
97+
or OPINFO_MAP[cur.value].prec < min_prec):
98+
break
99+
100+
# Inside this loop the current token is a binary operator
101+
assert cur.name == 'BINOP'
102+
103+
# Get the operator's precedence and associativity, and compute a
104+
# minimal precedence for the recursive call
105+
op = cur.value
106+
prec, assoc = OPINFO_MAP[op]
107+
next_min_prec = prec + 1 if assoc == 'LEFT' else prec
108+
109+
# Consume the current token and prepare the next one for the
110+
# recursive call
111+
tokenizer.get_next_token()
112+
atom_rhs = compute_expr(tokenizer, next_min_prec)
113+
114+
# Update lhs with the new value
115+
atom_lhs = compute_op(op, atom_lhs, atom_rhs)
116+
117+
return atom_lhs
118+
119+
120+
def compute_op(op, lhs, rhs):
121+
lhs = int(lhs); rhs = int(rhs)
122+
if op == '+': return lhs + rhs
123+
elif op == '-': return lhs - rhs
124+
elif op == '*': return lhs * rhs
125+
elif op == '/': return lhs / rhs
126+
elif op == '^': return lhs ** rhs
127+
else:
128+
parse_error('unknown operator "%s"' % op)
129+
130+
131+
def test():
132+
def compute(s):
133+
t = Tokenizer(s)
134+
t.get_next_token()
135+
return compute_expr(t, 1)
136+
137+
assert compute('1 + 2 * 3') == 7
138+
assert compute('7 - 9 * (2 - 3)') == 16
139+
assert compute('2 * 3 * 4') == 24
140+
assert compute('2 ^ 3 ^ 4') == 2 ** (3 ** 4)
141+
assert compute('(2 ^ 3) ^ 4') == 4096
142+
assert compute('5') == 5
143+
assert compute('4 + 2') == 6
144+
assert compute('9 - 8 - 7') == -6
145+
assert compute('9 - (8 - 7)') == 8
146+
assert compute('(9 - 8) - 7') == -6
147+
assert compute('2 + 3 ^ 2 * 3 + 4') == 33
148+
149+
150+
if __name__ == '__main__':
151+
#test()
152+
153+
t = Tokenizer('2 + 3^2*3 + 4')
154+
t.get_next_token()
155+
print compute_expr(t, min_prec=1)
156+

2013/twisted_irc_server.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#-------------------------------------------------------------------------------
2+
# twisted_irc_server.py
3+
#
4+
# A simple IRC server that flattens out what's happening behind the scenes of
5+
# 'twistd words'. Useful mainly to test clients on localhost.
6+
#
7+
# Eli Bendersky ([email protected])
8+
# Last updated: 2013.01.27
9+
# This code is in the public domain
10+
#-------------------------------------------------------------------------------
11+
import sys
12+
13+
from twisted.cred import checkers, portal
14+
from twisted.internet import reactor
15+
from twisted.internet.endpoints import TCP4ServerEndpoint
16+
from twisted.python import log
17+
from twisted.words import service
18+
19+
20+
ROOM = 'room'
21+
USERS = dict(
22+
user1='pass1',
23+
user2='pass2',
24+
user3='pass3',
25+
user4='pass4')
26+
27+
28+
if __name__ == '__main__':
29+
log.startLogging(sys.stdout)
30+
31+
# Initialize the Cred authentication system used by the IRC server.
32+
realm = service.InMemoryWordsRealm('testrealm')
33+
realm.addGroup(service.Group(ROOM))
34+
user_db = checkers.InMemoryUsernamePasswordDatabaseDontUse(**USERS)
35+
portal = portal.Portal(realm, [user_db])
36+
37+
# IRC server factory.
38+
ircfactory = service.IRCFactory(realm, portal)
39+
40+
# Connect a server to the TCP port 6667 endpoint and start listening.
41+
endpoint = TCP4ServerEndpoint(reactor, 6667)
42+
endpoint.listen(ircfactory)
43+
reactor.run()
44+

2013/twisted_irc_testbot.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#-------------------------------------------------------------------------------
2+
# twisted_irc_testbot.py
3+
#
4+
# A sample IRC bot based on the example in Twisted's docs.
5+
#
6+
# Eli Bendersky ([email protected])
7+
# Last updated: 2013.01.27
8+
# This code is in the public domain
9+
#-------------------------------------------------------------------------------
10+
import argparse
11+
import sys
12+
13+
from twisted.internet import reactor, protocol
14+
from twisted.python import log
15+
from twisted.words.protocols import irc
16+
17+
18+
class TestBot(irc.IRCClient):
19+
def __init__(self, channel, nickname, password):
20+
self.channel = channel
21+
self.nickname = nickname
22+
self.password = password
23+
24+
def connectionMade(self):
25+
irc.IRCClient.connectionMade(self)
26+
log.msg("[connected]")
27+
28+
def connectionLost(self, reason):
29+
irc.IRCClient.connectionLost(self, reason)
30+
log.msg("[disconnected]")
31+
32+
def signedOn(self):
33+
"""Called after sucessfully signing on to the server."""
34+
self.join(self.channel)
35+
36+
def joined(self, channel):
37+
"""Called when I finish joining a channel.
38+
39+
channel has the starting character intact.
40+
"""
41+
log.msg("[I have joined %s]" % channel)
42+
self.msg(channel, "user1: bonbon")
43+
44+
def privmsg(self, user, channel, msg):
45+
"""Called when I have a message from a user to me or a channel."""
46+
user = user.split('!', 1)[0]
47+
log.msg("<%s> %s" % (user, msg))
48+
49+
# Check to see if they're sending me a private message
50+
if channel == self.nickname:
51+
self.msg(user, 'Thanks for the private message')
52+
else:
53+
# Otherwise check to see if it is a message directed at me
54+
if msg.startswith(self.nickname + ":"):
55+
msg = "%s: I am a bot" % user
56+
self.msg(channel, msg)
57+
log.msg("<%s> %s" % (self.nickname, msg))
58+
59+
def lineReceived(self, line):
60+
"""Low level LineReceiver callback, used for debugging..."""
61+
log.msg('>> %s' % line)
62+
# Twisted's classes are old-style, so no super(), oh my...
63+
irc.IRCClient.lineReceived(self, line)
64+
65+
66+
class TestBotFactory(protocol.ClientFactory):
67+
def __init__(self, channel, nickname, password):
68+
self.channel = channel
69+
self.nickname = nickname
70+
self.password = password
71+
72+
def buildProtocol(self, addr):
73+
return TestBot(self.channel, self.nickname, self.password)
74+
75+
def clientConnectionLost(self, connector, reason):
76+
reactor.stop()
77+
connector.connect()
78+
79+
def clientConnectionFailed(self, connector, reason):
80+
reactor.stop()
81+
82+
83+
if __name__ == '__main__':
84+
argparser = argparse.ArgumentParser()
85+
argparser.add_argument('--server', help='the server to connect to',
86+
default='localhost')
87+
argparser.add_argument('--port', help='TCP port',
88+
default=6667)
89+
argparser.add_argument('--channel', help='channel/room name to join',
90+
default='room')
91+
argparser.add_argument('--nickname', default='user1')
92+
argparser.add_argument('--password', default='pass1')
93+
args = argparser.parse_args()
94+
95+
log.startLogging(sys.stdout)
96+
97+
# Create client, connect to server and run
98+
f = TestBotFactory(args.channel, args.nickname, args.password)
99+
reactor.connectTCP(args.server, args.port, f)
100+
reactor.run()
101+

0 commit comments

Comments
 (0)