source: proto/RE2Pablo/ref/re2pbs/bin/re2pbs.py @ 2226

Last change on this file since 2226 was 2226, checked in by ksherdy, 7 years ago

Initial check in.

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