source: proto/charsetcompiler/CC_compiler.py @ 3952

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

Compiler for Unicode general categories

File size: 10.3 KB
Line 
1#
2#  Character Class Compiler - callable CharClassCompiler object.
3#
4import charset_input_parser
5
6import UTF_encoding
7Encoding_Type = UTF_encoding.UTF_Encoding_Type
8
9from pablo_expr import *
10
11class CC_compiler():
12    def __init__(self, encoding, gensym_pattern, little_endian_bit_numbering = True, typedecl = 'BitBlock '):
13        self.mEncoding = encoding
14        self.little_endian = little_endian_bit_numbering
15        self.gensym_template = gensym_pattern
16        self.gensym_counter = 0
17        self.generated_code = []
18        self.common_expression_map = {}
19        self.typedecl = typedecl
20        predeclared = [self.bit_var(i) for i in range(0, self.mEncoding.bits)]
21        for sym in predeclared: self.common_expression_map[sym] = sym
22
23    def bit_var(self, n):
24
25            if len(self.mEncoding.basis_pattern) == 1:
26                return self.mEncoding.basis_pattern[0] % n
27           
28            if self.mEncoding.name == UTF_encoding.UTF16.name:
29                if options.little_endian == True:
30                    if n >= 8:
31                        return self.mEncoding.basis_pattern[0] % (n - 8)
32                    else:
33                        return self.mEncoding.basis_pattern[1] % n
34                else:
35                    if n <= 7:
36                        return self.mEncoding.basis_pattern[0] % n
37                    else:
38                        return self.mEncoding.basis_pattern[1] % (n - 8)
39
40            if self.mEncoding.name == UTF_encoding.UTF32.name:
41                if options.little_endian == True:
42                    if n >= 21:
43                        return "unused_bit%i" % (n - 21)
44                    elif n < 21 and n >= 16:
45                        return self.mEncoding.basis_pattern[0] % (n - 16)
46                    elif n < 16 and n >= 8:
47                        return self.mEncoding.basis_pattern[1] % (n - 8)
48                    elif n < 8:
49                        return self.mEncoding.basis_pattern[2] % n
50                else:
51                    if n <= 10:
52                        return "unused_bit%i" % n
53                    elif n > 10 and n <= 15:
54                        return self.mEncoding.basis_pattern[0] % (n - 8)
55                    elif n > 15 and n <= 23:
56                        return self.mEncoding.basis_pattern[1] % (n - 16)
57                    elif n > 23:
58                        return self.mEncoding.basis_pattern[2] % (n - 24)
59
60    def make_bitv(self, n):
61               
62            if self.little_endian == True:
63                return Var(self.bit_var(n))
64            else:
65                return Var(self.bit_var((self.mEncoding.bits - 1) -n)) 
66               
67    def make_bit_test(self, pattern, bit_count):
68            if bit_count == 0: return TrueLiteral()
69            bit_terms = []
70            test_bit = 2**(bit_count - 1)
71            for i in range(0, bit_count):
72                if (pattern & test_bit) == 0:
73                    bit_terms.append(make_not(self.make_bitv((self.mEncoding.bits - 1)-i)))   
74                else: bit_terms.append(self.make_bitv((self.mEncoding.bits - 1)-i))           
75                test_bit >>= 1
76            while len(bit_terms) > 1:
77                new_terms = []
78                for i in range(0, len(bit_terms)/ 2):
79                    new_terms.append(make_and(bit_terms[2*i], bit_terms[2*i+1]))
80                if len(bit_terms) % 2 == 1:
81                    new_terms.append(bit_terms[-1])
82                bit_terms = new_terms
83            return bit_terms[0]
84
85    def bit_pattern_expr(self, pattern, selected_bits):
86            if selected_bits == 0: return TrueLiteral()
87            bit_terms = []
88            bit_no = 0
89            while selected_bits:
90              test_bit = 1 << bit_no
91              if selected_bits & test_bit:
92                if (pattern & test_bit) == 0:
93                    bit_terms = [make_not(self.make_bitv(bit_no))] + bit_terms
94                else: bit_terms = [self.make_bitv(bit_no)] + bit_terms
95              else: bit_terms = [TrueLiteral()] + bit_terms
96              # Appending TrueLiteral() for nonselected bits is intended
97              # to keep consistent grouping of variables in the next loop.
98              selected_bits &= ~test_bit
99              bit_no += 1
100             
101            while len(bit_terms) > 1:
102                new_terms = []
103                for i in range(0, len(bit_terms)/ 2):
104                    new_terms.append(make_and(bit_terms[2*i], bit_terms[2*i+1]))
105                if len(bit_terms) % 2 == 1:
106                    new_terms.append(bit_terms[-1])
107                bit_terms = new_terms
108           
109            return bit_terms[0]
110   
111
112    def char_test_expr(self, chval):
113            return self.bit_pattern_expr(chval, self.mEncoding.mask) 
114
115    def GE_Range(self, N, n):
116            if N == 0: return TrueLiteral()
117            elif N % 2 == 0 and (n >> (N - 2)) == 0:
118                return make_or(make_or(self.make_bitv(N-1), self.make_bitv(N-2)),
119                                self.GE_Range(N - 2, n))
120            elif N % 2 == 0 and (n >> (N - 2)) == 3:   # >= 11xxxx
121                return make_and(make_and(self.make_bitv(N-1), self.make_bitv(N-2)),
122                                self.GE_Range(N - 2, n - (3 << (N-2))))
123            elif N >= 1:
124                hi_bit = n & (1 << (N-1))
125                lo_bits = n - hi_bit
126                lo_range = self.GE_Range(N-1, lo_bits)
127                if hi_bit == 0:
128                    # If the hi_bit of n is not set, then whenever the corresponding bit
129                    # is set in the target, the target will certainly be >=.  Otherwise,
130                    # the value of GE_range(N-1, lo_bits) is required.
131                    return make_or(self.make_bitv(N-1), lo_range)
132                else: 
133                    # If the hi_bit of n is set, then the corresponding bit must be set
134                    # in the target for >= and GE_range(N-1, lo_bits) must also be true.
135                    return make_and(self.make_bitv(N-1), lo_range)
136
137    def LE_Range(self, N, n):
138            # If an N-bit pattern is all ones, then it is always
139            # true that any n-bit value is LE this pattern.
140            # Handling this as a special case avoids an overflow
141            # issue with n+1 requiring more than N bits.
142            if n+1 == 2 ** N:
143                return TrueLiteral()
144            else:
145                return make_not(self.GE_Range(N, n+1))
146
147    def Make_Range(self, n1, n2):  # require n2 >= n1
148            diff_bits = n1 ^ n2
149            diff_count = 0
150            while diff_bits > 0:
151                diff_count += 1
152                diff_bits >>= 1
153            if n2 < n1 or diff_count > self.mEncoding.bits: raise Exception("Bad range: (%i, %i) % n1, n2.") 
154            mask = 2**(diff_count) - 1
155            #common = make_bit_test(n1 >> diff_count, 8 - diff_count)
156            common = self.bit_pattern_expr(n1 & ~mask, self.mEncoding.mask^mask)   
157            if diff_count == 0: return common
158            mask = 2**(diff_count-1) - 1
159            lo_test = self.GE_Range(diff_count-1, n1 & mask)
160            hi_test = self.LE_Range(diff_count-1, n2 & mask)
161            return make_and(common, make_sel(self.make_bitv(diff_count-1), hi_test, lo_test))
162
163
164    def char_or_range_expr(self, charset_item):
165            (lo, hi) = charset_item
166            if lo == hi:
167                return self.char_test_expr(lo)
168            else:
169                return self.Make_Range(lo, hi)
170
171    def charset_expr(self, chardef):
172            if chardef.items == []: return FalseLiteral()
173            if len(chardef.items) > 2:
174                combine = True
175                #If all of the charset items are single codepoints
176                #such that X0 == Y0, X1 == Y1 etc.
177                for item in chardef.items:
178                    if item[0] != item[1]:
179                        combine = False
180                        break
181                if combine == True:
182                    #If charset items are all of the form X1 = X0 + 2.
183                    for i in range(1 , len(chardef.items) - 1):
184                        curr_item = chardef.items[i]
185                        next_item = chardef.items[i+1]
186                        if curr_item[0] != next_item[0] - 2:
187                            combine = False
188                            break
189                if combine == True:
190                    lo = chardef.items[0][0]
191                    hi = chardef.items[-1][0]
192                    utf_temp = self.mEncoding.mask - 1
193                    lo &= utf_temp
194                    hi |= (self.mEncoding.mask ^ utf_temp)
195                    return self.char_or_range_expr((lo, hi))
196            e1 = self.char_or_range_expr(chardef.items[0])
197            for i in range(1, len(chardef.items)):   
198                e1 = make_or(e1, self.char_or_range_expr(chardef.items[i]))
199            if chardef.complemented: return make_not(e1)
200            else: return e1
201
202    def add_assignment(self, varname, expr):
203        self.common_expression_map[expr] = varname
204        #self.generated_code.append('%s%s = %s;\n' % (self.typedecl, varname, expr))
205        self.generated_code.append('\t%s%s = %s\n' % (self.typedecl, varname, expr))
206
207    def add_if_stmt(self, test_expr, generated_subcode):
208        test_line = 'if %s:\n' % (self.expr_string_to_variable(self.expr2py(test_expr)))
209        self.generated_code.append('\t' + test_line)
210        self.generated_code += ['\t' + line for line in generated_subcode]
211        self.generated_code.append('\t#end' + test_line)
212       
213
214    def expr_string_to_variable(self, expr_string):
215        if self.common_expression_map.has_key(expr_string):
216            return self.common_expression_map[expr_string]
217        else:
218            self.gensym_counter += 1                           
219            sym = self.gensym_template % self.gensym_counter 
220            self.add_assignment(sym, expr_string) 
221            return sym
222
223    def showcode(self):
224        s = ''
225        for stmt in self.generated_code: s += stmt
226        return s
227
228    def expr2py(self, expr):
229            """Translate a Boolean expression into three-address python code.
230            """
231            if isinstance(expr, TrueLiteral): return '-1'
232            elif isinstance(expr, FalseLiteral): return '0'
233            elif isinstance(expr, Var): return expr.varname
234            elif isinstance(expr, Not):
235               e = self.expr_string_to_variable(self.expr2py(expr.operand))
236               return '(~%s)' % (e)
237            elif isinstance(expr, Or):
238               e1 = self.expr_string_to_variable(self.expr2py(expr.operand1))
239               e2 = self.expr_string_to_variable(self.expr2py(expr.operand2))
240               return '(%s | %s)' % (e1, e2)
241            elif isinstance(expr, Xor):
242               e1 = self.expr_string_to_variable(self.expr2py(expr.operand1))
243               e2 = self.expr_string_to_variable(self.expr2py(expr.operand2))
244               return '(%s ^ %s)' % (e1, e2)
245            elif isinstance(expr, And):
246               if isinstance(expr.operand1, Not):
247                   e1 = self.expr_string_to_variable(self.expr2py(expr.operand1.operand))
248                   e2 = self.expr_string_to_variable(self.expr2py(expr.operand2))
249                   return '(%s &~ %s)' % (e2, e1)
250               elif isinstance(expr.operand2, Not):
251                   e1 = self.expr_string_to_variable(self.expr2py(expr.operand1))
252                   e2 = self.expr_string_to_variable(self.expr2py(expr.operand2.operand))
253                   return '(%s &~ %s)' % (e1, e2)
254               else:
255                   e1 = self.expr_string_to_variable(self.expr2py(expr.operand1))
256                   e2 = self.expr_string_to_variable(self.expr2py(expr.operand2))
257                   return '(%s & %s)' % (e1, e2)
258            elif isinstance(expr, Sel):
259               sel = self.expr_string_to_variable(self.expr2py(expr.sel))
260               e1 = self.expr_string_to_variable(self.expr2py(expr.true_branch))
261               e2 = self.expr_string_to_variable(self.expr2py(expr.false_branch))
262               return '((%s & %s)|(~(%s) & %s))' %(sel, e1, sel, e2)
263            elif isinstance(expr, Adv):
264               e = self.expr_string_to_variable(self.expr2py(expr.operand))
265               return 'Advance(%s, %i)' % (e, expr.offset)
266            else: raise Exception("Bad expression: %s" % repr(e))
267
268    def chardef2py(self, chardef):
269            self.add_assignment(chardef.name, self.expr2py(self.charset_expr(chardef)))
270   
271    def chardeflist2py(self, chardeflist):
272            for d in chardeflist:
273                self.chardef2py(d) 
274            return self.showcode()
275
276
Note: See TracBrowser for help on using the repository browser.