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
1660 lines (1488 sloc)
51.8 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
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 ''
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(){
771
var $ = $B.args('count', 4, {self:null, sub:null, start:null, stop:null},
776
var substr = $.self
777
if($.start!==null){
778
var _slice
779
if($.stop!==null){_slice = _b_.slice($.start, $.stop)}
780
else{_slice = _b_.slice($.start,$.self.length)}
781
substr = $StringDict.__getitem__.apply(null, [$.self].concat(_slice))
782
}else{
783
if($.self.length+$.sub.length==0){return 1} // ''.count('') = 1
784
}
785
if($.sub.length==0){
786
if($.start==$.self.length){return 1} // 'aaa'.count('',3) = 1
787
else if(substr.length==0){return 0} // 'aaa'.count('',4) = 0
788
return substr.length+1
789
}
790
var n=0, pos=0
791
while(pos<substr.length){
792
pos=substr.indexOf($.sub,pos)
794
}
795
return n
796
}
797
798
$StringDict.encode = function(self, encoding) {
799
if (encoding === undefined) encoding='utf-8'
800
if(encoding=='rot13' || encoding=='rot_13'){
801
// Special case : returns a string
802
var res = ''
803
for(var i=0, _len = self.length; i<_len ; i++){
804
var char = self.charAt(i)
805
if(('a'<=char && char<='m') || ('A'<=char && char<='M')){
806
res += String.fromCharCode(String.charCodeAt(char)+13)
807
}else if(('m'<char && char<='z') || ('M'<char && char<='Z')){
808
res += String.fromCharCode(String.charCodeAt(char)-13)
809
}else{res += char}
810
}
811
return res
812
}
814
}
815
816
$StringDict.endswith = function(self){
817
// Return True if the string ends with the specified suffix, otherwise
818
// return False. suffix can also be a tuple of suffixes to look for.
819
// With optional start, test beginning at that position. With optional
820
// end, stop comparing at that position.
822
{self:null, suffix:null, start:null, end:null},
823
['self', 'suffix', 'start', 'end'],
824
arguments,{start:0, end:self.length-1},null,null)
825
var suffixes = $ns['suffix']
826
if(!isinstance(suffixes,_b_.tuple)){suffixes=[suffixes]}
831
suffix = suffixes[i]
832
if(suffix.length<=s.length &&
833
s.substr(s.length-suffix.length)==suffix) return true
834
}
835
return false
836
}
837
838
$StringDict.expandtabs = function(self, tabsize) {
839
var $ = $B.args('expandtabs', 2, {self:null, tabsize:null},
840
['self', 'tabsize'], arguments, {tabsize:8}, null, null)
841
var s=$B.$GetInt($.tabsize), col=0,pos=0,res=''
842
if(s==1){return self.replace(/\t/g,' ')}
843
while(pos<self.length){
844
var car = self.charAt(pos)
845
switch(car){
846
case '\t':
847
while(col%s > 0){res += ' ';col++}
848
break
849
case '\r':
850
case '\n':
851
res += car
852
col = 0
853
break
854
default:
855
res += car
856
col++
857
break
858
}
859
pos++
860
}
861
862
return res
863
}
864
865
$StringDict.find = function(self){
866
// Return the lowest index in the string where substring sub is found,
867
// such that sub is contained in the slice s[start:end]. Optional
868
// arguments start and end are interpreted as in slice notation.
869
// Return -1 if sub is not found.
870
var start=0,end=self.length
872
{self:null, sub:null, start:null, end:null},
873
['self', 'sub', 'start','end'],
874
arguments,{start:0, end:self.length},null,null)
875
for(var attr in $ns){eval('var '+attr+'=$ns[attr]')}
876
if(!isinstance(sub,str)){throw _b_.TypeError(
877
"Can't convert '"+sub.__class__.__name__+"' object to str implicitly")}
878
if(!isinstance(start,_b_.int)||!isinstance(end,_b_.int)){
879
throw _b_.TypeError(
880
"slice indices must be integers or None or have an __index__ method")}
881
var s = self.substring(start,end)
882
var esc_sub = ''
884
switch(sub.charAt(i)) {
885
case '[':
886
case '.':
887
case '*':
888
case '+':
889
case '?':
890
case '|':
891
case '(':
892
case ')':
893
case '$':
894
case '^':
895
esc_sub += '\\'
896
}
897
esc_sub += sub.charAt(i)
898
}
899
var res = s.search(esc_sub)
900
if(res==-1) return -1
901
return start+res
902
}
903
908
// Parse a "format string", as described in the Python documentation
909
// Return a format object. For the format string
910
// a.x[z]!r:...
911
// the object has attributes :
912
// - name : "a"
913
// - name_ext : [".x", "[z]"]
914
// - conv : r
915
// - spec : rest of string after :
917
var elts = fmt_string.split(':'), name, conv, spec, name_ext=[]
918
if(elts.length==1){
919
// No : in the string : it only contains a name
920
name = fmt_string
921
}else{
922
// name is before the first ":"
923
// spec (the format specification) is after
924
name = elts[0]
925
spec = elts.splice(1).join(':')
926
}
927
928
var elts = name.split('!')
929
if(elts.length>1){
930
name=elts[0]
931
conv=elts[1] // conversion flag
932
if(conv.length!==1 || 'ras'.search(conv)==-1){
933
throw _b_.ValueError('wrong conversion flag '+conv)
934
}
935
}
937
if(name!==undefined){
938
// "name' may be a subscription or attribute
939
// Put these "extensions" in the list "name_ext"
940
function name_repl(match){
941
name_ext.push(match)
942
return ''
943
}
944
var name_ext_re = /\.[_a-zA-Z][_a-zA-Z0-9]*|\[[_a-zA-Z][_a-zA-Z0-9]*\]|\[[0-9]+\]/g
945
name = name.replace(name_ext_re, name_repl)
946
}
955
arguments, {}, 'args', 'kw')
956
957
// Parse self to detect formatting instructions
958
// Create a list "parts" made of sections of the string :
959
// - elements of even rank are literal text
960
// - elements of odd rank are "format objects", built from the
961
// format strings in self (of the form {...})
962
var pos=0, _len=self.length, car, text='', parts=[], rank=0, defaults={}
963
964
while(pos<_len){
965
car = self.charAt(pos)
966
if(car=='{' && self.charAt(pos+1)=='{'){
967
// replace {{ by literal {
968
text += '{'
969
pos+=2
970
}else if(car=='}' && self.charAt(pos+1)=='}'){
971
// replace }} by literal }
972
text += '}'
973
pos+=2
974
}else if(car=='{'){
975
// Start of a format string
976
977
// Store current literal text
978
parts.push(text)
979
980
// Search the end of the format string, ie the } closing the
981
// opening {. Since the string can contain other pairs {} for
982
// nested formatting, an integer nb is incremented for each { and
983
// decremented for each } ; the end of the format string is
984
// reached when nb==0
985
var end = pos+1, nb=1
986
while(end<_len){
987
if(self.charAt(end)=='{'){nb++;end++}
988
else if(self.charAt(end)=='}'){
989
nb--;end++
990
if(nb==0){
991
// End of format string
992
var fmt_string = self.substring(pos+1, end-1)
993
994
// Create a format object, by function parse_format
995
var fmt_obj = parse_format(fmt_string)
996
997
// If no name is explicitely provided, use the rank
998
if(!fmt_obj.name){
999
fmt_obj.name=rank+''
1000
rank++
1001
}
1004
// "spec" may contain "nested replacement fields"
1005
// In this case, evaluate them using the keyword
1006
// arguments passed to format()
1007
function replace_nested(name, key){
1010
}
1011
fmt_obj.spec = fmt_obj.spec.replace(/\{(.+?)\}/g,
1012
replace_nested)
1013
}
1014
1015
// Store format object in list "parts"
1016
parts.push(fmt_obj)
1017
text = ''
1018
break
1019
}
1020
}else{end++}
1022
if(nb>0){throw ValueError("wrong format "+self)}
1023
pos = end
1024
}else{text += car;pos++}
1028
// Apply formatting to the values passed to format()
1029
var res = '', fmt
1030
for(var i=0;i<parts.length;i++){
1031
// Literal text is added unchanged
1032
if(typeof parts[i]=='string'){res += parts[i];continue}
1033
1034
// Format objects
1035
fmt = parts[i]
1036
if(fmt.name.charAt(0).search(/\d/)>-1){
1037
// Numerical reference : use positional arguments
1038
var pos = parseInt(fmt.name),
1039
value = _b_.tuple.$dict.__getitem__($.args, pos)
1040
}else{
1041
// Use keyword arguments
1042
var value = _b_.dict.$dict.__getitem__($.kw, fmt.name)
1043
}
1044
// If name has extensions (attributes or subscriptions)
1045
for(var j=0;j<fmt.name_ext.length;j++){
1046
var ext = fmt.name_ext[j]
1047
if(ext.charAt(0)=='.'){
1048
// Attribute
1049
value = _b_.getattr(value, ext.substr(1))
1050
}else{
1051
// Subscription
1052
var key = ext.substr(1, ext.length-2)
1053
// An index made of digits is transformed into an integer
1054
if(key.charAt(0).search(/\d/)>-1){key = parseInt(key)}
1055
value = _b_.getattr(value, '__getitem__')(key)
1056
}
1057
}
1058
// If the conversion flag is set, first call a function to convert
1059
// the value
1060
if(fmt.conv=='a'){value = _b_.ascii(value)}
1061
else if(fmt.conv=='r'){value = _b_.repr(value)}
1062
else if(fmt.conv=='s'){value = _b_.str(value)}
1063
1064
// Call attribute __format__ to perform the actual formatting
1065
res += _b_.getattr(value, '__format__')(fmt.spec)
1068
}
1069
1070
$StringDict.format_map = function(self) {
1071
throw NotImplementedError("function format_map not implemented yet");
1072
}
1073
1074
$StringDict.index = function(self){
1075
// Like find(), but raise ValueError when the substring is not found.
1076
var res = $StringDict.find.apply(self,arguments)
1077
if(res===-1) throw _b_.ValueError("substring not found")
1078
return res
1079
}
1080
1081
$StringDict.isalnum = function(self) {return /^[a-z0-9]+$/i.test(self)}
1082
1083
$StringDict.isalpha = function(self) {return /^[a-z]+$/i.test(self)}
1084
1085
$StringDict.isdecimal = function(self) {
1086
// this is not 100% correct
1087
return /^[0-9]+$/.test(self)
1088
}
1089
1090
$StringDict.isdigit = function(self) { return /^[0-9]+$/.test(self)}
1091
1092
$StringDict.isidentifier = function(self) {
1093
1094
switch(self) {
1095
case 'False':
1096
case 'None':
1097
case 'True':
1098
case 'and':
1099
case 'as':
1100
case 'assert':
1101
case 'break':
1102
case 'class':
1103
case 'continue':
1104
case 'def':
1105
case 'del':
1106
case 'elif':
1107
case 'else':
1108
case 'except':
1109
case 'finally':
1110
case 'for':
1111
case 'from':
1112
case 'global':
1113
case 'if':
1114
case 'import':
1115
case 'in':
1116
case 'is':
1117
case 'lambda':
1118
case 'nonlocal':
1119
case 'not':
1120
case 'or':
1121
case 'pass':
1122
case 'raise':
1123
case 'return':
1124
case 'try':
1125
case 'while':
1126
case 'with':
1127
case 'yield':
1128
return true
1129
}
1130
1131
// fixme.. this isn't complete but should be a good start
1132
return /^[a-z][0-9a-z_]+$/i.test(self)
1133
}
1134
1136
1137
// not sure how to handle unicode variables
1138
$StringDict.isnumeric = function(self) {return /^[0-9]+$/.test(self)}
1139
1140
// inspired by http://www.codingforums.com/archive/index.php/t-17925.html
1141
$StringDict.isprintable = function(self) {return !/[^ -~]/.test(self)}
1142
1143
$StringDict.isspace = function(self) {return /^\s+$/i.test(self)}
1144
1145
$StringDict.istitle = function(self) {return /^([A-Z][a-z]+)(\s[A-Z][a-z]+)$/i.test(self)}
1146
1148
1149
$StringDict.join = function(self,obj){
1150
var iterable=iter(obj)
1151
var res = '',count=0
1152
while(1){
1153
try{
1154
var obj2 = next(iterable)
1155
if(!isinstance(obj2,str)){throw _b_.TypeError(
1156
"sequence item "+count+": expected str instance, "+$B.get_class(obj2).__name__+" found")}
1157
res += obj2+self
1158
count++
1159
}catch(err){
1161
else{throw err}
1162
}
1163
}
1164
if(count==0) return ''
1165
return res.substr(0,res.length-self.length)
1166
}
1167
1168
$StringDict.ljust = function(self, width, fillchar) {
1169
if (width <= self.length) return self
1170
if (fillchar === undefined) fillchar=' '
1171
return self + Array(width - self.length + 1).join(fillchar)
1172
}
1173
1174
$StringDict.lower = function(self){return self.toLowerCase()}
1175
1176
$StringDict.lstrip = function(self,x){
1177
var pattern = null
1178
if(x==undefined){pattern="\\s*"}
1179
else{pattern = "["+x+"]*"}
1180
var sp = new RegExp("^"+pattern)
1181
return self.replace(sp,"")
1182
}
1183
1184
// note, maketrans should be a static function.
1185
$StringDict.maketrans = function(from, to) {
1186
var _t=[]
1187
// make 'default' translate table
1188
for(var i=0; i < 256; i++) _t[i]=String.fromCharCode(i)
1189
1190
// make substitution in the translation table
1192
var _ndx=from.source[i].charCodeAt(0) //retrieve ascii code of char
1193
_t[_ndx]=to.source[i]
1194
}
1195
1196
// create a data structure that string.translate understands
1200
}
1201
return _d
1202
}
1203
1204
$StringDict.partition = function(self,sep) {
1205
if (sep === undefined) {
1206
throw Error("sep argument is required");
1207
return
1208
}
1209
var i=self.indexOf(sep)
1210
if (i== -1) return _b_.tuple([self, '', ''])
1211
return _b_.tuple([self.substring(0,i), sep, self.substring(i+sep.length)])
1212
}
1213
1214
function $re_escape(str)
1215
{
1216
var specials = "[.*+?|()$^"
1218
var re = new RegExp('\\'+specials.charAt(i),'g')
1219
str = str.replace(re, "\\"+specials.charAt(i))
1220
}
1221
return str
1222
}
1223
1224
$StringDict.replace = function(self, old, _new, count) {
1225
// Replaces occurrences of 'old' by '_new'. Count references
1226
// the number of times to replace. In CPython, negative or undefined
1227
// values of count means replace all.
1228
if (count === undefined) {
1229
count = -1;
1230
} else {
1231
// Validate instance type of 'count'
1232
if (!isinstance(count,[_b_.int,_b_.float])) {
1233
throw _b_.TypeError("'" + str(count.__class__) + "' object cannot be interpreted as an integer");
1234
} else if (isinstance(count, _b_.float)) {
1235
throw _b_.TypeError("integer argument expected, got float");
1238
1239
var res = self.valueOf();
1240
var pos = -1;
1241
if (count < 0) count = res.length;
1242
while (count > 0) {
1243
pos = res.indexOf(old, pos);
1244
if (pos < 0)
1245
break;
1246
res = res.substr(0, pos) + _new + res.substr(pos + old.length);
1247
pos = pos + _new.length;
1248
count--;
1249
}
1250
return res;
1251
}
1252
1253
$StringDict.rfind = function(self){
1254
// Return the highest index in the string where substring sub is found,
1255
// such that sub is contained within s[start:end]. Optional arguments
1256
// start and end are interpreted as in slice notation. Return -1 on failure.
1258
{self:null, sub:null, start:null, end:null},
1259
['self', 'sub', 'start', 'end'],
1260
arguments,{start:0, end:self.length},null,null)
1261
for(var attr in $ns){eval('var '+attr+'=$ns[attr]')}
1262
if(!isinstance(sub,str)){throw _b_.TypeError(
1263
"Can't convert '"+sub.__class__.__name__+"' object to str implicitly")}
1264
if(!isinstance(start,_b_.int)||!isinstance(end,_b_.int)){throw _b_.TypeError(
1265
"slice indices must be integers or None or have an __index__ method")}
1266
1267
var s = self.substring(start,end)
1268
1269
// why not use lastIndexOf, which passes all brython tests..?
1270
return self.lastIndexOf(sub)
1271
}
1272
1273
$StringDict.rindex = function(){
1274
// Like rfind() but raises ValueError when the substring sub is not found
1275
var res = $StringDict.rfind.apply(this,arguments)
1276
if(res==-1){throw _b_.ValueError("substring not found")}
1277
return res
1278
}
1279
1280
$StringDict.rjust = function(self) {
1282
{self:null, width:null, fillchar:null},
1283
['self', 'width', 'fillchar'],
1284
arguments,{fillchar:' '},null,null)
1285
for(var attr in $ns){eval('var '+attr+'=$ns[attr]')}
1286
1287
if (width <= self.length) return self
1288
1289
return Array(width - self.length + 1).join(fillchar) + self
1290
}
1291
1292
$StringDict.rpartition = function(self,sep) {
1293
if (sep === undefined) {
1294
throw Error("sep argument is required");
1295
return
1296
}
1297
var pos=self.length-sep.length
1298
while(1){
1299
if(self.substr(pos,sep.length)==sep){
1300
return _b_.tuple([self.substr(0,pos),sep,self.substr(pos+sep.length)])
1301
}else{
1302
pos--
1303
if(pos<0){return _b_.tuple(['','',self])}
1304
}
1305
}
1306
}
1307
1308
$StringDict.rsplit = function(self) {
1309
var args = [], pos=0
1310
for(var i=1,_len_i=arguments.length;i<_len_i;i++){args[pos++]=arguments[i]}
1312
var sep=None,maxsplit=-1
1313
if($ns['args'].length>=1){sep=$ns['args'][0]}
1314
if($ns['args'].length==2){maxsplit=$ns['args'][1]}
1321
if (array.length <= maxsplit || maxsplit == -1) return array
1322
1323
var s=[]
1324
1325
s = array.splice(array.length - maxsplit, array.length)
1326
s.splice(0, 0, array.join(sep))
1327
1328
return s
1329
}
1330
1331
$StringDict.rstrip = function(self,x){
1332
if(x==undefined){var pattern="\\s*"}
1333
else{var pattern = "["+x+"]*"}
1334
sp = new RegExp(pattern+'$')
1335
return str(self.replace(sp,""))
1336
}
1337
1338
$StringDict.split = function(self){
1339
var args = [], pos=0
1340
for(var i=1,_len_i=arguments.length;i<_len_i;i++){args[pos++]=arguments[i]}
1342
var sep=None,maxsplit=-1
1343
if($ns['args'].length>=1){sep=$ns['args'][0]}
1344
if($ns['args'].length==2){maxsplit=$ns['args'][1]}
1345
maxsplit = _b_.dict.$dict.get($ns['kw'],'maxsplit',maxsplit)
1347
if(sep===None){
1348
var res = []
1349
var pos = 0
1350
while(pos<self.length&&self.charAt(pos).search(/\s/)>-1){pos++}
1352
var name = ''
1353
while(1){
1354
if(self.charAt(pos).search(/\s/)===-1){
1355
if(name===''){name=self.charAt(pos)}
1356
else{name+=self.charAt(pos)}
1357
}else{
1358
if(name!==''){
1359
res.push(name)
1360
if(maxsplit!==-1&&res.length===maxsplit+1){
1361
res.pop()
1362
res.push(name+self.substr(pos))
1363
return res
1364
}
1365
name=''
1366
}
1367
}
1368
pos++
1369
if(pos>self.length-1){
1370
if(name){res.push(name)}
1371
break
1372
}
1373
}
1374
return res
1375
}else{
1376
var esc_sep = ''
1381
case '.':
1382
case '[':
1383
case ']':
1384
case '(':
1385
case ')':
1386
case '|':
1387
case '$':
1388
case '^':
1389
esc_sep += '\\'
1390
}
1391
esc_sep += sep.charAt(i)
1392
}
1393
var re = new RegExp(esc_sep)
1394
if (maxsplit==-1){
1395
// use native Javascript split on self
1396
return self.valueOf().split(re,maxsplit)
1397
}
1398
1399
// javascript split behavior is different from python when
1400
// a maxsplit argument is supplied. (see javascript string split
1401
// function docs for details)
1402
var l=self.valueOf().split(re,-1)
1405
if (b.length > 0) a.push(b.join(sep))
1406
1407
return a
1408
}
1409
}
1410
1411
$StringDict.splitlines = function(self){return $StringDict.split(self,'\n')}
1412
1413
$StringDict.startswith = function(self){
1414
// Return True if string starts with the prefix, otherwise return False.
1415
// prefix can also be a tuple of prefixes to look for. With optional
1416
// start, test string beginning at that position. With optional end,
1417
// stop comparing string at that position.
1419
{self:null, prefix:null, start:null, end:null},
1420
['self', 'prefix', 'start', 'end'],
1421
arguments,{start:0, end:self.length-1},null,null)
1422
var prefixes = $ns['prefix']
1423
if(!isinstance(prefixes,_b_.tuple)){prefixes=[prefixes]}
1429
if (s.indexOf(prefixes[i]) == 0) return true
1430
}
1431
return false
1432
}
1433
1434
$StringDict.strip = function(self,x){
1435
if(x==undefined){x = "\\s"}
1436
return $StringDict.rstrip($StringDict.lstrip(self,x),x)
1437
}
1438
1439
$StringDict.swapcase = function(self) {
1440
//inspired by http://www.geekpedia.com/code69_Swap-string-case-using-JavaScript.html
1441
return self.replace(/([a-z])|([A-Z])/g, function($0,$1,$2)
1442
{ return ($1) ? $0.toUpperCase() : $0.toLowerCase()
1443
})
1444
}
1445
1446
$StringDict.title = function(self) {
1447
//inspired from http://stackoverflow.com/questions/196972/convert-string-to-title-case-with-javascript
1448
return self.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
1449
}
1450
1451
$StringDict.translate = function(self,table) {
1456
if(repl==-1){res[pos++]=self.charAt(i)}
1457
else if(repl!==None){res[pos++]=repl}
1461
}
1462
1463
$StringDict.upper = function(self){return self.toUpperCase()}
1464
1465
$StringDict.zfill = function(self, width) {
1466
if (width === undefined || width <= self.length || !self.isnumeric()) {
1467
return self
1468
}
1469
1470
return Array(width - self.length +1).join('0');
1471
}
1472
1473
function str(arg){
1474
if(arg===undefined) return ''
1475
switch(typeof arg) {
1476
case 'string': return arg
1477
case 'number': return arg.toString()
1478
}
1484
// class or its subclasses, but the attribute __str__ of the
1485
// class metaclass (usually "type") or its subclasses (usually
1486
// "object")
1487
// The metaclass is the attribute __class__ of the class dictionary
1488
var func = $B.$type.__getattribute__(arg.$dict.__class__,'__str__')
1493
var f = getattr(arg,'__str__')
1494
// XXX fix : if not better than object.__str__, try __repr__
1495
return f()
1496
}
1497
catch(err){
1503
if($B.debug>1){console.log(err)}
1504
console.log('Warning - no method __str__ or __repr__, default to toString', arg)
1506
}
1507
}
1508
}
1509
str.__class__ = $B.$factory
1510
str.$dict = $StringDict
1511
$StringDict.$factory = str
1512
$StringDict.__new__ = function(cls){
1513
if(cls===undefined){
1514
throw _b_.TypeError('str.__new__(): not enough arguments')
1515
}
1516
return {__class__:cls.$dict}
1517
}
1518
1521
// dictionary and factory for subclasses of string
1522
var $StringSubclassDict = {
1523
__class__:$B.$type,
1524
__name__:'str'
1525
}
1526
1527
// the methods in subclass apply the methods in $StringDict to the
1528
// result of instance.valueOf(), which is a Javascript string
1529
for(var $attr in $StringDict){
1530
if(typeof $StringDict[$attr]=='function'){
1531
$StringSubclassDict[$attr]=(function(attr){
1532
return function(){
1538
}
1539
}
1540
return $StringDict[attr].apply(null,args)
1541
}
1542
})($attr)
1543
}
1544
}
1545
$StringSubclassDict.__mro__ = [$StringSubclassDict,$ObjectDict]
1546
1547
// factory for str subclasses
1548
$B.$StringSubclassFactory = {
1549
__class__:$B.$factory,
1550
$dict:$StringSubclassDict
1551
}
1552
1553
_b_.str = str
1554
1555
// Function to parse the 2nd argument of format()
1556
$B.parse_format_spec = function(spec){
1566
if(spec.charAt(1) && aligns.indexOf(spec.charAt(1))!=-1){
1567
// If the second char is also an alignment specifier, the
1568
// first char is the fill value
1569
this.fill = spec.charAt(0)
1570
this.align = spec.charAt(1)
1571
pos = 2
1572
}else{
1573
// The first character defines alignment : fill defaults to ' '
1574
this.align=aligns[align_pos];
1575
this.fill=' ';
1576
pos++
1577
}
1578
}else{
1579
align_pos = aligns.indexOf(spec.charAt(1))
1580
if(spec.charAt(1) && align_pos!=-1){
1581
// The second character defines alignment : fill is the first one
1582
this.align=aligns[align_pos]
1583
this.fill=spec.charAt(0)
1584
pos = 2
1585
}
1586
}
1587
var car = spec.charAt(pos)
1588
if(car=='+'||car=='-'||car==' '){
1589
this.sign=car;
1590
pos++;
1591
car=spec.charAt(pos);
1594
if(car=='0'){
1595
// sign-aware : equivalent to fill=0 and align=='='
1596
this.fill='0'
1597
this.align = '='
1598
pos++;car=spec.charAt(pos)
1599
}
1601
if(this.width===undefined){this.width=car}
1602
else{this.width+=car}
1603
pos++;car=spec.charAt(pos)
1605
if(this.width!==undefined){this.width=parseInt(this.width)}
1606
if(car==','){this.comma=true;pos++;car=spec.charAt(pos)}
1607
if(car=='.'){
1608
if(digits.indexOf(spec.charAt(pos+1))==-1){
1609
throw _b_.ValueError("Missing precision in format spec")
1610
}
1611
this.precision = spec.charAt(pos+1)
1612
pos+=2;car=spec.charAt(pos)
1613
while(car && digits.indexOf(car)>-1){
1614
this.precision+=car;pos++;car=spec.charAt(pos)
1615
}
1616
this.precision = parseInt(this.precision)
1617
}
1618
if(car && types.indexOf(car)>-1){this.type=car;pos++;car=spec.charAt(pos)}
1619
if(pos!==spec.length){
1620
console.log('error', spec, this, pos, spec.charAt(pos))
1621
throw _b_.ValueError("Invalid format specifier")
1622
}
1623
}
1624
this.toString = function(){
1625
return (this.fill===undefined ? '' : _b_.str(this.fill))+
1626
(this.align||'')+
1627
(this.sign||'')+
1628
(this.alternate ? '#' : '')+
1629
(this.sign_aware ? '0' : '')+
1630
(this.width || '')+
1631
(this.comma ? ',' : '')+
1632
(this.precision ? '.'+this.precision : '')+
1633
(this.type || '')
1634
}
1635
}
1636
1637
$B.format_width = function(s, fmt){
1638
if(fmt.width && s.length<fmt.width){
1639
var fill=fmt.fill || ' ', align = fmt.align || '<',
1640
missing = fmt.width-s.length
1641
switch(align){
1642
case '<':
1643
return s+fill.repeat(missing)
1644
case '>':
1645
return fill.repeat(missing)+s
1646
case '=':
1647
if('+-'.indexOf(s.charAt(0))>-1){
1648
return s.charAt(0)+fill.repeat(missing)+s.substr(1)
1649
}else{
1650
return fill.repeat(missing)+s
1651
}
1652
case '^':
1653
left = parseInt(missing/2)