Permalink
Jan 14, 2015
Jan 14, 2015
Jan 1, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 14, 2015
Jan 1, 2015
Jan 1, 2015
Jan 1, 2015
Newer
100644
1605 lines (1435 sloc)
50 KB
9
__name__:'str',
10
$native:true
11
}
12
13
$StringDict.__add__ = function(self,other){
14
if(!(typeof other==="string")){
15
try{return getattr(other,'__radd__')(self)}
16
catch(err){throw _b_.TypeError(
17
"Can't convert "+$B.get_class(other).__name__+" to str implicitely")}
18
}
19
return self+other
20
}
21
22
$StringDict.__contains__ = function(self,item){
23
if(!(typeof item==="string")){throw _b_.TypeError(
24
"'in <string>' requires string as left operand, not "+item.__class__)}
25
var nbcar = item.length
26
if(nbcar==0) return true // a string contains the empty string
27
if(self.length==0) return nbcar==0
29
if(self.substr(i,nbcar)==item) return true
30
}
31
return false
32
}
33
34
$StringDict.__delitem__ = function(){
35
throw _b_.TypeError("'str' object doesn't support item deletion")
36
}
37
38
// __dir__must be assigned explicitely because attribute resolution for builtin
39
// classes doesn't use __mro__
40
$StringDict.__dir__ = $ObjectDict.__dir__
41
42
$StringDict.__eq__ = function(self,other){
43
if(other===undefined){ // compare object "self" to class "str"
44
return self===str
45
}
46
if (_b_.isinstance(other, _b_.str)) {
47
return other.valueOf() == self.valueOf()
48
}
52
function preformat(self, fmt){
53
if(fmt.empty){return _b_.str(self)}
54
if(fmt.type && fmt.type!='s'){
55
throw _b_.ValueError("Unknown format code '"+fmt.type+
56
"' for object of type 'str'")
57
}
58
return self
59
}
60
61
$StringDict.__format__ = function(self, format_spec) {
62
var fmt = new $B.parse_format_spec(format_spec)
63
// For strings, alignment default to left
64
fmt.align = fmt.align || '<'
65
return $B.format_width(preformat(self, fmt), fmt)
66
}
67
68
$StringDict.__getitem__ = function(self,arg){
69
if(isinstance(arg,_b_.int)){
70
var pos = arg
71
if(arg<0) pos+=self.length
72
if(pos>=0 && pos<self.length) return self.charAt(pos)
73
throw _b_.IndexError('string index out of range')
74
}
75
if(isinstance(arg,slice)) {
76
var step = arg.step===None ? 1 : arg.step
77
if(step>0){
78
var start = arg.start===None ? 0 : arg.start
79
var stop = arg.stop===None ? getattr(self,'__len__')() : arg.stop
80
}else{
81
var start = arg.start===None ? getattr(self,'__len__')()-1 : arg.start
82
var stop = arg.stop===None ? 0 : arg.stop
83
}
84
if(start<0) start+=self.length
85
if(stop<0) stop+=self.length
86
var res = '',i=null
87
if(step>0){
88
if(stop<=start) return ''
89
for(var i=start;i<stop;i+=step) res += self.charAt(i)
90
} else {
91
if(stop>=start) return ''
92
for(var i=start;i>=stop;i+=step) res += self.charAt(i)
93
}
94
return res
95
}
96
if(isinstance(arg,bool)) return self.__getitem__(_b_.int(arg))
101
if (self === undefined) {
102
return $StringDict.__hashvalue__ || $B.$py_next_hash-- // for hash of string type (not instance of string)
103
}
104
105
//http://stackoverflow.com/questions/2909106/python-whats-a-correct-and-good-way-to-implement-hash
106
// this implementation for strings maybe good enough for us..
107
108
var hash=1;
110
hash=(101*hash + self.charCodeAt(i)) & 0xFFFFFFFF
111
}
112
113
return hash
114
}
115
116
$StringDict.__init__ = function(self,arg){
117
self.valueOf = function(){return arg}
118
self.toString = function(){return arg}
120
}
121
122
var $str_iterator = $B.$iterator_class('str_iterator')
123
$StringDict.__iter__ = function(self){
124
var items = self.split('') // list of all characters in string
125
return $B.$iterator(items,$str_iterator)
126
}
127
128
$StringDict.__len__ = function(self){return self.length}
129
132
var kwarg_key = new RegExp('([^\\)]*)\\)')
133
134
var NotANumber = function() {
135
this.name = 'NotANumber'
136
}
137
138
var number_check=function(s) {
139
if(!isinstance(s,[_b_.int,_b_.float])){
140
throw new NotANumber()
141
}
142
}
143
144
var get_char_array = function(size, char) {
145
if (size <= 0)
146
return ''
147
return new Array(size + 1).join(char)
148
}
149
150
var format_padding = function(s, flags, minus_one) {
151
var padding = flags.padding
152
if (!padding) { // undefined
153
return s
154
}
155
s = s.toString()
156
padding = parseInt(padding, 10)
157
if (minus_one) { // numeric formatting where sign goes in front of padding
158
padding -= 1
159
}
160
if (!flags.left) {
161
return get_char_array(padding - s.length, flags.pad_char) + s
162
} else {
163
// left adjusted
164
return s + get_char_array(padding - s.length, flags.pad_char)
165
}
166
}
167
168
var format_int_precision = function(val, flags) {
169
var precision = flags.precision
170
if (!precision) {
171
return val.toString()
172
}
173
precision = parseInt(precision, 10)
174
var s
175
if (val.__class__ === $B.LongInt.$dict) {
176
s=$B.LongInt.$dict.to_base(val, 10)
177
} else {
178
s=val.toString()
179
}
180
var sign = s[0]
181
if (s[0] === '-') {
182
return '-' + get_char_array(precision - s.length + 1, '0') + s.slice(1)
183
}
184
return get_char_array(precision - s.length, '0') + s
185
}
186
187
var format_float_precision = function(val, upper, flags, modifier) {
188
var precision = flags.precision
189
// val is a float
190
if (isFinite(val)) {
191
val = modifier(val, precision, flags, upper)
192
return val
193
}
194
if (val === Infinity) {
195
val = 'inf'
196
} else if (val === -Infinity) {
197
val = '-inf'
198
} else {
199
val = 'nan'
200
}
201
if (upper) {
202
return val.toUpperCase()
203
}
204
return val
206
}
207
208
var format_sign = function(val, flags) {
209
if (flags.sign) {
210
if (val >= 0) {
211
return "+"
213
} else if (flags.space) {
214
if (val >= 0) {
215
return " "
216
}
217
}
218
return ""
219
}
221
var str_format = function(val, flags) {
222
// string format supports left and right padding
223
flags.pad_char = " " // even if 0 padding is defined, don't use it
224
return format_padding(str(val), flags)
225
}
229
if (val.__class__ === $B.LongInt.$dict) {
230
val = $B.LongInt.$dict.to_base(val, 10)
231
} else {
232
val = parseInt(val)
233
}
234
235
var s = format_int_precision(val, flags)
236
if (flags.pad_char === '0') {
237
if (val < 0) {
238
s = s.substring(1)
239
return '-' + format_padding(s, flags, true)
240
}
241
var sign = format_sign(val, flags)
242
if (sign !== '') {
243
return sign + format_padding(s, flags, true)
244
}
245
}
246
247
return format_padding(format_sign(val, flags) + s, flags)
248
}
250
var repr_format = function(val, flags) {
251
flags.pad_char = " " // even if 0 padding is defined, don't use it
252
return format_padding(repr(val), flags)
253
}
255
var ascii_format = function(val, flags) {
256
flags.pad_char = " " // even if 0 padding is defined, don't use it
257
return format_padding(ascii(val), flags)
258
}
260
// converts to val to float and sets precision if missing
261
var _float_helper = function(val, flags) {
262
number_check(val)
263
if (!flags.precision) {
264
if (!flags.decimal_point) {
265
flags.precision = 6
266
} else {
267
flags.precision = 0
268
}
269
} else {
270
flags.precision = parseInt(flags.precision, 10)
271
validate_precision(flags.precision)
272
}
273
return parseFloat(val)
274
}
276
// used to capture and remove trailing zeroes
277
var trailing_zeros = /(.*?)(0+)([eE].*)/
278
var leading_zeros = /\.(0*)/
279
var trailing_dot = /\.$/
281
var validate_precision = function(precision) {
282
// force precision to limits of javascript
283
if (precision > 20) {
284
throw _b_.ValueError("precision too big")
285
}
286
}
287
288
// gG
289
var floating_point_format = function(val, upper, flags) {
290
val = _float_helper(val, flags)
291
var v = val.toString()
292
var v_len = v.length
293
var dot_idx = v.indexOf('.')
294
if (dot_idx < 0) {
295
dot_idx = v_len
296
}
297
if (val < 1 && val > -1) {
298
var zeros = leading_zeros.exec(v)
299
var numzeros
300
if (zeros) {
301
numzeros = zeros[1].length
302
} else {
303
numzeros = 0
304
}
305
if (numzeros >= 4) {
306
val = format_sign(val, flags) + format_float_precision(val, upper, flags, _floating_g_exp_helper)
307
if (!flags.alternate) {
308
var trl = trailing_zeros.exec(val)
309
if (trl) {
310
val = trl[1].replace(trailing_dot, '') + trl[3] // remove trailing
316
}
317
return format_padding(val, flags)
318
}
319
flags.precision += numzeros
320
return format_padding(format_sign(val, flags) + format_float_precision(val, upper, flags,
321
function(val, precision) {
322
val = val.toFixed(min(precision, v_len - dot_idx) + numzeros)
323
}), flags)
324
}
325
326
if (dot_idx > flags.precision) {
327
val = format_sign(val, flags) + format_float_precision(val, upper, flags, _floating_g_exp_helper)
328
if (!flags.alternate) {
329
var trl = trailing_zeros.exec(val)
330
if (trl) {
331
val = trl[1].replace(trailing_dot, '') + trl[3] // remove trailing
332
}
333
} else {
334
if (flags.precision <= 1) {
335
val = val[0] + '.' + val.substring(1)
336
}
337
}
338
return format_padding(val, flags)
339
}
340
return format_padding(format_sign(val, flags) + format_float_precision(val, upper, flags,
341
function(val, precision) {
342
if (!flags.decimal_point) {
343
precision = min(v_len - 1, 6)
344
} else if (precision > v_len) {
345
if (!flags.alternate) {
346
precision = v_len
348
}
349
if (precision < dot_idx) {
350
precision = dot_idx
351
}
352
return val.toFixed(precision - dot_idx)
353
}), flags)
354
}
356
var _floating_g_exp_helper = function(val, precision, flags, upper) {
357
if (precision) {
358
--precision
359
}
360
val = val.toExponential(precision)
361
// pad exponent to two digits
362
var e_idx = val.lastIndexOf('e')
363
if (e_idx > val.length - 4) {
364
val = val.substring(0, e_idx + 2) + '0' + val.substring(e_idx + 2)
365
}
366
if (upper) {
367
return val.toUpperCase()
368
}
369
return val
370
}
371
372
// fF
373
var floating_point_decimal_format = function(val, upper, flags) {
374
val = _float_helper(val, flags)
375
return format_padding(format_sign(val, flags) + format_float_precision(val, upper, flags,
376
function(val, precision, flags) {
377
val = val.toFixed(precision)
378
if (precision === 0 && flags.alternate) {
379
val += '.'
380
}
381
return val
382
}), flags)
383
}
384
385
var _floating_exp_helper = function(val, precision, flags, upper) {
386
val = val.toExponential(precision)
387
// pad exponent to two digits
388
var e_idx = val.lastIndexOf('e')
389
if (e_idx > val.length - 4) {
390
val = val.substring(0, e_idx + 2) + '0' + val.substring(e_idx + 2)
391
}
392
if (upper) {
393
return val.toUpperCase()
394
}
395
return val
396
}
397
398
// eE
399
var floating_point_exponential_format = function(val, upper, flags) {
400
val = _float_helper(val, flags)
401
402
return format_padding(format_sign(val, flags) + format_float_precision(val, upper, flags, _floating_exp_helper), flags)
403
}
404
405
var signed_hex_format = function(val, upper, flags) {
408
409
if (val.__class__ === $B.LongInt.$dict) {
410
ret=$B.LongInt.$dict.to_base(val, 16)
411
} else {
412
ret = parseInt(val)
413
ret = ret.toString(16)
414
}
415
ret = format_int_precision(ret, flags)
416
if (upper) {
417
ret = ret.toUpperCase()
418
}
419
if (flags.pad_char === '0') {
420
if (val < 0) {
421
ret = ret.substring(1)
422
ret = '-' + format_padding(ret, flags, true)
423
}
424
var sign = format_sign(val, flags)
425
if (sign !== '') {
426
ret = sign + format_padding(ret, flags, true)
428
}
429
430
if (flags.alternate) {
431
if (ret.charAt(0) === '-') {
432
if (upper) {
433
ret = "-0X" + ret.slice(1)
434
} else {
435
ret = "-0x" + ret.slice(1)
436
}
437
} else {
438
if (upper) {
439
ret = "0X" + ret
440
} else {
441
ret = "0x" + ret
442
}
443
}
444
}
445
return format_padding(format_sign(val, flags) + ret, flags)
446
}
450
var ret
451
452
if (val.__class__ === $B.LongInt.$dict) {
453
ret = $B.LongInt.$dict.to_base(8)
454
} else {
455
ret = parseInt(val)
456
ret = ret.toString(8)
457
}
458
461
if (flags.pad_char === '0') {
462
if (val < 0) {
463
ret = ret.substring(1)
464
ret = '-' + format_padding(ret, flags, true)
465
}
466
var sign = format_sign(val, flags)
467
if (sign !== '') {
468
ret = sign + format_padding(ret, flags, true)
469
}
471
472
if (flags.alternate) {
473
if (ret.charAt(0) === '-') {
474
ret = "-0o" + ret.slice(1)
475
} else {
476
ret = "0o" + ret
477
}
479
return format_padding(ret, flags)
480
}
481
482
var single_char_format = function(val, flags) {
483
if(isinstance(val,str) && val.length==1) return val
484
try {
485
val = _b_.int(val) // yes, floats are valid (they are cast to int)
486
} catch (err) {
487
throw _b_.TypeError('%c requires int or char')
488
}
489
return format_padding(chr(val), flags)
490
}
491
492
var num_flag = function(c, flags) {
493
if (c === '0' && !flags.padding && !flags.decimal_point && !flags.left) {
494
flags.pad_char = '0'
495
return
496
}
497
if (!flags.decimal_point) {
498
flags.padding = (flags.padding || "") + c
499
} else {
500
flags.precision = (flags.precision || "") + c
501
}
502
}
503
504
var decimal_point_flag = function(val, flags) {
505
if (flags.decimal_point) {
506
// can only have one decimal point
507
throw new UnsupportedChar()
508
}
509
flags.decimal_point = true
510
}
511
512
var neg_flag = function(val, flags) {
513
flags.pad_char = ' ' // overrides '0' flag
514
flags.left = true
515
}
516
517
var space_flag = function(val, flags) {
518
flags.space = true
519
}
520
521
var sign_flag = function(val, flags) {
522
flags.sign = true
523
}
524
525
var alternate_flag = function(val, flags) {
526
flags.alternate = true
527
}
528
530
's': str_format,
531
'd': num_format,
532
'i': num_format,
533
'u': num_format,
534
'o': octal_format,
535
'r': repr_format,
536
'a': ascii_format,
537
'g': function(val, flags) {return floating_point_format(val, false, flags)},
538
'G': function(val, flags) {return floating_point_format(val, true, flags)},
539
'f': function(val, flags) {return floating_point_decimal_format(val, false, flags)},
540
'F': function(val, flags) {return floating_point_decimal_format(val, true, flags)},
541
'e': function(val, flags) {return floating_point_exponential_format(val, false, flags)},
542
'E': function(val, flags) {return floating_point_exponential_format(val, true, flags)},
543
'x': function(val, flags) {return signed_hex_format(val, false, flags)},
544
'X': function(val, flags) {return signed_hex_format(val, true, flags)},
545
'c': single_char_format,
546
'0': function(val, flags) {return num_flag('0', flags)},
547
'1': function(val, flags) {return num_flag('1', flags)},
548
'2': function(val, flags) {return num_flag('2', flags)},
549
'3': function(val, flags) {return num_flag('3', flags)},
550
'4': function(val, flags) {return num_flag('4', flags)},
551
'5': function(val, flags) {return num_flag('5', flags)},
552
'6': function(val, flags) {return num_flag('6', flags)},
553
'7': function(val, flags) {return num_flag('7', flags)},
554
'8': function(val, flags) {return num_flag('8', flags)},
555
'9': function(val, flags) {return num_flag('9', flags)},
556
'-': neg_flag,
557
' ': space_flag,
558
'+': sign_flag,
559
'.': decimal_point_flag,
560
'#': alternate_flag
561
}
562
563
// exception thrown when an unsupported char is encountered in legacy format
564
var UnsupportedChar = function() {
565
this.name = "UnsupportedChar"
566
}
567
579
++pos
580
var rslt = kwarg_key.exec(s.substring(newpos))
581
if (!rslt) {
582
throw _b_.ValueError("incomplete format key")
583
}
584
var key = rslt[1]
585
newpos += rslt[0].length
586
try {
588
} catch(err) {
589
if (err.name === "KeyError") {
590
throw err
591
}
592
throw _b_.TypeError("format requires a mapping")
593
}
608
}
609
catch(err) {
610
if (err.name === "IndexError") {
611
throw _b_.TypeError("not enough arguments for format string")
612
} else {
613
throw err
614
}
620
// todo: get flags, type
621
// todo: string value based on flags, type, value
622
var flags = {'pad_char': ' '}
623
do {
630
if (ret !== undefined) {
631
return ret
632
}
633
++newpos
634
}
635
} catch (err) {
636
if (err.name === "UnsupportedChar") {
637
invalid_char = s[newpos]
638
if (invalid_char === undefined) {
639
throw _b_.ValueError("incomplete format")
640
}
641
throw _b_.ValueError("unsupported format character '" + invalid_char +
642
"' (0x" + invalid_char.charCodeAt(0).toString(16) + ") at index " + newpos)
643
} else if (err.name === "NotANumber") {
644
var try_char = s[newpos]
651
}
652
} else {
653
cls = cls.__name__
654
}
655
throw _b_.TypeError("%" + try_char + " format: a number is required, not " + cls)
656
} else {
657
throw err
658
}
680
}
681
}
682
} else {
683
// % at end of string
684
throw _b_.ValueError("incomplete format")
685
}
686
pos = newpos + 1
687
} while (pos < length)
688
691
692
$StringDict.__mro__ = [$StringDict,$ObjectDict]
693
694
$StringDict.__mul__ = function(self,other){
695
if(!isinstance(other,_b_.int)){throw _b_.TypeError(
696
"Can't multiply sequence by non-int of type '"+
697
$B.get_class(other).__name__+"'")}
698
$res = ''
699
for(var i=0;i<other;i++){$res+=self.valueOf()}
700
return $res
701
}
702
703
$StringDict.__ne__ = function(self,other){return other!==self.valueOf()}
704
705
$StringDict.__repr__ = function(self){
706
if(self.search('"')==-1 && self.search("'")==-1){
707
return "'"+self+"'"
708
}else if(self.search('"')==-1){
709
return '"'+self+'"'
710
}
711
var qesc = new RegExp("'","g") // to escape single quote
712
var res = self.replace(/\n/g,'\\\\n')
713
res = "'"+res.replace(qesc,"\\'")+"'"
714
return res
715
}
716
717
$StringDict.__setattr__ = function(self,attr,value){return setattr(self,attr,value)}
718
719
$StringDict.__setitem__ = function(self,attr,value){
720
throw _b_.TypeError("'str' object does not support item assignment")
721
}
722
$StringDict.__str__ = function(self){
723
if(self===undefined) return "<class 'str'>"
724
return self.toString()
725
}
726
$StringDict.toString = function(){return 'string!'}
727
728
// generate comparison methods
729
var $comp_func = function(self,other){
730
if(typeof other !=="string"){throw _b_.TypeError(
731
"unorderable types: 'str' > "+$B.get_class(other).__name__+"()")}
732
return self > other
733
}
734
$comp_func += '' // source code
735
var $comps = {'>':'gt','>=':'ge','<':'lt','<=':'le'}
736
for(var $op in $comps){
737
eval("$StringDict.__"+$comps[$op]+'__ = '+$comp_func.replace(/>/gm,$op))
738
}
739
740
// add "reflected" methods
741
$B.make_rmethods($StringDict)
742
743
// unsupported operations
744
var $notimplemented = function(self,other){
745
throw NotImplementedError("OPERATOR not implemented for class str")
746
}
747
748
$StringDict.capitalize = function(self){
749
if(self.length==0) return ''
750
return self.charAt(0).toUpperCase()+self.substr(1).toLowerCase()
751
}
752
753
$StringDict.casefold = function(self) {
754
throw _b_.NotImplementedError("function casefold not implemented yet");
755
}
756
757
$StringDict.center = function(self,width,fillchar){
758
if(fillchar===undefined){fillchar=' '}else{fillchar=fillchar}
759
if(width<=self.length) return self
760
761
var pad = parseInt((width-self.length)/2)
762
var res = Array(pad+1).join(fillchar) // is this statement faster than the for loop below?
763
res += self + res
764
if(res.length<width){res += fillchar}
765
return res
766
}
767
768
$StringDict.count = function(self,elt){
769
if(!(typeof elt==="string")){throw _b_.TypeError(
770
"Can't convert '"+elt.__class__.__name__+"' object to str implicitly")}
771
//needs to be non overlapping occurrences of substring in string.
772
var n=0, pos=0
773
while(1){
774
pos=self.indexOf(elt,pos)
775
if(pos>=0){ n++; pos+=elt.length} else break;
776
}
777
return n
778
}
779
780
$StringDict.encode = function(self, encoding) {
781
if (encoding === undefined) encoding='utf-8'
782
if(encoding=='rot13' || encoding=='rot_13'){
783
// Special case : returns a string
784
var res = ''
785
for(var i=0, _len = self.length; i<_len ; i++){
786
var char = self.charAt(i)
787
if(('a'<=char && char<='m') || ('A'<=char && char<='M')){
788
res += String.fromCharCode(String.charCodeAt(char)+13)
789
}else if(('m'<char && char<='z') || ('M'<char && char<='Z')){
790
res += String.fromCharCode(String.charCodeAt(char)-13)
791
}else{res += char}
792
}
793
return res
794
}
796
}
797
798
$StringDict.endswith = function(self){
799
// Return True if the string ends with the specified suffix, otherwise
800
// return False. suffix can also be a tuple of suffixes to look for.
801
// With optional start, test beginning at that position. With optional
802
// end, stop comparing at that position.
803
var $ns=$B.$MakeArgs1("$StringDict.endswith",4,
804
{self:null, suffix:null, start:null, end:null},
805
['self', 'suffix', 'start', 'end'],
806
arguments,{start:0, end:self.length-1},null,null)
807
var suffixes = $ns['suffix']
808
if(!isinstance(suffixes,_b_.tuple)){suffixes=[suffixes]}
813
suffix = suffixes[i]
814
if(suffix.length<=s.length &&
815
s.substr(s.length-suffix.length)==suffix) return true
816
}
817
return false
818
}
819
820
$StringDict.expandtabs = function(self, tabsize) {
821
tabsize=tabsize || 8
822
var _str=''
823
for (var i=0; i < tabsize; i++) _str+=' '
824
return self.valueOf().replace(/\t/g, _str)
825
}
826
827
$StringDict.find = function(self){
828
// Return the lowest index in the string where substring sub is found,
829
// such that sub is contained in the slice s[start:end]. Optional
830
// arguments start and end are interpreted as in slice notation.
831
// Return -1 if sub is not found.
832
var start=0,end=self.length
833
var $ns=$B.$MakeArgs1("$StringDict.find",4,
834
{self:null, sub:null, start:null, end:null},
835
['self', 'sub', 'start','end'],
836
arguments,{start:0, end:self.length},null,null)
837
for(var attr in $ns){eval('var '+attr+'=$ns[attr]')}
838
if(!isinstance(sub,str)){throw _b_.TypeError(
839
"Can't convert '"+sub.__class__.__name__+"' object to str implicitly")}
840
if(!isinstance(start,_b_.int)||!isinstance(end,_b_.int)){
841
throw _b_.TypeError(
842
"slice indices must be integers or None or have an __index__ method")}
843
var s = self.substring(start,end)
844
var esc_sub = ''
846
switch(sub.charAt(i)) {
847
case '[':
848
case '.':
849
case '*':
850
case '+':
851
case '?':
852
case '|':
853
case '(':
854
case ')':
855
case '$':
856
case '^':
857
esc_sub += '\\'
858
}
859
esc_sub += sub.charAt(i)
860
}
861
var res = s.search(esc_sub)
862
if(res==-1) return -1
863
return start+res
864
}
865
868
// Parse a "format string", as described in the Python documentation
869
// Return a format object. For the format string
870
// a.x[z]!r:...
871
// the object has attributes :
872
// - name : "a"
873
// - name_ext : [".x", "[z]"]
874
// - conv : r
875
// - spec : rest of string after :
877
var elts = fmt_string.split(':'), name, conv, spec, name_ext=[]
878
if(elts.length==1){
879
// No : in the string : it only contains a name
880
name = fmt_string
881
}else{
882
// name is before the first ":"
883
// spec (the format specification) is after
884
name = elts[0]
885
spec = elts.splice(1).join(':')
886
}
887
888
var elts = name.split('!')
889
if(elts.length>1){
890
name=elts[0]
891
conv=elts[1] // conversion flag
892
if(conv.length!==1 || 'ras'.search(conv)==-1){
893
throw _b_.ValueError('wrong conversion flag '+conv)
894
}
895
}
897
if(name!==undefined){
898
// "name' may be a subscription or attribute
899
// Put these "extensions" in the list "name_ext"
900
function name_repl(match){
901
name_ext.push(match)
902
return ''
903
}
904
var name_ext_re = /\.[_a-zA-Z][_a-zA-Z0-9]*|\[[_a-zA-Z][_a-zA-Z0-9]*\]|\[[0-9]+\]/g
905
name = name.replace(name_ext_re, name_repl)
906
}
914
var $ = $B.$MakeArgs1('format', 1, {self:null}, ['self'],
915
arguments, {}, 'args', 'kw')
916
917
// Parse self to detect formatting instructions
918
// Create a list "parts" made of sections of the string :
919
// - elements of even rank are literal text
920
// - elements of odd rank are "format objects", built from the
921
// format strings in self (of the form {...})
922
var pos=0, _len=self.length, car, text='', parts=[], rank=0, defaults={}
923
924
while(pos<_len){
925
car = self.charAt(pos)
926
if(car=='{' && self.charAt(pos+1)=='{'){
927
// replace {{ by literal {
928
text += '{'
929
pos+=2
930
}else if(car=='}' && self.charAt(pos+1)=='}'){
931
// replace }} by literal }
932
text += '}'
933
pos+=2
934
}else if(car=='{'){
935
// Start of a format string
936
937
// Store current literal text
938
parts.push(text)
939
940
// Search the end of the format string, ie the } closing the
941
// opening {. Since the string can contain other pairs {} for
942
// nested formatting, an integer nb is incremented for each { and
943
// decremented for each } ; the end of the format string is
944
// reached when nb==0
945
var end = pos+1, nb=1
946
while(end<_len){
947
if(self.charAt(end)=='{'){nb++;end++}
948
else if(self.charAt(end)=='}'){
949
nb--;end++
950
if(nb==0){
951
// End of format string
952
var fmt_string = self.substring(pos+1, end-1)
953
954
// Create a format object, by function parse_format
955
var fmt_obj = parse_format(fmt_string)
956
957
// If no name is explicitely provided, use the rank
958
if(!fmt_obj.name){
959
fmt_obj.name=rank+''
960
rank++
961
}
963
if(fmt_obj.spec!==undefined){
964
// "spec" may contain "nested replacement fields"
965
// In this case, evaluate them using the keyword
966
// arguments passed to format()
967
function replace_nested(name, key){
968
return _b_.dict.$dict.__getitem__($.kw, key)
969
}
970
fmt_obj.spec = fmt_obj.spec.replace(/\{(.+?)\}/g,
971
replace_nested)
972
}
973
974
// Store format object in list "parts"
975
parts.push(fmt_obj)
976
text = ''
977
break
978
}
979
}else{end++}
981
if(nb>0){throw ValueError("wrong format "+self)}
982
pos = end
983
}else{text += car;pos++}
987
// Apply formatting to the values passed to format()
988
var res = '', fmt
989
for(var i=0;i<parts.length;i++){
990
// Literal text is added unchanged
991
if(typeof parts[i]=='string'){res += parts[i];continue}
992
993
// Format objects
994
fmt = parts[i]
995
if(fmt.name.charAt(0).search(/\d/)>-1){
996
// Numerical reference : use positional arguments
997
var pos = parseInt(fmt.name),
998
value = _b_.tuple.$dict.__getitem__($.args, pos)
999
}else{
1000
// Use keyword arguments
1001
var value = _b_.dict.$dict.__getitem__($.kw, fmt.name)
1002
}
1003
// If name has extensions (attributes or subscriptions)
1004
for(var j=0;j<fmt.name_ext.length;j++){
1005
var ext = fmt.name_ext[j]
1006
if(ext.charAt(0)=='.'){
1007
// Attribute
1008
value = _b_.getattr(value, ext.substr(1))
1009
}else{
1010
// Subscription
1011
var key = ext.substr(1, ext.length-2)
1012
// An index made of digits is transformed into an integer
1013
if(key.charAt(0).search(/\d/)>-1){key = parseInt(key)}
1014
value = _b_.getattr(value, '__getitem__')(key)
1015
}
1016
}
1017
// If the conversion flag is set, first call a function to convert
1018
// the value
1019
if(fmt.conv=='a'){value = _b_.ascii(value)}
1020
else if(fmt.conv=='r'){value = _b_.repr(value)}
1021
else if(fmt.conv=='s'){value = _b_.str(value)}
1022
1023
// Call attribute __format__ to perform the actual formatting
1024
res += _b_.getattr(value, '__format__')(fmt.spec)
1027
}
1028
1029
$StringDict.format_map = function(self) {
1030
throw NotImplementedError("function format_map not implemented yet");
1031
}
1032
1033
$StringDict.index = function(self){
1034
// Like find(), but raise ValueError when the substring is not found.
1035
var res = $StringDict.find.apply(self,arguments)
1036
if(res===-1) throw _b_.ValueError("substring not found")
1037
return res
1038
}
1039
1040
$StringDict.isalnum = function(self) {return /^[a-z0-9]+$/i.test(self)}
1041
1042
$StringDict.isalpha = function(self) {return /^[a-z]+$/i.test(self)}
1043
1044
$StringDict.isdecimal = function(self) {
1045
// this is not 100% correct
1046
return /^[0-9]+$/.test(self)
1047
}
1048
1049
$StringDict.isdigit = function(self) { return /^[0-9]+$/.test(self)}
1050
1051
$StringDict.isidentifier = function(self) {
1052
1053
switch(self) {
1054
case 'False':
1055
case 'None':
1056
case 'True':
1057
case 'and':
1058
case 'as':
1059
case 'assert':
1060
case 'break':
1061
case 'class':
1062
case 'continue':
1063
case 'def':
1064
case 'del':
1065
case 'elif':
1066
case 'else':
1067
case 'except':
1068
case 'finally':
1069
case 'for':
1070
case 'from':
1071
case 'global':
1072
case 'if':
1073
case 'import':
1074
case 'in':
1075
case 'is':
1076
case 'lambda':
1077
case 'nonlocal':
1078
case 'not':
1079
case 'or':
1080
case 'pass':
1081
case 'raise':
1082
case 'return':
1083
case 'try':
1084
case 'while':
1085
case 'with':
1086
case 'yield':
1087
return true
1088
}
1089
1090
// fixme.. this isn't complete but should be a good start
1091
return /^[a-z][0-9a-z_]+$/i.test(self)
1092
}
1093
1094
$StringDict.islower = function(self) {return /^[a-z]+$/.test(self)}
1095
1096
// not sure how to handle unicode variables
1097
$StringDict.isnumeric = function(self) {return /^[0-9]+$/.test(self)}
1098
1099
// inspired by http://www.codingforums.com/archive/index.php/t-17925.html
1100
$StringDict.isprintable = function(self) {return !/[^ -~]/.test(self)}
1101
1102
$StringDict.isspace = function(self) {return /^\s+$/i.test(self)}
1103
1104
$StringDict.istitle = function(self) {return /^([A-Z][a-z]+)(\s[A-Z][a-z]+)$/i.test(self)}
1105
1106
$StringDict.isupper = function(self) {return /^[A-Z]+$/.test(self)}
1107
1108
$StringDict.join = function(self,obj){
1109
var iterable=iter(obj)
1110
var res = '',count=0
1111
while(1){
1112
try{
1113
var obj2 = next(iterable)
1114
if(!isinstance(obj2,str)){throw _b_.TypeError(
1115
"sequence item "+count+": expected str instance, "+$B.get_class(obj2).__name__+" found")}
1116
res += obj2+self
1117
count++
1118
}catch(err){
1120
else{throw err}
1121
}
1122
}
1123
if(count==0) return ''
1124
return res.substr(0,res.length-self.length)
1125
}
1126
1127
$StringDict.ljust = function(self, width, fillchar) {
1128
if (width <= self.length) return self
1129
if (fillchar === undefined) fillchar=' '
1130
return self + Array(width - self.length + 1).join(fillchar)
1131
}
1132
1133
$StringDict.lower = function(self){return self.toLowerCase()}
1134
1135
$StringDict.lstrip = function(self,x){
1136
var pattern = null
1137
if(x==undefined){pattern="\\s*"}
1138
else{pattern = "["+x+"]*"}
1139
var sp = new RegExp("^"+pattern)
1140
return self.replace(sp,"")
1141
}
1142
1143
// note, maketrans should be a static function.
1144
$StringDict.maketrans = function(from, to) {
1145
var _t=[]
1146
// make 'default' translate table
1147
for(var i=0; i < 256; i++) _t[i]=String.fromCharCode(i)
1148
1149
// make substitution in the translation table
1151
var _ndx=from.source[i].charCodeAt(0) //retrieve ascii code of char
1152
_t[_ndx]=to.source[i]
1153
}
1154
1155
// create a data structure that string.translate understands
1159
}
1160
return _d
1161
}
1162
1163
$StringDict.partition = function(self,sep) {
1164
if (sep === undefined) {
1165
throw Error("sep argument is required");
1166
return
1167
}
1168
var i=self.indexOf(sep)
1169
if (i== -1) return _b_.tuple([self, '', ''])
1170
return _b_.tuple([self.substring(0,i), sep, self.substring(i+sep.length)])
1171
}
1172
1173
function $re_escape(str)
1174
{
1175
var specials = "[.*+?|()$^"
1177
var re = new RegExp('\\'+specials.charAt(i),'g')
1178
str = str.replace(re, "\\"+specials.charAt(i))
1179
}
1180
return str
1181
}
1182
1183
$StringDict.replace = function(self, old, _new, count) {
1184
// Replaces occurrences of 'old' by '_new'. Count references
1185
// the number of times to replace. In CPython, negative or undefined
1186
// values of count means replace all.
1187
if (count === undefined) {
1188
count = -1;
1189
} else {
1190
// Validate instance type of 'count'
1191
if (!isinstance(count,[_b_.int,_b_.float])) {
1192
throw _b_.TypeError("'" + str(count.__class__) + "' object cannot be interpreted as an integer");
1193
} else if (isinstance(count, _b_.float)) {
1194
throw _b_.TypeError("integer argument expected, got float");
1197
1198
var res = self.valueOf();
1199
var pos = -1;
1200
if (count < 0) count = res.length;
1201
while (count > 0) {
1202
pos = res.indexOf(old, pos);
1203
if (pos < 0)
1204
break;
1205
res = res.substr(0, pos) + _new + res.substr(pos + old.length);
1206
pos = pos + _new.length;
1207
count--;
1208
}
1209
return res;
1210
}
1211
1212
$StringDict.rfind = function(self){
1213
// Return the highest index in the string where substring sub is found,
1214
// such that sub is contained within s[start:end]. Optional arguments
1215
// start and end are interpreted as in slice notation. Return -1 on failure.
1216
var $ns=$B.$MakeArgs1("$StringDict.find",4,
1217
{self:null, sub:null, start:null, end:null},
1218
['self', 'sub', 'start', 'end'],
1219
arguments,{start:0, end:self.length},null,null)
1220
for(var attr in $ns){eval('var '+attr+'=$ns[attr]')}
1221
if(!isinstance(sub,str)){throw _b_.TypeError(
1222
"Can't convert '"+sub.__class__.__name__+"' object to str implicitly")}
1223
if(!isinstance(start,_b_.int)||!isinstance(end,_b_.int)){throw _b_.TypeError(
1224
"slice indices must be integers or None or have an __index__ method")}
1225
1226
var s = self.substring(start,end)
1227
1228
// why not use lastIndexOf, which passes all brython tests..?
1229
return self.lastIndexOf(sub)
1230
}
1231
1232
$StringDict.rindex = function(){
1233
// Like rfind() but raises ValueError when the substring sub is not found
1234
var res = $StringDict.rfind.apply(this,arguments)
1235
if(res==-1){throw _b_.ValueError("substring not found")}
1236
return res
1237
}
1238
1239
$StringDict.rjust = function(self) {
1240
var $ns=$B.$MakeArgs1("$StringDict.rjust",3,
1241
{self:null, width:null, fillchar:null},
1242
['self', 'width', 'fillchar'],
1243
arguments,{fillchar:' '},null,null)
1244
for(var attr in $ns){eval('var '+attr+'=$ns[attr]')}
1245
1246
if (width <= self.length) return self
1247
1248
return Array(width - self.length + 1).join(fillchar) + self
1249
}
1250
1251
$StringDict.rpartition = function(self,sep) {
1252
if (sep === undefined) {
1253
throw Error("sep argument is required");
1254
return
1255
}
1256
var pos=self.length-sep.length
1257
while(1){
1258
if(self.substr(pos,sep.length)==sep){
1259
return _b_.tuple([self.substr(0,pos),sep,self.substr(pos+sep.length)])
1260
}else{
1261
pos--
1262
if(pos<0){return _b_.tuple(['','',self])}
1263
}
1264
}
1265
}
1266
1267
$StringDict.rsplit = function(self) {
1268
var args = [], pos=0
1269
for(var i=1,_len_i=arguments.length;i<_len_i;i++){args[pos++]=arguments[i]}
1271
var sep=None,maxsplit=-1
1272
if($ns['args'].length>=1){sep=$ns['args'][0]}
1273
if($ns['args'].length==2){maxsplit=$ns['args'][1]}
1280
if (array.length <= maxsplit || maxsplit == -1) return array
1281
1282
var s=[]
1283
1284
s = array.splice(array.length - maxsplit, array.length)
1285
s.splice(0, 0, array.join(sep))
1286
1287
return s
1288
}
1289
1290
$StringDict.rstrip = function(self,x){
1291
if(x==undefined){var pattern="\\s*"}
1292
else{var pattern = "["+x+"]*"}
1293
sp = new RegExp(pattern+'$')
1294
return str(self.replace(sp,""))
1295
}
1296
1297
$StringDict.split = function(self){
1298
var args = [], pos=0
1299
for(var i=1,_len_i=arguments.length;i<_len_i;i++){args[pos++]=arguments[i]}
1301
var sep=None,maxsplit=-1
1302
if($ns['args'].length>=1){sep=$ns['args'][0]}
1303
if($ns['args'].length==2){maxsplit=$ns['args'][1]}
1304
maxsplit = _b_.dict.$dict.get($ns['kw'],'maxsplit',maxsplit)
1306
if(sep===None){
1307
var res = []
1308
var pos = 0
1309
while(pos<self.length&&self.charAt(pos).search(/\s/)>-1){pos++}
1311
var name = ''
1312
while(1){
1313
if(self.charAt(pos).search(/\s/)===-1){
1314
if(name===''){name=self.charAt(pos)}
1315
else{name+=self.charAt(pos)}
1316
}else{
1317
if(name!==''){
1318
res.push(name)
1319
if(maxsplit!==-1&&res.length===maxsplit+1){
1320
res.pop()
1321
res.push(name+self.substr(pos))
1322
return res
1323
}
1324
name=''
1325
}
1326
}
1327
pos++
1328
if(pos>self.length-1){
1329
if(name){res.push(name)}
1330
break
1331
}
1332
}
1333
return res
1334
}else{
1335
var esc_sep = ''
1340
case '.':
1341
case '[':
1342
case ']':
1343
case '(':
1344
case ')':
1345
case '|':
1346
case '$':
1347
case '^':
1348
esc_sep += '\\'
1349
}
1350
esc_sep += sep.charAt(i)
1351
}
1352
var re = new RegExp(esc_sep)
1353
if (maxsplit==-1){
1354
// use native Javascript split on self
1355
return self.valueOf().split(re,maxsplit)
1356
}
1357
1358
// javascript split behavior is different from python when
1359
// a maxsplit argument is supplied. (see javascript string split
1360
// function docs for details)
1361
var l=self.valueOf().split(re,-1)
1364
if (b.length > 0) a.push(b.join(sep))
1365
1366
return a
1367
}
1368
}
1369
1370
$StringDict.splitlines = function(self){return $StringDict.split(self,'\n')}
1371
1372
$StringDict.startswith = function(self){
1373
// Return True if string starts with the prefix, otherwise return False.
1374
// prefix can also be a tuple of prefixes to look for. With optional
1375
// start, test string beginning at that position. With optional end,
1376
// stop comparing string at that position.
1377
var $ns=$B.$MakeArgs1("$StringDict.startswith",4,
1378
{self:null, prefix:null, start:null, end:null},
1379
['self', 'prefix', 'start', 'end'],
1380
arguments,{start:0, end:self.length-1},null,null)
1381
var prefixes = $ns['prefix']
1382
if(!isinstance(prefixes,_b_.tuple)){prefixes=[prefixes]}
1388
if (s.indexOf(prefixes[i]) == 0) return true
1389
}
1390
return false
1391
}
1392
1393
$StringDict.strip = function(self,x){
1394
if(x==undefined){x = "\\s"}
1395
return $StringDict.rstrip($StringDict.lstrip(self,x),x)
1396
}
1397
1398
$StringDict.swapcase = function(self) {
1399
//inspired by http://www.geekpedia.com/code69_Swap-string-case-using-JavaScript.html
1400
return self.replace(/([a-z])|([A-Z])/g, function($0,$1,$2)
1401
{ return ($1) ? $0.toUpperCase() : $0.toLowerCase()
1402
})
1403
}
1404
1405
$StringDict.title = function(self) {
1406
//inspired from http://stackoverflow.com/questions/196972/convert-string-to-title-case-with-javascript
1407
return self.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
1408
}
1409
1410
$StringDict.translate = function(self,table) {
1415
if(repl==-1){res[pos++]=self.charAt(i)}
1416
else if(repl!==None){res[pos++]=repl}
1420
}
1421
1422
$StringDict.upper = function(self){return self.toUpperCase()}
1423
1424
$StringDict.zfill = function(self, width) {
1425
if (width === undefined || width <= self.length || !self.isnumeric()) {
1426
return self
1427
}
1428
1429
return Array(width - self.length +1).join('0');
1430
}
1431
1432
function str(arg){
1433
if(arg===undefined) return ''
1434
switch(typeof arg) {
1435
case 'string': return arg
1436
case 'number': return arg.toString()
1437
}
1443
// class or its subclasses, but the attribute __str__ of the
1444
// class metaclass (usually "type") or its subclasses (usually
1445
// "object")
1446
// The metaclass is the attribute __class__ of the class dictionary
1447
var func = $B.$type.__getattribute__(arg.$dict.__class__,'__str__')
1452
var f = getattr(arg,'__str__')
1453
// XXX fix : if not better than object.__str__, try __repr__
1454
return f()
1455
}
1456
catch(err){
1462
if($B.debug>1){console.log(err)}
1463
console.log('Warning - no method __str__ or __repr__, default to toString', arg)
1465
}
1466
}
1467
}
1468
str.__class__ = $B.$factory
1469
str.$dict = $StringDict
1470
$StringDict.$factory = str
1471
$StringDict.__new__ = function(cls){
1472
if(cls===undefined){
1473
throw _b_.TypeError('str.__new__(): not enough arguments')
1474
}
1475
return {__class__:cls.$dict}
1476
}
1477
1480
// dictionary and factory for subclasses of string
1481
var $StringSubclassDict = {
1482
__class__:$B.$type,
1483
__name__:'str'
1484
}
1485
1486
// the methods in subclass apply the methods in $StringDict to the
1487
// result of instance.valueOf(), which is a Javascript string
1488
for(var $attr in $StringDict){
1489
if(typeof $StringDict[$attr]=='function'){
1490
$StringSubclassDict[$attr]=(function(attr){
1491
return function(){
1497
}
1498
}
1499
return $StringDict[attr].apply(null,args)
1500
}
1501
})($attr)
1502
}
1503
}
1504
$StringSubclassDict.__mro__ = [$StringSubclassDict,$ObjectDict]
1505
1506
// factory for str subclasses
1507
$B.$StringSubclassFactory = {
1508
__class__:$B.$factory,
1509
$dict:$StringSubclassDict
1510
}
1511
1512
_b_.str = str
1513
1514
// Function to parse the 2nd argument of format()
1515
$B.parse_format_spec = function(spec){
1519
var pos=0,
1520
aligns = '<>=^',
1521
digits = '0123456789',
1522
types = 'bcdeEfFgGnosxX%'
1523
var align_pos = aligns.indexOf(spec.charAt(0))
1524
if(align_pos!=-1){
1525
// The first character defines alignment : fill defaults to ' '
1526
this.align=aligns[align_pos];this.fill=' ';pos++
1528
else{
1529
align_pos = aligns.indexOf(spec.charAt(1))
1530
if(spec.charAt(1) && align_pos!=-1){
1531
// The second character defines alignment : fill is the first one
1532
this.align=aligns[align_pos]
1533
this.fill=spec.charAt(0)
1534
pos = 2
1535
}
1536
}
1537
var car = spec.charAt(pos)
1538
if(car=='+'||car=='-'||car==' '){
1539
this.sign=car;
1540
pos++;
1541
car=spec.charAt(pos);
1543
if(car=='#'){this.alternate=true;pos++;car=spec.charAt(pos)}
1544
if(car=='0'){this.sign_aware=true;pos++;car=spec.charAt(pos)}
1546
if(this.width===undefined){this.width=car}
1547
else{this.width+=car}
1548
pos++;car=spec.charAt(pos)
1550
if(this.width!==undefined){this.width=parseInt(this.width)}
1551
if(car==','){this.comma=true;pos++;car=spec.charAt(pos)}
1552
if(car=='.'){
1553
if(digits.indexOf(spec.charAt(pos+1))==-1){
1554
throw _b_.ValueError("Missing precision in format spec")
1555
}
1556
this.precision = spec.charAt(pos+1)
1557
pos+=2;car=spec.charAt(pos)
1558
while(car && digits.indexOf(car)>-1){
1559
this.precision+=car;pos++;car=spec.charAt(pos)
1560
}
1561
this.precision = parseInt(this.precision)
1562
}
1563
if(car && types.indexOf(car)>-1){this.type=car;pos++;car=spec.charAt(pos)}
1564
if(pos!==spec.length){
1565
console.log('error', spec, this, pos, spec.charAt(pos))
1566
throw _b_.ValueError("Invalid format specifier")
1567
}
1568
}
1569
this.toString = function(){
1570
return (this.fill===undefined ? '' : _b_.str(this.fill))+
1571
(this.align||'')+
1572
(this.sign||'')+
1573
(this.alternate ? '#' : '')+
1574
(this.sign_aware ? '0' : '')+
1575
(this.width || '')+
1576
(this.comma ? ',' : '')+
1577
(this.precision ? '.'+this.precision : '')+
1578
(this.type || '')
1579
}
1580
}
1581
1582
$B.format_width = function(s, fmt){
1583
if(fmt.width && s.length<fmt.width){
1584
var fill=fmt.fill || ' ', align = fmt.align || '<',
1585
missing = fmt.width-s.length
1586
switch(align){
1587
case '<':
1588
return s+fill.repeat(missing)
1589
case '>':
1590
return fill.repeat(missing)+s
1591
case '=':
1592
if('+-'.indexOf(s.charAt(0))==0){
1593
return s.charAt(0)+fill.repeat(missing-1)+s
1594
}else{
1595
return fill.repeat(missing)+s
1596
}
1597
case '^':
1598
left = parseInt(missing/2)