from __future__ import absolute_import, print_function from .ast_tools import token, symbol, ast_to_string, match, atom_list def slice_ast_to_dict(ast_seq): sl_vars = {} if isinstance(ast_seq, (list, tuple)): for pattern in slice_patterns: found,data = match(pattern,ast_seq) if found: sl_vars = {'begin':'_beg', 'end':'_end', 'step':'_stp', 'single_index':'_index'} for key in data: data[key] = ast_to_string(data[key]) sl_vars.update(data) break return sl_vars def build_slice_atom(slice_vars, position): # Note: This produces slices that are incorrect for Python # evaluation because of slicing being exclusive in Python # and inclusive for blitz on the top end of the range. # This difference should really be handle in a blitz specific transform, # but I've put it here for convenience. This doesn't cause any # problems in code, its just a maintenance hassle # (I'll forget I did it here) and inelegant. *FIX ME*. ########################################################################### # Handling negative indices. # # Range indices that begin with a negative sign, '-', are assumed to be # negative. Blitz++ interprets negative indices differently than # Python. To correct this, we subtract negative indices from the length # of the array (at run-time). If indices do not start with a negative # sign, they are assumed to be positive. # # This scheme doesn't work in the general case. For example, if you # are calculating negative indices from a math expression that doesn't # start with the negative sign, then it will be assumed positive and # hence generate wrong results (and maybe a seg-fault). # # I think this case can might be remedied by calculating all ranges on # the fly, and then subtracting them from the length of the array in # that dimension if they are negative. This is major code bloat in the # funcitons and more work. Save till later... ########################################################################### # I don't think the strip is necessary, but it insures # that '-' is the first sign for negative indices. if slice_vars['single_index'] != '_index': expr = '%(single_index)s' % slice_vars else: begin = slice_vars['begin'].strip() if begin[0] == '-': slice_vars['begin'] = 'N' + slice_vars['var']+repr(position)+begin end = slice_vars['end'].strip() if end != '_end' and end[0] != '-': #compensate for blitz using inclusive indexing on top end #of slice for positive indices. slice_vars['end'] = end + '-1' if end[0] == '-': slice_vars['end'] = 'N%s[%d]%s-1' % (slice_vars['var'],position,end) if slice_vars['step'] == '_stp': # this if/then isn't strictly necessary, it'll # just keep the output code a little cleaner expr = 'slice(%(begin)s,%(end)s)' % slice_vars else: expr = 'slice(%(begin)s,%(end)s,%(step)s)' % slice_vars val = atom_list(expr) return val def transform_subscript_list(subscript_dict): # this is gonna edit the ast_list... subscript_list = subscript_dict['subscript_list'] var = subscript_dict['var'] #skip the first entry (the subscript_list symbol) slice_position = -1 for i in range(1,len(subscript_list)): #skip commas... if subscript_list[i][0] != token.COMMA: slice_position += 1 slice_vars = slice_ast_to_dict(subscript_list[i]) slice_vars['var'] = var # create a slice(b,e,s) atom and insert in # place of the x:y:z atom in the tree. subscript_list[i] = build_slice_atom(slice_vars, slice_position) def harvest_subscript_dicts(ast_list): """ Needs Tests! """ subscript_lists = [] if isinstance(ast_list, list): found,data = match(indexed_array_pattern,ast_list) # data is a dict with 'var' = variable name # and 'subscript_list' = to the ast_seq for the subscript list if found: subscript_lists.append(data) for item in ast_list: if isinstance(item, list): subscript_lists.extend(harvest_subscript_dicts(item)) return subscript_lists def transform_slices(ast_list): """ Walk through an ast_list converting all x:y:z subscripts to slice(x,y,z) subscripts. """ all_dicts = harvest_subscript_dicts(ast_list) for subscript_dict in all_dicts: transform_subscript_list(subscript_dict) slice_patterns = [] CLN = (token.COLON,':') CLN2 = (symbol.sliceop, (token.COLON, ':')) CLN2_STEP = (symbol.sliceop, (token.COLON, ':'),['step']) # [begin:end:step] slice_patterns.append((symbol.subscript, ['begin'],CLN,['end'], CLN2_STEP)) # [:end:step] slice_patterns.append((symbol.subscript, CLN,['end'], CLN2_STEP)) # [begin::step] slice_patterns.append((symbol.subscript, ['begin'],CLN, CLN2_STEP)) # [begin:end:] slice_patterns.append((symbol.subscript, ['begin'],CLN,['end'], CLN2)) # [begin::] slice_patterns.append((symbol.subscript, ['begin'],CLN, CLN2)) # [:end:] slice_patterns.append((symbol.subscript, CLN,['end'], CLN2,)) # [::step] slice_patterns.append((symbol.subscript, CLN, CLN2_STEP)) # [::] slice_patterns.append((symbol.subscript, CLN, CLN2)) # begin:end variants slice_patterns.append((symbol.subscript, ['begin'],CLN,['end'])) slice_patterns.append((symbol.subscript, CLN,['end'])) slice_patterns.append((symbol.subscript, ['begin'],CLN)) slice_patterns.append((symbol.subscript, CLN)) # a[0] variant -- can't believe I left this out... slice_patterns.append((symbol.subscript,['single_index'])) indexed_array_pattern = \ (symbol.power, (symbol.atom,(token.NAME, ['var'])), (symbol.trailer, (token.LSQB, '['), ['subscript_list'], (token.RSQB, ']') ) )