#[no_std, cache_output] impl @string { is_empty: #[desc("Returns true if the string has a length of 0, false otherwise")] (self) { return self.length == 0; }, substr: #[desc("Gets a substring beginning at the specified start and ending at the specified end.")] (self, start: @number, end: @number) { if start > end { return $.substr(self, end, start); } return $.substr(self, start, end); }, join: #[desc("Joins a list using the string.")] (self, list: @array) { let out = ""; for i in list { out += @string(i); out += self; } return out.substr(0, out.length-self.length); }, split: #[desc("Splits the string by the specified seperator.")] (self, spstr: @string) { if spstr.length == 0 { return self as @array; } else { return $.split_str(self, spstr); } }, starts_with: #[desc("Checks does the string starts with a string.")] (self, substr: @string) { if substr == '' { return true } if substr.length > self.length { return false } return self.substr(0, substr.length) == substr; }, ends_with: #[desc("Checks does the string starts with a string.")] (self, substr: @string) { if substr == '' { return true } if substr.length > self.length { return false } return self.substr(self.length-substr.length, self.length) == substr; }, index: #[desc("Gets the index of a string, if it doesn't exists returns null.")] (self, substr: @string) { if substr == '' { return null } if substr.length > self.length { return null } r = self.length - substr.length + 1 for i in 0..r { if $.substr(self, i, i + substr.length) == substr { return i } } return null }, contains: #[desc("Checks if the string contains a string.")] (self, substr: @string) { return self has substr }, reverse: #[desc("Reverses the string.")] (self) { let ret_str = "" for i in ..self.length { ret_str += self[self.length - 1 - i] } return ret_str }, lowercase: #[desc("Makes whole string lowercase.")] (self) { dict = {A:'a',B:'b',C:'c',D:'d',E:'e',F:'f',G:'g',H:'h',I:'i',J:'j',K:'k',L:'l',M:'m',N:'n',O:'o',P:'p',Q:'q',R:'r',S:'s',T:'t',U:'u',V:'v',W:'w',X:'x',Y:'y',Z:'z'} return ''.join(self.split('').map(el => dict.get(el, el))) }, uppercase: #[desc("Makes whole string uppercase.")] (self) { dict = {a:'A',b:'B',c:'C',d:'D',e:'E',f:'F',g:'G',h:'H',i:'I',j:'J',k:'K',l:'L',m:'M',n:'N',o:'O',p:'P',q:'Q',r:'R',s:'S',t:'T',u:'U',v:'V',w:'W',x:'X',y:'Y',z:'Z'} return ''.join(self.split('').map(el => dict.get(el, el))) }, is_upper: #[desc("Checks if whole string is uppercase, ignores characters that is not in the alphabet.")] (self) { return self.uppercase() == self }, is_lower: #[desc("Checks if whole string is lowercase, ignores characters that is not in the alphabet.")] (self) { return self.lowercase() == self }, l_pad: #[desc("Returns a left-padded version of the string")] (self, times: @number, seq: @string = ' ') { return seq * times + self }, r_pad: #[desc("Returns a right-padded version of the string")] (self, times: @number, seq: @string = ' ') { return self + seq * times }, l_trim: #[desc("Returns a left-trimmed verison of the string") example(" str1 = ' abcd g ' str2 = ' pog __' $.assert(str1.l_trim() == 'abcd g ') $.assert(str2.l_trim() == 'pog __') ")] (self, tokens: @string | [@string] = [' ', '\n']) { // check for tokens for token in tokens { if token.length != 1 { throw "Trim tokens must all be 1-character long" } } let tokenlist = tokens if tokens.type == @string { tokenlist = [tokens] } // scan left let begin = 0 for i in ..self.length { if tokenlist.contains(self[i]) { continue } else { begin = i break } } return $.substr(self, begin, self.length) }, r_trim: #[desc("Returns a right-trimmed version of the string") example(" str = 'abcd ' str2 = ' abcd g ' str3 = ' pog __' $.assert(str.r_trim() == 'abcd') $.assert(str2.r_trim() == ' abcd g') $.assert(str3.r_trim(tokens = [' ', '_']) == ' pog') ")] (self, tokens: @string | [@string] = [' ', '\n']) { return self.reverse().l_trim(tokens).reverse() // lol }, trim: #[desc("Returns a trimmed version of the string") example(" str = 'abcd ' str2 = ' abcd g ' str3 = ' pog __' $.assert(str.trim() == 'abcd') $.assert(str2.trim() == 'abcd g') $.assert(str3.trim(tokens = [' ', '_'])) ")] (self, tokens: @string | [@string] = [' ', '\n']){ return self.l_trim(tokens).r_trim(tokens) }, fmt: #[desc("Returns a formtted version of the string. Accepts either a single argument or an array") example(" name1 = 'bob' name2 = 'alice' $.assert('hi {}'.fmt(name1) == 'hi bob') $.assert('hi {} and {}'.fmt([name1, name2]) == 'hi bob and alice') $.assert('hi {1} and {0}'.fmt([name1, name2]) == 'hi alice and bob') $.assert('{} has {} apples'.fmt([name1, 5]) == 'bob has 5 apples') ")] (self, subs) { blank_regex = "\\{\\}" numbered_regex = "\\{\\d+\\}" blanks = $.regex(blank_regex, self, 'findall', null) numbered = $.regex(numbered_regex, self, 'findall', null) if blanks.length != 0 && numbered.length != 0 { throw "FormatError: String format must only be either blank {} or numbered {1} {2}" } // turn single arg into array let args = subs if subs.type == @array else [subs] let ret_str = "" if blanks.length != 0 { if blanks.length != args.length { throw "FormatError: number of formats must be the same as number of args" } splitted = self.split("{}") for i in ..args.length { ret_str += splitted[i] + (args[i] as @string) } ret_str += splitted[-1] } else if numbered.length != 0 { // im not gonna make the error handling here better // maybe later, we can make it so any brace that has a number greater than args MUST be escpaed, but now it will be ignored ret_str = self for i in ..args.length { ret_str = $.regex("\\{" + i as @string + "\\}", ret_str, "replace", args[i] as @string) } } else { ret_str = self } return ret_str }, find: (self, re: @string) { $.regex(re, self, "match", null) }, findall: (self, re: @string) { $.regex(re, self, "findall", null) }, replace: (self, re: @string, replacement: @string) { $.regex(re, self, "replace", replacement) } }