source: proto/RE2PBS/bin/re2pbs.py @ 1154

Last change on this file since 1154 was 1154, checked in by ksherdy, 8 years ago

Minor template fixes.

File size: 16.5 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Objects to support AnTLR Grammar (Python)
4# Regular Expressions to Parallel Bit Stream equation generator.
5#
6# Copyright (c) 2011, Ken Herdy
7#
8# Version 0.7 - April 15, 2011
9#
10
11class Node:
12    def debug(self):
13      print 'Debug node.'
14
15class RE(Node):
16    def __init__(self):
17      self.union = Union()
18     
19    def debug(self):
20      return 'RE()->(%s)' % (self.union.debug())
21
22class Union(Node):
23    def __init__(self):
24      self.concatenation_list = []
25
26    def debug(self):
27      lst = []
28      for c in self.concatenation_list:
29        lst.append(c.debug())
30      return 'Union() -> (%s)' % ((',').join(lst))
31
32class Concatenation(Node):
33    def __init__(self): 
34      self.atom_list = []
35     
36    def debug(self):
37      return 'Concatentation() -> (%s)' % (self.atom_list)
38
39class Atom(Node):
40    def __init__(self, strm_name=None, quantifier=None):
41      self.strm_name = strm_name
42      self.quantifier = None
43     
44    def debug(self):
45      return 'Atom() -> (%s,%s)' % (self.strm_name, self.quantifier)
46
47class CharacterClass(Node):
48    def __init__(self):
49      self.single_or_range = []
50      self.invert = False
51     
52    def debug(self):
53      return 'CharacterClass() -> (%s,%s)' % (self.single_or_range, self.invert)
54
55class Range(Node):
56    def __init__(self, codepoint_lower=None, codepoint_upper=None):
57      self.codepoint_lower = codepoint_lower
58      self.codepoint_upper = codepoint_upper
59     
60    def debug(self):
61      return 'Range() -> (%s,%s)' % (self.codepoint_lower, self.codepoint_upper)
62
63class Single(Node):
64    def __init__(self, codepoint=None):
65      self.codepoint = codepoint
66     
67    def debug(self):
68      return 'Single() -> (%s)' % (self.codepoint)
69
70class PabloCode:
71    """ Methods to generate and format Pablo prototype statements and code. """
72    @classmethod
73    def Assign(cls, lhs, rhs):
74      return lhs + ' = ' + '(' + rhs +')'
75
76    @classmethod
77    def And(cls, lhs, rhs):
78      return '(' + lhs + ')' + ' & (' + rhs + ')'
79
80    @classmethod
81    def Nand(cls, lhs, rhs):
82      return '(' + lhs + ')' + ' &~ (' + rhs + ')'
83     
84    @classmethod       
85    def OrEq(cls, lhs, rhs):
86      return lhs + ' |= (' + rhs + ')'
87     
88    @classmethod
89    def Advance(cls, strm):
90      return 'bitutil.Advance(%s)' % (strm)
91   
92    @classmethod
93    def ScanThru(cls, cursor, strm):
94      return 'bitutil.ScanThru(%s,%s)' % (cursor, strm)
95
96    @classmethod
97    def indent(cls, level):
98      return ''.join(['\t' for i in range(0, level)])
99     
100class GeneratorFactory:
101    """ Parallel bit stream equation generator factory class.
102        Use this class create parallel bit stream class instances to
103        ensure that emitted cursor and error stream identifiers are unique.
104    """
105    def __init__(self):
106      self.count = 0
107           
108    def build_post_validator(self, level=0, cursor = 'cursor', error = 'error'):
109      self.count = self.count + 1
110      cursor += '_' + str(self.count)
111      error += '_' + str(self.count)
112      return PostValidator(level, cursor, error)
113
114    def build_pre_validator(self, level=0, cursor = 'cursor', error = 'error'):
115      self.count = self.count + 1
116      cursor += '_' + str(self.count)
117      error += '_' + str(self.count)
118      return PreValidator(level, cursor, error)
119
120import sys
121
122class Generator:
123    """ Parallel bit streams equation generator base class."""
124    def __init__(self, level=0, cursor='cursor', error='error'):
125      self.indent_level = PabloCode.indent(level)
126     
127      self.cursor_count = -1
128      self.cursor_base = cursor
129      self.cursor = self.next_cursor()
130     
131      self.error_count = -1   
132      self.error_base = error
133      self.error = self.next_error()
134         
135    def next_cursor(self):
136        self.cursor_count = self.cursor_count + 1
137        self.cursor = self.cursor_base + '_' + str(self.cursor_count)
138        return self.cursor
139       
140    def next_error(self):         
141        self.error_count = self.error_count + 1
142        self.error = self.error_base + '_' + str(self.error_count)
143        return self.error
144             
145    def make_init_stmts(self, strm):
146      print self.__class__.__name__ + ' not implemented.'
147      sys.exit(1)
148   
149    def make_match_stmts(self, strm, quantifier, follow_set_strm=None):
150      print self.__class__.__name__ + ' not implemented.'
151      sys.exit(1)
152   
153    def make_optional_stmts(self, strm, follow_set_strm=None):
154      print self.__class__.__name__ + ' not implemented.'
155      sys.exit(1)
156   
157    def make_kleene_star_stmts(self, strm, follow_set_strm=None):
158      print self.__class__.__name__ + ' not implemented.'
159      sys.exit(1)
160   
161    def make_kleene_cross_stmts(self, strm, follow_set_strm=None):
162      print self.__class__.__name__ + ' not implemented.'
163      sys.exit(1)
164
165    def make_single_stmts(self, strm, follow_set_strm=None):
166      print self.__class__.__name__ + ' not implemented.'
167      sys.exit(1)
168     
169    def make_finalization_stmts(self): 
170      print self.__class__.__name__ + ' not implemented.'
171      sys.exit(1)
172     
173    def make_debug_stmts(self):
174      print self.__class__.__name__ + ' not implemented.'
175      sys.exit(1)
176     
177    @classmethod         
178    def make_lexical_items(cls, strm_lst, level=1):
179        strms = ""
180        for strm in strm_lst:
181          strms += PabloCode.indent(level) + PabloCode.Assign(strm, '0')  +'\n'   
182        return strms 
183     
184     
185class PostValidator(Generator):
186   
187    """ Regular expression stream generator utilizing a post-validation strategy,
188        i.e. Advance cursor(s). Validate.
189       
190        This approach requires regular expression symbol lookahead to
191        determine 'Follow' lexical item streams for the identification of errors.
192    """
193       
194#       TODO
195#
196#       (1) Implemeent method definition: make_follow_set_strm .
197#       (2) Add post validation error handling & deletion of cursor positions in error.
198#       (3) Add debug code.
199#
200
201    def __init__(self, level=0, cursor='cursor', error='error'):
202      Generator.__init__(self, level, cursor, error)
203   
204    def make_init_stmts(self, strm):
205      code = self.indent_level  + PabloCode.Assign(self.error, '0') + '\n'
206      code += self.indent_level + PabloCode.Assign(self.cursor, PabloCode.Nand(strm, PabloCode.Advance(strm)))  + '\n'
207      return code
208   
209    def make_match_stmts(self, strm, quantifier=None, follow_set_strm=None):
210      if quantifier == '?':
211        return self.make_optional_stmts(strm, follow_set_strm)
212      elif quantifier == '+':
213        return self.make_kleene_cross_stmts(strm, follow_set_strm)
214      elif quantifier == '*':
215        return self.make_kleene_star_stmts(strm, follow_set_strm)
216      else:
217        return self.make_single_stmts(strm, follow_set_strm)
218       
219    def make_single_stmts(self, strm, follow_set_strm=None):
220      """ Cursor = Advance(Cursor)                      # C = C + C
221          Error  = Cursor &~ FollowSet(Strm)            # E = C &~ F
222          Cursor = Cursor &~ Error                      # C = C &~ E
223      """
224      code = self.indent_level + PabloCode.Assign(self.cursor, PabloCode.Advance(self.cursor)) + '\n'
225      if(follow_set_strm != None):
226            code += self.indent_level + PabloCode.OrEq(self.error, PabloCode.Nand(self.cursor, follow_set_strm)) + '\n'
227      return code
228
229    def make_optional_stmts(self, strm, follow_set_strm=None):
230      """ Temp   = Cursor                                       # T = C
231          Cursor = ScanThru(Cursor, (Strm & Cursor))            # C = ScanThru(C,(S & C)) OR C = C | Advance(C&S), this eliminates internal ScanThru addition.
232          Error  = Cursor1 &~ Temp &~ FollowSet(Strm)           # E = C &~ T &~ F, since C &~ (T | F)
233      """
234      code = self.indent_level + PabloCode.Assign(self.cursor, PabloCode.ScanThru(self.cursor, PabloCode.And(self.cursor, strm))) + '\n'
235      if(follow_set_strm != None):
236            code += self.indent_level + PabloCode.OrEq(self.error, PabloCode.Nand(self.cursor, follow_set_strm)) + '\n'
237      return code
238   
239    def make_kleene_star_stmts(self, strm, follow_set_strm=None):
240      """ Temp   = Cursor                                       # T  = C
241          Cursor = ScanThru(Cursor, Strm)                       # C  = ScanThru(C,S)         
242          Error  = Cursor1 &~ Temp &~ FollowSet(Strm)           # E  = C &~ T &~ F, since C &~ (T | F)
243      """
244      code = self.indent_level + PabloCode.Assign(self.cursor, PabloCode.ScanThru(self.cursor, strm)) + '\n'
245      if(follow_set_strm != None):
246            code += self.indent_level + PabloCode.OrEq(self.error, PabloCode.Nand(self.cursor, follow_set_strm)) + '\n'
247      return code
248   
249    def make_kleene_cross_stmts(self, strm, follow_set_strm=None):
250      #code = self.make_single_stmts(strm)
251      code = self.make_kleene_star_stmts(strm, follow_set_strm) 
252      return code
253
254class PreValidator(Generator):
255   
256    """ Regular expression stream generator using a pre-validation strategy,
257        i.e. Validate. Advance cursor(s). """
258
259    def __init__(self, level=0, cursor='cursor', error='error', debug=True):
260      Generator.__init__(self, level, cursor, error)
261           
262      self.debug = debug
263      if (self.debug):
264        self.re2pbs_debug_generator = DebugGenerator(level)
265   
266    def make_init_stmts(self, strm):
267      code = []
268
269      #----- op 1 -----
270      op = self.indent_level  + PabloCode.Assign(self.error, '0')
271      code.append(op)
272
273      if (self.debug):
274                label = self.indent_level + 'Init'
275                self.re2pbs_debug_generator.add_stmt(label)
276                label = self.indent_level + self.error
277                self.re2pbs_debug_generator.add_stmt(label, self.error)
278
279      #----- op 2 -----
280      op = self.indent_level + PabloCode.Assign(self.cursor, PabloCode.Nand(strm, PabloCode.Advance(strm)))
281      code.append(op)
282
283      if (self.debug):
284                label = self.indent_level + strm
285                self.re2pbs_debug_generator.add_stmt(label, strm)
286                self.re2pbs_debug_generator.add_stmt(op, self.cursor)
287
288      return '\n'.join(code) + '\n'
289   
290    def make_match_stmts(self, strm, quantifier=None, follow_set_strm=None):
291      if quantifier == '?':
292        return self.make_optional_stmts(strm, follow_set_strm)
293      elif quantifier == '+':
294        return self.make_kleene_cross_stmts(strm, follow_set_strm)
295      elif quantifier == '*':
296        return self.make_kleene_star_stmts(strm, follow_set_strm)
297      else:
298        return self.make_single_stmts(strm, follow_set_strm)
299       
300    def make_single_stmts(self, strm, follow_set_strm=None):     
301      # E = C &~ S
302      # C = C &~ E
303      # C = Advance(C)
304
305      code = []
306     
307      #----- op 1 -----
308      if (self.debug):
309                label = self.indent_level + 'Single Character'
310                self.re2pbs_debug_generator.add_stmt(label)
311                label = self.indent_level + self.cursor
312                self.re2pbs_debug_generator.add_stmt(label, self.cursor)
313                label = self.indent_level + strm
314                self.re2pbs_debug_generator.add_stmt(label, strm)
315     
316      cursor = self.cursor
317      op = self.indent_level + PabloCode.Assign(self.next_error(), PabloCode.Nand(cursor, strm))
318      code.append(op)
319
320      if (self.debug):
321                self.re2pbs_debug_generator.add_stmt(op, self.error)
322
323
324      #----- op 2 -----
325      cursor = self.cursor
326      op = self.indent_level + PabloCode.Assign(self.next_cursor(), PabloCode.Nand(cursor, self.error)) 
327      code.append(op)
328
329      if (self.debug):
330                self.re2pbs_debug_generator.add_stmt(op, self.cursor)
331
332      #----- op 3 -----
333      cursor = self.cursor
334      op = self.indent_level + PabloCode.Assign(self.next_cursor(), PabloCode.Advance(cursor))
335      code.append(op)
336
337      if (self.debug):
338                self.re2pbs_debug_generator.add_stmt(op, self.cursor)
339
340
341      return '\n'.join(code) + '\n'
342
343    def make_optional_stmts(self, strm, follow_set_strm=None):
344      # C = ScanThru(C, (C & S))
345      code = []     
346
347      #----- op 1 -----
348      if (self.debug):
349                label = self.indent_level + 'Optional Character'
350                self.re2pbs_debug_generator.add_stmt(label)
351                label = self.indent_level + self.cursor
352                self.re2pbs_debug_generator.add_stmt(label, self.cursor)
353                label = self.indent_level + strm
354                self.re2pbs_debug_generator.add_stmt(label, strm)
355
356      cursor = self.cursor
357      op = self.indent_level + PabloCode.Assign(self.next_cursor(), PabloCode.ScanThru(self.cursor, PabloCode.And(cursor, strm)))
358      code.append(op)
359
360      if (self.debug):
361                self.re2pbs_debug_generator.add_stmt(op, self.cursor)
362
363      return '\n'.join(code) + '\n'
364   
365    def make_kleene_star_stmts(self, strm, follow_set_strm=None):
366      # C = ScanThru(C, S)
367      code = []
368
369      #----- op 1 -----
370      if (self.debug):
371                label = self.indent_level + 'Kleene Star'
372                self.re2pbs_debug_generator.add_stmt(label)
373                label = self.indent_level + self.cursor
374                self.re2pbs_debug_generator.add_stmt(label, self.cursor)
375                label = self.indent_level + strm
376                self.re2pbs_debug_generator.add_stmt(label, strm)
377
378      cursor = self.cursor
379      op = self.indent_level + PabloCode.Assign(self.next_cursor(), PabloCode.ScanThru(cursor, strm))
380      code.append(op)
381
382      if (self.debug):
383                self.re2pbs_debug_generator.add_stmt(op, self.cursor)
384
385      return '\n'.join(code) + '\n'
386   
387    def make_kleene_cross_stmts(self, strm, follow_set_strm=None):
388      # E = C &~ S
389      # C = C &~ E
390      # C = Advance(C)
391      # C = ScanThru(C, S)
392      code = ''     
393
394      if (self.debug):
395                label = self.indent_level + 'Kleene Cross'
396                self.re2pbs_debug_generator.add_stmt(label)
397
398      code = self.make_single_stmts(strm)
399      code += self.make_kleene_star_stmts(strm, follow_set_strm)
400     
401      return code
402     
403    def make_finalization_stmts(self):
404     
405      code = []
406       
407      if self.error_count > 0:
408              op = self.indent_level + PabloCode.Assign(self.next_error(), self.error_base + '_' + str(0))
409              code.append(op)
410
411              if (self.debug):
412                        self.re2pbs_debug_generator.add_stmt(op, self.error)
413
414
415      for i in range(1, self.error_count):
416              op = self.indent_level + PabloCode.OrEq(self.error, self.error_base + '_' + str(i))
417              code.append(op)
418
419              if (self.debug):
420                        self.re2pbs_debug_generator.add_stmt(op, self.error)
421             
422      return '\n'.join(code) + '\n'
423
424
425    def make_debug_stmts(self):     
426      code = ""
427      if(self.debug):
428        code = self.indent_level + self.re2pbs_debug_generator.make_debug_stmts()
429
430      return code
431       
432class DebugGenerator:
433    def __init__(self, level=0):
434      self.indent_level = PabloCode.indent(level)
435      self.header = 'bitutil.print_aligned_streams([(\''+ self.indent_level + 'Input Data\', u8data), \n'
436      self.footer = '])'
437      self.stmts = []
438 
439    def add_stmt(self, label, strm=0):
440      stmt = self.indent_level + '(\'%s\', bitutil.bitstream2string(%s, lgth+1))' % (label, strm)
441      self.stmts.append(stmt)
442     
443    def make_debug_stmts(self):
444      return self.header + ',\n'.join(self.stmts) + self.footer + '\n'
445
446def readfile(filename):
447      f = open(filename, 'r')
448      contents = f.read() 
449      f.close()
450      return contents
451
452def writefile(filename, contents):
453      f=open(filename, 'w')
454      f.write(contents)
455      f.close()
456     
457import string     
458     
459class Program:
460  def __init__(self, prototype='prototype.py', template = "prototype_template.py"):
461      self.prototype = prototype
462      self.template = template
463      self.contents = readfile(template)     
464   
465  def generate(self, lex_strms, cc_code, pbs_code, pbs_debug=''):   
466      self.contents = string.replace(self.contents, '@lex_strms', lex_strms)
467      self.contents = string.replace(self.contents, '@cc_code', cc_code)
468      self.contents = string.replace(self.contents, '@pbs_code', pbs_code)
469      self.contents = string.replace(self.contents, '@pbs_debug', pbs_debug)
470      writefile(self.prototype, self.contents)
471
472def test():
473   
474    factory = GeneratorFactory()
475   
476    re2pbs_code_generator = factory.build_pre_validator()
477    pbs_code = re2pbs_code_generator.make_init_stmts('lex.Ampersand')                           # init   '&'
478    pbs_code += re2pbs_code_generator.make_match_stmts('lex.Ampersand', None, 'lex.Hash')       # expect '&'
479    pbs_code += re2pbs_code_generator.make_match_stmts('lex.Hash', None, 'lex.Digit')           # expect '#'
480    pbs_code += re2pbs_code_generator.make_match_stmts('lex.Digit', '+', 'lex.Semi')            # expect '[0-9]'
481    pbs_code += re2pbs_code_generator.make_match_stmts('lex.Semi', None, None)                  # expect ';'   
482    pbs_code += re2pbs_code_generator.make_finalization_stmts()
483
484    re2pbs_code_generator = factory.build_pre_validator()
485    pbs_code +=  re2pbs_code_generator.make_init_stmts('lex.Ampersand')                         # init   '&'
486    pbs_code +=  re2pbs_code_generator.make_match_stmts('lex.Ampersand', None, None)            # expect '&'
487    pbs_code +=  re2pbs_code_generator.make_match_stmts('lex.Hash', None, None)                 # expect '#'
488    pbs_code +=  re2pbs_code_generator.make_match_stmts('lex.x', None, None)                    # expect 'x'
489    pbs_code +=  re2pbs_code_generator.make_match_stmts('lex.Digit', '+', None)                 # expect '[0-9]'   
490    pbs_code +=  re2pbs_code_generator.make_match_stmts('lex.Semi', None, None)                 # expect ';'
491    pbs_code += re2pbs_code_generator.make_finalization_stmts()
492
493    print pbs_code
494    print re2pbs_code_generator.make_debug_stmts()
495
496
497if __name__ == "__main__":
498    test()
499 
Note: See TracBrowser for help on using the repository browser.