]> git.llucax.com Git - personal/documentos.git/blob - taller/ejercicios/ej1-20082/tp.py
Enunciado e implementación de referencia del ejercicio 1 de 2008-2
[personal/documentos.git] / taller / ejercicios / ej1-20082 / tp.py
1 #!/usr/bin/env python
2 # vim: set encoding=utf8 :
3
4 # Debug
5 ##########################################################################
6
7 DEBUG = False
8
9 def dprint(msg, *args):
10         if DEBUG:
11                 print msg % args
12
13
14 # Lexer
15 ##########################################################################
16
17 SP  = 1
18 EOL = 2
19 ID  = 3
20 ARR = 4
21 EOF = 5
22
23 tokstr = {
24         SP:  'SP',
25         EOL: 'EOL',
26         ID:  'ID',
27         ARR: 'ARR',
28         EOF: 'EOF',
29 }
30
31 class LexicError(Exception):
32         pass
33
34 def tokenize(str):
35         if not str:
36                 dprint('tokenize: %s', tokstr[EOF])
37                 return (EOF, '', '')
38         tok = None
39         val = ''
40         c = str[0]
41         if c == '\n':
42                 tok = EOL
43                 val = c
44                 str = str[1:]
45         elif c.isspace():
46                 tok = SP
47                 for c in str[1:]:
48                         if not c.isspace():
49                                 break
50                 i = str.find(c)
51                 str = str[i:]
52         elif c.isalpha():
53                 tok = ID
54                 for c in str[1:]:
55                         if not c.isalnum():
56                                 break
57                 i = str.find(c)
58                 val = str[:i]
59                 if str[i] == '*':
60                         tok = ARR
61                         i += 1
62                 str = str[i:]
63         else:
64                 raise LexicError("unexpected '%s'" % c)
65         dprint('tokenize: %r', (tokstr[tok], val, str[:20]))
66         return (tok, val, str)
67
68
69 # Parser
70 ##########################################################################
71
72 # "AST"
73
74 class Attr:
75         def __init__(self, type_name, is_array, name):
76                 self.type_name = type_name
77                 self.is_array = is_array
78                 self.name = name
79                 self.type = None # lo rellena el análisis semántico
80         def __str__(self):
81                 return repr(self)
82         def __repr__(self):
83                 type_name = self.type.name
84                 if self.is_array:
85                         type_name += '*'
86                 return 'Attr(%s, %s)' % (type_name, self.name)
87
88 class Message:
89         def __init__(self, name):
90                 self.name = name
91                 self.attrs = list()
92         def __str__(self):
93                 return repr(self)
94         def __repr__(self):
95                 return 'Message(%s, %s)' % (self.name, self.attrs)
96
97 # Fin "AST"
98
99 class SyntaxError(Exception):
100         pass
101
102 def skip(str, to_skip, current=None, val=''):
103         if current is None:
104                 current = to_skip
105         dprint('skip(%r, %s, %s)', str[:20], tokstr[to_skip], tokstr[current])
106         while to_skip == current:
107                 (current, val, str) = tokenize(str)
108         return (current, val, str)
109
110 def parse_attr(str):
111         (tok, val, str) = tokenize(str)
112         if tok == EOL: # fin de mensaje
113                 return (None, str)
114         (tok, val, str) = skip(str, SP, tok, val)
115         if tok != ID and tok != ARR:
116                 raise SyntaxError('ID or ARR expected, got %s' % tokstr[tok])
117         # tipo
118         is_array = False
119         if tok == ARR:
120                 is_array = True
121         type_name = val
122         # sp
123         (tok, val, str) = tokenize(str)
124         if tok != SP:
125                 raise SyntaxError('SP expected, got %s' % tokstr[tok])
126         (tok, val, str) = skip(str, SP, tok, val)
127         # nombre
128         if tok != ID:
129                 raise SyntaxError('ID expected, got %s' % tokstr[tok])
130         attr = Attr(type_name, is_array, val)
131         (tok, val, str) = skip(str, SP)
132         if tok != EOL:
133                 raise SyntaxError('EOL expected, got %s' % tokstr[tok])
134         return (attr, str)
135
136 def parse_message(str):
137         (tok, val, str) = skip(str, EOL)
138         (tok, val, str) = skip(str, SP, tok, val)
139         if tok == EOF:
140                 return (None, str)
141         elif tok != ID:
142                 raise SyntaxError('ID expected, got %s' % tokstr[tok])
143         msg = Message(val)
144         dprint('parse_message(): msg = %s', msg)
145         (tok, val, str) = skip(str, SP)
146         if tok != EOL:
147                 raise SyntaxError('EOL expected, got %s' % tokstr[tok])
148         # ya tengo EOL, vienen atributos (o fin de mensaje)
149         (attr, str) = parse_attr(str)
150         while attr:
151                 msg.attrs.append(attr)
152                 (attr, str) = parse_attr(str)
153         return (msg, str)
154
155 def parse(str):
156         dprint('parse(): str = %r...', str[:20])
157         msgs = list()
158         (msg, str) = parse_message(str)
159         while msg:
160                 msgs.append(msg)
161                 (msg, str) = parse_message(str)
162                 dprint('parse(): msg = %s', msg)
163                 dprint('parse(): str = %r...', str[:20])
164         return msgs
165
166
167 # Semantic Analysis
168 ##########################################################################
169
170 class SemanticError(Exception):
171         pass
172
173 def search(msgs, type_name):
174         for msg in msgs:
175                 if msg.name == type_name:
176                         return msg
177         return None
178
179 def check_types(msgs):
180         for msg in msgs:
181                 for attr in msg.attrs:
182                         m = search(msgs, attr.type_name)
183                         if m is None:
184                                 raise SemanticError('attribute %s in ' \
185                                                 'message %s has an ' \
186                                                 'unknown type %s' %
187                                                 (attr.name, msg.name,
188                                                         attr.type_name))
189                         attr.type = m
190
191
192 # Protocol Buffers
193 ##########################################################################
194
195 VARINT = 0
196 LENDEL = 2
197
198 valid_wire_types = (VARINT, LENDEL)
199
200 wirestr = {
201         VARINT: 'VARINT',
202         LENDEL: 'LENDEL',
203 }
204
205 indentation = '  '
206
207 class DecodeError(Exception):
208         pass
209
210 def decode_varint(str):
211         c = ord(str[0])
212         n = c & 0x7F
213         i = 1
214         while c & 0x80: # sigue
215                 if i == len(str):
216                         raise DecodeError('unexpected end of stream when '
217                                         'decoding a varint')
218                 c = ord(str[i])
219                 n += (c & 0x7F) << (7 * i)
220                 i += 1
221         return (n, str[i:])
222
223 def decode_key(str):
224         (key, str) = decode_varint(str)
225         wire_type = key & 0x0000000000000003
226         tag = key >> 3
227         return (tag, wire_type, str)
228
229 def check_wire_type(msg, attr, wire_type, expected_type):
230         if wire_type != expected_type:
231                 raise DecodeError('expected wire_type %s for attribute %s of '
232                                 'message %s, got %s' % (wirestr[expected_type],
233                                 attr.name, msg.name, wirestr[wire_type]))
234
235 def decode_attribute(str, msg, indent_level, index=0):
236         (tag, wire_type, str) = decode_key(str)
237         if wire_type not in valid_wire_types:
238                 raise DecodeError('unknown wire type %d' % wire_type)
239         if tag > len(msg.attrs):
240                 raise DecodeError('unknown tag %d for message %s'
241                                 % (tag, msg.name))
242         attr = msg.attrs[tag-1]
243         if not attr.is_array:
244                 print "%s%s:" % (indent_level * indentation, attr.name),
245         if attr.is_array and not index:
246                 print "%s%s: array" % (indent_level * indentation, attr.name)
247         if attr.is_array:
248                 print "%s%s: %s" % ((indent_level + 1) * indentation, index,
249                                 attr.type.name)
250                 index += 1
251         if attr.type.name == 'int':
252                 check_wire_type(msg, attr, wire_type, VARINT)
253                 (val, str) = decode_varint(str)
254                 print val
255         else:
256                 check_wire_type(msg, attr, wire_type, LENDEL)
257                 (length, str) = decode_varint(str)
258                 val = str[:length]
259                 str = str[length:]
260                 if attr.type.name == 'string': # submessage
261                         val = val.replace('\\', '\\\\').replace('"', '\\"')
262                         print '"%s"' % val
263                 else:
264                         if attr.is_array:
265                                 decode_array(val, attr.type, indent_level + 1)
266                         else:
267                                 decode_message(val, attr.type, indent_level + 1)
268         return (str, index)
269
270 def decode_array(str, msg, indent_level=1):
271         index = 0
272         while str:
273                 (str, index) = decode_attribute(str, msg, indent_level+1, index)
274
275 def decode_message(str, msg, indent_level=1):
276         print '%s' % msg.name
277         index = 0
278         while str:
279                 (str, index) = decode_attribute(str, msg, indent_level, index)
280
281
282 # Main
283 ##########################################################################
284
285 def main(*args):
286         msgs = [Message('int'), Message('string')] \
287                         + parse(file(args[0]).read())
288         check_types(msgs)
289         print msgs
290         msg = search(msgs, args[1])
291         if msg is None:
292                 print "can't find message with name %s in %s" % (args[1],
293                                 args[0])
294                 return 7
295         pbfile = sys.stdin
296         if len(args) > 2:
297                 pbfile = file(args[2])
298         decode_message(pbfile.read(), msg)
299         return 0
300
301 if __name__ == '__main__':
302         import sys
303         sys.exit(main(*sys.argv[1:]))
304