1
2
3
4
5
6
7
8
9
10
11 "Generate Python code from packet descriptions"
12 from __future__ import with_statement
13 import contextlib
14 import tokenize
15 import sys
16 import token
17 import cStringIO
18 import os
19
21
26
28 str=self.desc+"\nLine "+`self.token[2][0]`+":\n"
29 str+=self.token[4]
30 str+=" "*self.token[2][1]+"^\n"
31 str+=`self.token[:4]`
32 return str
33
36
38
39 LITERAL="LITERAL"
40
41
42
43 PACKETSTART="PACKETSTART"
44
45
46 PACKETEND="PACKETEND"
47
48
49 CONDITIONALSTART="CONDITIONALSTART"
50
51
52 CONDITIONALRESTART="CONDITIONALRESTART"
53
54
55 CONDITIONALEND="CONDITIONALEND"
56
57
58 FIELD="FIELD"
59
60
61 CODE="CODE"
62
63
64 ASSERTION="ASSERTION"
65
66 STATE_TOPLEVEL="STATE_TOPLEVEL"
67 STATE_PACKET="STATE_PACKET"
68 STATE_CONDITIONAL="STATE_CONDITIONAL"
69
70
71 - def __init__(self, tokenizer, autogennamebase):
72 self.tokenizer=tokenizer
73 self.pb=[]
74 self.state=[self.STATE_TOPLEVEL]
75 self.packetstack=[]
76 self.resultspb=[]
77 self.lines=[None]
78 self.autogennamebase=autogennamebase
79 self.deferredpackets=[]
80
82 return self.autogennamebase+`line`
83
85 "Returns a token howfar ahead"
86 assert howfar>=1
87 while len(self.pb)<howfar:
88 self.pb.append(self._realnext())
89 return self.pb[howfar-1]
90
92 "Gets the next token from our input, ignoring the pushback list"
93 while True:
94 t=self.tokenizer.next()
95 t=(token.tok_name[t[0]],)+t[1:]
96
97
98 if len(self.lines)==t[2][0]:
99 ll=t[4].split('\n')
100 self.lines.extend(ll[:-1])
101 elif t[3][0]>len(self.lines):
102
103 ll=t[4].split('\n')
104 ll=ll[:-1]
105 for i,l in zip(range(t[2][0],t[3][0]+1), ll):
106
107 if len(self.lines)==i:
108 self.lines.append(l)
109
110 if t[0]=='NL':
111 t=('NEWLINE',)+t[1:]
112 if t[0]!='COMMENT':
113 break
114
115 return t
116
118 "Gets next token ignoring newlines"
119 while True:
120 t=self._next()
121 if t[0]!='NEWLINE':
122 return t
123
125 "Gets next token from our input, looking in pushback list"
126 if len(self.pb):
127 t=self.pb[0]
128 self.pb=self.pb[1:]
129 return t
130 return self._realnext()
131
133 "consumes any newlines"
134 while True:
135 t=self._lookahead()
136 if t[0]!='NEWLINE':
137 break
138 self._next()
139
141 """Returns everything up to newline as a string. If end of line has backslash before it then
142 next line is returned as well"""
143 t=self._lookahead()
144 res=self._getline(t[2][0])[t[2][1]:]
145 while True:
146 while t[0]!='NEWLINE':
147 t=self._next()
148 if res[-2]!='\\':
149 break
150 t=self._next()
151 res+=self._getline(t[2][0])
152 return res
153
155 return self.lines[line]
156
159
161 res=None
162 if len(self.resultspb):
163 res=self.resultspb.pop()
164
165 if self.state[-1]==self.STATE_TOPLEVEL:
166 if res is not None:
167 return res
168
169
170 if len(self.deferredpackets):
171 res=self.deferredpackets[0]
172 self.deferredpackets=self.deferredpackets[1:]
173 return res
174
175
176 t=self._lookahead()
177 if t[0]=='NEWLINE':
178 self._next()
179 return self.next()
180 if t[0]=='OP' and t[1]=='%':
181 return (self.LITERAL, self._getliteral())
182 if t[0]=='NAME' and t[1]=='PACKET':
183 return self._processpacketheader()
184 if t[0]=='ENDMARKER':
185 raise StopIteration()
186 raise protoerror("Unexpected token", t)
187
188 if self.state[-1]==self.STATE_PACKET or self.state[-1]==self.STATE_CONDITIONAL:
189
190 if res is None:
191 res=self._processpacketfield()
192
193 if res[0]==self.PACKETSTART:
194 q=[res]
195 while True:
196 res=self.next()
197 q.append(res)
198 if res[0]==self.PACKETEND:
199 break
200 self.deferredpackets.extend(q)
201 return self.next()
202
203 return res
204
205 raise protoerror("Unexpected state", self._lookahead())
206
208 "Returns the section enclosed in %{ ... }%. The %{ and }% must be on lines by themselves."
209 t=self._next()
210 if t[0]!='OP' or t[1]!='%':
211 raise protoerror("Expecting '%{'", t)
212 t=self._next()
213 if t[0]!='OP' or t[1]!='{':
214 raise protoerror("Expecting '%{'", t)
215 t=self._next()
216 if t[0]!='NEWLINE':
217 raise protoerror("Expecting newline", t)
218
219 res=""
220 lastline=-1
221 while True:
222 t=self._lookahead()
223 t2=self._lookahead(2)
224 if t[0]=='OP' and t[1]=='%' and \
225 t2[0]=='OP' and t2[1]=='}':
226 self._next()
227 self._next()
228 t=self._next()
229 if t[0]!='NEWLINE':
230 raise protoerror("Expecting newline",t)
231 break
232 t=self._next()
233 res+=t[4]
234 lastline=t[2][0]
235 while self._lookahead()[2][0]==lastline:
236
237 self._next()
238 return res
239
241 """Returns a text string representing a dict. If the next token is
242 not a dict start then None is returned"""
243 res=None
244 t=self._lookahead()
245 if t[0]!='OP' or t[1]!="{":
246 return res
247 res=""
248 t=self._next()
249 start=t[2]
250 mostrecent=t
251 nest=1
252 while nest>0:
253 t=self._next()
254 if t[0]=='OP' and t[1]=='}':
255 nest-=1
256 continue
257 if t[0]=='OP' and t[1]=='{':
258 mostrecent=t
259 nest+=1
260 continue
261 if t[0]=='DEDENT' or t[0]=='INDENT' or t[0]=='ENDMARKER':
262 raise protoerror("Unterminated '{'", mostrecent)
263
264 end=t[3]
265 for line in range(start[0], end[0]+1):
266 l=self._getline(line)
267 if line==end[0]:
268 l=l[:end[1]]
269 if line==start[0]:
270 l=l[start[1]:]
271 res+=l
272
273 return res
274
276 t=self._next()
277 if t[0]!='NAME':
278 raise protoerror("expecting 'PACKET'", t)
279 thedict=self._getdict()
280 t=self._next()
281
282 themodifiers=""
283 while t[0]=='OP':
284 themodifiers+=t[1]
285 t=self._next()
286 if t[0]!='NAME':
287 raise protoerror("expecting packet name", t)
288 thename=t[1]
289 t=self._next()
290 if t[0]!='OP' and t[1]!=':':
291 raise protoerror("expecting ':'", t)
292
293
294 thecomment=None
295 seenindent=False
296 while True:
297 t=self._lookahead()
298 if t[0]=='NEWLINE':
299 self._next()
300 continue
301 if t[0]=='STRING':
302 if thecomment is not None:
303 raise protoerror("Duplicate string comment", t)
304 thecomment=self._next()[1]
305 continue
306 if t[0]=='INDENT':
307 if seenindent:
308 raise protoerror("Unexpected repeat indent", t)
309 seenindent=True
310 self._next()
311 continue
312 break
313
314 if not seenindent:
315 raise protoerror("Expecting an indent", t)
316 self._consumenl()
317
318 self.state.append(self.STATE_PACKET)
319 self.packetstack.append( (thename, thedict, thecomment, themodifiers) )
320 return self.PACKETSTART, thename, None, thedict, thecomment, themodifiers
321
323 """Read in one packet field"""
324 self._consumenl()
325 t=self._lookahead()
326
327 if t[0]=='DEDENT':
328
329 self._next()
330
331 x=self.state.pop()
332 if x==self.STATE_CONDITIONAL:
333
334 t=self._lookahead()
335 if t[0]=='NAME' and t[1] in ('else', 'elif'):
336 self.state.append(self.STATE_CONDITIONAL)
337 else:
338 return (self.CONDITIONALEND,)
339 else:
340 return (self.PACKETEND,)
341
342 if t[0]=='OP' and t[1]=='%':
343
344 return self.CODE, self._getliteral()
345
346
347 if t[0]=='NUMBER':
348 self._next()
349 thesize=int(t[1])
350 elif t[0]=='OP' and t[1]=='*':
351 self._next()
352 thesize=-1
353 elif t[0]=='NAME' and t[1].upper()=='P':
354 self._next()
355 thesize='P'
356 elif t[0]=='NAME' and t[1].upper()=='A':
357 self._next()
358 return self.ASSERTION, self._getuptoeol()
359 elif t[0]=='NAME' and t[1]=='if':
360 str=self._getuptoeol()
361 self._consumenl()
362 t=self._next()
363 if t[0]!='INDENT':
364 raise protoerror("Expecting an indent after if ...: statement", t)
365 self.state.append(self.STATE_CONDITIONAL)
366 return (self.CONDITIONALSTART, str)
367 elif t[0]=='NAME' and t[1] in ('elif', 'else'):
368 str=self._getuptoeol()
369 self._consumenl()
370 t=self._next()
371 if t[0]!='INDENT':
372 raise protoerror("Expecting an indent after else: or elif ...: statement", t)
373 if self.state[-1]!=self.STATE_CONDITIONAL:
374 raise protoerror('An if must precede an else or elif.', t)
375 return (self.CONDITIONALRESTART, str)
376 else:
377 raise protoerror("Expecting field size as an integer, *, P, A or 'if' statement", t)
378
379
380 t=self._next()
381 if t[0]!='NAME':
382 raise protoerror("Expecting field type", t)
383 thetype=t[1]
384
385
386 t=self._lookahead()
387 if t[0]=='OP' and t[1]=='.':
388 self._next()
389 t=self._next()
390 if t[0]!='NAME':
391 raise protoerror("Expecting a name after . in field type", t)
392 thetype+="."+t[1]
393
394
395 thedict=self._getdict()
396
397
398 themodifiers=""
399 t=self._next()
400 while t[0]=='OP':
401 themodifiers+=t[1]
402 t=self._next()
403 thevisible=t[0]=='NAME'
404 if thevisible:
405 thename=t[1]
406 else:
407
408 thename=self._getautogenname(t[2][0])
409
410
411 thedesc=None
412 t=self._lookahead()
413 if t[0]=='OP' and t[1]==':':
414
415 self._next()
416
417 seenindent=False
418
419
420 self._consumenl()
421
422 t=self._lookahead()
423 if t[0]=='STRING':
424 thedesc=t[1]
425 t=self._next()
426 elif t[0]=='INDENT':
427 seenindent=True
428 self._next()
429
430 self._consumenl()
431
432 if not seenindent:
433 t=self._next()
434 if t[0]!='INDENT':
435 raise protoerror("Expected an indent after : based field", t)
436
437
438 autoclass=self._getautogenname(t[2][0])
439 self.resultspb.append( (self.PACKETSTART, autoclass, None, None, "'Anonymous inner class'", "") )
440 self.state.append(self.STATE_PACKET)
441
442 return self.FIELD, thename, thesize, thetype, "{'elementclass': "+autoclass+"}", \
443 thedict, thedesc, themodifiers, thevisible
444
445
446 if t[0]=='STRING':
447 thedesc=t[1]
448 self._next()
449
450 self._consumenl()
451
452 if thedesc is None:
453 t=self._lookahead()
454 if t[0]=='STRING':
455 thedesc=t[1]
456 self._next()
457 self._consumenl()
458
459 return self.FIELD, thename, thesize, thetype, None, thedict, thedesc,\
460 themodifiers, thevisible
461
464
465
468 self.tokenizer=tokenizer
469
471 tokens=self.tokenizer
472 out=cStringIO.StringIO()
473
474 print >>out, "# THIS FILE IS AUTOMATICALLY GENERATED. EDIT THE SOURCE FILE NOT THIS ONE"
475
476 for t in tokens:
477 if t[0]==tokens.LITERAL:
478 out.write(t[1])
479 continue
480 if t[0]==tokens.PACKETSTART:
481 classdetails=t
482 classfields=[]
483 classcodes=[]
484 continue
485 if t[0]==tokens.PACKETEND:
486 self.genclasscode(out, classdetails, classfields, classcodes)
487 continue
488 if t[0]==tokens.CODE:
489 classcodes.append(t)
490 else:
491 classfields.append(t)
492
493 return out.getvalue()
494
496 classname=namestuff[1]
497 tokens=self.tokenizer
498 _read_only='-' in namestuff[5]
499 print >>out, "class %s(BaseProtogenClass):" % (classname,)
500 if namestuff[4] is not None:
501 print >>out, indent()+namestuff[4]
502 if _read_only:
503 print >>out, indent(1)+"# Read-From-Buffer-Only Class"
504
505 fieldlist=[f[1] for f in fields if f[0]==tokens.FIELD and f[8]]
506
507 print >>out, indent(1)+"__fields="+`fieldlist`
508 print >>out, ""
509
510
511
512 print >>out, indent()+"def __init__(self, *args, **kwargs):"
513 print >>out, indent(2)+"dict={}"
514 if namestuff[2] is not None:
515 print >>out, indent(2)+"# Default generator arguments"
516 print >>out, indent(2)+"dict.update("+namestuff[2]+")"
517 if namestuff[3] is not None:
518 print >>out, indent(2)+"# User specified arguments in the packet description"
519 print >>out, indent(2)+"dict.update("+namestuff[3]+")"
520 print >>out, indent(2)+"# What was supplied to this function"
521 print >>out, indent(2)+"dict.update(kwargs)"
522 print >>out, indent(2)+"# Parent constructor"
523 print >>out, indent(2)+"super(%s,self).__init__(**dict)"%(namestuff[1],)
524 print >>out, indent(2)+"if self.__class__ is %s:" % (classname,)
525 print >>out, indent(3)+"self._update(args,dict)"
526 print >>out, "\n"
527
528 print >>out, indent()+"def getfields(self):"
529 print >>out, indent(2)+"return self.__fields"
530 print >>out, "\n"
531
532 print >>out, indent()+"def _update(self, args, kwargs):"
533 print >>out, indent(2)+"super(%s,self)._update(args,kwargs)"%(namestuff[1],)
534 print >>out, indent(2)+"keys=kwargs.keys()"
535 print >>out, indent(2)+"for key in keys:"
536 print >>out, indent(3)+"if key in self.__fields:"
537 print >>out, indent(4)+"setattr(self, key, kwargs[key])"
538 print >>out, indent(4)+"del kwargs[key]"
539 print >>out, indent(2)+"# Were any unrecognized kwargs passed in?"
540 print >>out, indent(2)+"if __debug__:"
541 print >>out, indent(3)+"self._complainaboutunusedargs(%s,kwargs)" % (namestuff[1],)
542
543 if len(fields)==1:
544 print >>out, indent(2)+"if len(args):"
545
546 d=[]
547 if f[2]>=0:
548 d.append("{'sizeinbytes': "+`f[2]`+"}")
549 for xx in 4,5:
550 if f[xx] is not None:
551 d.append(f[xx])
552 for dd in d: assert dd[0]=="{" and dd[-1]=='}'
553 d=[dd[1:-1] for dd in d]
554 print >>out, indent(3)+"dict2={%s}" % (", ".join(d),)
555 print >>out, indent(3)+"dict2.update(kwargs)"
556 print >>out, indent(3)+"kwargs=dict2"
557 print >>out, indent(3)+"self.__field_%s=%s(*args,**dict2)" % (f[1],f[3])
558
559 else:
560 print >>out, indent(2)+"if len(args): raise TypeError('Unexpected arguments supplied: '+`args`)"
561 print >>out, indent(2)+"# Make all P fields that haven't already been constructed"
562 for f in fields:
563 if f[0]==tokens.FIELD and f[2]=='P':
564
565 print >>out, indent(2)+"try: self.__field_"+f[1]
566 print >>out, indent(2)+"except:"
567 self.makefield(out, 3, f)
568
569 print >>out, "\n"
570
571
572 i=2
573 print >>out, indent()+"def writetobuffer(self,buf,autolog=True,logtitle=\"<written data>\"):"
574 print >>out, indent(i)+"'Writes this packet to the supplied buffer'"
575 if _read_only:
576 print >>out, indent(i)+"raise NotImplementedError"
577 else:
578 print >>out, indent(i)+"self._bufferstartoffset=buf.getcurrentoffset()"
579 for f in fields:
580 if f[0]==tokens.FIELD and f[2]!='P':
581 if '+' in f[7]:
582 print >>out, indent(i)+"try: self.__field_%s" % (f[1],)
583 print >>out, indent(i)+"except:"
584 self.makefield(out, i+1, f, isreading=False)
585 print >>out, indent(i)+"self.__field_"+f[1]+".writetobuffer(buf)"
586 elif f[0]==tokens.CONDITIONALSTART:
587 print >>out, indent(i)+f[1]
588 i+=1
589 elif f[0]==tokens.CONDITIONALRESTART:
590 print >>out, indent(i-1)+f[1]
591 elif f[0]==tokens.CONDITIONALEND:
592 i-=1
593 assert i==2
594 print >>out, indent(2)+"self._bufferendoffset=buf.getcurrentoffset()"
595 print >>out, indent(2)+"if autolog and self._bufferstartoffset==0: self.autologwrite(buf, logtitle=logtitle)"
596 print >>out, "\n"
597
598
599 print >>out, indent()+"def readfrombuffer(self,buf,autolog=True,logtitle=\"<read data>\"):"
600 print >>out, indent(2)+"'Reads this packet from the supplied buffer'"
601 i=2
602 print >>out, indent(2)+"self._bufferstartoffset=buf.getcurrentoffset()"
603 print >>out, indent(2)+"if autolog and self._bufferstartoffset==0: self.autologread(buf, logtitle=logtitle)"
604 for f in fields:
605 if f[0]==tokens.FIELD:
606 if f[2]=='P':
607 continue
608 if _read_only and not f[8]:
609
610 print >>out, indent(i)+self._maketempfieldstr(f)+".readfrombuffer(buf)"
611 else:
612 self.makefield(out, i, f)
613 print >>out, indent(i)+"self.__field_%s.readfrombuffer(buf)" % (f[1],)
614 elif f[0]==tokens.CONDITIONALSTART:
615 print >>out, indent(i)+f[1]
616 i+=1
617 elif f[0]==tokens.CONDITIONALRESTART:
618 print >>out, indent(i-1)+f[1]
619 elif f[0]==tokens.CONDITIONALEND:
620 i-=1
621 assert i==2
622 print >>out, indent(2)+"self._bufferendoffset=buf.getcurrentoffset()"
623 print >>out, "\n"
624
625
626 for f in fields:
627 if f[0]==tokens.FIELD and f[8]:
628
629 print >>out, indent()+"def __getfield_%s(self):" % (f[1],)
630 if '+' in f[7]:
631 print >>out, indent(2)+"try: self.__field_%s" % (f[1],)
632 print >>out, indent(2)+"except:"
633 self.makefield(out, 3, f)
634 print >>out, indent(2)+"return self.__field_%s.getvalue()\n" % (f[1],)
635
636 print >>out, indent()+"def __setfield_%s(self, value):" % (f[1],)
637 print >>out, indent(2)+"if isinstance(value,%s):" % (f[3],)
638 print >>out, indent(3)+"self.__field_%s=value" % (f[1],)
639 print >>out, indent(2)+"else:"
640 self.makefield(out, 3, f, "value,", isreading=False)
641 print >>out, ""
642
643 print >>out, indent()+"def __delfield_%s(self): del self.__field_%s\n" % (f[1], f[1])
644
645 print >>out, indent()+"%s=property(__getfield_%s, __setfield_%s, __delfield_%s, %s)\n" % (f[1], f[1], f[1], f[1], f[6])
646 if '++' in f[7]:
647
648 print >>out, indent()+"def ss_pb_count_respset_%s_attr(self, **kwargs):"%f[1]
649 print >>out, indent(2)+"self.%s"%f[1]
650 print >>out, indent(2)+"self.__field_%s.update(**kwargs)\n"%f[1]
651
652
653 print >>out, indent()+"def iscontainer(self):"
654 print >>out, indent(2)+"return True\n"
655
656 print >>out, indent()+"def containerelements(self):"
657 i=2
658 _pass_flg= { i: True }
659 for f in fields:
660 if f[0]==tokens.FIELD and f[8]:
661 print >>out, indent(i)+"yield ('%s', self.__field_%s, %s)" % (f[1], f[1], f[6])
662 _pass_flg[i]=False
663 elif f[0]==tokens.CONDITIONALSTART:
664 print >>out, indent(i)+f[1]
665 _pass_flg[i]=False
666 i+=1
667 _pass_flg[i]=True
668 elif f[0]==tokens.CONDITIONALRESTART:
669 if _pass_flg[i]:
670 print >>out, indent(i)+"pass"
671 else:
672 _pass_flg[i]=True
673 print >>out, indent(i-1)+f[1]
674 elif f[0]==tokens.CONDITIONALEND:
675 if _pass_flg[i]:
676 print >>out, indent(i)+"pass"
677 i-=1
678 assert i==2
679
680
681 print >>out
682 for _l in codes:
683 print >>out, _l[1]
684
685 print >>out, "\n\n"
686
687 - def makefield(self, out, indentamount, field, args="", isreading=True):
688 d=[]
689 if field[2]!='P' and field[2]>=0:
690 d.append("{'sizeinbytes': "+`field[2]`+"}")
691 if not (isreading and '*' in field[7]):
692 for xx in 4,5:
693 if field[xx] is not None:
694 d.append(field[xx])
695
696 for dd in d:
697 assert dd[0]=='{' and dd[-1]=='}'
698
699 if len(d)==0:
700 print >>out, indent(indentamount)+"self.__field_%s=%s(%s)" % (field[1], field[3], args)
701 return
702
703 d=[dd[1:-1] for dd in d]
704 dd="{"+", ".join(d)+"}"
705 print >>out, indent(indentamount)+"self.__field_%s=%s(%s**%s)" % (field[1], field[3], args, dd)
706
708
709 d=[]
710 if field[2]!='P' and field[2]>=0:
711 d.append("{'sizeinbytes': "+`field[2]`+"}")
712 if not (isreading and '*' in field[7]):
713 for xx in 4,5:
714 if field[xx] is not None:
715 d.append(field[xx])
716
717 for dd in d:
718 assert dd[0]=='{' and dd[-1]=='}'
719
720 if len(d)==0:
721 return "%s(%s)" % (fields[3], args)
722
723 d=[dd[1:-1] for dd in d]
724 dd="{"+", ".join(d)+"}"
725 return "%s(%s**%s)" % (field[3], args, dd)
726
727
729 print "Processing",inputfilename,"to",outputfilename
730 fn=os.path.basename(outputfilename)
731 fn=os.path.splitext(fn)[0]
732 with contextlib.nested(file(inputfilename, "rtU"),
733 file(outputfilename, "wt")) as (f, f2):
734 tokens=tokenize.generate_tokens(f.readline)
735 tt=protogentokenizer(tokens, "_gen_"+fn+"_")
736 cg=codegen(tt)
737 f2.write(cg.gencode())
738
739 if __name__=='__main__':
740 if len(sys.argv)>3 or (len(sys.argv)==2 and sys.argv[1]=="--help"):
741 print "protogen compiles all .p files in this directory to .py"
742 print "protogen foo.p compiles foo.p to foo.py"
743 print "protogen foo.p bar.py compiles foo.p to bar.py"
744 sys.exit(1)
745 elif len(sys.argv)==3:
746 processfile(sys.argv[1], sys.argv[2])
747 elif len(sys.argv)==2:
748 processfile(sys.argv[1], sys.argv[1]+"y")
749 elif len(sys.argv)==1:
750 import glob
751 for f in glob.glob("*.p"):
752 processfile(f, f+"y")
753 for f in glob.glob("phones/*.p"):
754 processfile(f, f+"y")
755