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