# -*- coding: utf-8 -*- from .jsregexp import Exec import re DIGS = set('0123456789') WHITE = u"\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF" def replacement_template(rep, source, span, npar): """Takes the replacement template and some info about the match and returns filled template """ n = 0 res = '' while n < len(rep)-1: char = rep[n] if char=='$': if rep[n+1]=='$': res += '$' n += 2 continue elif rep[n+1]=='`': # replace with string that is BEFORE match res += source[:span[0]] n += 2 continue elif rep[n+1]=='\'': # replace with string that is AFTER match res += source[span[1]:] n += 2 continue elif rep[n+1] in DIGS: dig = rep[n+1] if n+2len(npar): res += '$'+dig else: # None - undefined has to be replaced with '' res += npar[num-1] if npar[num-1] else '' n += 1 + len(dig) continue res += char n += 1 if nthat: return this.Js(1) return this.Js(0) def match(regexp): this.cok() s = this.to_string() r = this.RegExp(regexp) if regexp.Class!='RegExp' else regexp if not r.glob: return Exec(r, s) r.put('lastIndex', this.Js(0)) found = [] previous_last_index = 0 last_match = True while last_match: result = Exec(r, s) if result.is_null(): last_match=False else: this_index = r.get('lastIndex').value if this_index==previous_last_index: r.put('lastIndex', this.Js(this_index+1)) previous_last_index += 1 else: previous_last_index = this_index matchStr = result.get('0') found.append(matchStr) if not found: return this.null return found def replace(searchValue, replaceValue): # VERY COMPLICATED. to check again. this.cok() string = this.to_string() s = string.value res = '' if not replaceValue.is_callable(): replaceValue = replaceValue.to_string().value func = False else: func = True # Replace all ( global ) if searchValue.Class == 'RegExp' and searchValue.glob: last = 0 for e in re.finditer(searchValue.pat, s): res += s[last:e.span()[0]] if func: # prepare arguments for custom func (replaceValue) args = (e.group(),) + e.groups() + (e.span()[1], string) # convert all types to JS args = map(this.Js, args) res += replaceValue(*args).to_string().value else: res += replacement_template(replaceValue, s, e.span(), e.groups()) last = e.span()[1] res += s[last:] return this.Js(res) elif searchValue.Class=='RegExp': e = re.search(searchValue.pat, s) if e is None: return string span = e.span() pars = e.groups() match = e.group() else: match = searchValue.to_string().value ind = s.find(match) if ind==-1: return string span = ind, ind + len(match) pars = () res = s[:span[0]] if func: args = (match,) + pars + (span[1], string) # convert all types to JS this_ = this args = tuple([this_.Js(x) for x in args]) res += replaceValue(*args).to_string().value else: res += replacement_template(replaceValue, s, span, pars) res += s[span[1]:] return res def search(regexp): this.cok() string = this.to_string() if regexp.Class=='RegExp': rx = regexp else: rx = this.RegExp(regexp) res = re.search(rx.pat, string.value) if res is not None: return this.Js(res.span()[0]) return -1 def slice(start, end): this.cok() s = this.to_string() start = start.to_int() length = len(s.value) end = length if end.is_undefined() else end.to_int() #From = max(length+start, 0) if start<0 else min(length, start) #To = max(length+end, 0) if end<0 else min(length, end) return s.value[start:end] def split (separator, limit): # its a bit different that re.split! this.cok() S = this.to_string() s = S.value lim = 2**32-1 if limit.is_undefined() else limit.to_uint32() if not lim: return [] if separator.is_undefined(): return [s] len_s = len(s) res = [] R = separator if separator.Class=='RegExp' else separator.to_string() if not len_s: if SplitMatch(s, 0, R) is None: return [S] return [] p = q = 0 while q!=len_s: e, cap = SplitMatch(s, q, R) if e is None or e==p: q += 1 continue res.append(s[p:q]) p = q = e if len(res)==lim: return res for element in cap: res.append(this.Js(element)) if len(res)==lim: return res res.append(s[p:]) return res def substring (start, end): this.cok() s = this.to_string().value start = start.to_int() length = len(s) end = length if end.is_undefined() else end.to_int() fstart = min(max(start, 0), length) fend = min(max(end, 0), length) return this.Js(s[min(fstart, fend):max(fstart, fend)]) def substr(start, length): #I hate this function and its description in specification r1 = this.to_string().value r2 = start.to_int() r3 = 10**20 if length.is_undefined() else length.to_int() r4 = len(r1) r5 = r2 if r2>=0 else max(0, r2+r4) r6 = min(max(r3 ,0), r4 - r5) if r6<=0: return '' return r1[r5:r5+r6] def toLowerCase(): this.cok() return this.Js(this.to_string().value.lower()) def toLocaleLowerCase(): this.cok() return this.Js(this.to_string().value.lower()) def toUpperCase(): this.cok() return this.Js(this.to_string().value.upper()) def toLocaleUpperCase(): this.cok() return this.Js(this.to_string().value.upper()) def trim(): this.cok() return this.Js(this.to_string().value.strip(WHITE)) def SplitMatch(s, q, R): # s is Py String to match, q is the py int match start and R is Js RegExp or String. if R.Class=='RegExp': res = R.match(s, q) return (None, ()) if res is None else (res.span()[1], res.groups()) # R is just a string if s[q:].startswith(R.value): return q+len(R.value), () return None, ()