Path: blob/main/python/pylang/src/baselib/containers.py
1398 views
# vim:fileencoding=utf-81# License: BSD2# Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>34# globals:ρσ_iterator_symbol, ρσ_kwargs_symbol, ρσ_arraylike, ρσ_repr56def ρσ_equals(a, b):7if a is b:8return True9type_a = jstype(a)10type_b = jstype(b)11# WARNING: We have to use "is" here to avoid recursive call to ρσ_equals by getting "===".12# However, in genuine Python is comparison with a constant is a WARNING/Error.13if type_a is type_b and (type_a is 'number' or type_a is 'string'14or type_a is 'boolean'):15return a is b16if a and jstype(a.__eq__) is 'function':17return a.__eq__(b)18if b and jstype(b.__eq__) is 'function':19return b.__eq__(a)20if ρσ_arraylike(a) and ρσ_arraylike(b):21if a.length != b.length:22return False23for i in range(len(a)):24if not (a[i] == b[i]):25return False26return True27if jstype(a) is 'object' and jstype(28b) is 'object' and a is not None and b is not None and (29(a.constructor is Object or Object.getPrototypeOf(a) is None)30and31(b.constructor is Object or Object.getPrototypeOf(b) is None)):32# Do a dict like comparison as this is most likely either a JS has33# (Object.create(null) or a JS object used as a hash (v"{}"))34akeys, bkeys = Object.keys(a), Object.keys(b)35if akeys.length is not bkeys.length:36return False37for j in range(len(akeys)):38key = akeys[j]39if not (a[key] == b[key]):40return False41return True42return False4344def ρσ_not_equals(a, b):45if a is b:46return False47if a and jstype(a.__ne__) is 'function':48return a.__ne__(b)49if b and jstype(b.__ne__) is 'function':50return b.__ne__(a)51return not ρσ_equals(a, b)5253v'var equals = ρσ_equals'5455# list {{{5657def ρσ_list_extend(iterable):58if Array.isArray(iterable) or jstype(iterable) is 'string':59# Allocate all new memory in one operation60start = this.length61this.length += iterable.length62for v'var i = 0; i < iterable.length; i++':63this[start + i] = iterable[i] # noqa:undef64else:65iterator = iterable.keys() if jstype(Map) is 'function' and v'iterable instanceof Map' else iterable[ρσ_iterator_symbol]()66result = iterator.next()67while not result.done:68this.push(result.value)69result = iterator.next()7071def ρσ_list_index(val, start, stop):72start = start or 073if start < 0:74start = this.length + start75if start < 0:76raise ValueError(val + ' is not in list')77if stop is undefined:78idx = this.indexOf(val, start)79if idx is -1:80raise ValueError(val + ' is not in list')81return idx82if stop < 0:83stop = this.length + stop84for v'var i = start; i < stop; i++':85if this[i] == val:86return i # noqa:undef87raise ValueError(val + ' is not in list')8889def ρσ_list_pop(index):90if this.length is 0:91raise IndexError('list is empty')92if index is undefined:93index = -194ans = this.splice(index, 1)95if not ans.length:96raise IndexError('pop index out of range')97return ans[0]9899def ρσ_list_remove(value):100idx = this.indexOf(value)101if idx is -1:102raise ValueError(value + ' not in list')103this.splice(idx, 1)104105def ρσ_list_to_string():106return '[' + this.join(', ') + ']'107108def ρσ_list_insert(index, val):109if index < 0:110index += this.length111index = min(this.length, max(index, 0))112if index is 0:113this.unshift(val)114return115for v'var i = this.length; i > index; i--':116this[i] = this[i - 1] # noqa:undef117this[index] = val118119def ρσ_list_copy():120return ρσ_list_constructor(this)121122def ρσ_list_clear():123this.length = 0124125def ρσ_list_as_array():126return Array.prototype.slice.call(this)127128def ρσ_list_count(value):129return this.reduce(def(n, val): return n + (val is value);, 0)130131def ρσ_list_sort_key(value):132t = jstype(value)133if t is 'string' or t is 'number':134return value135return value.toString()136137def ρσ_list_sort_cmp(a, b, ap, bp):138if a < b:139return -1140if a > b:141return 1142return ap - bp143144def ρσ_list_sort(key=None, reverse=False):145key = key or ρσ_list_sort_key146mult = -1 if reverse else 1147keymap = dict()148posmap = dict()149for v'var i=0; i < this.length; i++':150k = this[i] # noqa:undef151keymap.set(k, key(k))152posmap.set(k, i)153this.sort(def (a, b): return mult * ρσ_list_sort_cmp(keymap.get(a), keymap.get(b), posmap.get(a), posmap.get(b));)154155def ρσ_list_concat(): # ensure concat() returns an object of type list156ans = Array.prototype.concat.apply(this, arguments)157ρσ_list_decorate(ans)158return ans159160def ρσ_list_slice(): # ensure slice() returns an object of type list161ans = Array.prototype.slice.apply(this, arguments)162ρσ_list_decorate(ans)163return ans164165def ρσ_list_iterator(value):166self = this167return {168'_i':-1,169'_list':self,170'next':def():171this._i += 1172if this._i >= this._list.length:173return {'done':True}174return {'done':False, 'value':this._list[this._i]}175,176}177178def ρσ_list_len():179return this.length180181def ρσ_list_contains(val):182for v'var i = 0; i < this.length; i++':183if this[i] == val:184return True185return False186187def ρσ_list_eq(other):188if not ρσ_arraylike(other):189return False190if this.length != other.length:191return False192for v'var i = 0; i < this.length; i++':193if not (this[i] == other[i]):194return False195return True196197def ρσ_list_mul(other):198# In Javascript it seems that the fastest way199# is to directly assign using a for loop. Everything200# else seems much slower. This is something that201# Python is just much faster at, perhaps because Javascript202# doesn't really have arrays (they are really hash maps).203ans = []204k = int(other)205n = this.length206r"%js let s=0; for(let i=0; i<k; i++) { for(let j=0; j<n; j++) {ans[s++]=this[j];}}"207return ans208209def ρσ_list_decorate(ans):210ans.append = Array.prototype.push211ans.toString = ρσ_list_to_string212ans.inspect = ρσ_list_to_string213ans.extend = ρσ_list_extend214ans.index = ρσ_list_index215ans.pypop = ρσ_list_pop216ans.remove = ρσ_list_remove217ans.insert = ρσ_list_insert218ans.copy = ρσ_list_copy219ans.clear = ρσ_list_clear220ans.count = ρσ_list_count221ans.concat = ρσ_list_concat222ans.pysort = ρσ_list_sort223ans.slice = ρσ_list_slice224ans.as_array = ρσ_list_as_array225ans.__len__ = ρσ_list_len226ans.__contains__ = ρσ_list_contains227ans.__eq__ = ρσ_list_eq228ans.__mul__ = ρσ_list_mul229ans.constructor = ρσ_list_constructor230if jstype(ans[ρσ_iterator_symbol]) is not 'function':231# Happens on ES 5 runtimes232ans[ρσ_iterator_symbol] = ρσ_list_iterator233return ans234235def ρσ_list_constructor(iterable):236if iterable is undefined:237ans = v'[]'238elif ρσ_arraylike(iterable):239ans = new Array(iterable.length)240for v'var i = 0; i < iterable.length; i++':241ans[i] = iterable[i] # noqa:undef242elif jstype(iterable[ρσ_iterator_symbol]) is 'function':243ans = Array.from(iterable)244elif jstype(iterable) is 'number':245# non-pythonic optimization to allocate all needed memory in a single operation246ans = new Array(iterable)247else:248ans = Object.keys(iterable)249return ρσ_list_decorate(ans)250ρσ_list_constructor.__name__ = 'list'251252v'var list = ρσ_list_constructor, list_wrap = ρσ_list_decorate'253254def sorted(iterable, key=None, reverse=False):255ans = ρσ_list_constructor(iterable)256ans.pysort(key, reverse)257return ans258# }}}259260# set {{{261v'var ρσ_global_object_id = 0, ρσ_set_implementation'262263def ρσ_set_keyfor(x):264t = jstype(x)265if t is 'string' or t is 'number' or t is 'boolean':266return '_' + t[0] + x267if v'x === null': # also matches undefined268return "__!@#$0"269ans = x.ρσ_hash_key_prop270if ans is undefined:271v'ans = "_!@#$" + (++ρσ_global_object_id)'272Object.defineProperty(x, 'ρσ_hash_key_prop', { 'value': ans })273return ans274275def ρσ_set_polyfill():276this._store = {}277this.size = 0278279ρσ_set_polyfill.prototype.add = def(x):280key = ρσ_set_keyfor(x)281if not Object.prototype.hasOwnProperty.call(this._store, key):282this.size += 1283this._store[key] = x284return this285286ρσ_set_polyfill.prototype.clear = def(x):287this._store = {}288this.size = 0289290ρσ_set_polyfill.prototype.delete = def(x):291key = ρσ_set_keyfor(x)292if Object.prototype.hasOwnProperty.call(this._store, key):293this.size -= 1294v'delete this._store[key]'295return True296return False297298ρσ_set_polyfill.prototype.has = def(x):299return Object.prototype.hasOwnProperty.call(this._store, ρσ_set_keyfor(x))300301ρσ_set_polyfill.prototype.values = def(x):302ans = v"{'_keys': Object.keys(this._store), '_i':-1, '_s':this._store}"303ans[ρσ_iterator_symbol] = def():304return this305ans['next'] = def():306this._i += 1307if this._i >= this._keys.length:308return v"{'done': true}"309return v"{'done':false, 'value':this._s[this._keys[this._i]]}"310return ans311312if jstype(Set) is not 'function' or jstype(Set.prototype.delete) is not 'function':313v'ρσ_set_implementation = ρσ_set_polyfill'314else:315v'ρσ_set_implementation = Set'316317def ρσ_set(iterable):318if v'this instanceof ρσ_set':319this.jsset = new ρσ_set_implementation() # noqa:undef320ans = this321if iterable is undefined:322return ans323s = ans.jsset324if ρσ_arraylike(iterable):325for v'var i = 0; i < iterable.length; i++':326s.add(iterable[i])327elif jstype(iterable[ρσ_iterator_symbol]) is 'function':328iterator = iterable.keys() if jstype(Map) is 'function' and v'iterable instanceof Map' else iterable[ρσ_iterator_symbol]()329result = iterator.next()330while not result.done:331s.add(result.value)332result = iterator.next()333else:334keys = Object.keys(iterable)335for v'var j=0; j < keys.length; j++':336s.add(keys[j])337return ans338else:339return new ρσ_set(iterable)340ρσ_set.prototype.__name__ = 'set'341342# These are for JavaScript users' convenience343Object.defineProperties(ρσ_set.prototype, {344'length': { 'get': def(): return this.jsset.size; },345'size': { 'get': def(): return this.jsset.size; },346})347348ρσ_set.prototype.__len__ = def(): return this.jsset.size349ρσ_set.prototype.has = ρσ_set.prototype.__contains__ = def(x): return this.jsset.has(x)350ρσ_set.prototype.add = def(x): this.jsset.add(x)351ρσ_set.prototype.clear = def(): this.jsset.clear()352ρσ_set.prototype.copy = def(): return ρσ_set(this)353ρσ_set.prototype.discard = def(x): this.jsset.delete(x)354ρσ_set.prototype[ρσ_iterator_symbol] = def(): return this.jsset.values()355356ρσ_set.prototype.difference = def():357ans = new ρσ_set()358s = ans.jsset359iterator = this.jsset.values()360r = iterator.next()361while not r.done:362x = r.value363has = False364for v'var i = 0; i < arguments.length; i++':365if arguments[i].has(x): # noqa:undef366has = True367break368if not has:369s.add(x)370r = iterator.next()371return ans372373ρσ_set.prototype.difference_update = def():374s = this.jsset375remove = v'[]'376iterator = s.values()377r = iterator.next()378while not r.done:379x = r.value380for v'var i = 0; i < arguments.length; i++':381if arguments[i].has(x): # noqa:undef382remove.push(x)383break384r = iterator.next()385for v'var j = 0; j < remove.length; j++':386s.delete(remove[j]) # noqa:undef387388ρσ_set.prototype.intersection = def():389ans = new ρσ_set()390s = ans.jsset391iterator = this.jsset.values()392r = iterator.next()393while not r.done:394x = r.value395has = True396for v'var i = 0; i < arguments.length; i++':397if not arguments[i].has(x): # noqa:undef398has = False399break400if has:401s.add(x)402r = iterator.next()403return ans404405ρσ_set.prototype.intersection_update = def():406s = this.jsset407remove = v'[]'408iterator = s.values()409r = iterator.next()410while not r.done:411x = r.value412for v'var i = 0; i < arguments.length; i++':413if not arguments[i].has(x): # noqa:undef414remove.push(x)415break416r = iterator.next()417for v'var j = 0; j < remove.length; j++':418s.delete(remove[j]) # noqa:undef419420ρσ_set.prototype.isdisjoint = def(other):421iterator = this.jsset.values()422r = iterator.next()423while not r.done:424x = r.value425if other.has(x):426return False427r = iterator.next()428return True429430ρσ_set.prototype.issubset = def(other):431iterator = this.jsset.values()432r = iterator.next()433while not r.done:434x = r.value435if not other.has(x):436return False437r = iterator.next()438return True439440ρσ_set.prototype.issuperset = def(other):441s = this.jsset442iterator = other.jsset.values()443r = iterator.next()444while not r.done:445x = r.value446if not s.has(x):447return False448r = iterator.next()449return True450451ρσ_set.prototype.pop = def():452iterator = this.jsset.values()453r = iterator.next()454if r.done:455raise KeyError('pop from an empty set')456this.jsset.delete(r.value)457return r.value458459ρσ_set.prototype.remove = def(x):460if not this.jsset.delete(x):461raise KeyError(x.toString())462463ρσ_set.prototype.symmetric_difference = def(other):464return this.union(other).difference(this.intersection(other))465466ρσ_set.prototype.symmetric_difference_update = def(other):467common = this.intersection(other)468this.update(other)469this.difference_update(common)470471ρσ_set.prototype.union = def():472ans = ρσ_set(this)473ans.update.apply(ans, arguments)474return ans475476ρσ_set.prototype.update = def():477s = this.jsset478for v'var i=0; i < arguments.length; i++':479iterator = arguments[i][ρσ_iterator_symbol]() # noqa:undef480r = iterator.next()481while not r.done:482s.add(r.value)483r = iterator.next()484485ρσ_set.prototype.toString = ρσ_set.prototype.__repr__ = ρσ_set.prototype.__str__ = ρσ_set.prototype.inspect = def():486return '{' + list(this).join(', ') + '}'487488ρσ_set.prototype.__eq__ = def(other):489if not v'other instanceof this.constructor':490return False491if other.size is not this.size:492return False493if other.size is 0:494return True495iterator = other[ρσ_iterator_symbol]()496r = iterator.next()497while not r.done:498if not this.has(r.value):499return False500r = iterator.next()501return True502503def ρσ_set_wrap(x):504ans = new ρσ_set()505ans.jsset = x506return ans507508v'var set = ρσ_set, set_wrap = ρσ_set_wrap'509# }}}510511# dict {{{512v'var ρσ_dict_implementation'513514def ρσ_dict_polyfill():515this._store = {}516this.size = 0517518ρσ_dict_polyfill.prototype.set = def(x, value):519key = ρσ_set_keyfor(x)520if not Object.prototype.hasOwnProperty.call(this._store, key):521this.size += 1522this._store[key] = v'[x, value]'523return this524525ρσ_dict_polyfill.prototype.clear = def(x):526this._store = {}527this.size = 0528529ρσ_dict_polyfill.prototype.delete = def(x):530key = ρσ_set_keyfor(x)531if Object.prototype.hasOwnProperty.call(this._store, key):532this.size -= 1533v'delete this._store[key]'534return True535return False536537ρσ_dict_polyfill.prototype.has = def(x):538return Object.prototype.hasOwnProperty.call(this._store, ρσ_set_keyfor(x))539540ρσ_dict_polyfill.prototype.get = def(x):541try:542return this._store[ρσ_set_keyfor(x)][1]543except TypeError: # Key is not present544return undefined545546ρσ_dict_polyfill.prototype.values = def(x):547ans = v"{'_keys': Object.keys(this._store), '_i':-1, '_s':this._store}"548ans[ρσ_iterator_symbol] = def():549return this550ans['next'] = def():551this._i += 1552if this._i >= this._keys.length:553return v"{'done': true}"554return v"{'done':false, 'value':this._s[this._keys[this._i]][1]}"555return ans556557ρσ_dict_polyfill.prototype.keys = def(x):558ans = v"{'_keys': Object.keys(this._store), '_i':-1, '_s':this._store}"559ans[ρσ_iterator_symbol] = def():560return this561ans['next'] = def():562this._i += 1563if this._i >= this._keys.length:564return v"{'done': true}"565return v"{'done':false, 'value':this._s[this._keys[this._i]][0]}"566return ans567568ρσ_dict_polyfill.prototype.entries = def(x):569ans = v"{'_keys': Object.keys(this._store), '_i':-1, '_s':this._store}"570ans[ρσ_iterator_symbol] = def():571return this572ans['next'] = def():573this._i += 1574if this._i >= this._keys.length:575return v"{'done': true}"576return v"{'done':false, 'value':this._s[this._keys[this._i]]}"577return ans578579if jstype(Map) is not 'function' or jstype(Map.prototype.delete) is not 'function':580v'ρσ_dict_implementation = ρσ_dict_polyfill'581else:582v'ρσ_dict_implementation = Map'583584def ρσ_dict(iterable, **kw):585if v'this instanceof ρσ_dict':586# TODO: this is really for copying dicts.587this.jsmap = new ρσ_dict_implementation() # noqa:undef588if iterable is not undefined:589this.update(iterable)590this.update(kw)591return this592else:593return new ρσ_dict(iterable, **kw)594ρσ_dict.prototype.__name__ = 'dict'595596597# These are for JavaScript users' convenience598Object.defineProperties(ρσ_dict.prototype, {599'length': { 'get': def(): return this.jsmap.size; },600'size': { 'get': def(): return this.jsmap.size; },601})602603ρσ_dict.prototype.__len__ = def(): return this.jsmap.size604ρσ_dict.prototype.has = ρσ_dict.prototype.__contains__ = def(x): return this.jsmap.has(x)605ρσ_dict.prototype.set = ρσ_dict.prototype.__setitem__ = def(key, value): this.jsmap.set(key, value)606ρσ_dict.prototype.__delitem__ = def (key): this.jsmap.delete(key)607ρσ_dict.prototype.clear = def(): this.jsmap.clear()608ρσ_dict.prototype.copy = def(): return ρσ_dict(this)609ρσ_dict.prototype.keys = def(): return this.jsmap.keys()610ρσ_dict.prototype.values = def(): return this.jsmap.values()611ρσ_dict.prototype.items = ρσ_dict.prototype.entries = def(): return this.jsmap.entries()612ρσ_dict.prototype[ρσ_iterator_symbol] = def(): return this.jsmap.keys()613614ρσ_dict.prototype.__getitem__ = def (key):615ans = this.jsmap.get(key)616if ans is undefined and not this.jsmap.has(key):617raise KeyError(key + '')618return ans619620ρσ_dict.prototype.get = def (key, defval):621ans = this.jsmap.get(key)622if ans is undefined and not this.jsmap.has(key):623return None if defval is undefined else defval624return ans625626ρσ_dict.prototype.set_default = def (key, defval):627j = this.jsmap628if not j.has(key):629j.set(key, defval)630return defval631return j.get(key)632633ρσ_dict.fromkeys = ρσ_dict.prototype.fromkeys = def (iterable, value=None):634ans = ρσ_dict()635iterator = iter(iterable)636r = iterator.next()637while not r.done:638ans.set(r.value, value)639r = iterator.next()640return ans641642ρσ_dict.prototype.pop = def (key, defval):643ans = this.jsmap.get(key)644if ans is undefined and not this.jsmap.has(key):645if defval is undefined:646raise KeyError(key)647return defval648this.jsmap.delete(key)649return ans650651ρσ_dict.prototype.popitem = def ():652r = this.jsmap.entries().next()653if r.done:654raise KeyError('dict is empty')655this.jsmap.delete(r.value[0])656return r.value657658ρσ_dict.prototype.update = def ():659if arguments.length is 0:660return661m = this.jsmap662iterable = arguments[0]663if Array.isArray(iterable):664for v'var i = 0; i < iterable.length; i++':665m.set(iterable[i][0], iterable[i][1])666elif v'iterable instanceof ρσ_dict':667iterator = iterable.items()668result = iterator.next()669while not result.done:670m.set(result.value[0], result.value[1])671result = iterator.next()672elif jstype(Map) is 'function' and v'iterable instanceof Map':673iterator = iterable.entries()674result = iterator.next()675while not result.done:676m.set(result.value[0], result.value[1])677result = iterator.next()678elif jstype(iterable[ρσ_iterator_symbol]) is 'function':679iterator = iterable[ρσ_iterator_symbol]()680result = iterator.next()681while not result.done:682m.set(result.value[0], result.value[1])683result = iterator.next()684else:685keys = Object.keys(iterable)686for v'var j=0; j < keys.length; j++':687if keys[j] is not ρσ_iterator_symbol:688m.set(keys[j], iterable[keys[j]])689if arguments.length > 1:690ρσ_dict.prototype.update.call(this, arguments[1])691692ρσ_dict.prototype.toString = ρσ_dict.prototype.inspect = ρσ_dict.prototype.__str__ = ρσ_dict.prototype.__repr__ = def():693entries = v'[]'694iterator = this.jsmap.entries()695r = iterator.next()696while not r.done:697entries.push(ρσ_repr(r.value[0]) + ': ' + ρσ_repr(r.value[1]))698r = iterator.next()699return '{' + entries.join(', ') + '}'700701ρσ_dict.prototype.__eq__ = def(other):702if not v'(other instanceof this.constructor)':703return False704if other.size is not this.size:705return False706if other.size is 0:707return True708iterator = other.items()709r = iterator.next()710while not r.done:711x = this.jsmap.get(r.value[0])712if (x is undefined and not this.jsmap.has(r.value[0])) or x is not r.value[1]:713return False714r = iterator.next()715return True716717ρσ_dict.prototype.as_object = def(other):718ans = {}719iterator = this.jsmap.entries()720r = iterator.next()721while not r.done:722ans[r.value[0]] = r.value[1]723r = iterator.next()724return ans725726def ρσ_dict_wrap(x):727ans = new ρσ_dict()728ans.jsmap = x729return ans730731v'var dict = ρσ_dict, dict_wrap = ρσ_dict_wrap'732733# }}}734735736