Add String#repeat method (#67)

This commit is contained in:
Max Graey 2018-04-07 19:59:26 +03:00 committed by Daniel Wirtz
parent f1b00c90be
commit b7ef21950b
8 changed files with 897 additions and 62 deletions

2
std/assembly.d.ts vendored
View File

@ -373,6 +373,8 @@ declare class String {
trim(): string;
trimLeft(): string;
trimRight(): string;
repeat(count?: i32): string;
toString(): string;
}
/** Class for representing a runtime error. Base class of all errors. */

View File

@ -111,7 +111,7 @@ export class String {
@operator("==")
private static __eq(left: String, right: String): bool {
if (left === right) return true;
if (left === null) return right === null;
if (left === null) return right === null;
if (right === null) return false;
var leftLength = left.length;
@ -131,7 +131,7 @@ export class String {
@operator(">")
private static __gt(left: String, right: String): bool {
if (left === null || right === null) return false;
if (left === right || left === null || right === null) return false;
var leftLength = left.length;
var rightLength = right.length;
@ -149,8 +149,9 @@ export class String {
@operator(">=")
private static __gte(left: String, right: String): bool {
if (left === null) return right === null;
else if (right === null) return false;
if (left === right) return true;
if (left === null) return right === null;
if (right === null) return false;
var leftLength = left.length;
var rightLength = right.length;
@ -168,7 +169,7 @@ export class String {
@operator("<")
private static __lt(left: String, right: String): bool {
if (left === null || right === null) return false;
if (left === right || left === null || right === null) return false;
var leftLength = left.length;
var rightLength = right.length;
@ -186,8 +187,9 @@ export class String {
@operator("<=")
private static __lte(left: String, right: String): bool {
if (left === null) return right === null;
else if (right === null) return false;
if (left === right) return true;
if (left === null) return right === null;
if (right === null) return false;
var leftLength = left.length;
var rightLength = right.length;
@ -231,6 +233,7 @@ export class String {
startsWith(searchString: String, position: i32 = 0): bool {
assert(this !== null);
if (searchString === null) searchString = changetype<String>("null");
var pos: isize = position;
var len: isize = this.length;
var start: isize = min<isize>(max<isize>(pos, 0), len);
@ -292,6 +295,7 @@ export class String {
trim(): String {
assert(this !== null);
var length: usize = this.length;
while (
length &&
isWhiteSpaceOrLineTerminator(
@ -377,6 +381,40 @@ export class String {
);
return out;
}
repeat(count: i32 = 0): String {
assert(this !== null);
var length = this.length;
// Most browsers can't handle strings 1 << 28 chars or longer
if (count < 0 || length * count > (1 << 28)) {
throw new RangeError("Invalid count value");
}
if (count === 0 || !length) return EMPTY;
if (count === 1) return this;
var result = allocate(length * count);
var strLen = length << 1;
/*
* TODO possible improvments: reuse existing result for exponentially concats like:
* 'a' + 'a' => 'aa' + 'aa' => 'aaaa' + 'aaaa' etc
*/
for (let offset = 0, len = strLen * count; offset < len; offset += strLen) {
move_memory(
changetype<usize>(result) + HEADER_SIZE + offset,
changetype<usize>(this) + HEADER_SIZE,
strLen
);
}
return result;
}
toString(): String {
return this;
}
}
export function parseInt(str: String, radix: i32 = 0): f64 {

5
std/portable.d.ts vendored
View File

@ -247,12 +247,16 @@ declare class Float32Array extends Array<f32> {}
declare class Float64Array extends Array<f64> {}
declare class String {
static fromCharCode(ls: i32, hs?: i32): string;
static fromCharCodes(arr: u16[]): string;
static fromCodePoint(cp: i32): string;
static fromCodePoints(arr: i32[]): string;
readonly length: i32;
private constructor();
indexOf(subject: string, position?: i32): i32;
includes(other: string): bool;
lastIndexOf(subject: string, position?: i32): i32;
@ -262,6 +266,7 @@ declare class String {
startsWith(subject: string): bool;
endsWith(subject: string): bool;
replace(search: string, replacement: string): string;
repeat(count?: i32): string;
toString(): string;
}

View File

@ -125,7 +125,7 @@
(call $abort
(i32.const 0)
(i32.const 8)
(i32.const 232)
(i32.const 234)
(i32.const 4)
)
(unreachable)

View File

@ -195,7 +195,7 @@
(call $abort
(i32.const 0)
(i32.const 8)
(i32.const 232)
(i32.const 234)
(i32.const 4)
)
(unreachable)

View File

@ -15,7 +15,7 @@
(global $std/string/nullStr (mut i32) (i32.const 0))
(global $argumentCount (mut i32) (i32.const 0))
(global $std/string/c (mut i32) (i32.const 0))
(global $HEAP_BASE i32 (i32.const 436))
(global $HEAP_BASE i32 (i32.const 520))
(memory $0 1)
(data (i32.const 4) "\10\00\00\00h\00i\00,\00 \00I\00\'\00m\00 \00a\00 \00s\00t\00r\00i\00n\00g")
(data (i32.const 40) "\0d\00\00\00s\00t\00d\00/\00s\00t\00r\00i\00n\00g\00.\00t\00s")
@ -45,6 +45,11 @@
(data (i32.const 404) "\02\00\00\00a\00a")
(data (i32.const 412) "\03\00\00\00a\00b\00c")
(data (i32.const 424) "\03\00\00\001\002\003")
(data (i32.const 436) "\03\00\00\00a\00a\00a")
(data (i32.const 448) "\08\00\00\00a\00b\00a\00b\00a\00b\00a\00b")
(data (i32.const 468) "\05\00\00\00a\00a\00a\00a\00a")
(data (i32.const 484) "\06\00\00\00a\00a\00a\00a\00a\00a")
(data (i32.const 500) "\07\00\00\00a\00a\00a\00a\00a\00a\00a")
(export "getString" (func $std/string/getString))
(export "memory" (memory $0))
(start $start)
@ -156,7 +161,7 @@
(call $abort
(i32.const 0)
(i32.const 72)
(i32.const 232)
(i32.const 234)
(i32.const 4)
)
(unreachable)
@ -376,7 +381,7 @@
(call $abort
(i32.const 0)
(i32.const 72)
(i32.const 211)
(i32.const 213)
(i32.const 4)
)
(unreachable)
@ -1087,7 +1092,7 @@
(call $abort
(i32.const 0)
(i32.const 72)
(i32.const 431)
(i32.const 469)
(i32.const 10)
)
(unreachable)
@ -3352,8 +3357,20 @@
(i32.and
(if (result i32)
(tee_local $2
(i32.eqz
(get_local $0)
(i32.and
(if (result i32)
(tee_local $2
(i32.eq
(get_local $0)
(get_local $1)
)
)
(get_local $2)
(i32.eqz
(get_local $0)
)
)
(i32.const 1)
)
)
(get_local $2)
@ -3421,14 +3438,17 @@
(local $2 i32)
(local $3 i32)
(if
(get_local $0)
(if
(i32.eqz
(get_local $1)
)
(return
(i32.const 0)
)
(i32.eq
(get_local $0)
(get_local $1)
)
(return
(i32.const 1)
)
)
(if
(i32.eqz
(get_local $0)
)
(return
(i32.eqz
@ -3436,6 +3456,14 @@
)
)
)
(if
(i32.eqz
(get_local $1)
)
(return
(i32.const 0)
)
)
(set_local $2
(i32.load
(get_local $1)
@ -3495,8 +3523,20 @@
(i32.and
(if (result i32)
(tee_local $2
(i32.eqz
(get_local $0)
(i32.and
(if (result i32)
(tee_local $2
(i32.eq
(get_local $0)
(get_local $1)
)
)
(get_local $2)
(i32.eqz
(get_local $0)
)
)
(i32.const 1)
)
)
(get_local $2)
@ -3564,14 +3604,17 @@
(local $2 i32)
(local $3 i32)
(if
(get_local $0)
(if
(i32.eqz
(get_local $1)
)
(return
(i32.const 0)
)
(i32.eq
(get_local $0)
(get_local $1)
)
(return
(i32.const 1)
)
)
(if
(i32.eqz
(get_local $0)
)
(return
(i32.eqz
@ -3579,6 +3622,14 @@
)
)
)
(if
(i32.eqz
(get_local $1)
)
(return
(i32.const 0)
)
)
(set_local $2
(i32.load
(get_local $0)
@ -3631,7 +3682,154 @@
(i32.const 0)
)
)
(func $start (; 28 ;) (type $v)
(func $~lib/string/String#repeat (; 28 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
(if
(i32.eqz
(get_local $0)
)
(block
(call $abort
(i32.const 0)
(i32.const 72)
(i32.const 386)
(i32.const 4)
)
(unreachable)
)
)
(set_local $3
(i32.load
(get_local $0)
)
)
(if
(i32.and
(if (result i32)
(tee_local $2
(i32.lt_s
(get_local $1)
(i32.const 0)
)
)
(get_local $2)
(i32.gt_s
(i32.mul
(get_local $3)
(get_local $1)
)
(i32.const 268435456)
)
)
(i32.const 1)
)
(unreachable)
)
(if
(i32.and
(if (result i32)
(tee_local $2
(i32.eqz
(get_local $1)
)
)
(get_local $2)
(i32.eqz
(get_local $3)
)
)
(i32.const 1)
)
(return
(i32.const 332)
)
)
(if
(i32.eq
(get_local $1)
(i32.const 1)
)
(return
(get_local $0)
)
)
(set_local $4
(call $~lib/internal/string/allocate
(i32.mul
(get_local $3)
(get_local $1)
)
)
)
(set_local $2
(i32.const 0)
)
(set_local $1
(i32.mul
(tee_local $3
(i32.shl
(get_local $3)
(i32.const 1)
)
)
(get_local $1)
)
)
(loop $continue|0
(if
(i32.lt_s
(get_local $2)
(get_local $1)
)
(block
(call $~lib/memory/move_memory
(i32.add
(i32.add
(get_local $4)
(i32.const 4)
)
(get_local $2)
)
(i32.add
(get_local $0)
(i32.const 4)
)
(get_local $3)
)
(set_local $2
(i32.add
(get_local $2)
(get_local $3)
)
)
(br $continue|0)
)
)
)
(get_local $4)
)
(func $~lib/string/String#repeat|trampoline (; 29 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(block $1of1
(block $0of1
(block $oob
(br_table $0of1 $1of1 $oob
(get_global $argumentCount)
)
)
(unreachable)
)
(set_local $1
(i32.const 0)
)
)
(call $~lib/string/String#repeat
(get_local $0)
(get_local $1)
)
)
(func $start (; 30 ;) (type $v)
(set_global $~lib/allocator/arena/startOffset
(i32.and
(i32.add
@ -4446,5 +4644,190 @@
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 332)
(i32.const 100)
)
(i32.const 332)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 66)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(block (result i32)
(set_global $argumentCount
(i32.const 0)
)
(call $~lib/string/String#repeat|trampoline
(i32.const 316)
(i32.const 0)
)
)
(i32.const 332)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 67)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 316)
(i32.const 1)
)
(i32.const 316)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 68)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 316)
(i32.const 2)
)
(i32.const 404)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 69)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 316)
(i32.const 3)
)
(i32.const 436)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 70)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 388)
(i32.const 4)
)
(i32.const 448)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 71)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 316)
(i32.const 5)
)
(i32.const 468)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 72)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 316)
(i32.const 6)
)
(i32.const 484)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 73)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 316)
(i32.const 7)
)
(i32.const 500)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 74)
(i32.const 0)
)
(unreachable)
)
)
)
)

View File

@ -3,7 +3,7 @@ import "allocator/arena";
// preliminary
var str: string = "hi, I'm a string";
var nullStr: String;
var nullStr: string;
// exactly once in static memory
assert(changetype<usize>(str) == changetype<usize>("hi, I'm a string"));
@ -62,3 +62,13 @@ assert("" >= "");
assert("" <= "");
assert("123".length == 3);
assert("".repeat(100) == "");
assert("a".repeat() == "");
assert("a".repeat(1) == "a");
assert("a".repeat(2) == "aa");
assert("a".repeat(3) == "aaa");
assert("ab".repeat(4) == "abababab");
assert("a".repeat(5) == "aaaaa");
assert("a".repeat(6) == "aaaaaa");
assert("a".repeat(7) == "aaaaaaa");

View File

@ -47,7 +47,7 @@
(global $~lib/internal/string/CharCode.z i32 (i32.const 122))
(global $~lib/internal/string/EMPTY i32 (i32.const 332))
(global $std/string/c (mut i32) (i32.const 0))
(global $HEAP_BASE i32 (i32.const 436))
(global $HEAP_BASE i32 (i32.const 520))
(memory $0 1)
(data (i32.const 4) "\10\00\00\00h\00i\00,\00 \00I\00\'\00m\00 \00a\00 \00s\00t\00r\00i\00n\00g\00")
(data (i32.const 40) "\0d\00\00\00s\00t\00d\00/\00s\00t\00r\00i\00n\00g\00.\00t\00s\00")
@ -78,6 +78,11 @@
(data (i32.const 404) "\02\00\00\00a\00a\00")
(data (i32.const 412) "\03\00\00\00a\00b\00c\00")
(data (i32.const 424) "\03\00\00\001\002\003\00")
(data (i32.const 436) "\03\00\00\00a\00a\00a\00")
(data (i32.const 448) "\08\00\00\00a\00b\00a\00b\00a\00b\00a\00b\00")
(data (i32.const 468) "\05\00\00\00a\00a\00a\00a\00a\00")
(data (i32.const 484) "\06\00\00\00a\00a\00a\00a\00a\00a\00")
(data (i32.const 500) "\07\00\00\00a\00a\00a\00a\00a\00a\00a\00")
(export "getString" (func $std/string/getString))
(export "memory" (memory $0))
(start $start)
@ -206,7 +211,7 @@
(call $abort
(i32.const 0)
(i32.const 72)
(i32.const 232)
(i32.const 234)
(i32.const 4)
)
(unreachable)
@ -464,7 +469,7 @@
(call $abort
(i32.const 0)
(i32.const 72)
(i32.const 211)
(i32.const 213)
(i32.const 4)
)
(unreachable)
@ -1266,7 +1271,7 @@
(call $abort
(i32.const 0)
(i32.const 72)
(i32.const 431)
(i32.const 469)
(i32.const 10)
)
(unreachable)
@ -3851,9 +3856,21 @@
(i32.and
(if (result i32)
(tee_local $2
(i32.eq
(get_local $0)
(i32.const 0)
(i32.and
(if (result i32)
(tee_local $2
(i32.eq
(get_local $0)
(get_local $1)
)
)
(get_local $2)
(i32.eq
(get_local $0)
(i32.const 0)
)
)
(i32.const 1)
)
)
(get_local $2)
@ -3934,6 +3951,15 @@
(local $4 i32)
(local $5 i32)
(local $6 i32)
(if
(i32.eq
(get_local $0)
(get_local $1)
)
(return
(i32.const 1)
)
)
(if
(i32.eq
(get_local $0)
@ -3945,14 +3971,14 @@
(i32.const 0)
)
)
(if
(i32.eq
(get_local $1)
(i32.const 0)
)
(return
(i32.const 0)
)
)
(if
(i32.eq
(get_local $1)
(i32.const 0)
)
(return
(i32.const 0)
)
)
(set_local $2
@ -4027,9 +4053,21 @@
(i32.and
(if (result i32)
(tee_local $2
(i32.eq
(get_local $0)
(i32.const 0)
(i32.and
(if (result i32)
(tee_local $2
(i32.eq
(get_local $0)
(get_local $1)
)
)
(get_local $2)
(i32.eq
(get_local $0)
(i32.const 0)
)
)
(i32.const 1)
)
)
(get_local $2)
@ -4110,6 +4148,15 @@
(local $4 i32)
(local $5 i32)
(local $6 i32)
(if
(i32.eq
(get_local $0)
(get_local $1)
)
(return
(i32.const 1)
)
)
(if
(i32.eq
(get_local $0)
@ -4121,14 +4168,14 @@
(i32.const 0)
)
)
(if
(i32.eq
(get_local $1)
(i32.const 0)
)
(return
(i32.const 0)
)
)
(if
(i32.eq
(get_local $1)
(i32.const 0)
)
(return
(i32.const 0)
)
)
(set_local $2
@ -4193,7 +4240,172 @@
)
)
)
(func $start (; 28 ;) (type $v)
(func $~lib/string/String#repeat (; 28 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
(local $5 i32)
(local $6 i32)
(if
(i32.eqz
(i32.ne
(get_local $0)
(i32.const 0)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 72)
(i32.const 386)
(i32.const 4)
)
(unreachable)
)
)
(set_local $2
(i32.load
(get_local $0)
)
)
(if
(i32.and
(if (result i32)
(tee_local $3
(i32.lt_s
(get_local $1)
(i32.const 0)
)
)
(get_local $3)
(i32.gt_s
(i32.mul
(get_local $2)
(get_local $1)
)
(i32.shl
(i32.const 1)
(i32.const 28)
)
)
)
(i32.const 1)
)
(unreachable)
)
(if
(i32.and
(if (result i32)
(tee_local $3
(i32.eq
(get_local $1)
(i32.const 0)
)
)
(get_local $3)
(i32.eqz
(get_local $2)
)
)
(i32.const 1)
)
(return
(i32.const 332)
)
)
(if
(i32.eq
(get_local $1)
(i32.const 1)
)
(return
(get_local $0)
)
)
(set_local $4
(call $~lib/internal/string/allocate
(i32.mul
(get_local $2)
(get_local $1)
)
)
)
(set_local $5
(i32.shl
(get_local $2)
(i32.const 1)
)
)
(block $break|0
(block
(set_local $3
(i32.const 0)
)
(set_local $6
(i32.mul
(get_local $5)
(get_local $1)
)
)
)
(loop $continue|0
(if
(i32.lt_s
(get_local $3)
(get_local $6)
)
(block
(block
(call $~lib/memory/move_memory
(i32.add
(i32.add
(get_local $4)
(i32.const 4)
)
(get_local $3)
)
(i32.add
(get_local $0)
(i32.const 4)
)
(get_local $5)
)
)
(set_local $3
(i32.add
(get_local $3)
(get_local $5)
)
)
(br $continue|0)
)
)
)
)
(return
(get_local $4)
)
)
(func $~lib/string/String#repeat|trampoline (; 29 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(block $1of1
(block $0of1
(block $oob
(br_table $0of1 $1of1 $oob
(get_global $argumentCount)
)
)
(unreachable)
)
(set_local $1
(i32.const 0)
)
)
(call $~lib/string/String#repeat
(get_local $0)
(get_local $1)
)
)
(func $start (; 30 ;) (type $v)
(set_global $~lib/allocator/arena/startOffset
(i32.and
(i32.add
@ -5077,5 +5289,190 @@
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 332)
(i32.const 100)
)
(i32.const 332)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 66)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(block (result i32)
(set_global $argumentCount
(i32.const 0)
)
(call $~lib/string/String#repeat|trampoline
(i32.const 316)
(i32.const 0)
)
)
(i32.const 332)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 67)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 316)
(i32.const 1)
)
(i32.const 316)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 68)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 316)
(i32.const 2)
)
(i32.const 404)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 69)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 316)
(i32.const 3)
)
(i32.const 436)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 70)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 388)
(i32.const 4)
)
(i32.const 448)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 71)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 316)
(i32.const 5)
)
(i32.const 468)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 72)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 316)
(i32.const 6)
)
(i32.const 484)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 73)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $~lib/string/String.__eq
(call $~lib/string/String#repeat
(i32.const 316)
(i32.const 7)
)
(i32.const 500)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 40)
(i32.const 74)
(i32.const 0)
)
(unreachable)
)
)
)
)