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
1609 lines (1438 sloc)
50.1 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
var res = self.replace(/\n/g,'\\\\n')
707
// escape the escape char
708
res = res.replace(/\\/g, '\\\\')
709
if(res.search('"')==-1 && res.search("'")==-1){
710
return "'"+res+"'"
719
$StringDict.__setattr__ = function(self,attr,value){return setattr(self,attr,value)}
720
721
$StringDict.__setitem__ = function(self,attr,value){
722
throw _b_.TypeError("'str' object does not support item assignment")
723
}
724
$StringDict.__str__ = function(self){
725
if(self===undefined) return "<class 'str'>"
726
return self.toString()
727
}
728
$StringDict.toString = function(){return 'string!'}
729
730
// generate comparison methods
731
var $comp_func = function(self,other){
732
if(typeof other !=="string"){throw _b_.TypeError(
733
"unorderable types: 'str' > "+$B.get_class(other).__name__+"()")}
734
return self > other
735
}
736
$comp_func += '' // source code
737
var $comps = {'>':'gt','>=':'ge','<':'lt','<=':'le'}
738
for(var $op in $comps){
739
eval("$StringDict.__"+$comps[$op]+'__ = '+$comp_func.replace(/>/gm,$op))
740
}
741
742
// add "reflected" methods
743
$B.make_rmethods($StringDict)
744
745
// unsupported operations
746
var $notimplemented = function(self,other){
747
throw NotImplementedError("OPERATOR not implemented for class str")
748
}
749
750
$StringDict.capitalize = function(self){
751
if(self.length==0) return ''
752
return self.charAt(0).toUpperCase()+self.substr(1).toLowerCase()
753
}
754
755
$StringDict.casefold = function(self) {
756
throw _b_.NotImplementedError("function casefold not implemented yet");
757
}
758
759
$StringDict.center = function(self,width,fillchar){
760
if(fillchar===undefined){fillchar=' '}else{fillchar=fillchar}
761
if(width<=self.length) return self
762
763
var pad = parseInt((width-self.length)/2)
764
var res = Array(pad+1).join(fillchar) // is this statement faster than the for loop below?
765
res += self + res
766
if(res.length<width){res += fillchar}
767
return res
768
}
769
770
$StringDict.count = function(self,elt){
771
if(!(typeof elt==="string")){throw _b_.TypeError(
772
"Can't convert '"+elt.__class__.__name__+"' object to str implicitly")}
773
//needs to be non overlapping occurrences of substring in string.
774
var n=0, pos=0
775
while(1){
776
pos=self.indexOf(elt,pos)
777
if(pos>=0){ n++; pos+=elt.length} else break;
778
}
779
return n
780
}
781
782
$StringDict.encode = function(self, encoding) {
783
if (encoding === undefined) encoding='utf-8'
784
if(encoding=='rot13' || encoding=='rot_13'){
785
// Special case : returns a string
786
var res = ''
787
for(var i=0, _len = self.length; i<_len ; i++){
788
var char = self.charAt(i)
789
if(('a'<=char && char<='m') || ('A'<=char && char<='M')){
790
res += String.fromCharCode(String.charCodeAt(char)+13)
791
}else if(('m'<char && char<='z') || ('M'<char && char<='Z')){
792
res += String.fromCharCode(String.charCodeAt(char)-13)
793
}else{res += char}
794
}
795
return res
796
}
798
}
799
800
$StringDict.endswith = function(self){
801
// Return True if the string ends with the specified suffix, otherwise
802
// return False. suffix can also be a tuple of suffixes to look for.
803
// With optional start, test beginning at that position. With optional
804
// end, stop comparing at that position.
805
var $ns=$B.$MakeArgs1("$StringDict.endswith",4,
806
{self:null, suffix:null, start:null, end:null},
807
['self', 'suffix', 'start', 'end'],
808
arguments,{start:0, end:self.length-1},null,null)
809
var suffixes = $ns['suffix']
810
if(!isinstance(suffixes,_b_.tuple)){suffixes=[suffixes]}
815
suffix = suffixes[i]
816
if(suffix.length<=s.length &&
817
s.substr(s.length-suffix.length)==suffix) return true
818
}
819
return false
820
}
821
822
$StringDict.expandtabs = function(self, tabsize) {
823
tabsize=tabsize || 8
824
var _str=''
825
for (var i=0; i < tabsize; i++) _str+=' '
826
return self.valueOf().replace(/\t/g, _str)
827
}
828
829
$StringDict.find = function(self){
830
// Return the lowest index in the string where substring sub is found,
831
// such that sub is contained in the slice s[start:end]. Optional
832
// arguments start and end are interpreted as in slice notation.
833
// Return -1 if sub is not found.
834
var start=0,end=self.length
835
var $ns=$B.$MakeArgs1("$StringDict.find",4,
836
{self:null, sub:null, start:null, end:null},
837
['self', 'sub', 'start','end'],
838
arguments,{start:0, end:self.length},null,null)
839
for(var attr in $ns){eval('var '+attr+'=$ns[attr]')}
840
if(!isinstance(sub,str)){throw _b_.TypeError(
841
"Can't convert '"+sub.__class__.__name__+"' object to str implicitly")}
842
if(!isinstance(start,_b_.int)||!isinstance(end,_b_.int)){
843
throw _b_.TypeError(
844
"slice indices must be integers or None or have an __index__ method")}
845
var s = self.substring(start,end)
846
var esc_sub = ''
848
switch(sub.charAt(i)) {
849
case '[':
850
case '.':
851
case '*':
852
case '+':
853
case '?':
854
case '|':
855
case '(':
856
case ')':
857
case '$':
858
case '^':
859
esc_sub += '\\'
860
}
861
esc_sub += sub.charAt(i)
862
}
863
var res = s.search(esc_sub)
864
if(res==-1) return -1
865
return start+res
866
}
867
872
// Parse a "format string", as described in the Python documentation
873
// Return a format object. For the format string
874
// a.x[z]!r:...
875
// the object has attributes :
876
// - name : "a"
877
// - name_ext : [".x", "[z]"]
878
// - conv : r
879
// - spec : rest of string after :
881
var elts = fmt_string.split(':'), name, conv, spec, name_ext=[]
882
if(elts.length==1){
883
// No : in the string : it only contains a name
884
name = fmt_string
885
}else{
886
// name is before the first ":"
887
// spec (the format specification) is after
888
name = elts[0]
889
spec = elts.splice(1).join(':')
890
}
891
892
var elts = name.split('!')
893
if(elts.length>1){
894
name=elts[0]
895
conv=elts[1] // conversion flag
896
if(conv.length!==1 || 'ras'.search(conv)==-1){
897
throw _b_.ValueError('wrong conversion flag '+conv)
898
}
899
}
901
if(name!==undefined){
902
// "name' may be a subscription or attribute
903
// Put these "extensions" in the list "name_ext"
904
function name_repl(match){
905
name_ext.push(match)
906
return ''
907
}
908
var name_ext_re = /\.[_a-zA-Z][_a-zA-Z0-9]*|\[[_a-zA-Z][_a-zA-Z0-9]*\]|\[[0-9]+\]/g
909
name = name.replace(name_ext_re, name_repl)
910
}
918
var $ = $B.$MakeArgs1('format', 1, {self:null}, ['self'],
919
arguments, {}, 'args', 'kw')
920
921
// Parse self to detect formatting instructions
922
// Create a list "parts" made of sections of the string :
923
// - elements of even rank are literal text
924
// - elements of odd rank are "format objects", built from the
925
// format strings in self (of the form {...})
926
var pos=0, _len=self.length, car, text='', parts=[], rank=0, defaults={}
927
928
while(pos<_len){
929
car = self.charAt(pos)
930
if(car=='{' && self.charAt(pos+1)=='{'){
931
// replace {{ by literal {
932
text += '{'
933
pos+=2
934
}else if(car=='}' && self.charAt(pos+1)=='}'){
935
// replace }} by literal }
936
text += '}'
937
pos+=2
938
}else if(car=='{'){
939
// Start of a format string
940
941
// Store current literal text
942
parts.push(text)
943
944
// Search the end of the format string, ie the } closing the
945
// opening {. Since the string can contain other pairs {} for
946
// nested formatting, an integer nb is incremented for each { and
947
// decremented for each } ; the end of the format string is
948
// reached when nb==0
949
var end = pos+1, nb=1
950
while(end<_len){
951
if(self.charAt(end)=='{'){nb++;end++}
952
else if(self.charAt(end)=='}'){
953
nb--;end++
954
if(nb==0){
955
// End of format string
956
var fmt_string = self.substring(pos+1, end-1)
957
958
// Create a format object, by function parse_format
959
var fmt_obj = parse_format(fmt_string)
960
961
// If no name is explicitely provided, use the rank
962
if(!fmt_obj.name){
963
fmt_obj.name=rank+''
964
rank++
965
}
967
if(fmt_obj.spec!==undefined){
968
// "spec" may contain "nested replacement fields"
969
// In this case, evaluate them using the keyword
970
// arguments passed to format()
971
function replace_nested(name, key){
972
return _b_.dict.$dict.__getitem__($.kw, key)
973
}
974
fmt_obj.spec = fmt_obj.spec.replace(/\{(.+?)\}/g,
975
replace_nested)
976
}
977
978
// Store format object in list "parts"
979
parts.push(fmt_obj)
980
text = ''
981
break
982
}
983
}else{end++}
985
if(nb>0){throw ValueError("wrong format "+self)}
986
pos = end
987
}else{text += car;pos++}
991
// Apply formatting to the values passed to format()
992
var res = '', fmt
993
for(var i=0;i<parts.length;i++){
994
// Literal text is added unchanged
995
if(typeof parts[i]=='string'){res += parts[i];continue}
996
997
// Format objects
998
fmt = parts[i]
999
if(fmt.name.charAt(0).search(/\d/)>-1){
1000
// Numerical reference : use positional arguments
1001
var pos = parseInt(fmt.name),
1002
value = _b_.tuple.$dict.__getitem__($.args, pos)
1003
}else{
1004
// Use keyword arguments
1005
var value = _b_.dict.$dict.__getitem__($.kw, fmt.name)
1006
}
1007
// If name has extensions (attributes or subscriptions)
1008
for(var j=0;j<fmt.name_ext.length;j++){
1009
var ext = fmt.name_ext[j]
1010
if(ext.charAt(0)=='.'){
1011
// Attribute
1012
value = _b_.getattr(value, ext.substr(1))
1013
}else{
1014
// Subscription
1015
var key = ext.substr(1, ext.length-2)
1016
// An index made of digits is transformed into an integer
1017
if(key.charAt(0).search(/\d/)>-1){key = parseInt(key)}
1018
value = _b_.getattr(value, '__getitem__')(key)
1019
}
1020
}
1021
// If the conversion flag is set, first call a function to convert
1022
// the value
1023
if(fmt.conv=='a'){value = _b_.ascii(value)}
1024
else if(fmt.conv=='r'){value = _b_.repr(value)}
1025
else if(fmt.conv=='s'){value = _b_.str(value)}
1026
1027
// Call attribute __format__ to perform the actual formatting
1028
res += _b_.getattr(value, '__format__')(fmt.spec)
1031
}
1032
1033
$StringDict.format_map = function(self) {
1034
throw NotImplementedError("function format_map not implemented yet");
1035
}
1036
1037
$StringDict.index = function(self){
1038
// Like find(), but raise ValueError when the substring is not found.
1039
var res = $StringDict.find.apply(self,arguments)
1040
if(res===-1) throw _b_.ValueError("substring not found")
1041
return res
1042
}
1043
1044
$StringDict.isalnum = function(self) {return /^[a-z0-9]+$/i.test(self)}
1045
1046
$StringDict.isalpha = function(self) {return /^[a-z]+$/i.test(self)}
1047
1048
$StringDict.isdecimal = function(self) {
1049
// this is not 100% correct
1050
return /^[0-9]+$/.test(self)
1051
}
1052
1053
$StringDict.isdigit = function(self) { return /^[0-9]+$/.test(self)}
1054
1055
$StringDict.isidentifier = function(self) {
1056
1057
switch(self) {
1058
case 'False':
1059
case 'None':
1060
case 'True':
1061
case 'and':
1062
case 'as':
1063
case 'assert':
1064
case 'break':
1065
case 'class':
1066
case 'continue':
1067
case 'def':
1068
case 'del':
1069
case 'elif':
1070
case 'else':
1071
case 'except':
1072
case 'finally':
1073
case 'for':
1074
case 'from':
1075
case 'global':
1076
case 'if':
1077
case 'import':
1078
case 'in':
1079
case 'is':
1080
case 'lambda':
1081
case 'nonlocal':
1082
case 'not':
1083
case 'or':
1084
case 'pass':
1085
case 'raise':
1086
case 'return':
1087
case 'try':
1088
case 'while':
1089
case 'with':
1090
case 'yield':
1091
return true
1092
}
1093
1094
// fixme.. this isn't complete but should be a good start
1095
return /^[a-z][0-9a-z_]+$/i.test(self)
1096
}
1097
1098
$StringDict.islower = function(self) {return /^[a-z]+$/.test(self)}
1099
1100
// not sure how to handle unicode variables
1101
$StringDict.isnumeric = function(self) {return /^[0-9]+$/.test(self)}
1102
1103
// inspired by http://www.codingforums.com/archive/index.php/t-17925.html
1104
$StringDict.isprintable = function(self) {return !/[^ -~]/.test(self)}
1105
1106
$StringDict.isspace = function(self) {return /^\s+$/i.test(self)}
1107
1108
$StringDict.istitle = function(self) {return /^([A-Z][a-z]+)(\s[A-Z][a-z]+)$/i.test(self)}
1109
1110
$StringDict.isupper = function(self) {return /^[A-Z]+$/.test(self)}
1111
1112
$StringDict.join = function(self,obj){
1113
var iterable=iter(obj)
1114
var res = '',count=0
1115
while(1){
1116
try{
1117
var obj2 = next(iterable)
1118
if(!isinstance(obj2,str)){throw _b_.TypeError(
1119
"sequence item "+count+": expected str instance, "+$B.get_class(obj2).__name__+" found")}
1120
res += obj2+self
1121
count++
1122
}catch(err){
1124
else{throw err}
1125
}
1126
}
1127
if(count==0) return ''
1128
return res.substr(0,res.length-self.length)
1129
}
1130
1131
$StringDict.ljust = function(self, width, fillchar) {
1132
if (width <= self.length) return self
1133
if (fillchar === undefined) fillchar=' '
1134
return self + Array(width - self.length + 1).join(fillchar)
1135
}
1136
1137
$StringDict.lower = function(self){return self.toLowerCase()}
1138
1139
$StringDict.lstrip = function(self,x){
1140
var pattern = null
1141
if(x==undefined){pattern="\\s*"}
1142
else{pattern = "["+x+"]*"}
1143
var sp = new RegExp("^"+pattern)
1144
return self.replace(sp,"")
1145
}
1146
1147
// note, maketrans should be a static function.
1148
$StringDict.maketrans = function(from, to) {
1149
var _t=[]
1150
// make 'default' translate table
1151
for(var i=0; i < 256; i++) _t[i]=String.fromCharCode(i)
1152
1153
// make substitution in the translation table
1155
var _ndx=from.source[i].charCodeAt(0) //retrieve ascii code of char
1156
_t[_ndx]=to.source[i]
1157
}
1158
1159
// create a data structure that string.translate understands
1163
}
1164
return _d
1165
}
1166
1167
$StringDict.partition = function(self,sep) {
1168
if (sep === undefined) {
1169
throw Error("sep argument is required");
1170
return
1171
}
1172
var i=self.indexOf(sep)
1173
if (i== -1) return _b_.tuple([self, '', ''])
1174
return _b_.tuple([self.substring(0,i), sep, self.substring(i+sep.length)])
1175
}
1176
1177
function $re_escape(str)
1178
{
1179
var specials = "[.*+?|()$^"
1181
var re = new RegExp('\\'+specials.charAt(i),'g')
1182
str = str.replace(re, "\\"+specials.charAt(i))
1183
}
1184
return str
1185
}
1186
1187
$StringDict.replace = function(self, old, _new, count) {
1188
// Replaces occurrences of 'old' by '_new'. Count references
1189
// the number of times to replace. In CPython, negative or undefined
1190
// values of count means replace all.
1191
if (count === undefined) {
1192
count = -1;
1193
} else {
1194
// Validate instance type of 'count'
1195
if (!isinstance(count,[_b_.int,_b_.float])) {
1196
throw _b_.TypeError("'" + str(count.__class__) + "' object cannot be interpreted as an integer");
1197
} else if (isinstance(count, _b_.float)) {
1198
throw _b_.TypeError("integer argument expected, got float");
1201
1202
var res = self.valueOf();
1203
var pos = -1;
1204
if (count < 0) count = res.length;
1205
while (count > 0) {
1206
pos = res.indexOf(old, pos);
1207
if (pos < 0)
1208
break;
1209
res = res.substr(0, pos) + _new + res.substr(pos + old.length);
1210
pos = pos + _new.length;
1211
count--;
1212
}
1213
return res;
1214
}
1215
1216
$StringDict.rfind = function(self){
1217
// Return the highest index in the string where substring sub is found,
1218
// such that sub is contained within s[start:end]. Optional arguments
1219
// start and end are interpreted as in slice notation. Return -1 on failure.
1220
var $ns=$B.$MakeArgs1("$StringDict.find",4,
1221
{self:null, sub:null, start:null, end:null},
1222
['self', 'sub', 'start', 'end'],
1223
arguments,{start:0, end:self.length},null,null)
1224
for(var attr in $ns){eval('var '+attr+'=$ns[attr]')}
1225
if(!isinstance(sub,str)){throw _b_.TypeError(
1226
"Can't convert '"+sub.__class__.__name__+"' object to str implicitly")}
1227
if(!isinstance(start,_b_.int)||!isinstance(end,_b_.int)){throw _b_.TypeError(
1228
"slice indices must be integers or None or have an __index__ method")}
1229
1230
var s = self.substring(start,end)
1231
1232
// why not use lastIndexOf, which passes all brython tests..?
1233
return self.lastIndexOf(sub)
1234
}
1235
1236
$StringDict.rindex = function(){
1237
// Like rfind() but raises ValueError when the substring sub is not found
1238
var res = $StringDict.rfind.apply(this,arguments)
1239
if(res==-1){throw _b_.ValueError("substring not found")}
1240
return res
1241
}
1242
1243
$StringDict.rjust = function(self) {
1244
var $ns=$B.$MakeArgs1("$StringDict.rjust",3,
1245
{self:null, width:null, fillchar:null},
1246
['self', 'width', 'fillchar'],
1247
arguments,{fillchar:' '},null,null)
1248
for(var attr in $ns){eval('var '+attr+'=$ns[attr]')}
1249
1250
if (width <= self.length) return self
1251
1252
return Array(width - self.length + 1).join(fillchar) + self
1253
}
1254
1255
$StringDict.rpartition = function(self,sep) {
1256
if (sep === undefined) {
1257
throw Error("sep argument is required");
1258
return
1259
}
1260
var pos=self.length-sep.length
1261
while(1){
1262
if(self.substr(pos,sep.length)==sep){
1263
return _b_.tuple([self.substr(0,pos),sep,self.substr(pos+sep.length)])
1264
}else{
1265
pos--
1266
if(pos<0){return _b_.tuple(['','',self])}
1267
}
1268
}
1269
}
1270
1271
$StringDict.rsplit = function(self) {
1272
var args = [], pos=0
1273
for(var i=1,_len_i=arguments.length;i<_len_i;i++){args[pos++]=arguments[i]}
1275
var sep=None,maxsplit=-1
1276
if($ns['args'].length>=1){sep=$ns['args'][0]}
1277
if($ns['args'].length==2){maxsplit=$ns['args'][1]}
1284
if (array.length <= maxsplit || maxsplit == -1) return array
1285
1286
var s=[]
1287
1288
s = array.splice(array.length - maxsplit, array.length)
1289
s.splice(0, 0, array.join(sep))
1290
1291
return s
1292
}
1293
1294
$StringDict.rstrip = function(self,x){
1295
if(x==undefined){var pattern="\\s*"}
1296
else{var pattern = "["+x+"]*"}
1297
sp = new RegExp(pattern+'$')
1298
return str(self.replace(sp,""))
1299
}
1300
1301
$StringDict.split = function(self){
1302
var args = [], pos=0
1303
for(var i=1,_len_i=arguments.length;i<_len_i;i++){args[pos++]=arguments[i]}
1305
var sep=None,maxsplit=-1
1306
if($ns['args'].length>=1){sep=$ns['args'][0]}
1307
if($ns['args'].length==2){maxsplit=$ns['args'][1]}
1308
maxsplit = _b_.dict.$dict.get($ns['kw'],'maxsplit',maxsplit)
1310
if(sep===None){
1311
var res = []
1312
var pos = 0
1313
while(pos<self.length&&self.charAt(pos).search(/\s/)>-1){pos++}
1315
var name = ''
1316
while(1){
1317
if(self.charAt(pos).search(/\s/)===-1){
1318
if(name===''){name=self.charAt(pos)}
1319
else{name+=self.charAt(pos)}
1320
}else{
1321
if(name!==''){
1322
res.push(name)
1323
if(maxsplit!==-1&&res.length===maxsplit+1){
1324
res.pop()
1325
res.push(name+self.substr(pos))
1326
return res
1327
}
1328
name=''
1329
}
1330
}
1331
pos++
1332
if(pos>self.length-1){
1333
if(name){res.push(name)}
1334
break
1335
}
1336
}
1337
return res
1338
}else{
1339
var esc_sep = ''
1344
case '.':
1345
case '[':
1346
case ']':
1347
case '(':
1348
case ')':
1349
case '|':
1350
case '$':
1351
case '^':
1352
esc_sep += '\\'
1353
}
1354
esc_sep += sep.charAt(i)
1355
}
1356
var re = new RegExp(esc_sep)
1357
if (maxsplit==-1){
1358
// use native Javascript split on self
1359
return self.valueOf().split(re,maxsplit)
1360
}
1361
1362
// javascript split behavior is different from python when
1363
// a maxsplit argument is supplied. (see javascript string split
1364
// function docs for details)
1365
var l=self.valueOf().split(re,-1)
1368
if (b.length > 0) a.push(b.join(sep))
1369
1370
return a
1371
}
1372
}
1373
1374
$StringDict.splitlines = function(self){return $StringDict.split(self,'\n')}
1375
1376
$StringDict.startswith = function(self){
1377
// Return True if string starts with the prefix, otherwise return False.
1378
// prefix can also be a tuple of prefixes to look for. With optional
1379
// start, test string beginning at that position. With optional end,
1380
// stop comparing string at that position.
1381
var $ns=$B.$MakeArgs1("$StringDict.startswith",4,
1382
{self:null, prefix:null, start:null, end:null},
1383
['self', 'prefix', 'start', 'end'],
1384
arguments,{start:0, end:self.length-1},null,null)
1385
var prefixes = $ns['prefix']
1386
if(!isinstance(prefixes,_b_.tuple)){prefixes=[prefixes]}
1392
if (s.indexOf(prefixes[i]) == 0) return true
1393
}
1394
return false
1395
}
1396
1397
$StringDict.strip = function(self,x){
1398
if(x==undefined){x = "\\s"}
1399
return $StringDict.rstrip($StringDict.lstrip(self,x),x)
1400
}
1401
1402
$StringDict.swapcase = function(self) {
1403
//inspired by http://www.geekpedia.com/code69_Swap-string-case-using-JavaScript.html
1404
return self.replace(/([a-z])|([A-Z])/g, function($0,$1,$2)
1405
{ return ($1) ? $0.toUpperCase() : $0.toLowerCase()
1406
})
1407
}
1408
1409
$StringDict.title = function(self) {
1410
//inspired from http://stackoverflow.com/questions/196972/convert-string-to-title-case-with-javascript
1411
return self.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
1412
}
1413
1414
$StringDict.translate = function(self,table) {
1419
if(repl==-1){res[pos++]=self.charAt(i)}
1420
else if(repl!==None){res[pos++]=repl}
1424
}
1425
1426
$StringDict.upper = function(self){return self.toUpperCase()}
1427
1428
$StringDict.zfill = function(self, width) {
1429
if (width === undefined || width <= self.length || !self.isnumeric()) {
1430
return self
1431
}
1432
1433
return Array(width - self.length +1).join('0');
1434
}
1435
1436
function str(arg){
1437
if(arg===undefined) return ''
1438
switch(typeof arg) {
1439
case 'string': return arg
1440
case 'number': return arg.toString()
1441
}
1447
// class or its subclasses, but the attribute __str__ of the
1448
// class metaclass (usually "type") or its subclasses (usually
1449
// "object")
1450
// The metaclass is the attribute __class__ of the class dictionary
1451
var func = $B.$type.__getattribute__(arg.$dict.__class__,'__str__')
1456
var f = getattr(arg,'__str__')
1457
// XXX fix : if not better than object.__str__, try __repr__
1458
return f()
1459
}
1460
catch(err){
1466
if($B.debug>1){console.log(err)}
1467
console.log('Warning - no method __str__ or __repr__, default to toString', arg)
1469
}
1470
}
1471
}
1472
str.__class__ = $B.$factory
1473
str.$dict = $StringDict
1474
$StringDict.$factory = str
1475
$StringDict.__new__ = function(cls){
1476
if(cls===undefined){
1477
throw _b_.TypeError('str.__new__(): not enough arguments')
1478
}
1479
return {__class__:cls.$dict}
1480
}
1481
1484
// dictionary and factory for subclasses of string
1485
var $StringSubclassDict = {
1486
__class__:$B.$type,
1487
__name__:'str'
1488
}
1489
1490
// the methods in subclass apply the methods in $StringDict to the
1491
// result of instance.valueOf(), which is a Javascript string
1492
for(var $attr in $StringDict){
1493
if(typeof $StringDict[$attr]=='function'){
1494
$StringSubclassDict[$attr]=(function(attr){
1495
return function(){
1501
}
1502
}
1503
return $StringDict[attr].apply(null,args)
1504
}
1505
})($attr)
1506
}
1507
}
1508
$StringSubclassDict.__mro__ = [$StringSubclassDict,$ObjectDict]
1509
1510
// factory for str subclasses
1511
$B.$StringSubclassFactory = {
1512
__class__:$B.$factory,
1513
$dict:$StringSubclassDict
1514
}
1515
1516
_b_.str = str
1517
1518
// Function to parse the 2nd argument of format()
1519
$B.parse_format_spec = function(spec){
1523
var pos=0,
1524
aligns = '<>=^',
1525
digits = '0123456789',
1526
types = 'bcdeEfFgGnosxX%'
1527
var align_pos = aligns.indexOf(spec.charAt(0))
1528
if(align_pos!=-1){
1529
// The first character defines alignment : fill defaults to ' '
1530
this.align=aligns[align_pos];this.fill=' ';pos++
1532
else{
1533
align_pos = aligns.indexOf(spec.charAt(1))
1534
if(spec.charAt(1) && align_pos!=-1){
1535
// The second character defines alignment : fill is the first one
1536
this.align=aligns[align_pos]
1537
this.fill=spec.charAt(0)
1538
pos = 2
1539
}
1540
}
1541
var car = spec.charAt(pos)
1542
if(car=='+'||car=='-'||car==' '){
1543
this.sign=car;
1544
pos++;
1545
car=spec.charAt(pos);
1547
if(car=='#'){this.alternate=true;pos++;car=spec.charAt(pos)}
1548
if(car=='0'){this.sign_aware=true;pos++;car=spec.charAt(pos)}
1550
if(this.width===undefined){this.width=car}
1551
else{this.width+=car}
1552
pos++;car=spec.charAt(pos)
1554
if(this.width!==undefined){this.width=parseInt(this.width)}
1555
if(car==','){this.comma=true;pos++;car=spec.charAt(pos)}
1556
if(car=='.'){
1557
if(digits.indexOf(spec.charAt(pos+1))==-1){
1558
throw _b_.ValueError("Missing precision in format spec")
1559
}
1560
this.precision = spec.charAt(pos+1)
1561
pos+=2;car=spec.charAt(pos)
1562
while(car && digits.indexOf(car)>-1){
1563
this.precision+=car;pos++;car=spec.charAt(pos)
1564
}
1565
this.precision = parseInt(this.precision)
1566
}
1567
if(car && types.indexOf(car)>-1){this.type=car;pos++;car=spec.charAt(pos)}
1568
if(pos!==spec.length){
1569
console.log('error', spec, this, pos, spec.charAt(pos))
1570
throw _b_.ValueError("Invalid format specifier")
1571
}
1572
}
1573
this.toString = function(){
1574
return (this.fill===undefined ? '' : _b_.str(this.fill))+
1575
(this.align||'')+
1576
(this.sign||'')+
1577
(this.alternate ? '#' : '')+
1578
(this.sign_aware ? '0' : '')+
1579
(this.width || '')+
1580
(this.comma ? ',' : '')+
1581
(this.precision ? '.'+this.precision : '')+
1582
(this.type || '')
1583
}
1584
}
1585
1586
$B.format_width = function(s, fmt){
1587
if(fmt.width && s.length<fmt.width){
1588
var fill=fmt.fill || ' ', align = fmt.align || '<',
1589
missing = fmt.width-s.length
1590
switch(align){
1591
case '<':
1592
return s+fill.repeat(missing)
1593
case '>':
1594
return fill.repeat(missing)+s
1595
case '=':
1596
if('+-'.indexOf(s.charAt(0))>-1){
1597
return s.charAt(0)+fill.repeat(missing)+s.substr(1)
1598
}else{
1599
return fill.repeat(missing)+s
1600
}
1601
case '^':
1602
left = parseInt(missing/2)