source: proto/charsetcompiler/charset_compiler.py @ 3942

Last change on this file since 3942 was 3942, checked in by cameron, 5 years ago

Refactor pulling out bitwise_expr module; prepare toAst method

File size: 20.2 KB
Line 
1#
2#  Character Class Compiler
3#
4#  Version 0.9 - June 24, 2013
5#
6#  Copyright (c) 2007-13, Robert D. Cameron
7#  Licensed to the public under Open Software License 3.0
8#
9#  Initial UTF-16 and UTF-32 support added by Dale Denis, June 2013.
10#
11#  TO DO
12#    - add autosensing of 16/32 bit characters from input files
13#    - optimization of range logic for 16-bit char sets.
14#
15#--------------------------------------------------------------------------
16#
17#  Data types
18#  1. Character Set Definitions
19#  2. Boolean Expressions
20#  3. Code Generator Objects
21#
22import sys, optparse
23import re, binascii, string
24import EBCDIC
25
26
27import charset_def
28CharDef = charset_def.CharDef
29CharSetDef = charset_def.CharSetDef
30
31import charsets
32DefinitionSet = charsets.DefinitionSet
33
34import charset_input_parser
35
36import UTF_encoding
37Encoding_Type = UTF_encoding.UTF_Encoding_Type
38
39from bitwise_expr import *
40
41
42def bit_var(n):
43
44#    return 'bit[%i]' % n
45
46    global options
47    global Encoding
48
49    if len(UTF_encoding.Encoding.basis_pattern) == 1:
50        return UTF_encoding.Encoding.basis_pattern[0] % n
51   
52    if UTF_encoding.Encoding.name == UTF_encoding.UTF16.name:
53        if options.little_endian == True:
54            if n >= 8:
55                return UTF_encoding.Encoding.basis_pattern[0] % (n - 8)
56            else:
57                return UTF_encoding.Encoding.basis_pattern[1] % n
58        else:
59            if n <= 7:
60                return UTF_encoding.Encoding.basis_pattern[0] % n
61            else:
62                return UTF_encoding.Encoding.basis_pattern[1] % (n - 8)
63
64    if UTF_encoding.Encoding.name == UTF_encoding.UTF32.name:
65        if options.little_endian == True:
66            if n >= 21:
67                return "unused_bit%i" % (n - 21)
68            elif n < 21 and n >= 16:
69                return UTF_encoding.Encoding.basis_pattern[0] % (n - 16)
70            elif n < 16 and n >= 8:
71                return UTF_encoding.Encoding.basis_pattern[1] % (n - 8)
72            elif n < 8:
73                return UTF_encoding.Encoding.basis_pattern[2] % n
74        else:
75            if n <= 10:
76                return "unused_bit%i" % n
77            elif n > 10 and n <= 15:
78                return UTF_encoding.Encoding.basis_pattern[0] % (n - 8)
79            elif n > 15 and n <= 23:
80                return UTF_encoding.Encoding.basis_pattern[1] % (n - 16)
81            elif n > 23:
82                return UTF_encoding.Encoding.basis_pattern[2] % (n - 24)
83
84def make_bitv(n):
85       
86    global options
87
88    if options.little_endian == True:
89        return Var(bit_var(n))
90    else:
91        return Var(bit_var((UTF_encoding.Encoding.bits - 1) -n)) 
92       
93def make_bit_test(pattern, bit_count):
94    if bit_count == 0: return TrueLiteral()
95    bit_terms = []
96    test_bit = 2**(bit_count - 1)
97    for i in range(0, bit_count):
98        if (pattern & test_bit) == 0:
99            bit_terms.append(make_not(make_bitv((UTF_encoding.Encoding.bits - 1)-i)))   
100        else: bit_terms.append(make_bitv((UTF_encoding.Encoding.bits - 1)-i))           
101        test_bit >>= 1
102    while len(bit_terms) > 1:
103        new_terms = []
104        for i in range(0, len(bit_terms)/ 2):
105            new_terms.append(make_and(bit_terms[2*i], bit_terms[2*i+1]))
106        if len(bit_terms) % 2 == 1:
107            new_terms.append(bit_terms[-1])
108        bit_terms = new_terms
109    return bit_terms[0]
110
111def bit_pattern_expr(pattern, selected_bits):
112    if selected_bits == 0: return TrueLiteral()
113    bit_terms = []
114    bit_no = 0
115    while selected_bits:
116      test_bit = 1 << bit_no
117      if selected_bits & test_bit:
118        if (pattern & test_bit) == 0:
119            bit_terms = [make_not(make_bitv(bit_no))] + bit_terms
120        else: bit_terms = [make_bitv(bit_no)] + bit_terms
121      else: bit_terms = [TrueLiteral()] + bit_terms
122      # Appending TrueLiteral() for nonselected bits is intended
123      # to keep consistent grouping of variables in the next loop.
124      selected_bits &= ~test_bit
125      bit_no += 1
126     
127    while len(bit_terms) > 1:
128        new_terms = []
129        for i in range(0, len(bit_terms)/ 2):
130            new_terms.append(make_and(bit_terms[2*i], bit_terms[2*i+1]))
131        if len(bit_terms) % 2 == 1:
132            new_terms.append(bit_terms[-1])
133        bit_terms = new_terms
134   
135    return bit_terms[0]
136   
137
138def char_test_expr(ch):
139    #return make_bit_test(ord(ch), 8)
140    return bit_pattern_expr(ord(ch), UTF_encoding.Encoding.mask) 
141
142def GE_Range(N, n):
143
144    if N == 0: return TrueLiteral()
145    elif N % 2 == 0 and (n >> (N - 2)) == 0:
146        return make_or(make_or(make_bitv(N-1), make_bitv(N-2)),
147                        GE_Range(N - 2, n))
148    elif N % 2 == 0 and (n >> (N - 2)) == 3:   # >= 11xxxx
149        return make_and(make_and(make_bitv(N-1), make_bitv(N-2)),
150                        GE_Range(N - 2, n - (3 << (N-2))))
151    elif N >= 1:
152        hi_bit = n & (1 << (N-1))
153        lo_bits = n - hi_bit
154        lo_range = GE_Range(N-1, lo_bits)
155        if hi_bit == 0:
156            # If the hi_bit of n is not set, then whenever the corresponding bit
157            # is set in the target, the target will certainly be >=.  Otherwise,
158            # the value of GE_range(N-1, lo_bits) is required.
159            return make_or(make_bitv(N-1), lo_range)
160        else: 
161            # If the hi_bit of n is set, then the corresponding bit must be set
162            # in the target for >= and GE_range(N-1, lo_bits) must also be true.
163            return make_and(make_bitv(N-1), lo_range)
164
165def LE_Range(N, n):
166    # If an N-bit pattern is all ones, then it is always
167    # true that any n-bit value is LE this pattern.
168    # Handling this as a special case avoids an overflow
169    # issue with n+1 requiring more than N bits.
170    if n+1 == 2 ** N:
171        return TrueLiteral()
172    else:
173        return make_not(GE_Range(N, n+1))
174
175BadRange = Exception()
176
177def Make_Range(n1, n2):  # require n2 >= n1
178    diff_bits = n1 ^ n2
179    diff_count = 0
180    while diff_bits > 0:
181        diff_count += 1
182        diff_bits >>= 1
183    if n2 < n1 or diff_count > UTF_encoding.Encoding.bits: raise BadRange() 
184    mask = 2**(diff_count) - 1
185    #common = make_bit_test(n1 >> diff_count, 8 - diff_count)
186    common = bit_pattern_expr(n1 & ~mask, UTF_encoding.Encoding.mask^mask)   
187    if diff_count == 0: return common
188    mask = 2**(diff_count-1) - 1
189    lo_test = GE_Range(diff_count-1, n1 & mask)
190    hi_test = LE_Range(diff_count-1, n2 & mask)
191
192    return make_and(common, make_sel(make_bitv(diff_count-1), hi_test, lo_test))
193
194BadCharSetItem = Exception()
195
196def char_or_range_expr(charset_item):
197    if len(charset_item) == 1:
198        return char_test_expr(charset_item[0])
199    elif len(charset_item) == 3:
200        if charset_item[1] == '-' and ord(charset_item[0]) <= ord(charset_item[2]):
201             return Make_Range(ord(charset_item[0]), ord(charset_item[2]))
202    raise BadCharSetItem
203
204def charset_expr(chardef):
205    if chardef.items == []: return FalseLiteral()
206    if len(chardef.items) > 1:
207        combine = True
208        #If all of the charset items are single codepoints
209        #such that X0 == Y0, X1 == Y1 etc.
210        for i in range(1, len(chardef.items)):
211            if len(chardef.items[i]) == 3:
212                combine = False
213                break
214        if combine == True:
215            #If charset items are all of the form X1 = X0 + 2.
216            for i in range(1 , len(chardef.items) - 1):
217                curr_item = chardef.items[i]
218                next_item = chardef.items[i+1]
219                if ord(curr_item) != ord(next_item) - 2:
220                    combine = False
221                    break
222        if combine == True:
223            first_item = ord(chardef.items[0])
224            last_item = ord(chardef.items[len(chardef.items)-1])
225            utf_temp = UTF_encoding.Encoding.mask - 1
226            first_item &= utf_temp
227            last_item |= (UTF_encoding.Encoding.mask ^ utf_temp)
228            return char_or_range_expr(chr(first_item) + '-' + chr(last_item))
229    e1 = char_or_range_expr(chardef.items[0])
230    for i in range(1, len(chardef.items)):   
231        e1 = make_or(e1, char_or_range_expr(chardef.items[i]))
232    if chardef.complemented: return make_not(e1)
233    else: return e1
234
235#
236#
237#  Code Generation
238#
239class CodeGenObject:
240    def __init__(self, predeclared, typedecl='BitBlock '):
241        self.gensym_template = options.gensym_pattern
242        self.gensym_counter = 0
243        self.generated_code = []
244        self.common_expression_map = {}
245        for sym in predeclared: self.common_expression_map[sym] = sym             
246        self.typedecl = typedecl
247    def add_assignment(self, varname, expr):
248        self.common_expression_map[expr] = varname
249        #self.generated_code.append('%s%s = %s;\n' % (self.typedecl, varname, expr))
250        self.generated_code.append('\t%s%s = %s\n' % (self.typedecl, varname, expr))
251    def expr_string_to_variable(self, expr_string):
252        if self.common_expression_map.has_key(expr_string):
253            return self.common_expression_map[expr_string]
254        else:
255            self.gensym_counter += 1                           
256            sym = self.gensym_template % self.gensym_counter 
257            self.add_assignment(sym, expr_string) 
258            return sym
259
260    def showcode(self):
261        s = ''
262        for stmt in self.generated_code: s += stmt
263        return s
264
265def expr2simd(genobj, expr):
266    """Translate a Boolean expression into three-address Altivec code
267       using code generator object genobj.
268    """
269    if isinstance(expr, TrueLiteral): return 'simd_const_1(1)'
270    elif isinstance(expr, FalseLiteral): return 'simd_const_1(0)'
271    elif isinstance(expr, Var): return expr.varname
272    elif isinstance(expr, Not):
273       e = genobj.expr_string_to_variable(expr2simd(genobj, expr.operand))
274       return 'simd_andc(simd_const_1(1), %s)' % (e)
275    elif isinstance(expr, Or):
276       e1 = genobj.expr_string_to_variable(expr2simd(genobj, expr.operand1))
277       e2 = genobj.expr_string_to_variable(expr2simd(genobj, expr.operand2))
278       return 'simd_or(%s, %s)' % (e1, e2)
279    elif isinstance(expr, Xor):
280       e1 = genobj.expr_string_to_variable(expr2simd(genobj, expr.operand1))
281       e2 = genobj.expr_string_to_variable(expr2simd(genobj, expr.operand2))
282       return 'simd_xor(%s, %s)' % (e1, e2)
283    elif isinstance(expr, And):
284       if isinstance(expr.operand1, Not):
285           e1 = genobj.expr_string_to_variable(expr2simd(genobj, expr.operand1.operand))
286           e2 = genobj.expr_string_to_variable(expr2simd(genobj, expr.operand2))
287           return 'simd_andc(%s, %s)' % (e2, e1)
288       elif isinstance(expr.operand2, Not):
289           e1 = genobj.expr_string_to_variable(expr2simd(genobj, expr.operand1))
290           e2 = genobj.expr_string_to_variable(expr2simd(genobj, expr.operand2.operand))
291           return 'simd_andc(%s, %s)' % (e1, e2)
292       else:
293           e1 = genobj.expr_string_to_variable(expr2simd(genobj, expr.operand1))
294           e2 = genobj.expr_string_to_variable(expr2simd(genobj, expr.operand2))
295           return 'simd_and(%s, %s)' % (e1, e2)
296    elif isinstance(expr, Sel):
297       sel = genobj.expr_string_to_variable(expr2simd(genobj, expr.sel))
298       e1 = genobj.expr_string_to_variable(expr2simd(genobj, expr.true_branch))
299       e2 = genobj.expr_string_to_variable(expr2simd(genobj, expr.false_branch))
300       return 'simd_if(%s, %s, %s)' %(sel, e1, e2)
301
302def chardef2simd(genobj, chardef):
303    genobj.add_assignment(chardef.name, expr2simd(genobj, charset_expr(chardef)))
304
305def chardeflist2simd(chardeflist):
306    cgo = CodeGenObject([bit_var(i) for i in range(0, UTF_encoding.Encoding.bits)])
307    for d in chardeflist:
308        chardef2simd(cgo, d)
309    return cgo.showcode()
310
311def expr2py(genobj, expr):
312    """Translate a Boolean expression into three-address python code
313       using code generator object genobj.
314    """
315    if isinstance(expr, TrueLiteral): return '-1'
316    elif isinstance(expr, FalseLiteral): return '0'
317    elif isinstance(expr, Var): return expr.varname
318    elif isinstance(expr, Not):
319       e = genobj.expr_string_to_variable(expr2py(genobj, expr.operand))
320       return '(~%s)' % (e)
321    elif isinstance(expr, Or):
322       e1 = genobj.expr_string_to_variable(expr2py(genobj, expr.operand1))
323       e2 = genobj.expr_string_to_variable(expr2py(genobj, expr.operand2))
324       return '(%s | %s)' % (e1, e2)
325    elif isinstance(expr, Xor):
326       e1 = genobj.expr_string_to_variable(expr2py(genobj, expr.operand1))
327       e2 = genobj.expr_string_to_variable(expr2py(genobj, expr.operand2))
328       return '(%s ^ %s)' % (e1, e2)
329    elif isinstance(expr, And):
330       if isinstance(expr.operand1, Not):
331           e1 = genobj.expr_string_to_variable(expr2py(genobj, expr.operand1.operand))
332           e2 = genobj.expr_string_to_variable(expr2py(genobj, expr.operand2))
333           return '(%s &~ %s)' % (e2, e1)
334       elif isinstance(expr.operand2, Not):
335           e1 = genobj.expr_string_to_variable(expr2py(genobj, expr.operand1))
336           e2 = genobj.expr_string_to_variable(expr2py(genobj, expr.operand2.operand))
337           return '(%s &~ %s)' % (e1, e2)
338       else:
339           e1 = genobj.expr_string_to_variable(expr2py(genobj, expr.operand1))
340           e2 = genobj.expr_string_to_variable(expr2py(genobj, expr.operand2))
341           return '(%s & %s)' % (e1, e2)
342    elif isinstance(expr, Sel):
343       sel = genobj.expr_string_to_variable(expr2py(genobj, expr.sel))
344       e1 = genobj.expr_string_to_variable(expr2py(genobj, expr.true_branch))
345       e2 = genobj.expr_string_to_variable(expr2py(genobj, expr.false_branch))
346       return '((%s & %s)|(~(%s) & %s))' %(sel, e1, sel, e2)
347
348def chardef2py(genobj, chardef):
349    genobj.add_assignment(chardef.name, expr2py(genobj, charset_expr(chardef)))
350   
351def py_chardefmap(chardeflist):
352    defs = ["'%s' : %s" % (d.name,d.name) for d in chardeflist]
353    return  '{%s}' % string.join(defs, ',\n\t')
354
355def chardeflist2py(chardeflist):
356    cgo = CodeGenObject([bit_var(i) for i in range(0, UTF_encoding.Encoding.bits)],'') 
357    for d in chardeflist:
358        chardef2py(cgo, d) 
359    return cgo.showcode()# + "  return "+ py_chardefmap(chardeflist) + "\n"
360
361def main():   
362
363    global options
364    # Option definition
365    option_parser = optparse.OptionParser(usage='python %prog [options] <input file>', version='0.8')
366 
367    option_parser.add_option('-u', '--character_encoding',
368                             dest='character_encoding',
369                             type='string',
370                             default='Default',
371                             help='character encoding; default: UTF-8',
372                             ) 
373    option_parser.add_option('-b', '--basis_pattern', 
374                             dest='basis_pattern',
375                             type='string',
376                             default='basis_bits.bit_%i',
377                             help='pattern for basis bit streams; default: basis_bits.bit_%i',
378                             )
379    option_parser.add_option('-l', '--little_endian',
380                             dest='little_endian',
381                             action='store_true',
382                             default=False,
383                             help='sets bit numbering of the output to little-endian',
384                             )
385    option_parser.add_option('-g', '--gensym_pattern', 
386                             dest='gensym_pattern',
387                             type='string',
388                             default='temp%i',
389                             help='pattern for generated temporaries; default: temp%i',
390                             )
391    option_parser.add_option('-E', '--EBCDIC', 
392                             dest='use_EBCDIC',
393                             action='store_true', 
394                             default=False,
395                             help='generate definitions for EBCDIC input',
396                             )
397    option_parser.add_option('-p', '--pablo', 
398                             dest='Pablo_skeleton',
399                             action='store_true', 
400                             default=False,
401                             help='generate pablo skeleton',
402                             )
403    option_parser.add_option('-t', '--test', 
404                             dest='test_skeleton',
405                             action='store_true', 
406                             default=False,
407                             help='generate pablo test skeleton',
408                             )
409    options, args = option_parser.parse_args(sys.argv[1:])
410
411    # Set the encoding.
412       
413    #If the user has entered the encoding type as a command-line argument
414    #then the encoding type that is to be used is locked.
415    if options.character_encoding == UTF_encoding.UTF32.name:
416        UTF_encoding.Encoding = Encoding_Type(options.character_encoding, 
417        UTF_encoding.UTF32.bits, UTF_encoding.UTF32.mask, False, True)
418    elif options.character_encoding == UTF_encoding.UTF16.name:
419        UTF_encoding.Encoding = Encoding_Type(options.character_encoding, 
420        UTF_encoding.UTF16.bits, UTF_encoding.UTF16.mask, False, True)
421    elif options.character_encoding == UTF_encoding.UTF8.name: 
422        UTF_encoding.Encoding = Encoding_Type(options.character_encoding,
423        UTF_encoding.UTF8.bits, UTF_encoding.UTF8.mask, False, True)
424    elif options.character_encoding == 'Default': 
425        UTF_encoding.Encoding = Encoding_Type(UTF_encoding.UTF8.name, 
426        UTF_encoding.UTF8.bits, UTF_encoding.UTF8.mask, True, False)
427    else:
428        print "ERROR: Invalid encoding format."
429        return
430
431    # If we have a valid encoding format then set the basis pattern.
432    UTF_encoding.Encoding.basis_pattern = string.split(options.basis_pattern, ",")
433    if len(UTF_encoding.Encoding.basis_pattern) == 1:
434        # If we have the default basis pattern string then adjust it
435        # for UTF-16 or UTF-32.  If the encoding is UTF-8 then we will
436        # leave it as is.
437        if "basis_bits.bit_%i" in UTF_encoding.Encoding.basis_pattern[0]:
438            if UTF_encoding.UTF16.name in UTF_encoding.Encoding.name:
439                UTF_encoding.Encoding.basis_pattern[0] = "u16_bit%i"
440            elif UTF_encoding.UTF32.name in UTF_encoding.Encoding.name:
441                UTF_encoding.Encoding.basis_pattern[0] = "u32_bit%i"
442    elif len(UTF_encoding.Encoding.basis_pattern) == 2:
443        if UTF_encoding.UTF16.name not in UTF_encoding.Encoding.name:
444            print "ERROR: Invalid encoding for the basis pattern variables."
445            return
446    elif len(UTF_encoding.Encoding.basis_pattern) == 3:
447        if UTF_encoding.UTF32.name not in UTF_encoding.Encoding.name:
448            print "ERROR: Invalid encoding for the basis pattern variables."
449            return
450    else:
451        print "ERROR: Invalid number of basis pattern variables."
452        return
453               
454           
455    # Positional arguments
456    if (len(args) == 1):
457        # if the specified argument is not in the DefinitionSet, then assume that it's a filename
458        if args[0] not in DefinitionSet:
459            #define the characters in the list
460            defs = charset_input_parser.input_chardef(args[0])
461            if UTF_encoding.Encoding.encoding_error == True:
462               if UTF_encoding.Encoding.default:
463                  print "ERROR: The input file contains characters with mixed encodings."
464               else:
465                  print ''.join(["ERROR: The input file contains encodings that are not ",
466                                  UTF_encoding.Encoding.name, "."])
467               return
468        else: defs = DefinitionSet[args[1]]
469        if options.use_EBCDIC:
470            defs = EBCDIC.ascii2ebcdic_chardeflist(defs)
471        stmts = chardeflist2py(defs)
472        if options.Pablo_skeleton or options.test_skeleton:
473          b = string.split(options.basis_pattern, ".")
474          if len(b) == 2: 
475            basis_struct = string.upper(b[0][0]) + b[0][1:]
476            basis_struct_var = b[0]
477            bit_pattern = b[1]
478          else: 
479            basis_struct = 'Bits'
480            basis_stuct_var = 'bits'
481            bit_pattern = bit[0]
482          struct_defs = "class %s():\n" % basis_struct
483          for i in range(8): struct_defs += "\t" + bit_pattern % i + " = 0\n"
484          struct_sets = {}
485          for d in defs:
486            n = string.split(d.name, ".")
487            if len(n) == 2:
488              if not n[0] in struct_sets.keys(): struct_sets[n[0]] = []
489              struct_sets[n[0]].append(n[1])
490          for k in struct_sets.keys():
491            struct_defs += "\nclass %s():\n" % (string.upper(k[0])+k[1:])
492            for f in struct_sets[k]: struct_defs += "\t" + f + " = 0\n"
493          print struct_defs
494          params = ", ".join([basis_struct_var] + struct_sets.keys())
495          print "\ndef Do_defs(%s):\n" % params
496          print stmts
497          if options.test_skeleton:
498            for d in defs:
499              print '\tprint_register<BitBlock>("%s",%s);\n' % (d.name, d.name)
500          print "\ndef main():\n\tDo_defs(%s)\n" % params
501        else: 
502           print stmts
503           #fo = open("icgrep_dev/Z_equations.txt", "wb")
504           #fo.write(stmts);
505
506           # Close opend file
507           #fo.close()
508    else:
509        option_parser.print_usage()
510       
511
512if __name__ == "__main__": main()
513
Note: See TracBrowser for help on using the repository browser.