diff options
Diffstat (limited to 'v_windows/v/vlib/builtin/js')
-rw-r--r-- | v_windows/v/vlib/builtin/js/array.js.v | 253 | ||||
-rw-r--r-- | v_windows/v/vlib/builtin/js/builtin.js.v | 61 | ||||
-rw-r--r-- | v_windows/v/vlib/builtin/js/builtin.v | 84 | ||||
-rw-r--r-- | v_windows/v/vlib/builtin/js/byte.js.v | 16 | ||||
-rw-r--r-- | v_windows/v/vlib/builtin/js/int.js.v | 8 | ||||
-rw-r--r-- | v_windows/v/vlib/builtin/js/jsfns.js.v | 125 | ||||
-rw-r--r-- | v_windows/v/vlib/builtin/js/jsfns_browser.js.v | 59 | ||||
-rw-r--r-- | v_windows/v/vlib/builtin/js/jsfns_node.js.v | 31 | ||||
-rw-r--r-- | v_windows/v/vlib/builtin/js/map.js.v | 26 | ||||
-rw-r--r-- | v_windows/v/vlib/builtin/js/string.js.v | 720 |
10 files changed, 1383 insertions, 0 deletions
diff --git a/v_windows/v/vlib/builtin/js/array.js.v b/v_windows/v/vlib/builtin/js/array.js.v new file mode 100644 index 0000000..495e656 --- /dev/null +++ b/v_windows/v/vlib/builtin/js/array.js.v @@ -0,0 +1,253 @@ +module builtin + +struct array { + arr JS.Array +pub: + len int + cap int +} + +#function flatIntoArray(target, source, sourceLength, targetIndex, depth) { +#"use strict"; +# +#for (var sourceIndex = 0; sourceIndex < sourceLength; ++sourceIndex) { +#if (sourceIndex in source) { +#var element = source[sourceIndex]; +#if (depth > 0 && Array.isArray(element)) +#targetIndex = flatIntoArray(target, element, element.length, targetIndex, depth - 1); +#else { +#target[targetIndex] = element; +#++targetIndex; +#} +#} +#} +#return targetIndex; +#} +#function flatArray(target,depth) { +#var array = target +#var length = array.length; +#var depthNum = 1; +# +#if (depth !== undefined) +#depthNum = +depth +# +#var result = [] +# +#flatIntoArray(result, array, length, 0, depthNum); +#return result; +#} + +[unsafe] +pub fn (a array) repeat_to_depth(count int, depth int) array { + if count < 0 { + panic('array.repeat: count is negative: $count') + } + mut arr := empty_array() + #let tmp = new Array(a.arr.length * +count); + #tmp.fill(a.arr); + # + #arr.arr = flatArray(tmp,depth+1); + + return arr +} + +// last returns the last element of the array. +pub fn (a array) last() voidptr { + mut res := voidptr(0) + #res = a.arr[a.len-1]; + + return res +} + +fn (a array) get(ix int) voidptr { + mut result := voidptr(0) + #result = a.arr[ix] + + return result +} + +pub fn (a array) repeat(count int) array { + unsafe { + return a.repeat_to_depth(count, 0) + } +} + +fn empty_array() array { + mut arr := array{} + #arr = new array([]) + + return arr +} + +fn (a &array) set_len(i int) { + #a.arr.length=i +} + +pub fn (mut a array) sort_with_compare(compare voidptr) { + #a.val.arr.sort(compare) +} + +pub fn (mut a array) sort() { + #a.val.arr.sort($sortComparator) +} + +pub fn (a array) index(v string) int { + for i in 0 .. a.len { + #if (a.arr[i].toString() == v.toString()) + + { + return i + } + } + return -1 +} + +pub fn (a array) slice(start int, end int) array { + mut result := a + #result = new array(a.arr.slice(start,end)) + + return result +} + +pub fn (mut a array) insert(i int, val voidptr) { + #a.val.arr.splice(i,0,val) +} + +pub fn (mut a array) insert_many(i int, val voidptr, size int) { + #a.val.arr.splice(i,0,...val.slice(0,+size)) +} + +pub fn (mut a array) join(separator string) string { + mut res := '' + #res = new builtin.string(a.val.arr.join(separator +'')); + + return res +} + +fn (a array) push(val voidptr) { + #a.arr.push(val) +} + +pub fn (a array) str() string { + mut res := '' + #res = new builtin.string(a + '') + + return res +} + +#array.prototype[Symbol.iterator] = function () { return this.arr[Symbol.iterator](); } +#array.prototype.entries = function () { let result = []; for (const [key,val] of this.arr.entries()) { result.push([new int(key), val]); } return result[Symbol.iterator](); } +#array.prototype.map = function(callback) { return new builtin.array(this.arr.map(callback)); } +#array.prototype.filter = function(callback) { return new array(this.arr.filter( function (it) { return (+callback(it)) != 0; } )); } +#Object.defineProperty(array.prototype,'cap',{ get: function () { return this.len; } }) +#array.prototype.any = function (value) { +#let val ;if (typeof value == 'function') { val = function (x) { return value(x); } } else { val = function (x) { return vEq(x,value); } } +#for (let i = 0;i < this.arr.length;i++) +#if (val(this.arr[i])) +#return true; +# +#return false; +#} + +#array.prototype.all = function (value) { +#let val ;if (typeof value == 'function') { val = function (x) { return value(x); } } else { val = function (x) { return vEq(x,value); } } +#for (let i = 0;i < this.arr.length;i++) +#if (!val(this.arr[i])) +#return false; +# +#return true; +#} +// delete deletes array element at index `i`. +pub fn (mut a array) delete(i int) { + a.delete_many(i, 1) +} + +// delete_many deletes `size` elements beginning with index `i` +pub fn (mut a array) delete_many(i int, size int) { + #a.val.arr.splice(i.valueOf(),size.valueOf()) +} + +// prepend prepends one value to the array. +pub fn (mut a array) prepend(val voidptr) { + a.insert(0, val) +} + +// prepend_many prepends another array to this array. +[unsafe] +pub fn (mut a array) prepend_many(val voidptr, size int) { + unsafe { a.insert_many(0, val, size) } +} + +pub fn (a array) reverse() array { + mut res := array{} + #res.arr = Array.from(a.arr).reverse() + + return res +} + +pub fn (mut a array) reverse_in_place() { + #a.val.arr.reverse() +} + +#array.prototype.$includes = function (elem) { return this.arr.find(function(e) { return vEq(elem,e); }) !== undefined;} + +// reduce executes a given reducer function on each element of the array, +// resulting in a single output value. +pub fn (a array) reduce(iter fn (int, int) int, accum_start int) int { + mut accum_ := accum_start + #for (let i = 0;i < a.arr.length;i++) { + #accum_ = iter(accum_, a.arr[i]) + #} + + return accum_ +} + +pub fn (mut a array) pop() voidptr { + mut res := voidptr(0) + #res = a.val.arr.pop() + + return res +} + +pub fn (a array) first() voidptr { + mut res := voidptr(0) + #res = a.arr[0] + + return res +} + +#array.prototype.toString = function () { +#let res = "[" +#for (let i = 0; i < this.arr.length;i++) { +#res += this.arr[i].toString(); +#if (i != this.arr.length-1) +#res += ', ' +#} +#res += ']' +#return res; +# +#} + +pub fn (a array) contains(key voidptr) bool { + #for (let i = 0; i < a.arr.length;i++) + #if (vEq(a.arr[i],key)) return new bool(true); + + return false +} + +// delete_last effectively removes last element of an array. +pub fn (mut a array) delete_last() { + #a.val.arr.pop(); +} + +[unsafe] +pub fn (a array) free() { +} + +// todo: once (a []byte) will work rewrite this +pub fn (a array) bytestr() string { + res := '' + #a.arr.forEach((item) => res.str += String.fromCharCode(+item)) + + return res +} diff --git a/v_windows/v/vlib/builtin/js/builtin.js.v b/v_windows/v/vlib/builtin/js/builtin.js.v new file mode 100644 index 0000000..f64da00 --- /dev/null +++ b/v_windows/v/vlib/builtin/js/builtin.js.v @@ -0,0 +1,61 @@ +module builtin + +// used to generate JS throw statements. +pub fn js_throw(s any) { + #throw s +} + +pub fn println(s string) { + $if js_freestanding { + #print(s.str) + } $else { + #console.log(s.str) + } +} + +pub fn print(s string) { + $if js_node { + #$process.stdout.write(s.str) + } $else { + panic('Cannot `print` in a browser, use `println` instead') + } +} + +pub fn eprintln(s string) { + $if js_freestanding { + #print(s.str) + } $else { + #console.error(s.str) + } +} + +pub fn eprint(s string) { + $if js_node { + #$process.stderr.write(s.str) + } $else { + panic('Cannot `eprint` in a browser, use `println` instead') + } +} + +// Exits the process in node, and halts execution in the browser +// because `process.exit` is undefined. Workaround for not having +// a 'real' way to exit in the browser. +pub fn exit(c int) { + JS.process.exit(c) + js_throw('exit($c)') +} + +fn opt_ok(data voidptr, option Option) { + #option.state = 0 + #option.err = none__ + #option.data = data +} + +pub fn unwrap(opt string) string { + mut o := Option{} + #o = opt + if o.state != 0 { + js_throw(o.err) + } + return opt +} diff --git a/v_windows/v/vlib/builtin/js/builtin.v b/v_windows/v/vlib/builtin/js/builtin.v new file mode 100644 index 0000000..7c82d1a --- /dev/null +++ b/v_windows/v/vlib/builtin/js/builtin.v @@ -0,0 +1,84 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module builtin + +fn (a any) toString() + +pub fn panic(s string) { + eprintln('V panic: $s') + exit(1) +} + +// IError holds information about an error instance +pub interface IError { + msg string + code int +} + +// Error is the default implementation of IError, that is returned by e.g. `error()` +pub struct Error { +pub: + msg string + code int +} + +struct None__ { + msg string + code int +} + +fn (_ None__) str() string { + return 'none' +} + +pub const none__ = IError(None__{'', 0}) + +pub struct Option { + state byte + err IError = none__ +} + +pub fn (err IError) str() string { + return match err { + None__ { 'none' } + Error { err.msg } + else { '$err.type_name(): $err.msg' } + } +} + +pub fn (o Option) str() string { + if o.state == 0 { + return 'Option{ ok }' + } + if o.state == 1 { + return 'Option{ none }' + } + return 'Option{ error: "$o.err" }' +} + +fn trace_error(x string) { + eprintln('> ${@FN} | $x') +} + +// error returns a default error instance containing the error given in `message`. +// Example: `if ouch { return error('an error occurred') }` +[inline] +pub fn error(message string) IError { + // trace_error(message) + return &Error{ + msg: message + } +} + +// error_with_code returns a default error instance containing the given `message` and error `code`. +// `if ouch { return error_with_code('an error occurred', 1) }` +[inline] +pub fn error_with_code(message string, code int) IError { + // trace_error('$message | code: $code') + return &Error{ + msg: message + code: code + } +} diff --git a/v_windows/v/vlib/builtin/js/byte.js.v b/v_windows/v/vlib/builtin/js/byte.js.v new file mode 100644 index 0000000..af1803b --- /dev/null +++ b/v_windows/v/vlib/builtin/js/byte.js.v @@ -0,0 +1,16 @@ +module builtin + +pub fn (b byte) is_space() bool { + mut result := false + #result.val = /^\s*$/.test(String.fromCharCode(b)) + + return result +} + +pub fn (c byte) is_letter() bool { + result := false + + #result.val = (c.val >= `a`.charCodeAt() && c.val <= `z`.charCodeAt()) || (c.val >= `A`.charCodeAt() && c.val <= `Z`.charCodeAt()) + + return result +} diff --git a/v_windows/v/vlib/builtin/js/int.js.v b/v_windows/v/vlib/builtin/js/int.js.v new file mode 100644 index 0000000..08e52a9 --- /dev/null +++ b/v_windows/v/vlib/builtin/js/int.js.v @@ -0,0 +1,8 @@ +module builtin + +pub fn (i int) str() string { + mut res := '' + #res = new builtin.string( i ) + + return res +} diff --git a/v_windows/v/vlib/builtin/js/jsfns.js.v b/v_windows/v/vlib/builtin/js/jsfns.js.v new file mode 100644 index 0000000..277c702 --- /dev/null +++ b/v_windows/v/vlib/builtin/js/jsfns.js.v @@ -0,0 +1,125 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// This file contains JS functions present in both node and the browser. +// They have been ported from their TypeScript definitions. + +module builtin + +pub struct JS.Number {} + +pub struct JS.String { + length JS.Number +} + +pub struct JS.Boolean {} + +pub struct JS.Array { + length JS.Number +} + +pub struct JS.Map {} + +// browser: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Error +// node: https://nodejs.org/api/errors.html#errors_class_error +pub struct JS.Error { +pub: + name string + message string + stack string +} + +// Type prototype functions +fn (v JS.String) toString() JS.String +fn (v JS.Number) toString() JS.String +fn (v JS.Boolean) toString() JS.String +fn (v JS.Array) toString() JS.String +fn (v JS.Map) toString() JS.String + +// Hack for "`[]JS.String` is not a struct" when returning arr.length or arr.len +// TODO: Fix []JS.String not a struct error +fn native_str_arr_len(arr []JS.String) int { + len := 0 + #len = arr.length + + return len +} + +// Top level functions +fn JS.eval(string) any +fn JS.parseInt(string, f64) JS.Number +fn JS.parseFloat(string) JS.Number +fn JS.isNaN(f64) bool +fn JS.isFinite(f64) bool +fn JS.decodeURI(string) string +fn JS.decodeURIComponent(string) string +fn JS.encodeURI(string) string + +type EncodeURIComponentArg = bool | f64 | string + +fn JS.encodeURIComponent(EncodeURIComponentArg) string +fn JS.escape(string) string +fn JS.unescape(string) string + +// console +fn JS.console.assert(bool, ...any) +fn JS.console.clear() +fn JS.console.count(string) +fn JS.console.countReset(string) +fn JS.console.debug(...any) +fn JS.console.dir(any, any) +fn JS.console.dirxml(...any) +fn JS.console.error(...any) +fn JS.console.exception(string, ...any) +fn JS.console.group(...any) +fn JS.console.groupCollapsed(...any) +fn JS.console.groupEnd() +fn JS.console.info(...any) +fn JS.console.log(...any) +fn JS.console.table(any, []string) +fn JS.console.time(string) +fn JS.console.timeEnd(string) +fn JS.console.timeLog(string, ...any) +fn JS.console.timeStamp(string) +fn JS.console.trace(...any) +fn JS.console.warn(...any) + +// Math +fn JS.Math.abs(f64) f64 +fn JS.Math.acos(f64) f64 +fn JS.Math.asin(f64) f64 +fn JS.Math.atan(f64) f64 +fn JS.Math.atan2(f64, f64) f64 +fn JS.Math.ceil(f64) f64 +fn JS.Math.cos(f64) f64 +fn JS.Math.exp(f64) f64 +fn JS.Math.floor(f64) f64 +fn JS.Math.log(f64) f64 +fn JS.Math.max(...f64) f64 +fn JS.Math.min(...f64) f64 +fn JS.Math.pow(f64, f64) f64 +fn JS.Math.random() f64 +fn JS.Math.round(f64) f64 +fn JS.Math.sin(f64) f64 +fn JS.Math.sqrt(f64) f64 +fn JS.Math.tan(f64) f64 + +// JSON +fn JS.JSON.stringify(any) string +fn JS.JSON.parse(string) any + +// String +fn (v JS.String) slice(a int, b int) JS.String +fn (v JS.String) split(dot JS.String) []JS.String +fn (s JS.String) indexOf(needle JS.String) int +fn (s JS.String) lastIndexOf(needle JS.String) int + +fn (s JS.String) charAt(i int) JS.String +fn (s JS.String) charCodeAt(i int) byte +fn (s JS.String) toUpperCase() JS.String +fn (s JS.String) toLowerCase() JS.String +fn (s JS.String) concat(a JS.String) JS.String +fn (s JS.String) includes(substr JS.String) bool +fn (s JS.String) endsWith(substr JS.String) bool +fn (s JS.String) startsWith(substr JS.String) bool diff --git a/v_windows/v/vlib/builtin/js/jsfns_browser.js.v b/v_windows/v/vlib/builtin/js/jsfns_browser.js.v new file mode 100644 index 0000000..72daec3 --- /dev/null +++ b/v_windows/v/vlib/builtin/js/jsfns_browser.js.v @@ -0,0 +1,59 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// This file contains JS functions only present in the browser. +// They have been ported from their TypeScript definitions. + +module builtin + +// Window +fn JS.atob(string) string +fn JS.btoa(string) string +fn JS.clearInterval(int) +fn JS.clearTimeout(int) + +// fn JS.createImageBitmap(ImageBitmapSource, ImageBitmapOptions) Promise<ImageBitmap> +// fn JS.createImageBitmap(ImageBitmapSource, int, int, int, int, ImageBitmapOptions) Promise<ImageBitmap> + +// TODO: js async attribute +// [js_async] +// fn JS.fetch(RequestInfo, RequestInit) Promise<Response> +fn JS.queueMicrotask(fn ()) +fn JS.setInterval(any, int, ...any) int +fn JS.setTimeout(any, int, ...any) int + +fn JS.alert(any) +fn JS.blur() +fn JS.captureEvents() +fn JS.close() +fn JS.confirm(string) bool + +// fn JS.departFocus(NavigationReason, FocusNavigationOrigin) +fn JS.focus() + +// fn JS.getComputedStyle(Element, string | null) CSSStyleDeclaration +// fn JS.getMatchedCSSRules(Element, string | null) CSSRuleList +// fn JS.getSelection() Selection | null +// fn JS.matchMedia(string) MediaQueryList +fn JS.moveBy(int, int) +fn JS.moveTo(int, int) +fn JS.msWriteProfilerMark(string) + +// fn JS.open(string, string, string, bool) ?Window +// fn JS.postMessage(any, string, []Transferable) +fn JS.print() +fn JS.prompt(string, string) ?string +fn JS.releaseEvents() +fn JS.resizeBy(int, int) +fn JS.resizeTo(int, int) + +// fn JS.scroll(ScrollToOptions) +fn JS.scroll(int, int) + +// fn JS.scrollBy(ScrollToOptions) +fn JS.scrollBy(int, int) + +// fn JS.scrollTo(ScrollToOptions) +fn JS.scrollTo(int, int) +fn JS.stop() diff --git a/v_windows/v/vlib/builtin/js/jsfns_node.js.v b/v_windows/v/vlib/builtin/js/jsfns_node.js.v new file mode 100644 index 0000000..6f65629 --- /dev/null +++ b/v_windows/v/vlib/builtin/js/jsfns_node.js.v @@ -0,0 +1,31 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// This file contains JS functions only present in node.js. +// They have been ported from their TypeScript definitions. + +module builtin + +pub struct JS.node_process { +pub: + arch string + argsv []string + env []string + platform string + version string + // TODO: add all properties +} + +// hack to access process properties +pub fn js_node_process() JS.node_process { + #return process + + return JS.node_process{} +} + +fn JS.process.exit(int) +fn JS.process.stdout.write(string) bool +fn JS.process.stdout.writeln(string) bool +fn JS.process.stderr.write(string) bool +fn JS.process.stderr.writeln(string) bool diff --git a/v_windows/v/vlib/builtin/js/map.js.v b/v_windows/v/vlib/builtin/js/map.js.v new file mode 100644 index 0000000..033a5fd --- /dev/null +++ b/v_windows/v/vlib/builtin/js/map.js.v @@ -0,0 +1,26 @@ +module builtin + +struct map { + m JS.Map + len int +} + +// Removes the mapping of a particular key from the map. +[unsafe] +pub fn (mut m map) delete(key voidptr) { + #m.map.delete(key) +} + +pub fn (m &map) free() {} + +#map.prototype[Symbol.iterator] = function () { return this.map[Symbol.iterator](); } + +#map.prototype.toString = function () { +#function fmtKey(key) { return typeof key == 'string' ? '\'' + key + '\'' : key} +#let res = '{' +#for (const entry of this) { +#res += fmtKey(entry[0]) + ': ' + entry[0]; +#} +#res += '}' +#return res; +#} diff --git a/v_windows/v/vlib/builtin/js/string.js.v b/v_windows/v/vlib/builtin/js/string.js.v new file mode 100644 index 0000000..8b2933d --- /dev/null +++ b/v_windows/v/vlib/builtin/js/string.js.v @@ -0,0 +1,720 @@ +module builtin + +pub struct string { +pub: + str JS.String + len int +} + +pub fn (s string) slice(a int, b int) string { + return string(s.str.slice(a, b)) +} + +pub fn (s string) after(dot string) string { + return string(s.str.slice(s.str.lastIndexOf(dot.str) + 1, int(s.str.length))) +} + +pub fn (s string) after_char(dot byte) string { + // TODO: Implement after byte + return s +} + +pub fn (s string) all_after(dot string) string { + return string(s.str.slice(s.str.indexOf(dot.str) + 1, int(s.str.length))) +} + +// why does this exist? +pub fn (s string) all_after_last(dot string) string { + return s.after(dot) +} + +pub fn (s string) all_before(dot string) string { + return string(s.str.slice(0, s.str.indexOf(dot.str))) +} + +pub fn (s string) all_before_last(dot string) string { + return string(s.str.slice(0, s.str.lastIndexOf(dot.str))) +} + +pub fn (s string) bool() bool { + return s == 'true' +} + +pub fn (s string) split(dot string) []string { + mut arr := s.str.split(dot.str).map(string(it)) + #arr = new array(arr) + + return arr +} + +pub fn (s string) bytes() []byte { + sep := '' + mut arr := s.str.split(sep.str).map(it.charCodeAt(0)) + #arr = new array(arr) + + return arr +} + +pub fn (s string) capitalize() string { + part := string(s.str.slice(1, int(s.str.length))) + return string(s.str.charAt(0).toUpperCase().concat(part.str)) +} + +pub fn (s string) clone() string { + return string(s.str) +} + +pub fn (s string) contains(substr string) bool { + return s.str.includes(substr.str) +} + +pub fn (s string) contains_any(chars string) bool { + sep := '' + for x in chars.str.split(sep.str) { + if s.str.includes(x) { + return true + } + } + return false +} + +pub fn (s string) contains_any_substr(chars []string) bool { + for x in chars { + if s.str.includes(x.str) { + return true + } + } + return false +} + +pub fn (s string) count(substr string) int { + // TODO: "error: `[]JS.String` is not a struct" when returning arr.length or arr.len + arr := s.str.split(substr.str) + return native_str_arr_len(arr) +} + +pub fn (s string) ends_with(p string) bool { + mut res := false + #res.val = s.str.endsWith(p.str) + + return res +} + +pub fn (s string) starts_with(p string) bool { + return s.str.startsWith(p.str) +} + +pub fn (s string) fields() []string { + mut res := []string{} + mut word_start := 0 + mut word_len := 0 + mut is_in_word := false + mut is_space := false + for i, c in s { + is_space = c in [32, 9, 10] + if !is_space { + word_len++ + } + if !is_in_word && !is_space { + word_start = i + is_in_word = true + continue + } + if is_space && is_in_word { + res << s[word_start..word_start + word_len] + is_in_word = false + word_len = 0 + word_start = 0 + continue + } + } + if is_in_word && word_len > 0 { + // collect the remainder word at the end + res << s[word_start..s.len] + } + return res +} + +pub fn (s string) find_between(start string, end string) string { + return string(s.str.slice(s.str.indexOf(start.str) + 1, s.str.indexOf(end.str))) +} + +// unnecessary in the JS backend, implemented for api parity. +pub fn (s string) free() {} + +pub fn (s string) hash() int { + mut h := u32(0) + if h == 0 && s.len > 0 { + for c in s { + h = h * 31 + u32(c) + } + } + return int(h) +} + +// int returns the value of the string as an integer `'1'.int() == 1`. +pub fn (s string) int() int { + return int(JS.parseInt(s)) +} + +// i64 returns the value of the string as i64 `'1'.i64() == i64(1)`. +pub fn (s string) i64() i64 { + return i64(JS.parseInt(s)) +} + +// i8 returns the value of the string as i8 `'1'.i8() == i8(1)`. +pub fn (s string) i8() i8 { + return i8(JS.parseInt(s)) +} + +// i16 returns the value of the string as i16 `'1'.i16() == i16(1)`. +pub fn (s string) i16() i16 { + return i16(JS.parseInt(s)) +} + +// f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`. +pub fn (s string) f32() f32 { + // return C.atof(&char(s.str)) + return f32(JS.parseFloat(s)) +} + +// f64 returns the value of the string as f64 `'1.0'.f64() == f64(1)`. +pub fn (s string) f64() f64 { + return f64(JS.parseFloat(s)) +} + +// u16 returns the value of the string as u16 `'1'.u16() == u16(1)`. +pub fn (s string) u16() u16 { + return u16(JS.parseInt(s)) +} + +// u32 returns the value of the string as u32 `'1'.u32() == u32(1)`. +pub fn (s string) u32() u32 { + return u32(JS.parseInt(s)) +} + +// u64 returns the value of the string as u64 `'1'.u64() == u64(1)`. +pub fn (s string) u64() u64 { + return u64(JS.parseInt(s)) +} + +// trim_right strips any of the characters given in `cutset` from the right of the string. +// Example: assert ' Hello V d'.trim_right(' d') == ' Hello V' +pub fn (s string) trim_right(cutset string) string { + if s.len < 1 || cutset.len < 1 { + return s.clone() + } + + mut pos := s.len - 1 + + for pos >= 0 { + mut found := false + for cs in cutset { + if s[pos] == cs { + found = true + } + } + if !found { + break + } + pos-- + } + + if pos < 0 { + return '' + } + + return s[..pos + 1] +} + +// trim_left strips any of the characters given in `cutset` from the left of the string. +// Example: assert 'd Hello V developer'.trim_left(' d') == 'Hello V developer' +[direct_array_access] +pub fn (s string) trim_left(cutset string) string { + if s.len < 1 || cutset.len < 1 { + return s.clone() + } + mut pos := 0 + for pos < s.len { + mut found := false + for cs in cutset { + if s[pos] == cs { + found = true + break + } + } + if !found { + break + } + pos++ + } + return s[pos..] +} + +// trim_prefix strips `str` from the start of the string. +// Example: assert 'WorldHello V'.trim_prefix('World') == 'Hello V' +pub fn (s string) trim_prefix(str string) string { + if s.starts_with(str) { + return s[str.len..] + } + return s.clone() +} + +// trim_suffix strips `str` from the end of the string. +// Example: assert 'Hello VWorld'.trim_suffix('World') == 'Hello V' +pub fn (s string) trim_suffix(str string) string { + if s.ends_with(str) { + return s[..s.len - str.len] + } + return s.clone() +} + +// compare_strings returns `-1` if `a < b`, `1` if `a > b` else `0`. +pub fn compare_strings(a &string, b &string) int { + if a < b { + return -1 + } + if a > b { + return 1 + } + return 0 +} + +// compare_strings_reverse returns `1` if `a < b`, `-1` if `a > b` else `0`. +fn compare_strings_reverse(a &string, b &string) int { + if a < b { + return 1 + } + if a > b { + return -1 + } + return 0 +} + +// compare_strings_by_len returns `-1` if `a.len < b.len`, `1` if `a.len > b.len` else `0`. +fn compare_strings_by_len(a &string, b &string) int { + if a.len < b.len { + return -1 + } + if a.len > b.len { + return 1 + } + return 0 +} + +// compare_lower_strings returns the same as compare_strings but converts `a` and `b` to lower case before comparing. +fn compare_lower_strings(a &string, b &string) int { + aa := a.to_lower() + bb := b.to_lower() + return compare_strings(&aa, &bb) +} + +// at returns the byte at index `idx`. +// Example: assert 'ABC'.at(1) == byte(`B`) +fn (s string) at(idx int) byte { + mut result := byte(0) + #result = new byte(s.str.charCodeAt(result)) + + return result +} + +pub fn (s string) to_lower() string { + mut result := '' + #let str = s.str.toLowerCase() + #result = new string(str) + + return result +} + +// TODO: check if that behaves the same as V's own string.replace(old_sub,new_sub): +pub fn (s string) replace(old_sub string, new_sub string) string { + mut result := '' + #result = new string( s.str.replaceAll(old_sub.str, new_sub.str) ) + + return result +} + +pub fn (s string) to_upper() string { + mut result := '' + #let str = s.str.toUpperCase() + #result = new string(str) + + return result +} + +// sort sorts the string array. +pub fn (mut s []string) sort() { + s.sort_with_compare(compare_strings) +} + +// sort_ignore_case sorts the string array using case insesitive comparing. +pub fn (mut s []string) sort_ignore_case() { + s.sort_with_compare(compare_lower_strings) +} + +// sort_by_len sorts the the string array by each string's `.len` length. +pub fn (mut s []string) sort_by_len() { + s.sort_with_compare(compare_strings_by_len) +} + +// str returns a copy of the string +pub fn (s string) str() string { + return s.clone() +} + +pub fn (s string) repeat(count int) string { + mut result := '' + #result = new string(s.str.repeat(count)) + + return result +} + +// TODO(playX): Use this iterator instead of using .split('').map(c => byte(c)) +#function string_iterator(string) { this.stringIteratorFieldIndex = 0; this.stringIteratorIteratedString = string.str; } +#string_iterator.prototype.next = function next() { +#var done = true; +#var value = undefined; +#var position = this.stringIteratorFieldIndex; +#if (position !== -1) { +#var string = this.stringIteratorIteratedString; +#var length = string.length >>> 0; +#if (position >= length) { +#this.stringIteratorFieldIndex = -1; +#} else { +#done = false; +#var first = string.charCodeAt(position); +#if (first < 0xD800 || first > 0xDBFF || position + 1 === length) +#value = new byte(string[position]); +#else { +#value = new byte(string[position]+string[position+1]) +#} +#this.stringIteratorFieldIndex = position + value.length; +#} +#} +#return { +#value, done +#} +#} +#string.prototype[Symbol.iterator] = function () { return new string_iterator(this) } + +// TODO: Make these functions actually work. +// strip_margin allows multi-line strings to be formatted in a way that removes white-space +// before a delimeter. by default `|` is used. +// Note: the delimiter has to be a byte at this time. That means surrounding +// the value in ``. +// +// Example: +// st := 'Hello there, +// |this is a string, +// | Everything before the first | is removed'.strip_margin() +// Returns: +// Hello there, +// this is a string, +// Everything before the first | is removed +pub fn (s string) strip_margin() string { + return s.strip_margin_custom(`|`) +} + +// strip_margin_custom does the same as `strip_margin` but will use `del` as delimiter instead of `|` +[direct_array_access] +pub fn (s string) strip_margin_custom(del byte) string { + mut sep := del + if sep.is_space() { + eprintln('Warning: `strip_margin` cannot use white-space as a delimiter') + eprintln(' Defaulting to `|`') + sep = `|` + } + // don't know how much space the resulting string will be, but the max it + // can be is this big + mut ret := []byte{} + #ret = new array() + + mut count := 0 + for i := 0; i < s.len; i++ { + if s[i] in [10, 13] { + unsafe { + ret[count] = s[i] + } + count++ + // CRLF + if s[i] == 13 && i < s.len - 1 && s[i + 1] == 10 { + unsafe { + ret[count] = s[i + 1] + } + count++ + i++ + } + for s[i] != sep { + i++ + if i >= s.len { + break + } + } + } else { + unsafe { + ret[count] = s[i] + } + count++ + } + } + /* + unsafe { + ret[count] = 0 + return ret.vstring_with_len(count) + }*/ + mut result := '' + #for (let x of ret.arr) result.str += String.fromCharCode(x.val) + + return result +} + +// split_nth splits the string based on the passed `delim` substring. +// It returns the first Nth parts. When N=0, return all the splits. +// The last returned element has the remainder of the string, even if +// the remainder contains more `delim` substrings. +[direct_array_access] +pub fn (s string) split_nth(delim string, nth int) []string { + mut res := []string{} + mut i := 0 + + match delim.len { + 0 { + i = 1 + for ch in s { + if nth > 0 && i >= nth { + res << s[i..] + break + } + res << ch.str() + i++ + } + return res + } + 1 { + mut start := 0 + delim_byte := delim[0] + + for i < s.len { + if s[i] == delim_byte { + was_last := nth > 0 && res.len == nth - 1 + if was_last { + break + } + val := s[start..i] //.substr(start, i) + res << val + start = i + delim.len + i = start + } else { + i++ + } + } + + // Then the remaining right part of the string + if nth < 1 || res.len < nth { + res << s[start..] + } + return res + } + else { + mut start := 0 + // Take the left part for each delimiter occurence + for i <= s.len { + is_delim := i + delim.len <= s.len && s[i..i + delim.len] == delim + if is_delim { + was_last := nth > 0 && res.len == nth - 1 + if was_last { + break + } + val := s[start..i] // .substr(start, i) + res << val + start = i + delim.len + i = start + } else { + i++ + } + } + // Then the remaining right part of the string + if nth < 1 || res.len < nth { + res << s[start..] + } + return res + } + } +} + +struct RepIndex { + idx int + val_idx int +} + +// replace_each replaces all occurences of the string pairs given in `vals`. +// Example: assert 'ABCD'.replace_each(['B','C/','C','D','D','C']) == 'AC/DC' +[direct_array_access] +pub fn (s string) replace_each(vals []string) string { + if s.len == 0 || vals.len == 0 { + return s.clone() + } + + if vals.len % 2 != 0 { + eprintln('string.replace_each(): odd number of strings') + return s.clone() + } + + // `rep` - string to replace + // `with_` - string to replace with_ + // Remember positions of all rep strings, and calculate the length + // of the new string to do just one allocation. + + mut idxs := []RepIndex{} + mut idx := 0 + mut new_len := s.len + s_ := s.clone() + #function setCharAt(str,index,chr) { + #if(index > str.length-1) return str; + #return str.substring(0,index) + chr + str.substring(index+1); + #} + + for rep_i := 0; rep_i < vals.len; rep_i = rep_i + 2 { + rep := vals[rep_i] + + mut with_ := vals[rep_i + 1] + with_ = with_ + + for { + idx = s_.index_after(rep, idx) + if idx == -1 { + break + } + + for i in 0 .. rep.len { + mut j_ := i + j_ = j_ + #s_.str = setCharAt(s_.str,idx + i, String.fromCharCode(127)) + } + + rep_idx := RepIndex{ + idx: 0 + val_idx: 0 + } + // todo: primitives should always be copied + #rep_idx.idx = idx.val + #rep_idx.val_idx = new int(rep_i.val) + idxs << rep_idx + idx += rep.len + new_len += with_.len - rep.len + } + } + + if idxs.len == 0 { + return s.clone() + } + + idxs.sort(a.idx < b.idx) + + mut b := '' + #for (let i = 0; i < new_len.val;i++) b.str += String.fromCharCode(127) + + new_len = new_len + mut idx_pos := 0 + mut cur_idx := idxs[idx_pos] + mut b_i := 0 + for i := 0; i < s.len; i++ { + if i == cur_idx.idx { + rep := vals[cur_idx.val_idx] + with_ := vals[cur_idx.val_idx + 1] + for j in 0 .. with_.len { + mut j_ := j + + j_ = j_ + #b.str = setCharAt(b.str,b_i, with_.str[j]) + //#b.str[b_i] = with_.str[j] + b_i++ + } + i += rep.len - 1 + idx_pos++ + if idx_pos < idxs.len { + cur_idx = idxs[idx_pos] + } + } else { + #b.str = setCharAt(b.str,b_i,s.str[i]) //b.str[b_i] = s.str[i] + b_i++ + } + } + + return b +} + +// last_index returns the position of the last occurence of the input string. +fn (s string) last_index_(p string) int { + if p.len > s.len || p.len == 0 { + return -1 + } + mut i := s.len - p.len + for i >= 0 { + mut j := 0 + for j < p.len && s[i + j] == p[j] { + j++ + } + if j == p.len { + return i + } + i-- + } + return -1 +} + +// last_index returns the position of the last occurence of the input string. +pub fn (s string) last_index(p string) ?int { + idx := s.last_index_(p) + if idx == -1 { + return none + } + return idx +} + +pub fn (s string) trim_space() string { + res := '' + #res.str = s.str.trim() + + return res +} + +pub fn (s string) index_after(p string, start int) int { + if p.len > s.len { + return -1 + } + + mut strt := start + if start < 0 { + strt = 0 + } + if start >= s.len { + return -1 + } + mut i := strt + + for i < s.len { + mut j := 0 + mut ii := i + for j < p.len && s[ii] == p[j] { + j++ + ii++ + } + + if j == p.len { + return i + } + i++ + } + return -1 +} + +pub fn (s string) split_into_lines() []string { + mut res := []string{} + #let i = 0 + #s.str.split('\n').forEach((str) => { + #res.arr[i] = new string(str); + #}) + + return res +} |