Package lepl :: Package support :: Module list
[hide private]
[frames] | no frames]

Source Code for Module lepl.support.list

  1   
  2  # The contents of this file are subject to the Mozilla Public License 
  3  # (MPL) Version 1.1 (the "License"); you may not use this file except 
  4  # in compliance with the License. You may obtain a copy of the License 
  5  # at http://www.mozilla.org/MPL/ 
  6  # 
  7  # Software distributed under the License is distributed on an "AS IS" 
  8  # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 
  9  # the License for the specific language governing rights and 
 10  # limitations under the License. 
 11  # 
 12  # The Original Code is LEPL (http://www.acooke.org/lepl) 
 13  # The Initial Developer of the Original Code is Andrew Cooke. 
 14  # Portions created by the Initial Developer are Copyright (C) 2009-2010 
 15  # Andrew Cooke (andrew@acooke.org). All Rights Reserved. 
 16  # 
 17  # Alternatively, the contents of this file may be used under the terms 
 18  # of the LGPL license (the GNU Lesser General Public License, 
 19  # http://www.gnu.org/licenses/lgpl.html), in which case the provisions 
 20  # of the LGPL License are applicable instead of those above. 
 21  # 
 22  # If you wish to allow use of your version of this file only under the 
 23  # terms of the LGPL License and not to allow others to use your version 
 24  # of this file under the MPL, indicate your decision by deleting the 
 25  # provisions above and replace them with the notice and other provisions 
 26  # required by the LGPL License.  If you do not delete the provisions 
 27  # above, a recipient may use your version of this file under either the 
 28  # MPL or the LGPL License. 
 29   
 30  ''' 
 31  Support for S-expression ASTs using subclasses of Python's list class. 
 32   
 33  The general support works with any nested iterables (except strings). 
 34  ''' 
 35   
 36  from functools import reduce 
 37   
 38  from lepl.support.lib import fmt, basestring 
 39  from lepl.support.node import Node 
 40   
 41   
42 -class List(list):
43 ''' 44 Extend a list for use in ASTs. 45 46 Note that the argument is treated in exactly the same way as list(). That 47 means it takes a single list or generator as an argument, so to use 48 literally you might type List([1,2,3]) - note the "extra" list. 49 ''' 50
51 - def __repr__(self):
52 return self.__class__.__name__ + '(...)'
53
54 - def __str__(self):
55 return sexpr_to_tree(self)
56 57
58 -def clone_iterable(type_, items):
59 ''' 60 Clone a class that wraps data in an AST. 61 ''' 62 if issubclass(type_, Node): 63 return type_(*list(items)) 64 elif issubclass(type_, basestring): 65 return type_('').join(items) 66 else: 67 return type_(items)
68 69
70 -def sexpr_fold(per_list=None, per_item=None, 71 exclude=lambda x: isinstance(x, basestring)):
72 ''' 73 We need some kind of fold-like procedure for generalising operations on 74 arbitrarily nested iterables. We can't use a normal fold because Python 75 doesn't have the equivalent of cons, etc; this tries to be more Pythonic 76 (see comments later). 77 78 We divide everything into iterables ("lists") and atomic values ("items"). 79 per_list is called with a generator over the (transformed) top-most list, 80 in order. Items (ie atomic values) in that list, when requested from the 81 generator, will be processed by per_item; iterables will be processed by a 82 separate call to per_list (ie recursively). 83 84 So this is more like a recursive map than a fold, but with Python's 85 mutable state and lack of typing it appears to be equally powerful. 86 Note that per_list is passed the previous type, which can be used for 87 dispatching operations. 88 ''' 89 if per_list is None: 90 per_list = clone_iterable 91 if per_item is None: 92 per_item = lambda x: x 93 def items(iterable): 94 for item in iterable: 95 try: 96 if not exclude(item): 97 if isinstance(item, dict): 98 yield per_list(type(item), items(item.items())) 99 else: 100 yield per_list(type(item), items(iter(item))) 101 continue 102 except TypeError: 103 pass 104 yield per_item(item)
105 return lambda list_: per_list(type(list_), items(iter(list_))) 106 107 108 clone_sexpr = sexpr_fold() 109 ''' 110 Clone a set of listed iterables. 111 ''' 112 113 count_sexpr = sexpr_fold(per_list=lambda type_, items: sum(items), 114 per_item=lambda item: 1) 115 ''' 116 Count the number of value nodes in an AST. 117 118 (Note that size(List) gives the number of entries in that list, counting each 119 sublist as "1", while this descends embedded lists, counting their non-iterable 120 contents. 121 ''' 122 123 join = lambda items: reduce(lambda x, y: x+y, items, []) 124 ''' 125 Flatten a list of lists by one level, so [[1],[2, [3]]] becomes [1,2,[3]]. 126 127 Note: this will *only* work correctly if all entries are lists. 128 ''' 129 130 sexpr_flatten = sexpr_fold(per_list=lambda type_, items: join(items), 131 per_item=lambda item: [item]) 132 ''' 133 Flatten a list completely, so [[1],[2, [3]]] becomes [1,2,3] 134 ''' 135 136 _fmt={} 137 _fmt[list] = '[{1}]' 138 _fmt[tuple] = '({1})' 139 140 sexpr_to_str = sexpr_fold(per_list=lambda type_, items: 141 fmt(_fmt.get(type_, '{0}([{1}])'), 142 type_.__name__, ','.join(items)), 143 per_item=lambda item: repr(item)) 144 ''' 145 A flat representation of nested lists (a set of constructors). 146 ''' 147
148 -def sexpr_to_tree(list_):
149 ''' 150 Generate a tree using the same "trick" as `GraphStr`. 151 152 The initial fold returns a function (str, str) -> list(str) at each 153 level. 154 ''' 155 def per_item(item): 156 def fun(first, _rest): 157 return [first + repr(item)]
158 return fun 159 def per_list(type_, list_): 160 def fun(first, rest): 161 yield [first + str(type_.__name__)] 162 force = list(list_) # need to access last item explicitly 163 if force: 164 for item in force[:-1]: 165 yield item(rest + ' +- ', rest + ' | ') 166 yield force[-1](rest + ' `- ', rest + ' ') 167 return lambda first, rest: join(list(fun(first, rest))) 168 fold = sexpr_fold(per_list, per_item) 169 return '\n'.join(fold(list_)('', '')) 170 171
172 -def sexpr_throw(node):
173 ''' 174 Raise an error, if one exists in the results (AST trees are traversed). 175 Otherwise, the results are returned (invoke with ``>>``). 176 ''' 177 def throw_or_copy(type_, items): 178 clone = clone_iterable(type_, items) 179 if isinstance(clone, Exception): 180 raise clone 181 else: 182 return clone
183 return sexpr_fold(per_list=throw_or_copy)(node) 184