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
RevLine 
[609]1#
2#  Character Class Compiler
3#
[3349]4#  Version 0.9 - June 24, 2013
[609]5#
[3349]6#  Copyright (c) 2007-13, Robert D. Cameron
7#  Licensed to the public under Open Software License 3.0
[609]8#
[3349]9#  Initial UTF-16 and UTF-32 support added by Dale Denis, June 2013.
[609]10#
[3349]11#  TO DO
12#    - add autosensing of 16/32 bit characters from input files
13#    - optimization of range logic for 16-bit char sets.
[609]14#
15#--------------------------------------------------------------------------
16#
17#  Data types
18#  1. Character Set Definitions
19#  2. Boolean Expressions
20#  3. Code Generator Objects
21#
[1941]22import sys, optparse
[609]23import re, binascii, string
[2974]24import EBCDIC
[609]25
[3942]26
[915]27import charset_def
28CharDef = charset_def.CharDef
29CharSetDef = charset_def.CharSetDef
[609]30
[915]31import charsets
32DefinitionSet = charsets.DefinitionSet
[609]33
[915]34import charset_input_parser
[609]35
[3413]36import UTF_encoding
37Encoding_Type = UTF_encoding.UTF_Encoding_Type
[3349]38
[3942]39from bitwise_expr import *
[609]40
41
42def bit_var(n):
[3349]43
44#    return 'bit[%i]' % n
45
[1941]46    global options
[3349]47    global Encoding
48
[3413]49    if len(UTF_encoding.Encoding.basis_pattern) == 1:
50        return UTF_encoding.Encoding.basis_pattern[0] % n
[3349]51   
[3413]52    if UTF_encoding.Encoding.name == UTF_encoding.UTF16.name:
[3349]53        if options.little_endian == True:
54            if n >= 8:
[3413]55                return UTF_encoding.Encoding.basis_pattern[0] % (n - 8)
[3349]56            else:
[3413]57                return UTF_encoding.Encoding.basis_pattern[1] % n
[3349]58        else:
59            if n <= 7:
[3413]60                return UTF_encoding.Encoding.basis_pattern[0] % n
[3349]61            else:
[3413]62                return UTF_encoding.Encoding.basis_pattern[1] % (n - 8)
[3349]63
[3413]64    if UTF_encoding.Encoding.name == UTF_encoding.UTF32.name:
[3349]65        if options.little_endian == True:
66            if n >= 21:
67                return "unused_bit%i" % (n - 21)
68            elif n < 21 and n >= 16:
[3413]69                return UTF_encoding.Encoding.basis_pattern[0] % (n - 16)
[3349]70            elif n < 16 and n >= 8:
[3413]71                return UTF_encoding.Encoding.basis_pattern[1] % (n - 8)
[3349]72            elif n < 8:
[3413]73                return UTF_encoding.Encoding.basis_pattern[2] % n
[3349]74        else:
75            if n <= 10:
76                return "unused_bit%i" % n
77            elif n > 10 and n <= 15:
[3413]78                return UTF_encoding.Encoding.basis_pattern[0] % (n - 8)
[3349]79            elif n > 15 and n <= 23:
[3413]80                return UTF_encoding.Encoding.basis_pattern[1] % (n - 16)
[3349]81            elif n > 23:
[3413]82                return UTF_encoding.Encoding.basis_pattern[2] % (n - 24)
[3349]83
[609]84def make_bitv(n):
[3349]85       
86    global options
[609]87
[3349]88    if options.little_endian == True:
89        return Var(bit_var(n))
90    else:
[3413]91        return Var(bit_var((UTF_encoding.Encoding.bits - 1) -n)) 
[3349]92       
[609]93def make_bit_test(pattern, bit_count):
[2229]94    if bit_count == 0: return TrueLiteral()
[609]95    bit_terms = []
96    test_bit = 2**(bit_count - 1)
97    for i in range(0, bit_count):
98        if (pattern & test_bit) == 0:
[3413]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))           
[609]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
[2996]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
[3349]126     
[2996]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
[3349]134   
[2996]135    return bit_terms[0]
136   
[609]137
138def char_test_expr(ch):
[2996]139    #return make_bit_test(ord(ch), 8)
[3413]140    return bit_pattern_expr(ord(ch), UTF_encoding.Encoding.mask) 
[609]141
142def GE_Range(N, n):
[3349]143
[609]144    if N == 0: return TrueLiteral()
145    elif N % 2 == 0 and (n >> (N - 2)) == 0:
[2996]146        return make_or(make_or(make_bitv(N-1), make_bitv(N-2)),
[609]147                        GE_Range(N - 2, n))
148    elif N % 2 == 0 and (n >> (N - 2)) == 3:   # >= 11xxxx
[2996]149        return make_and(make_and(make_bitv(N-1), make_bitv(N-2)),
[609]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.
[2996]159            return make_or(make_bitv(N-1), lo_range)
[609]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.
[2996]163            return make_and(make_bitv(N-1), lo_range)
[609]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
[3413]183    if n2 < n1 or diff_count > UTF_encoding.Encoding.bits: raise BadRange() 
[2996]184    mask = 2**(diff_count) - 1
185    #common = make_bit_test(n1 >> diff_count, 8 - diff_count)
[3413]186    common = bit_pattern_expr(n1 & ~mask, UTF_encoding.Encoding.mask^mask)   
[609]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)
[3349]191
[2996]192    return make_and(common, make_sel(make_bitv(diff_count-1), hi_test, lo_test))
[609]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):
[2232]205    if chardef.items == []: return FalseLiteral()
[3928]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))
[609]229    e1 = char_or_range_expr(chardef.items[0])
[3349]230    for i in range(1, len(chardef.items)):   
[609]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 '):
[1942]241        self.gensym_template = options.gensym_pattern
[609]242        self.gensym_counter = 0
243        self.generated_code = []
244        self.common_expression_map = {}
[3349]245        for sym in predeclared: self.common_expression_map[sym] = sym             
[609]246        self.typedecl = typedecl
247    def add_assignment(self, varname, expr):
248        self.common_expression_map[expr] = varname
[1052]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))
[609]251    def expr_string_to_variable(self, expr_string):
[3349]252        if self.common_expression_map.has_key(expr_string):
[609]253            return self.common_expression_map[expr_string]
254        else:
[3349]255            self.gensym_counter += 1                           
256            sym = self.gensym_template % self.gensym_counter 
257            self.add_assignment(sym, expr_string) 
[609]258            return sym
[3349]259
[609]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):
[3413]306    cgo = CodeGenObject([bit_var(i) for i in range(0, UTF_encoding.Encoding.bits)])
[609]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)))
[3349]350   
[609]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):
[3413]356    cgo = CodeGenObject([bit_var(i) for i in range(0, UTF_encoding.Encoding.bits)],'') 
[609]357    for d in chardeflist:
[666]358        chardef2py(cgo, d) 
[609]359    return cgo.showcode()# + "  return "+ py_chardefmap(chardeflist) + "\n"
360
[3413]361def main():   
[609]362
[1941]363    global options
364    # Option definition
365    option_parser = optparse.OptionParser(usage='python %prog [options] <input file>', version='0.8')
[3349]366 
367    option_parser.add_option('-u', '--character_encoding',
368                             dest='character_encoding',
369                             type='string',
[3413]370                             default='Default',
[3349]371                             help='character encoding; default: UTF-8',
372                             ) 
[1941]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                             )
[3349]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                             )
[1942]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                             )
[2974]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                             )
[2993]397    option_parser.add_option('-p', '--pablo', 
398                             dest='Pablo_skeleton',
399                             action='store_true', 
400                             default=False,
401                             help='generate pablo skeleton',
402                             )
[2994]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                             )
[1941]409    options, args = option_parser.parse_args(sys.argv[1:])
410
[3349]411    # Set the encoding.
[3413]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)
[3349]427    else:
428        print "ERROR: Invalid encoding format."
[3413]429        return
[3349]430
431    # If we have a valid encoding format then set the basis pattern.
[3413]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
[3349]453               
454           
[1941]455    # Positional arguments
[3413]456    if (len(args) == 1):
[609]457        # if the specified argument is not in the DefinitionSet, then assume that it's a filename
[1941]458        if args[0] not in DefinitionSet:
[609]459            #define the characters in the list
[1941]460            defs = charset_input_parser.input_chardef(args[0])
[3413]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
[2974]468        else: defs = DefinitionSet[args[1]]
469        if options.use_EBCDIC:
[3349]470            defs = EBCDIC.ascii2ebcdic_chardeflist(defs)
[2993]471        stmts = chardeflist2py(defs)
[2994]472        if options.Pablo_skeleton or options.test_skeleton:
[2993]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
[2994]497          if options.test_skeleton:
498            for d in defs:
499              print '\tprint_register<BitBlock>("%s",%s);\n' % (d.name, d.name)
[2993]500          print "\ndef main():\n\tDo_defs(%s)\n" % params
[3928]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()
[609]508    else:
[1941]509        option_parser.print_usage()
[3349]510       
[609]511
512if __name__ == "__main__": main()
[1941]513
Note: See TracBrowser for help on using the repository browser.