def compile_test(self, *exprs): proc_var = lisp.env_curr() tpl = 'if not ($#):\n raise _ME()\n%s' % (proc_var,) source = (_S('and'),) + tuple(exprs) test_code = self.compile_lisp(source) return pycode.create( tpl, test_code, shortcut_guards = list(exprs))
def pattern_ext_not(compiler, element): proc_var = lisp.env_curr() matched_var = pycode.name('switch') tpl = """\ try: $# %s = False except _ME: %s = True if not %s: raise _ME, %s %s""" % (matched_var, matched_var, matched_var, proc_var, proc_var) return pycode.create(tpl, compiler.compile(proc_var, element[1]))
def pattern_ext_some(compiler, element): proc_var = lisp.env_curr() element_var = pycode.name('pattern_some') matched_var = pycode.name('switch') tpl = \ '%s = False\n' % (matched_var,) + \ 'for %s in %s\n' % (element_var, proc_var) + \ ' try:\n' + \ ' $#\n' + \ ' %s = True\n' % (matched_var,) + \ ' break\n' + \ ' except _ME:\n' + \ ' continue\n' + \ 'if not %s:\n' % (matched_var,) + \ ' raise _ME, %s\n' % (proc_var,) + \ proc_var return pycode.create(tpl, compiler.compile(element_var, element[1]))
def compile_pattern(self, pattern): proc_var = lisp.env_curr() # Combine effect of each pattern element # Count metadata for caller shortcuts = [] is_raise_random = False output = pycode.create(proc_var) for element in pattern: code = self.compile_pattern_element(element) if code.meta.get('shortcut_nop', False): continue output += code is_raise_random = is_raise_random or code.meta.get( 'raise_random', False) if 'shortcut_equal' in code.meta: shortcut = ('equal', code.meta['shortcut_equal']) elif 'shortcut_bind' in code.meta: shortcut = ('bind', code.meta['shortcut_bind']) else: shortcut = (None, None) shortcuts.append(shortcut) if len(shortcuts) == 0: return output.add_meta(shortcut_nop=True) equals = [x[1] for x in shortcuts if x[0] == 'equal'] if len(equals) == len(shortcuts): return output.add_meta(shortcut_equal=equals[0]) binds = [x[1] for x in shortcuts if x[0] == 'bind'] if len(binds) == len(shortcuts): join_binds = [] for item in binds: join_binds.extend(item) return output.add_meta(shortcut_bind=join_binds, raise_random=is_raise_random) return output.add_meta(raise_random=is_raise_random)
def compile_pattern_element(self, element): proc_var = lisp.env_curr() # '_' is a blank space holder that do nothing if element == _S('_'): return pycode.create(proc_var, shortcut_nop = True) # Name binding bind_to, may_raise = Compiler.get_binding(element) if bind_to is not None: return pycode.create( '%s=%s\n%s' % (bind_to, proc_var, proc_var), shortcut_bind = [bind_to], raise_random = may_raise) opname = lisp.getop(element) # Force equal testing if opname == '\'': return self.compile_equal(element[1]) # Type checking if opname == ':': # NOTE: infact, type checking expression could throw exception return self.compile_test((_S('isinstance'), _S('_'), element[1])) if opname == '?': return self.compile_test(*element[1:]).add_meta(raise_random = True) # Expanded matchings if opname in self.EXTS: return self.EXTS[opname](self, element) # Extractor if type(element) is tuple and len(element) > 0: return self.compile_call( (_S('#'),) + element[1:], (element[0], _S('_'))).add_meta(raise_random = True) # Now all come to the structual matching return self.compile_pattern_struct(element)
def compile_pattern(self, pattern): proc_var = lisp.env_curr() # Combine effect of each pattern element # Count metadata for caller shortcuts = [] is_raise_random = False output = pycode.create(proc_var) for element in pattern: code = self.compile_pattern_element(element) if code.meta.get('shortcut_nop', False): continue output += code is_raise_random = is_raise_random or code.meta.get('raise_random', False) if 'shortcut_equal' in code.meta: shortcut = ('equal', code.meta['shortcut_equal']) elif 'shortcut_bind' in code.meta: shortcut = ('bind', code.meta['shortcut_bind']) else: shortcut = (None, None) shortcuts.append(shortcut) if len(shortcuts) == 0: return output.add_meta(shortcut_nop = True) equals = [x[1] for x in shortcuts if x[0] == 'equal'] if len(equals) == len(shortcuts): return output.add_meta(shortcut_equal = equals[0]) binds = [x[1] for x in shortcuts if x[0] == 'bind'] if len(binds) == len(shortcuts): join_binds = [] for item in binds: join_binds.extend(item) return output.add_meta(shortcut_bind = join_binds, raise_random = is_raise_random) return output.add_meta(raise_random = is_raise_random)
def compile_pattern_element(self, element): proc_var = lisp.env_curr() # '_' is a blank space holder that do nothing if element == _S('_'): return pycode.create(proc_var, shortcut_nop=True) # Name binding bind_to, may_raise = Compiler.get_binding(element) if bind_to is not None: return pycode.create('%s=%s\n%s' % (bind_to, proc_var, proc_var), shortcut_bind=[bind_to], raise_random=may_raise) opname = lisp.getop(element) # Force equal testing if opname == '\'': return self.compile_equal(element[1]) # Type checking if opname == ':': # NOTE: infact, type checking expression could throw exception return self.compile_test((_S('isinstance'), _S('_'), element[1])) if opname == '?': return self.compile_test(*element[1:]).add_meta(raise_random=True) # Expanded matchings if opname in self.EXTS: return self.EXTS[opname](self, element) # Extractor if type(element) is tuple and len(element) > 0: return self.compile_call( (_S('#'), ) + element[1:], (element[0], _S('_'))).add_meta(raise_random=True) # Now all come to the structual matching return self.compile_pattern_struct(element)
def compile_pattern_struct(self, source): proc_var = lisp.env_curr() if type(source) is not list or len(source) < 1: return self.compile_equal(source) if len(source) >= 2 and source[-2] == _S('.'): car_part = source[:-2] cdr_part = source[-1] else: car_part = source cdr_part = _S('_') if not car_part: return self.compile(cdr_part) # Compile each element, do special handling with equal and bind shortcuts equal_elements = [] bind_elements = [] custom_elements = [] is_need_wrap = [False] def handle_element_code(idx_expr, src_var, element_code): is_need_wrap[0] = is_need_wrap[0] or \ element_code.meta.get('raise_random', False) if 'shortcut_nop' in element_code.meta: pass elif 'shortcut_equal' in element_code.meta: equal_elements.append((idx_expr, element_code)) elif 'shortcut_bind' in element_code.meta: bind_elements.append((idx_expr, element_code)) else: custom_elements.append((idx_expr, src_var, element_code)) for element_idx in xrange(len(car_part)): idx_expr = '%s[%d]' % (proc_var, element_idx,) src_var = '%s_%d' % (proc_var, element_idx) element_code = self.compile(src_var, car_part[element_idx]) handle_element_code(idx_expr, src_var, element_code) # Handle cdr_expr as the same if len(car_part) < len(source): cdr_expr = '%s[%d:]' % (proc_var, len(car_part),) src_var = proc_var + '_cdr' cdr_code = self.compile(src_var, cdr_part) handle_element_code(cdr_expr, src_var, cdr_code) # Shortcut: if all element is equal check # Treat as a equal check of whole if len(equal_elements) == len(source): return self.compile_equal(source) code = pycode.create(proc_var).add_meta(shortcut_nop = True) # Compile equal_elemens and length check if equal_elements: fetch_part = '(' + ','.join([x[0] for x in equal_elements]) + ',)' equal_data = tuple(x[1].meta['shortcut_equal'] for x in equal_elements) if len(car_part) == len(source): tpl = 'if len(%s) != %d or $# != $#:\n raise _ME(%s)\n%s' % ( proc_var, len(car_part), proc_var, proc_var) else: tpl = 'if len(%s) < %d or $# != $#:\n raise _ME(%s)\n%s' % ( proc_var, len(car_part), proc_var, proc_var) code += pycode.create(tpl, equal_data, pycode.create(fetch_part)) elif len(car_part) == len(source): code += pycode.create('if len(%s) != %d:\n raise _ME(%s)\n%s' % ( proc_var, len(car_part), proc_var, proc_var)) else: code += pycode.create('if len(%s) < %d:\n raise _ME(%s)\n%s' % ( proc_var, len(car_part), proc_var, proc_var)) for bind_idx, bind_code in bind_elements: code += pycode.create('%s=%s\n%s' % ('='.join(bind_code.meta['shortcut_bind']), bind_idx, proc_var)) if custom_elements: for source_idx, src_var, element_code in custom_elements: code += pycode.create('%s = %s\n%s' % (src_var, source_idx, src_var)) code += element_code return code.add_meta(raise_random = is_need_wrap[0])
def pattern_ext_all(compiler, element): proc_var = lisp.env_curr() element_var = pycode.name('pattern_all') tpl = 'for %s in %s:\n $#\n%s' % (element_var, proc_var, proc_var) return pycode.create(tpl, compiler.compile(element_var, element[1]))
def compile_pattern_struct(self, source): proc_var = lisp.env_curr() if type(source) is not list or len(source) < 1: return self.compile_equal(source) if len(source) >= 2 and source[-2] == _S('.'): car_part = source[:-2] cdr_part = source[-1] else: car_part = source cdr_part = _S('_') if not car_part: return self.compile(cdr_part) # Compile each element, do special handling with equal and bind shortcuts equal_elements = [] bind_elements = [] custom_elements = [] is_need_wrap = [False] def handle_element_code(idx_expr, src_var, element_code): is_need_wrap[0] = is_need_wrap[0] or \ element_code.meta.get('raise_random', False) if 'shortcut_nop' in element_code.meta: pass elif 'shortcut_equal' in element_code.meta: equal_elements.append((idx_expr, element_code)) elif 'shortcut_bind' in element_code.meta: bind_elements.append((idx_expr, element_code)) else: custom_elements.append((idx_expr, src_var, element_code)) for element_idx in xrange(len(car_part)): idx_expr = '%s[%d]' % ( proc_var, element_idx, ) src_var = '%s_%d' % (proc_var, element_idx) element_code = self.compile(src_var, car_part[element_idx]) handle_element_code(idx_expr, src_var, element_code) # Handle cdr_expr as the same if len(car_part) < len(source): cdr_expr = '%s[%d:]' % ( proc_var, len(car_part), ) src_var = proc_var + '_cdr' cdr_code = self.compile(src_var, cdr_part) handle_element_code(cdr_expr, src_var, cdr_code) # Shortcut: if all element is equal check # Treat as a equal check of whole if len(equal_elements) == len(source): return self.compile_equal(source) code = pycode.create(proc_var).add_meta(shortcut_nop=True) # Compile equal_elemens and length check if equal_elements: fetch_part = '(' + ','.join([x[0] for x in equal_elements]) + ',)' equal_data = tuple(x[1].meta['shortcut_equal'] for x in equal_elements) if len(car_part) == len(source): tpl = 'if len(%s) != %d or $# != $#:\n raise _ME(%s)\n%s' % ( proc_var, len(car_part), proc_var, proc_var) else: tpl = 'if len(%s) < %d or $# != $#:\n raise _ME(%s)\n%s' % ( proc_var, len(car_part), proc_var, proc_var) code += pycode.create(tpl, equal_data, pycode.create(fetch_part)) elif len(car_part) == len(source): code += pycode.create( 'if len(%s) != %d:\n raise _ME(%s)\n%s' % (proc_var, len(car_part), proc_var, proc_var)) else: code += pycode.create( 'if len(%s) < %d:\n raise _ME(%s)\n%s' % (proc_var, len(car_part), proc_var, proc_var)) for bind_idx, bind_code in bind_elements: code += pycode.create('%s=%s\n%s' % ('='.join( bind_code.meta['shortcut_bind']), bind_idx, proc_var)) if custom_elements: for source_idx, src_var, element_code in custom_elements: code += pycode.create('%s = %s\n%s' % (src_var, source_idx, src_var)) code += element_code return code.add_meta(raise_random=is_need_wrap[0])
def compile_test(self, *exprs): proc_var = lisp.env_curr() tpl = 'if not ($#):\n raise _ME()\n%s' % (proc_var, ) source = (_S('and'), ) + tuple(exprs) test_code = self.compile_lisp(source) return pycode.create(tpl, test_code, shortcut_guards=list(exprs))