diff options
Diffstat (limited to 'v_windows/v/old/vlib/v/gen/js')
42 files changed, 6103 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/v/gen/js/builtin_types.v b/v_windows/v/old/vlib/v/gen/js/builtin_types.v new file mode 100644 index 0000000..1bd1c15 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/builtin_types.v @@ -0,0 +1,393 @@ +module js + +import v.ast + +fn (mut g JsGen) to_js_typ_def_val(s string) string { + mut dval := '' + match s { + 'JS.Number' { dval = '0' } + 'JS.String' { dval = '""' } + 'JS.Boolean' { dval = 'false' } + 'JS.Array', 'JS.Map' { dval = '' } + else { dval = '{}' } + } + return dval +} + +fn (mut g JsGen) to_js_typ_val(t ast.Type) string { + sym := g.table.get_type_symbol(t) + mut styp := '' + mut prefix := if g.file.mod.name == 'builtin' { 'new ' } else { '' } + match sym.kind { + .i8, .i16, .int, .i64, .byte, .u8, .u16, .u32, .u64, .f32, .f64, .int_literal, + .float_literal, .size_t { + styp = '$prefix${g.sym_to_js_typ(sym)}(0)' + } + .bool { + styp = '$prefix${g.sym_to_js_typ(sym)}(false)' + } + .string { + styp = '$prefix${g.sym_to_js_typ(sym)}("")' + } + .map { + styp = 'new Map()' + } + .array { + styp = '$prefix${g.sym_to_js_typ(sym)}()' + } + .struct_ { + styp = 'new ${g.js_name(sym.name)}(${g.to_js_typ_def_val(sym.name)})' + } + .voidptr { + styp = 'null' + } + else { + // TODO + styp = 'undefined' + } + } + return styp +} + +fn (mut g JsGen) sym_to_js_typ(sym ast.TypeSymbol) string { + mut styp := '' + match sym.kind { + .i8 { + styp = 'i8' + } + .i16 { + styp = 'i16' + } + .int { + styp = 'int' + } + .i64 { + styp = 'i64' + } + .byte { + styp = 'byte' + } + .u16 { + styp = 'u16' + } + .u32 { + styp = 'u32' + } + .u64 { + styp = 'u64' + } + .f32 { + styp = 'f32' + } + .f64 { + styp = 'f64' + } + .int_literal { + styp = 'int_literal' + } + .float_literal { + styp = 'float_literal' + } + .size_t { + styp = 'size_t' + } + .bool { + styp = 'bool' + } + .string { + styp = 'string' + } + .map { + styp = 'map' + } + .array { + styp = 'array' + } + .voidptr { + styp = 'any' + } + else { + // TODO + styp = 'undefined' + } + } + return styp +} + +// V type to JS type +pub fn (mut g JsGen) typ(t ast.Type) string { + sym := g.table.get_type_symbol(t) + mut styp := '' + match sym.kind { + .placeholder { + // This should never happen: means checker bug + styp = 'any' + } + .void { + styp = 'void' + } + .voidptr { + styp = 'any' + } + .byteptr, .charptr { + styp = '${g.sym_to_js_typ(sym)}' + } + .i8, .i16, .int, .i64, .byte, .u8, .u16, .u32, .u64, .f32, .f64, .int_literal, + .float_literal, .size_t { + styp = '${g.sym_to_js_typ(sym)}' + } + .bool { + styp = '${g.sym_to_js_typ(sym)}' + } + .none_ { + styp = 'undefined' + } + .string, .char { + styp = '${g.sym_to_js_typ(sym)}' + } + // 'array_array_int' => 'number[][]' + .array { + info := sym.info as ast.Array + styp = '${g.sym_to_js_typ(sym)}(${g.typ(info.elem_type)})' + } + .array_fixed { + info := sym.info as ast.ArrayFixed + styp = '${g.sym_to_js_typ(sym)}(${g.typ(info.elem_type)})' + } + .chan { + styp = 'chan' + } + // 'map[string]int' => 'Map<string, number>' + .map { + info := sym.info as ast.Map + key := g.typ(info.key_type) + val := g.typ(info.value_type) + styp = 'Map<$key, $val>' + } + .any { + styp = 'any' + } + // ns.Foo => alias["Foo"]["prototype"] + .struct_ { + styp = g.struct_typ(sym.name) + } + .generic_struct_inst {} + // 'multi_return_int_int' => '[number, number]' + .multi_return { + info := sym.info as ast.MultiReturn + types := info.types.map(g.typ(it)) + joined := types.join(', ') + styp = '[$joined]' + } + .sum_type { + // TODO: Implement sumtypes + styp = 'union_sym_type' + } + .alias { + // TODO: Implement aliases + styp = 'alias' + } + .enum_ { + // NB: We could declare them as TypeScript enums but TS doesn't like + // our namespacing so these break if declared in a different module. + // Until this is fixed, We need to use the type of an enum's members + // rather than the enum itself, and this can only be 'number' for now + styp = 'number' + } + // 'anon_fn_7_7_1' => '(a number, b number) => void' + .function { + info := sym.info as ast.FnType + styp = g.fn_typ(info.func.params, info.func.return_type) + } + .interface_ { + styp = g.js_name(sym.name) + } + .rune { + styp = 'any' + } + .aggregate { + panic('TODO: unhandled aggregate in JS') + } + .thread { + panic('TODO: unhandled thread in JS') + } + } + /* + else { + println('jsgen.typ: Unhandled type $t') + styp = sym.name + } + */ + if styp.starts_with('JS.') { + return styp[3..] + } + return styp +} + +fn (mut g JsGen) fn_typ(args []ast.Param, return_type ast.Type) string { + mut res := '(' + for i, arg in args { + res += '$arg.name: ${g.typ(arg.typ)}' + if i < args.len - 1 { + res += ', ' + } + } + return res + ') => ' + g.typ(return_type) +} + +fn (mut g JsGen) struct_typ(s string) string { + ns := get_ns(s) + if ns == 'JS' { + return s[3..] + } + mut name := if ns == g.ns.name { s.split('.').last() } else { g.get_alias(s) } + mut styp := '' + for i, v in name.split('.') { + if i == 0 { + styp = v + } else { + styp += '["$v"]' + } + } + if ns in ['', g.ns.name] { + return styp + } + return styp + '["prototype"]' +} + +struct BuiltinPrototypeConfig { + typ_name string + val_name string = 'val' + default_value string + constructor string = 'this.val = val' + value_of string = 'this.val' + to_string string = 'this.val.toString()' + eq string = 'this.val === other.val' + to_jsval string = 'this' + extras string + has_strfn bool +} + +fn (mut g JsGen) gen_builtin_prototype(c BuiltinPrototypeConfig) { + g.writeln('function ${c.typ_name}($c.val_name = $c.default_value) { $c.constructor }') + g.writeln('${c.typ_name}.prototype = {') + g.inc_indent() + g.writeln('$c.val_name: $c.default_value,') + if c.extras.len > 0 { + g.writeln('$c.extras,') + } + for method in g.method_fn_decls[c.typ_name] { + g.inside_def_typ_decl = true + g.gen_method_decl(method) + g.inside_def_typ_decl = false + g.writeln(',') + } + g.writeln('valueOf() { return $c.value_of },') + g.writeln('toString() { return $c.to_string },') + g.writeln('eq(other) { return $c.eq },') + g.writeln('\$toJS() { return $c.to_jsval }, ') + if c.has_strfn { + g.writeln('str() { return new string(this.toString()) }') + } + g.dec_indent() + g.writeln('};\n') +} + +// generate builtin type definitions, used for casting and methods. +fn (mut g JsGen) gen_builtin_type_defs() { + g.inc_indent() + for typ_name in v_types { + // TODO: JsDoc + match typ_name { + 'i8', 'i16', 'int', 'i64', 'u16', 'u32', 'u64', 'int_literal', 'size_t' { + // TODO: Bounds checking + g.gen_builtin_prototype( + typ_name: typ_name + default_value: 'new Number(0)' + constructor: 'this.val = val | 0' + value_of: 'this.val | 0' + to_string: 'this.valueOf().toString()' + eq: 'this.valueOf() === other.valueOf()' + to_jsval: '+this' + ) + } + 'byte' { + g.gen_builtin_prototype( + typ_name: typ_name + default_value: 'new Number(0)' + constructor: 'this.val = typeof(val) == "string" ? val.charCodeAt() : (val | 0)' + value_of: 'this.val | 0' + to_string: 'new string(this.val + "")' + eq: 'this.valueOf() === other.valueOf()' + to_jsval: '+this' + ) + } + 'f32', 'f64', 'float_literal' { + g.gen_builtin_prototype( + typ_name: typ_name + default_value: 'new Number(0)' + to_jsval: '+this' + ) + } + 'bool' { + g.gen_builtin_prototype( + constructor: 'this.val = +val !== 0' + typ_name: typ_name + default_value: 'new Boolean(false)' + to_jsval: '+this != 0' + ) + } + 'string' { + g.gen_builtin_prototype( + typ_name: typ_name + val_name: 'str' + default_value: 'new String("")' + constructor: 'this.str = str.toString(); this.len = this.str.length' + value_of: 'this.str' + to_string: 'this.str' + eq: 'this.str === other.str' + has_strfn: false + to_jsval: 'this.str' + ) + } + 'map' { + g.gen_builtin_prototype( + typ_name: typ_name + val_name: 'map' + default_value: 'new Map()' + constructor: 'this.map = map' + value_of: 'this' + to_string: 'this.map.toString()' + eq: 'vEq(this, other)' + to_jsval: 'this.map' + ) + } + 'array' { + g.gen_builtin_prototype( + typ_name: typ_name + val_name: 'arr' + default_value: 'new Array()' + constructor: 'this.arr = arr' + value_of: 'this' + to_string: 'JSON.stringify(this.arr.map(it => it.valueOf()))' + eq: 'vEq(this, other)' + to_jsval: 'this.arr' + ) + } + 'any' { + g.gen_builtin_prototype( + typ_name: typ_name + val_name: 'any' + default_value: 'null' + constructor: 'this.val = any' + value_of: 'this.val' + to_string: '"&" + this.val' + eq: 'this == other' // compare by ptr + to_jsval: 'this.val.\$toJS()' + ) + } + else {} + } + } + g.dec_indent() +} diff --git a/v_windows/v/old/vlib/v/gen/js/comptime.v b/v_windows/v/old/vlib/v/gen/js/comptime.v new file mode 100644 index 0000000..d4c55ee --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/comptime.v @@ -0,0 +1,311 @@ +module js + +import v.ast +import v.pref + +fn (mut g JsGen) comp_if(node ast.IfExpr) { + if !node.is_expr && !node.has_else && node.branches.len == 1 { + if node.branches[0].stmts.len == 0 { + // empty ifdef; result of target OS != conditional => skip + return + } + } + + for i, branch in node.branches { + if i == node.branches.len - 1 && node.has_else { + g.writeln('else') + } else { + if i == 0 { + g.write('if (') + } else { + g.write('else if (') + } + g.comp_if_cond(branch.cond, branch.pkg_exist) + g.writeln(')') + } + + if node.is_expr { + print('$branch.stmts') + len := branch.stmts.len + if len > 0 { + last := branch.stmts[len - 1] as ast.ExprStmt + if len > 1 { + tmp := g.new_tmp_var() + g.inc_indent() + g.writeln('let $tmp;') + g.writeln('{') + g.stmts(branch.stmts[0..len - 1]) + g.write('\t$tmp = ') + g.stmt(last) + g.writeln('}') + g.dec_indent() + g.writeln('$tmp;') + } else { + g.stmt(last) + } + } + } else { + g.writeln('{') + g.stmts(branch.stmts) + g.writeln('}') + } + } +} + +/* +// returning `false` means the statements inside the $if can be skipped +*/ +// returns the value of the bool comptime expression +fn (mut g JsGen) comp_if_cond(cond ast.Expr, pkg_exist bool) bool { + match cond { + ast.BoolLiteral { + g.expr(cond) + return true + } + ast.ParExpr { + g.write('(') + is_cond_true := g.comp_if_cond(cond.expr, pkg_exist) + g.write(')') + return is_cond_true + } + ast.PrefixExpr { + g.write(cond.op.str()) + return g.comp_if_cond(cond.right, pkg_exist) + } + ast.PostfixExpr { + ifdef := g.comp_if_to_ifdef((cond.expr as ast.Ident).name, true) or { + verror(err.msg) + return false + } + g.write('$ifdef') + return true + } + ast.InfixExpr { + match cond.op { + .and, .logical_or { + l := g.comp_if_cond(cond.left, pkg_exist) + g.write(' $cond.op ') + r := g.comp_if_cond(cond.right, pkg_exist) + return if cond.op == .and { l && r } else { l || r } + } + .key_is, .not_is { + left := cond.left + mut name := '' + mut exp_type := ast.Type(0) + got_type := (cond.right as ast.TypeNode).typ + // Handle `$if x is Interface {` + // mut matches_interface := 'false' + if left is ast.TypeNode && cond.right is ast.TypeNode + && g.table.get_type_symbol(got_type).kind == .interface_ { + // `$if Foo is Interface {` + interface_sym := g.table.get_type_symbol(got_type) + if interface_sym.info is ast.Interface { + // q := g.table.get_type_symbol(interface_sym.info.types[0]) + checked_type := g.unwrap_generic(left.typ) + // TODO PERF this check is run twice (also in the checker) + // store the result in a field + is_true := g.table.does_type_implement_interface(checked_type, + got_type) + // true // exp_type in interface_sym.info.types + if cond.op == .key_is { + if is_true { + g.write('1') + } else { + g.write('0') + } + return is_true + } else if cond.op == .not_is { + if is_true { + g.write('0') + } else { + g.write('1') + } + return !is_true + } + // matches_interface = '/*iface:$got_type $exp_type*/ true' + //} + } + } else if left is ast.SelectorExpr { + name = '${left.expr}.$left.field_name' + exp_type = g.comptime_var_type_map[name] + } else if left is ast.TypeNode { + name = left.str() + // this is only allowed for generics currently, otherwise blocked by checker + exp_type = g.unwrap_generic(left.typ) + } + + if cond.op == .key_is { + g.write('$exp_type == $got_type') + return exp_type == got_type + } else { + g.write('$exp_type != $got_type') + return exp_type != got_type + } + } + .eq, .ne { + // TODO Implement `$if method.args.len == 1` + g.write('1') + return true + } + else { + return true + } + } + } + ast.Ident { + ifdef := g.comp_if_to_ifdef(cond.name, false) or { 'true' } // handled in checker + g.write('$ifdef') + return true + } + ast.ComptimeCall { + g.write('$pkg_exist') + return true + } + else { + // should be unreachable, but just in case + g.write('1') + return true + } + } +} + +fn (mut g JsGen) comp_if_to_ifdef(name string, is_comptime_optional bool) ?string { + match name { + // platforms/os-es: + 'windows' { + return '(\$process.platform == "windows")' + } + 'ios' { + return '(\$process.platform == "darwin")' + } + 'macos' { + return '(\$process.platform == "darwin")' + } + 'mach' { + return '(\$process.platform == "darwin")' + } + 'darwin' { + return '(\$process.platform == "darwin")' + } + 'linux' { + return '(\$process.platform == "linux")' + } + 'freebsd' { + return '(\$process.platform == "freebsd")' + } + 'openbsd' { + return '(\$process.platform == "openbsd")' + } + 'bsd' { + return '(\$process.platform == "freebsd" || (\$process.platform == "openbsd"))' + } + 'android' { + return '(\$process.platform == "android")' + } + 'solaris' { + return '(\$process.platform == "sunos")' + } + 'js_node' { + if g.pref.backend == .js_node { + return 'true' + } else { + return 'false' + } + } + 'js_freestanding' { + if g.pref.backend == .js_freestanding { + return 'true' + } else { + return 'false' + } + } + 'js_browser' { + if g.pref.backend == .js_browser { + return 'true' + } else { + return 'false' + } + } + // + 'js' { + return 'true' + } + // compilers: + 'gcc' { + return 'false' + } + 'tinyc' { + return 'false' + } + 'clang' { + return 'false' + } + 'mingw' { + return 'false' + } + 'msvc' { + return 'false' + } + 'cplusplus' { + return 'false' + } + // other: + 'threads' { + return 'false' + } + 'gcboehm' { + return 'false' + } + // todo(playX): these should return true or false depending on CLI options + 'debug' { + return 'false' + } + 'prod' { + return 'false' + } + 'test' { + return 'false' + } + 'glibc' { + return 'false' + } + 'prealloc' { + return 'false' + } + 'no_bounds_checking' { + return 'CUSTOM_DEFINE_no_bounds_checking' + } + 'freestanding' { + return '_VFREESTANDING' + } + // architectures: + 'amd64' { + return '(\$process.arch == "x64")' + } + 'aarch64', 'arm64' { + return '(\$process.arch == "arm64)' + } + // bitness: + 'x64' { + return '(\$process.arch == "x64")' + } + 'x32' { + return '(\$process.arch == "x32")' + } + // endianness: + 'little_endian' { + return '(\$os.endianess == "LE")' + } + 'big_endian' { + return '(\$os.endianess == "BE")' + } + else { + if is_comptime_optional + || (g.pref.compile_defines_all.len > 0 && name in g.pref.compile_defines_all) { + return 'CUSTOM_DEFINE_$name' + } + return error('bad os ifdef name "$name"') // should never happen, caught in the checker + } + } + return none +} diff --git a/v_windows/v/old/vlib/v/gen/js/fast_deep_equal.js b/v_windows/v/old/vlib/v/gen/js/fast_deep_equal.js new file mode 100644 index 0000000..50d37e8 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/fast_deep_equal.js @@ -0,0 +1,72 @@ +// https://www.npmjs.com/package/fast-deep-equal - 3/3/2021 +const envHasBigInt64Array = typeof BigInt64Array !== 'undefined'; +function vEq(a, b) { + if (a === b) return true; + + if (a && b && typeof a == 'object' && typeof b == 'object') { + if (a.constructor !== b.constructor) return false; + // we want to convert all V types to JS for comparison. + if ('$toJS' in a) + a = a.$toJS(); + + if ('$toJS' in b) + b = b.$toJS(); + + var length, i, keys; + if (Array.isArray(a)) { + length = a.length; + if (length != b.length) return false; + for (i = length; i-- !== 0;) + if (!vEq(a[i], b[i])) return false; + return true; + } + + + if ((a instanceof Map) && (b instanceof Map)) { + if (a.size !== b.size) return false; + for (i of a.entries()) + if (!b.has(i[0])) return false; + for (i of a.entries()) + if (!vEq(i[1], b.get(i[0]))) return false; + return true; + } + + if ((a instanceof Set) && (b instanceof Set)) { + if (a.size !== b.size) return false; + for (i of a.entries()) + if (!b.has(i[0])) return false; + return true; + } + + if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) { + length = a.length; + if (length != b.length) return false; + for (i = length; i-- !== 0;) + if (a[i] !== b[i]) return false; + return true; + } + + + if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags; + if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf(); + if (a.toString !== Object.prototype.toString) return a.toString() === b.toString(); + + keys = Object.keys(a); + length = keys.length; + if (length !== Object.keys(b).length) return false; + + for (i = length; i-- !== 0;) + if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; + + for (i = length; i-- !== 0;) { + var key = keys[i]; + + if (!vEq(a[key], b[key])) return false; + } + + return true; + } + + // true if both NaN, false otherwise + return a !== a && b !== b; +};
\ No newline at end of file diff --git a/v_windows/v/old/vlib/v/gen/js/js.v b/v_windows/v/old/vlib/v/gen/js/js.v new file mode 100644 index 0000000..6ec623e --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/js.v @@ -0,0 +1,2103 @@ +module js + +import strings +import v.ast +import v.token +import v.pref +import v.util +import v.util.version +import v.depgraph +import encoding.base64 +import v.gen.js.sourcemap + +struct MutArg { + tmp_var string + expr ast.Expr = ast.empty_expr() +} + +const ( + // https://ecma-international.org/ecma-262/#sec-reserved-words + js_reserved = ['await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', + 'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'finally', 'for', 'function', + 'if', 'implements', 'import', 'in', 'instanceof', 'interface', 'let', 'new', 'package', + 'private', 'protected', 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', + 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield', 'Number', 'String', 'Boolean', + 'Array', 'Map'] + // used to generate type structs + v_types = ['i8', 'i16', 'int', 'i64', 'byte', 'u16', 'u32', 'u64', 'f32', 'f64', + 'int_literal', 'float_literal', 'size_t', 'bool', 'string', 'map', 'array', 'any'] + shallow_equatables = [ast.Kind.i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, + .int_literal, .float_literal, .size_t, .bool, .string] +) + +struct SourcemapHelper { + src_path string + src_line u32 + ns_pos u32 +} + +struct Namespace { + name string +mut: + out strings.Builder = strings.new_builder(128) + pub_vars []string + imports map[string]string + indent int + methods map[string][]ast.FnDecl + sourcemap_helper []SourcemapHelper +} + +[heap] +struct JsGen { + pref &pref.Preferences +mut: + table &ast.Table + definitions strings.Builder + ns &Namespace + namespaces map[string]&Namespace + doc &JsDoc + enable_doc bool + file &ast.File + tmp_count int + inside_ternary bool + inside_loop bool + inside_map_set bool // map.set(key, value) + inside_builtin bool + generated_builtin bool + inside_def_typ_decl bool + is_test bool + stmt_start_pos int + defer_stmts []ast.DeferStmt + fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 + str_types []string // types that need automatic str() generation + method_fn_decls map[string][]ast.FnDecl + builtin_fns []string // Functions defined in `builtin` + empty_line bool + cast_stack []ast.Type + call_stack []ast.CallExpr + is_vlines_enabled bool // is it safe to generate #line directives when -g is passed + sourcemap sourcemap.SourceMap // maps lines in generated javascrip file to original source files and line + comptime_var_type_map map[string]ast.Type + defer_ifdef string +} + +pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { + mut g := &JsGen{ + definitions: strings.new_builder(100) + table: table + pref: pref + fn_decl: 0 + empty_line: true + doc: 0 + ns: 0 + enable_doc: true + file: 0 + } + g.doc = new_jsdoc(g) + // TODO: Add '[-no]-jsdoc' flag + if pref.is_prod { + g.enable_doc = false + g.is_vlines_enabled = false + } + g.init() + mut graph := depgraph.new_dep_graph() + if g.pref.sourcemap { + mut sg := sourcemap.generate_empty_map() + g.sourcemap = sg.add_map('', '', g.pref.sourcemap_src_included, 0, 0) + } + // Get class methods + for file in files { + g.file = file + g.enter_namespace(g.file.mod.name) + g.is_test = g.pref.is_test + g.find_class_methods(file.stmts) + g.escape_namespace() + } + + for file in files { + g.file = file + g.enter_namespace(g.file.mod.name) + g.is_test = g.pref.is_test + // store imports + mut imports := []string{} + for imp in g.file.imports { + imports << imp.mod + } + graph.add(g.file.mod.name, imports) + // builtin types + if g.file.mod.name == 'builtin' && !g.generated_builtin { + g.gen_builtin_type_defs() + g.writeln('Object.defineProperty(array.prototype,"len", { get: function() {return new builtin.int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }); ') + g.writeln('Object.defineProperty(string.prototype,"len", { get: function() {return new builtin.int(this.str.length);}, set: function(l) {/* ignore */ } }); ') + g.writeln('Object.defineProperty(map.prototype,"len", { get: function() {return new builtin.int(this.map.length);}, set: function(l) { this.map.length = l.valueOf(); } }); ') + g.writeln('Object.defineProperty(array.prototype,"length", { get: function() {return new builtin.int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }); ') + g.generated_builtin = true + } + + g.stmts(file.stmts) + // store the current namespace + g.escape_namespace() + } + // resolve imports + deps_resolved := graph.resolve() + nodes := deps_resolved.nodes + mut out := g.hashes() + g.definitions.str() + // equality check for js objects + // TODO: Fix msvc bug that's preventing $embed_file('fast_deep_equal.js') + // unsafe { + // mut eq_fn := $embed_file('fast_deep_equal.js') + // out += eq_fn.data().vstring() + //} + out += fast_deep_eq_fn + for node in nodes { + name := g.js_name(node.name).replace('.', '_') + if g.enable_doc { + out += '/** @namespace $name */\n' + } + out += 'const $name = (function (' + mut namespace := g.namespaces[node.name] + mut first := true + for _, val in namespace.imports { + if !first { + out += ', ' + } + first = false + out += val + } + out += ') {\n\t' + namespace_code := namespace.out.str() + if g.pref.sourcemap { + // calculate current output start line + mut current_line := u32(out.count('\n') + 1) + mut sm_pos := u32(0) + for sourcemap_ns_entry in namespace.sourcemap_helper { + // calculate final generated location in output based on position + current_segment := namespace_code.substr(int(sm_pos), int(sourcemap_ns_entry.ns_pos)) + current_line += u32(current_segment.count('\n')) + current_column := if last_nl_pos := current_segment.last_index('\n') { + u32(current_segment.len - last_nl_pos - 1) + } else { + u32(0) + } + g.sourcemap.add_mapping(sourcemap_ns_entry.src_path, sourcemap.SourcePosition{ + source_line: sourcemap_ns_entry.src_line + source_column: 0 // sourcemap_ns_entry.src_column + }, current_line, current_column, '') + sm_pos = sourcemap_ns_entry.ns_pos + } + } + out += namespace_code + + // public scope + out += '\n' + if g.enable_doc { + out += '\n\t/* module exports */' + } + out += '\n\treturn {' + // export builtin types + if name == 'builtin' { + for typ in js.v_types { + out += '\n\t\t$typ,' + } + } + for i, pub_var in namespace.pub_vars { + out += '\n\t\t$pub_var' + if i < namespace.pub_vars.len - 1 { + out += ',' + } + } + if namespace.pub_vars.len > 0 { + out += '\n\t' + } + out += '};' + out += '\n})(' + first = true + for key, _ in namespace.imports { + if !first { + out += ', ' + } + first = false + out += key.replace('.', '_') + } + out += ');\n' + // generate builtin basic type casts + if name == 'builtin' { + out += '// builtin type casts\n' + out += 'const [' + for i, typ in js.v_types { + if i > 0 { + out += ', ' + } + out += '$typ' + } + out += '] = [' + for i, typ in js.v_types { + if i > 0 { + out += ',' + } + out += '\n\tfunction(val) { return new builtin.${typ}(val) }' + } + out += '\n]\n' + } + } + if pref.is_shared { + // Export, through CommonJS, the module of the entry file if `-shared` was passed + export := nodes[nodes.len - 1].name + out += 'if (typeof module === "object" && module.exports) module.exports = $export;\n' + } + out += '\n' + if g.pref.sourcemap { + out += g.create_sourcemap() + } + return out +} + +fn (g JsGen) create_sourcemap() string { + mut sm := g.sourcemap + mut out := '\n//# sourceMappingURL=data:application/json;base64,' + out += base64.encode(sm.to_json().str().bytes()) + out += '\n' + + return out +} + +pub fn (mut g JsGen) enter_namespace(name string) { + if g.namespaces[name] == 0 { + // create a new namespace + ns := &Namespace{ + name: name + } + g.namespaces[name] = ns + g.ns = ns + } else { + g.ns = g.namespaces[name] + } + g.inside_builtin = name == 'builtin' +} + +pub fn (mut g JsGen) escape_namespace() { + g.ns = &Namespace(0) + g.inside_builtin = false +} + +pub fn (mut g JsGen) push_pub_var(s string) { + g.ns.pub_vars << g.js_name(s) +} + +pub fn (mut g JsGen) find_class_methods(stmts []ast.Stmt) { + for stmt in stmts { + match stmt { + ast.FnDecl { + if stmt.is_method { + // Found struct method, store it to be generated along with the class. + mut class_name := g.table.get_type_name(stmt.receiver.typ) + // Workaround until `map[key] << val` works. + mut arr := g.method_fn_decls[class_name] + arr << stmt + g.method_fn_decls[class_name] = arr + } + } + else {} + } + } +} + +pub fn (mut g JsGen) init() { + g.definitions.writeln('// Generated by the V compiler\n') + g.definitions.writeln('"use strict";') + g.definitions.writeln('') + g.definitions.writeln('var \$global = (new Function("return this"))();') + g.definitions.writeln('function \$ref(value) { this.val = value; } ') + g.definitions.writeln('\$ref.prototype.valueOf = function() { return this.val; } ') + if g.pref.backend != .js_node { + g.definitions.writeln('const \$process = {') + g.definitions.writeln(' arch: "js",') + if g.pref.backend == .js_freestanding { + g.definitions.writeln(' platform: "freestanding"') + } else { + g.definitions.writeln(' platform: "browser"') + } + g.definitions.writeln('}') + + g.definitions.writeln('const \$os = {') + g.definitions.writeln(' endianess: "LE",') + + g.definitions.writeln('}') + } else { + g.definitions.writeln('const \$os = require("os");') + g.definitions.writeln('const \$process = process;') + } +} + +pub fn (g JsGen) hashes() string { + mut res := '// V_COMMIT_HASH $version.vhash()\n' + res += '// V_CURRENT_COMMIT_HASH ${version.githash(g.pref.building_v)}\n' + return res +} + +[noreturn] +fn verror(msg string) { + eprintln('jsgen error: $msg') + exit(1) +} + +[inline] +pub fn (mut g JsGen) gen_indent() { + if g.ns.indent > 0 && g.empty_line { + g.ns.out.write_string(util.tabs(g.ns.indent)) + } + g.empty_line = false +} + +[inline] +pub fn (mut g JsGen) inc_indent() { + g.ns.indent++ +} + +[inline] +pub fn (mut g JsGen) dec_indent() { + g.ns.indent-- +} + +[inline] +pub fn (mut g JsGen) write(s string) { + if g.ns == 0 { + verror('g.write: not in a namespace') + } + g.gen_indent() + g.ns.out.write_string(s) +} + +[inline] +pub fn (mut g JsGen) writeln(s string) { + if g.ns == 0 { + verror('g.writeln: not in a namespace') + } + g.gen_indent() + g.ns.out.writeln(s) + g.empty_line = true +} + +[inline] +pub fn (mut g JsGen) new_tmp_var() string { + g.tmp_count++ + return '_tmp$g.tmp_count' +} + +// 'mod1.mod2.fn' => 'mod1.mod2' +// 'fn' => '' +[inline] +fn get_ns(s string) string { + idx := s.last_index('.') or { return '' } + return s.substr(0, idx) +} + +fn (mut g JsGen) get_alias(name string) string { + ns := get_ns(name) + if ns == '' { + return name + } + alias := g.ns.imports[ns] + if alias == '' { + return name + } + return alias + '.' + name.split('.').last() +} + +fn (mut g JsGen) js_name(name_ string) string { + mut is_js := false + mut name := name_ + if name.starts_with('JS.') { + name = name[3..] + is_js = true + } + ns := get_ns(name) + name = if g.ns == 0 { + name + } else if ns == g.ns.name { + name.split('.').last() + } else { + g.get_alias(name) + } + mut parts := name.split('.') + if !is_js { + for i, p in parts { + if p in js.js_reserved { + parts[i] = 'v_$p' + } + } + } + return parts.join('.') +} + +fn (mut g JsGen) stmts(stmts []ast.Stmt) { + g.inc_indent() + for stmt in stmts { + g.stmt(stmt) + } + g.dec_indent() +} + +[inline] +fn (mut g JsGen) write_v_source_line_info(pos token.Position) { + // g.inside_ternary == 0 && + if g.pref.sourcemap { + g.ns.sourcemap_helper << SourcemapHelper{ + src_path: util.vlines_escape_path(g.file.path, g.pref.ccompiler) + src_line: u32(pos.line_nr + 1) + ns_pos: u32(g.ns.out.len) + } + } + if g.pref.is_vlines && g.is_vlines_enabled { + g.write(' /* ${pos.line_nr + 1} $g.ns.out.len */ ') + } +} + +fn (mut g JsGen) gen_global_decl(node ast.GlobalDecl) { + mod := if g.pref.build_mode == .build_module { 'enumerable: false' } else { 'enumerable: true' } + for field in node.fields { + if field.has_expr { + tmp_var := g.new_tmp_var() + g.write('const $tmp_var = ') + g.expr(field.expr) + g.writeln(';') + g.writeln('Object.defineProperty(\$global,"$field.name", { + configurable: false, + $mod , + writable: true, + value: $tmp_var + } + ); // global') + } else { + // TODO(playXE): Initialize with default value of type + } + } +} + +fn (mut g JsGen) stmt(node ast.Stmt) { + g.stmt_start_pos = g.ns.out.len + match node { + ast.EmptyStmt {} + ast.AsmStmt { + panic('inline asm is not supported by js') + } + ast.AssertStmt { + g.write_v_source_line_info(node.pos) + g.gen_assert_stmt(node) + } + ast.AssignStmt { + g.write_v_source_line_info(node.pos) + g.gen_assign_stmt(node) + } + ast.Block { + g.write_v_source_line_info(node.pos) + g.gen_block(node) + g.writeln('') + } + ast.BranchStmt { + g.write_v_source_line_info(node.pos) + g.gen_branch_stmt(node) + } + ast.CompFor {} + ast.ConstDecl { + g.write_v_source_line_info(node.pos) + g.gen_const_decl(node) + } + ast.DeferStmt { + g.defer_stmts << node + } + ast.EnumDecl { + g.write_v_source_line_info(node.pos) + g.gen_enum_decl(node) + g.writeln('') + } + ast.ExprStmt { + g.write_v_source_line_info(node.pos) + g.gen_expr_stmt(node) + } + ast.FnDecl { + g.write_v_source_line_info(node.pos) + g.fn_decl = unsafe { &node } + g.gen_fn_decl(node) + } + ast.ForCStmt { + g.write_v_source_line_info(node.pos) + g.gen_for_c_stmt(node) + g.writeln('') + } + ast.ForInStmt { + g.write_v_source_line_info(node.pos) + g.gen_for_in_stmt(node) + g.writeln('') + } + ast.ForStmt { + g.write_v_source_line_info(node.pos) + g.gen_for_stmt(node) + g.writeln('') + } + ast.GlobalDecl { + g.write_v_source_line_info(node.pos) + g.gen_global_decl(node) + g.writeln('') + } + ast.GotoLabel { + g.write_v_source_line_info(node.pos) + g.writeln('${g.js_name(node.name)}:') + } + ast.GotoStmt { + // skip: JS has no goto + } + ast.HashStmt { + g.write_v_source_line_info(node.pos) + g.gen_hash_stmt(node) + } + ast.Import { + g.ns.imports[node.mod] = node.alias + } + ast.InterfaceDecl { + g.write_v_source_line_info(node.pos) + g.gen_interface_decl(node) + } + ast.Module { + // skip: namespacing implemented externally + } + ast.NodeError {} + ast.Return { + if g.defer_stmts.len > 0 { + g.gen_defer_stmts() + } + g.gen_return_stmt(node) + } + ast.SqlStmt {} + ast.StructDecl { + g.write_v_source_line_info(node.pos) + g.gen_struct_decl(node) + } + ast.TypeDecl { + // skip JS has no typedecl + } + } +} + +fn (mut g JsGen) expr(node ast.Expr) { + match node { + ast.NodeError {} + ast.EmptyExpr {} + ast.CTempVar { + g.write('/* ast.CTempVar: node.name */') + } + ast.DumpExpr { + g.write('/* ast.DumpExpr: $node.expr */') + } + ast.AnonFn { + g.gen_fn_decl(node.decl) + } + ast.ArrayInit { + g.gen_array_init_expr(node) + } + ast.AsCast { + // skip: JS has no types, so no need to cast + // TODO: Is jsdoc needed here for TS support? + } + ast.Assoc { + // TODO + } + ast.BoolLiteral { + if node.val == true { + g.write('true') + } else { + g.write('false') + } + } + ast.CallExpr { + g.gen_call_expr(node) + } + ast.ChanInit { + // TODO + } + ast.CastExpr { + g.gen_type_cast_expr(node) + } + ast.CharLiteral { + // todo(playX): char type? + g.write("new builtin.string('$node.val')") + } + ast.Comment {} + ast.ConcatExpr { + // TODO + } + ast.EnumVal { + sym := g.table.get_type_symbol(node.typ) + styp := g.js_name(sym.name) + g.write('${styp}.$node.val') + } + ast.FloatLiteral { + g.gen_float_literal_expr(node) + } + ast.GoExpr { + g.gen_go_expr(node) + } + ast.Ident { + g.gen_ident(node) + } + ast.IfExpr { + g.gen_if_expr(node) + } + ast.IfGuardExpr { + // TODO no optionals yet + } + ast.IndexExpr { + g.gen_index_expr(node) + } + ast.InfixExpr { + g.gen_infix_expr(node) + } + ast.IntegerLiteral { + g.gen_integer_literal_expr(node) + } + ast.LockExpr { + g.gen_lock_expr(node) + } + ast.MapInit { + g.gen_map_init_expr(node) + } + ast.MatchExpr { + // TODO + } + ast.None { + // TODO + } + ast.OrExpr { + // TODO + } + ast.ParExpr { + g.write('(') + g.expr(node.expr) + g.write(')') + } + ast.PostfixExpr { + g.expr(node.expr) + g.write(node.op.str()) + } + ast.PrefixExpr { + if node.op in [.amp, .mul] { + // C pointers/references: ignore them + if node.op == .amp { + if !node.right_type.is_pointer() { + // kind of weird way to handle references but it allows us to access type methods easily. + g.write('(function(x) {') + g.write(' return { val: x, __proto__: Object.getPrototypeOf(x), valueOf: function() { return this.val; } }})( ') + g.expr(node.right) + g.write(')') + } else { + g.expr(node.right) + } + } else { + g.write('(') + g.expr(node.right) + g.write(').valueOf()') + } + } else { + g.write(node.op.str()) + g.write('(') + g.expr(node.right) + g.write('.valueOf()') + g.write(')') + } + } + ast.RangeExpr { + // Only used in IndexExpr, requires index type info + } + ast.SelectExpr { + // TODO: to be implemented + } + ast.SelectorExpr { + g.gen_selector_expr(node) + } + ast.SizeOf, ast.IsRefType { + // TODO + } + ast.OffsetOf { + // TODO + } + ast.SqlExpr { + // TODO + } + ast.StringInterLiteral { + g.gen_string_inter_literal(node) + } + ast.StringLiteral { + g.gen_string_literal(node) + } + ast.StructInit { + // TODO: once generic fns/unwrap_generic is implemented + // if node.unresolved { + // g.expr(ast.resolve_init(node, g.unwrap_generic(node.typ), g.table)) + // } else { + // // `user := User{name: 'Bob'}` + // g.gen_struct_init(node) + // } + // `user := User{name: 'Bob'}` + g.gen_struct_init(node) + } + ast.TypeNode { + // skip: JS has no types + // TODO maybe? + } + ast.Likely { + g.write('(') + g.expr(node.expr) + g.write(')') + } + ast.TypeOf { + g.gen_typeof_expr(node) + // TODO: Should this print the V type or the JS type? + } + ast.AtExpr { + g.write('"$node.val"') + } + ast.ComptimeCall { + // TODO + } + ast.ComptimeSelector { + // TODO + } + ast.UnsafeExpr { + g.expr(node.expr) + } + ast.ArrayDecompose {} + } +} + +// TODO +fn (mut g JsGen) gen_assert_stmt(a ast.AssertStmt) { + if !a.is_used { + return + } + g.writeln('// assert') + g.write('if( ') + g.expr(a.expr) + g.write(' ) {') + s_assertion := a.expr.str().replace('"', "'") + mut mod_path := g.file.path.replace('\\', '\\\\') + if g.is_test { + g.writeln(' g_test_oks++;') + g.writeln(' cb_assertion_ok("$mod_path", ${a.pos.line_nr + 1}, "assert $s_assertion", "${g.fn_decl.name}()" );') + g.writeln('} else {') + g.writeln(' g_test_fails++;') + g.writeln(' cb_assertion_failed("$mod_path", ${a.pos.line_nr + 1}, "assert $s_assertion", "${g.fn_decl.name}()" );') + g.writeln(' exit(1);') + g.writeln('}') + return + } + g.writeln('} else {') + g.inc_indent() + g.writeln('builtin.eprintln("$mod_path:${a.pos.line_nr + 1}: FAIL: fn ${g.fn_decl.name}(): assert $s_assertion");') + g.writeln('builtin.exit(1);') + g.dec_indent() + g.writeln('}') +} + +fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) { + if stmt.left.len > stmt.right.len { + // multi return + g.write('const [') + for i, left in stmt.left { + if !left.is_blank_ident() { + g.expr(left) + } + if i < stmt.left.len - 1 { + g.write(', ') + } + } + g.write('] = ') + g.expr(stmt.right[0]) + g.writeln(';') + } else { + // `a := 1` | `a,b := 1,2` + for i, left in stmt.left { + mut op := stmt.op + if stmt.op == .decl_assign { + op = .assign + } + val := stmt.right[i] + mut is_mut := false + if left is ast.Ident { + is_mut = left.is_mut + if left.kind == .blank_ident || left.name in ['', '_'] { + tmp_var := g.new_tmp_var() + // TODO: Can the tmp_var declaration be omitted? + g.write('const $tmp_var = ') + g.expr(val) + g.writeln(';') + continue + } + } + mut styp := g.typ(stmt.left_types[i]) + if !g.inside_loop && styp.len > 0 { + g.doc.gen_typ(styp) + } + if stmt.op == .decl_assign { + if g.inside_loop || is_mut { + g.write('let ') + } else { + g.write('const ') + } + } + g.expr(left) + mut is_ptr := false + if stmt.op == .assign && stmt.left_types[i].is_ptr() { + is_ptr = true + g.write('.val') + } + if g.inside_map_set && op == .assign { + g.inside_map_set = false + g.write(', ') + g.expr(val) + if is_ptr { + g.write('.val') + } + g.write(')') + } else { + g.write(' $op ') + // TODO: Multiple types?? + should_cast := + (g.table.type_kind(stmt.left_types.first()) in js.shallow_equatables) + && (g.cast_stack.len <= 0 || stmt.left_types.first() != g.cast_stack.last()) + + if should_cast { + g.cast_stack << stmt.left_types.first() + if g.file.mod.name == 'builtin' { + g.write('new ') + } + g.write('${g.typ(stmt.left_types.first())}(') + } + g.expr(val) + if is_ptr { + g.write('.val') + } + if should_cast { + g.write(')') + g.cast_stack.delete_last() + } + } + if g.inside_loop { + g.write('; ') + } else { + g.writeln(';') + } + } + } +} + +fn (mut g JsGen) gen_attrs(attrs []ast.Attr) { + for attr in attrs { + g.writeln('/* [$attr.name] */') + } +} + +fn (mut g JsGen) gen_block(it ast.Block) { + g.writeln('{') + g.stmts(it.stmts) + g.writeln('}') +} + +fn (mut g JsGen) gen_branch_stmt(it ast.BranchStmt) { + // continue or break + g.write(it.kind.str()) + g.writeln(';') +} + +fn (mut g JsGen) gen_const_decl(it ast.ConstDecl) { + for field in it.fields { + g.doc.gen_const(g.typ(field.typ)) + if field.is_pub { + g.push_pub_var(field.name) + } + g.write('const ${g.js_name(field.name)} = ') + g.expr(field.expr) + g.writeln(';') + } + g.writeln('') +} + +fn (mut g JsGen) gen_defer_stmts() { + g.writeln('(function defer() {') + for defer_stmt in g.defer_stmts { + g.stmts(defer_stmt.stmts) + } + g.defer_stmts = [] + g.writeln('})();') +} + +fn (mut g JsGen) gen_enum_decl(it ast.EnumDecl) { + g.doc.gen_enum() + g.writeln('const ${g.js_name(it.name)} = {') + g.inc_indent() + mut i := 0 + for field in it.fields { + g.write('$field.name: ') + if field.has_expr && field.expr is ast.IntegerLiteral { + i = field.expr.val.int() + } + g.writeln('$i,') + i++ + } + g.dec_indent() + g.writeln('};') + if it.is_pub { + g.push_pub_var(it.name) + } +} + +fn (mut g JsGen) gen_expr_stmt(it ast.ExprStmt) { + g.expr(it.expr) + if !it.is_expr && it.expr !is ast.IfExpr && !g.inside_ternary { + g.writeln(';') + } +} + +fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) { + if it.no_body || it.is_method { + // Struct methods are handled by class generation code. + return + } + if g.inside_builtin { + g.builtin_fns << it.name + } + cur_fn_decl := g.fn_decl + g.gen_method_decl(it) + g.fn_decl = cur_fn_decl +} + +fn fn_has_go(node ast.FnDecl) bool { + mut has_go := false + for stmt in node.stmts { + if stmt is ast.ExprStmt { + if stmt.expr is ast.GoExpr { + has_go = true + break + } + } + } + return has_go +} + +fn (mut g JsGen) gen_method_decl(it ast.FnDecl) { + unsafe { + g.fn_decl = &it + } + has_go := fn_has_go(it) + is_main := it.name == 'main.main' + g.gen_attrs(it.attrs) + if is_main { + // there is no concept of main in JS but we do have iife + g.writeln('/* program entry point */') + + g.write('(') + if has_go { + g.write('async ') + } + g.write('function(') + } else if it.is_anon { + g.write('function (') + } else { + mut name := g.js_name(it.name) + c := name[0] + if c in [`+`, `-`, `*`, `/`] { + name = util.replace_op(name) + } + // type_name := g.typ(it.return_type) + // generate jsdoc for the function + g.doc.gen_fn(it) + if has_go { + g.write('async ') + } + if !it.is_method { + g.write('function ') + } else { + if it.attrs.contains('js_getter') { + g.write('get ') + } else if it.attrs.contains('js_setter') { + g.write('set ') + } + } + g.write('${name}(') + if it.is_pub && !it.is_method { + g.push_pub_var(name) + } + } + mut args := it.params + if it.is_method { + args = args[1..] + } + g.fn_args(args, it.is_variadic) + if it.is_method { + if args.len > 0 { + g.write(', ') + } + g.write('${it.params[0].name} = this') + } + g.writeln(') {') + for i, arg in args { + is_varg := i == args.len - 1 && it.is_variadic + if is_varg { + name := g.js_name(arg.name) + g.writeln('$name = new array($name);') + } + } + + g.stmts(it.stmts) + g.write('}') + if is_main { + g.write(')();') + } + if !it.is_anon && !it.is_method { + g.writeln('\n') + } + g.fn_decl = voidptr(0) +} + +fn (mut g JsGen) fn_args(args []ast.Param, is_variadic bool) { + for i, arg in args { + name := g.js_name(arg.name) + is_varg := i == args.len - 1 && is_variadic + if is_varg { + g.write('...$name') + } else { + g.write(name) + } + // if its not the last argument + if i < args.len - 1 { + g.write(', ') + } + } +} + +fn (mut g JsGen) gen_for_c_stmt(it ast.ForCStmt) { + g.inside_loop = true + g.write('for (') + if it.has_init { + g.stmt(it.init) + } else { + g.write('; ') + } + if it.has_cond { + g.write('+') // convert to number or boolean + g.expr(it.cond) + } + g.write('; ') + if it.has_inc { + g.stmt(it.inc) + } + g.writeln(') {') + g.stmts(it.stmts) + g.writeln('}') + g.inside_loop = false +} + +fn (mut g JsGen) gen_for_in_stmt(it ast.ForInStmt) { + if it.is_range { + // `for x in 1..10 {` + mut i := it.val_var + if i in ['', '_'] { + i = g.new_tmp_var() + } + g.inside_loop = true + g.write('for (let $i = ') + g.expr(it.cond) + g.write('; $i < ') + g.expr(it.high) + g.writeln('; $i = new builtin.int($i + 1)) {') + g.inside_loop = false + g.stmts(it.stmts) + g.writeln('}') + } else if it.kind in [.array, .string] || it.cond_type.has_flag(.variadic) { + // `for num in nums {` + val := if it.val_var in ['', '_'] { '_' } else { it.val_var } + // styp := g.typ(it.val_type) + if it.key_var.len > 0 { + g.write('for (const [$it.key_var, $val] of ') + if it.kind == .string { + g.write('Array.from(') + g.expr(it.cond) + if it.cond_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.str.split(\'\').entries(), ([$it.key_var, $val]) => [$it.key_var, ') + if g.ns.name == 'builtin' { + g.write('new ') + } + g.write('byte($val)])') + } else { + g.expr(it.cond) + if it.cond_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.entries()') + } + } else { + g.write('for (const $val of ') + g.expr(it.cond) + if it.cond_type.is_ptr() { + g.write('.valueOf()') + } + if it.kind == .string { + g.write(".str.split('')") + } + // cast characters to bytes + if val !in ['', '_'] && it.kind == .string { + g.write('.map(c => ') + if g.ns.name == 'builtin' { + g.write('new ') + } + g.write('byte(c))') + } + } + g.writeln(') {') + g.stmts(it.stmts) + g.writeln('}') + } else if it.kind == .map { + // `for key, val in map[string]int {` + // key_styp := g.typ(it.key_type) + // val_styp := g.typ(it.val_type) + key := if it.key_var in ['', '_'] { '' } else { it.key_var } + val := if it.val_var in ['', '_'] { '' } else { it.val_var } + g.write('for (let [$key, $val] of ') + g.expr(it.cond) + if it.cond_type.is_ptr() { + g.write('.valueOf()') + } + g.writeln(') {') + g.stmts(it.stmts) + g.writeln('}') + } +} + +fn (mut g JsGen) gen_for_stmt(it ast.ForStmt) { + g.write('while (') + if it.is_inf { + g.write('true') + } else { + g.write('+') // convert expr to number or boolean + g.expr(it.cond) + } + g.writeln(') {') + g.stmts(it.stmts) + g.writeln('}') +} + +fn (mut g JsGen) gen_go_expr(node ast.GoExpr) { + // TODO Handle joinable expressions + // node.is_expr + mut name := node.call_expr.name + if node.call_expr.is_method { + receiver_sym := g.table.get_type_symbol(node.call_expr.receiver_type) + name = receiver_sym.name + '.' + name + } + // todo: please add a name feild without the mod name for ast.CallExpr + if name.starts_with('${node.call_expr.mod}.') { + name = name[node.call_expr.mod.len + 1..] + } + g.writeln('await new Promise(function(resolve){') + g.inc_indent() + g.write('${name}(') + for i, arg in node.call_expr.args { + g.expr(arg.expr) + if i < node.call_expr.args.len - 1 { + g.write(', ') + } + } + g.writeln(');') + g.writeln('resolve();') + g.dec_indent() + g.writeln('});') +} + +fn (mut g JsGen) gen_import_stmt(it ast.Import) { + g.ns.imports[it.mod] = it.alias +} + +fn (mut g JsGen) gen_interface_decl(it ast.InterfaceDecl) { + // JS is dynamically typed, so we don't need any codegen at all + // We just need the JSDoc so TypeScript type checking works + g.doc.gen_interface(it) + // This is a hack to make the interface's type accessible outside its namespace + // TODO: interfaces are always `pub`? + name := g.js_name(it.name) + g.push_pub_var('/** @type $name */\n\t\t$name: undefined') +} + +fn (mut g JsGen) gen_return_stmt(it ast.Return) { + if it.exprs.len == 0 { + // Returns nothing + g.writeln('return;') + return + } + g.write('return ') + if it.exprs.len == 1 { + g.expr(it.exprs[0]) + } else { // Multi return + g.gen_array_init_values(it.exprs) + } + g.writeln(';') +} + +fn (mut g JsGen) gen_hash_stmt(it ast.HashStmt) { + g.writeln(it.val) +} + +fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) { + mut name := node.name + if name.starts_with('JS.') { + return + } + if name in js.v_types && g.ns.name == 'builtin' { + return + } + js_name := g.js_name(name) + g.gen_attrs(node.attrs) + g.doc.gen_fac_fn(node.fields) + g.write('function ${js_name}({ ') + for i, field in node.fields { + g.write('$field.name = ') + if field.has_default_expr { + g.expr(field.default_expr) + } else { + g.write('${g.to_js_typ_val(field.typ)}') + } + if i < node.fields.len - 1 { + g.write(', ') + } + } + g.writeln(' }) {') + g.inc_indent() + for field in node.fields { + g.writeln('this.$field.name = $field.name') + } + g.dec_indent() + g.writeln('};') + g.writeln('${js_name}.prototype = {') + g.inc_indent() + for embed in node.embeds { + etyp := g.typ(embed.typ) + g.writeln('...${etyp}.prototype,') + } + fns := g.method_fn_decls[name] + for field in node.fields { + typ := g.typ(field.typ) + g.doc.gen_typ(typ) + g.write('$field.name: ${g.to_js_typ_val(field.typ)}') + g.writeln(',') + } + for cfn in fns { + g.gen_method_decl(cfn) + g.writeln(',') + } + // gen toString method + fn_names := fns.map(it.name) + if 'toString' !in fn_names { + g.writeln('toString() {') + g.inc_indent() + g.write('return `$js_name {') + for i, field in node.fields { + if i == 0 { + g.write(' ') + } else { + g.write(', ') + } + match g.typ(field.typ).split('.').last() { + 'string' { g.write('$field.name: "\${this["$field.name"].toString()}"') } + else { g.write('$field.name: \${this["$field.name"].toString()} ') } + } + } + g.writeln('}`') + g.dec_indent() + g.writeln('},') + } + g.writeln('\$toJS() { return this; }') + g.dec_indent() + g.writeln('};\n') + if node.is_pub { + g.push_pub_var(name) + } +} + +fn (mut g JsGen) gen_array_init_expr(it ast.ArrayInit) { + // NB: Fixed arrays and regular arrays are handled the same, since fixed arrays: + // 1) Are only available for number types + // 2) Give the code unnecessary complexity + // 3) Have several limitations like missing most `Array.prototype` methods + // 4) Modern engines can optimize regular arrays into typed arrays anyways, + // offering similar performance + g.write('new array(') + g.inc_indent() + + if it.has_len { + t1 := g.new_tmp_var() + t2 := g.new_tmp_var() + g.writeln('(function() {') + g.inc_indent() + g.writeln('const $t1 = [];') + g.write('for (let $t2 = 0; $t2 < ') + g.expr(it.len_expr) + g.writeln('; $t2++) {') + g.inc_indent() + g.write('${t1}.push(') + if it.has_default { + g.expr(it.default_expr) + } else { + // Fill the array with the default values for its type + t := g.to_js_typ_val(it.elem_type) + g.write(t) + } + g.writeln(');') + g.dec_indent() + g.writeln('};') + g.writeln('return $t1;') + g.dec_indent() + g.write('})()') + } else if it.is_fixed && it.exprs.len == 1 { + // [100]byte codegen + t1 := g.new_tmp_var() + t2 := g.new_tmp_var() + g.writeln('(function() {') + g.inc_indent() + g.writeln('const $t1 = [];') + g.write('for (let $t2 = 0; $t2 < ') + g.expr(it.exprs[0]) + g.writeln('; $t2++) {') + g.inc_indent() + g.write('${t1}.push(') + if it.has_default { + g.expr(it.default_expr) + } else { + // Fill the array with the default values for its type + t := g.to_js_typ_val(it.elem_type) + g.write(t) + } + g.writeln(');') + g.dec_indent() + g.writeln('};') + g.writeln('return $t1;') + g.dec_indent() + g.write('})()') + } else { + g.gen_array_init_values(it.exprs) + } + g.dec_indent() + g.write(')') +} + +fn (mut g JsGen) gen_array_init_values(exprs []ast.Expr) { + g.write('[') + for i, expr in exprs { + g.expr(expr) + if i < exprs.len - 1 { + g.write(', ') + } + } + g.write(']') +} + +fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { + g.call_stack << it + mut name := g.js_name(it.name) + call_return_is_optional := it.return_type.has_flag(.optional) + if call_return_is_optional { + g.writeln('(function(){') + g.inc_indent() + g.writeln('try {') + g.inc_indent() + g.write('return builtin.unwrap(') + } + g.expr(it.left) + + if it.is_method { // foo.bar.baz() + sym := g.table.get_type_symbol(it.receiver_type) + g.write('.') + if sym.kind == .array && it.name in ['map', 'filter'] { + // Prevent 'it' from getting shadowed inside the match + node := it + g.write(it.name) + g.write('(') + expr := node.args[0].expr + match expr { + ast.AnonFn { + g.gen_fn_decl(expr.decl) + g.write(')') + return + } + ast.Ident { + if expr.kind == .function { + g.write(g.js_name(expr.name)) + g.write(')') + return + } else if expr.kind == .variable { + v_sym := g.table.get_type_symbol(expr.var_info().typ) + if v_sym.kind == .function { + g.write(g.js_name(expr.name)) + g.write(')') + return + } + } + } + else {} + } + + g.write('it => ') + g.expr(node.args[0].expr) + g.write(')') + return + } + + left_sym := g.table.get_type_symbol(it.left_type) + if left_sym.kind == .array { + node := it + match node.name { + 'insert' { + arg2_sym := g.table.get_type_symbol(node.args[1].typ) + is_arg2_array := arg2_sym.kind == .array && node.args[1].typ == node.left_type + if is_arg2_array { + g.write('insert_many(') + } else { + g.write('insert(') + } + + g.expr(node.args[0].expr) + g.write(',') + if is_arg2_array { + g.expr(node.args[1].expr) + g.write('.arr,') + g.expr(node.args[1].expr) + g.write('.len') + } else { + g.expr(node.args[1].expr) + } + g.write(')') + return + } + 'prepend' { + arg_sym := g.table.get_type_symbol(node.args[0].typ) + is_arg_array := arg_sym.kind == .array && node.args[0].typ == node.left_type + if is_arg_array { + g.write('prepend_many(') + } else { + g.write('prepend(') + } + + if is_arg_array { + g.expr(node.args[0].expr) + g.write('.arr, ') + g.expr(node.args[0].expr) + g.write('.len') + } else { + g.expr(node.args[0].expr) + } + g.write(')') + return + } + else {} + } + } + } else { + if name in g.builtin_fns { + g.write('builtin.') + } + } + g.write('${name}(') + for i, arg in it.args { + g.expr(arg.expr) + if i != it.args.len - 1 { + g.write(', ') + } + } + // end method call + g.write(')') + if call_return_is_optional { + // end unwrap + g.writeln(')') + g.dec_indent() + // begin catch block + g.writeln('} catch(err) {') + g.inc_indent() + // gen or block contents + match it.or_block.kind { + .block { + if it.or_block.stmts.len > 1 { + g.stmts(it.or_block.stmts[..it.or_block.stmts.len - 1]) + } + g.write('return ') + g.stmt(it.or_block.stmts.last()) + } + .propagate { + panicstr := '`optional not set (\${err})`' + if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' { + g.writeln('return builtin.panic($panicstr)') + } else { + g.writeln('builtin.js_throw(err)') + } + } + else {} + } + // end catch + g.dec_indent() + g.writeln('}') + // end anon fn + g.dec_indent() + g.write('})()') + } + g.call_stack.delete_last() +} + +fn (mut g JsGen) gen_ident(node ast.Ident) { + mut name := g.js_name(node.name) + if node.kind == .blank_ident || name in ['', '_'] { + name = g.new_tmp_var() + } + // TODO `is` + // TODO handle optionals + g.write(name) + + // TODO: Generate .val for basic types +} + +fn (mut g JsGen) gen_lock_expr(node ast.LockExpr) { + // TODO: implement this +} + +fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { + if node.is_comptime { + g.comp_if(node) + return + } + type_sym := g.table.get_type_symbol(node.typ) + // one line ?: + if node.is_expr && node.branches.len >= 2 && node.has_else && type_sym.kind != .void { + // `x := if a > b { } else if { } else { }` + g.write('(') + g.inside_ternary = true + for i, branch in node.branches { + if i > 0 { + g.write(' : ') + } + if i < node.branches.len - 1 || !node.has_else { + g.write('(') + g.expr(branch.cond) + g.write(')') + g.write('.valueOf()') + g.write(' ? ') + } + g.stmts(branch.stmts) + } + g.inside_ternary = false + g.write(')') + } else { + // mut is_guard = false + for i, branch in node.branches { + if i == 0 { + match branch.cond { + ast.IfGuardExpr { + // TODO optionals + } + else { + g.write('if (') + if '$branch.cond' == 'js' { + g.write('true') + } else { + g.write('(') + g.expr(branch.cond) + g.write(')') + g.write('.valueOf()') + } + g.writeln(') {') + } + } + } else if i < node.branches.len - 1 || !node.has_else { + g.write('} else if (') + g.write('(') + g.expr(branch.cond) + g.write(')') + g.write('.valueOf()') + g.writeln(') {') + } else if i == node.branches.len - 1 && node.has_else { + /* + if is_guard { + //g.writeln('} if (!$guard_ok) { /* else */') + } else { + */ + g.writeln('} else {') + // } + } + g.stmts(branch.stmts) + } + /* + if is_guard { + g.write('}') + } + */ + g.writeln('}') + g.writeln('') + } +} + +fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) { + left_typ := g.table.get_type_symbol(expr.left_type) + // TODO: Handle splice setting if it's implemented + if expr.index is ast.RangeExpr { + g.expr(expr.left) + if expr.left_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.slice(') + if expr.index.has_low { + g.expr(expr.index.low) + } else { + g.write('0') + } + g.write(', ') + if expr.index.has_high { + g.expr(expr.index.high) + } else { + g.expr(expr.left) + if expr.left_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.length') + } + g.write(')') + } else if left_typ.kind == .map { + g.expr(expr.left) + if expr.is_setter { + g.inside_map_set = true + g.write('.map.set(') + } else { + g.write('.map.get(') + } + g.expr(expr.index) + g.write('.toString()') + if !expr.is_setter { + g.write(')') + } + } else if left_typ.kind == .string { + if expr.is_setter { + // TODO: What's the best way to do this? + // 'string'[3] = `o` + } else { + // TODO: Maybe use u16 there? JS String returns values up to 2^16-1 + g.write('new byte(') + g.expr(expr.left) + if expr.left_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.str.charCodeAt(') + g.expr(expr.index) + g.write('))') + } + } else { + // TODO Does this cover all cases? + g.expr(expr.left) + if expr.left_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.arr') + g.write('[+') + g.cast_stack << ast.int_type_idx + g.expr(expr.index) + g.cast_stack.delete_last() + g.write(']') + } +} + +fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { + l_sym := g.table.get_type_symbol(it.left_type) + r_sym := g.table.get_type_symbol(it.right_type) + + is_not := it.op in [.not_in, .not_is, .ne] + if is_not { + g.write('!(') + } + + if it.op == .eq || it.op == .ne { + // Shallow equatables + if l_sym.kind in js.shallow_equatables && r_sym.kind in js.shallow_equatables { + // wrap left expr in parens so binary operations will work correctly. + g.write('(') + g.expr(it.left) + g.write(')') + g.write('.eq(') + g.cast_stack << int(l_sym.kind) + g.expr(it.right) + g.cast_stack.delete_last() + g.write(')') + } else { + g.write('vEq(') + g.expr(it.left) + g.write(', ') + g.expr(it.right) + g.write(')') + } + } else if l_sym.kind == .array && it.op == .left_shift { // arr << 1 + g.write('Array.prototype.push.call(') + g.expr(it.left) + g.write('.arr,') + array_info := l_sym.info as ast.Array + // arr << [1, 2] + if r_sym.kind == .array && array_info.elem_type != it.right_type { + g.write('...') + } + g.expr(it.right) + g.write(')') + } else if r_sym.kind in [.array, .map, .string] && it.op in [.key_in, .not_in] { + g.expr(it.right) + if r_sym.kind == .map { + g.write('.map.has(') + } else if r_sym.kind == .string { + g.write('.str.includes(') + } else { + g.write('.\$includes(') + } + g.expr(it.left) + if l_sym.kind == .string { + g.write('.str') + } + g.write(')') + } else if it.op in [.key_is, .not_is] { // foo is Foo + g.expr(it.left) + g.write(' instanceof ') + g.write(g.typ(it.right_type)) + } else { + is_arithmetic := it.op in [token.Kind.plus, .minus, .mul, .div, .mod] + mut needs_cast := is_arithmetic && it.left_type != it.right_type + mut greater_typ := 0 + // todo(playX): looks like this cast is always required to perform .eq operation on types. + if true || needs_cast { + greater_typ = g.greater_typ(it.left_type, it.right_type) + if g.cast_stack.len > 0 { + needs_cast = g.cast_stack.last() != greater_typ + } + } + + if true || needs_cast { + if g.ns.name == 'builtin' { + g.write('new ') + } + g.write('${g.typ(greater_typ)}(') + g.cast_stack << greater_typ + } + g.expr(it.left) + g.write(' $it.op ') + g.expr(it.right) + + if true || needs_cast { + g.cast_stack.delete_last() + g.write(')') + } + } + + if is_not { + g.write(')') + } +} + +fn (mut g JsGen) greater_typ(left ast.Type, right ast.Type) ast.Type { + l := int(left) + r := int(right) + lr := [l, r] + if ast.string_type_idx in lr { + return ast.Type(ast.string_type_idx) + } + should_float := (l in ast.integer_type_idxs && r in ast.float_type_idxs) + || (r in ast.integer_type_idxs && l in ast.float_type_idxs) + if should_float { + if ast.f64_type_idx in lr { + return ast.Type(ast.f64_type_idx) + } + if ast.f32_type_idx in lr { + return ast.Type(ast.f32_type_idx) + } + return ast.Type(ast.float_literal_type) + } + should_int := (l in ast.integer_type_idxs && r in ast.integer_type_idxs) + if should_int { + // cant add to u64 - if (ast.u64_type_idx in lr) { return ast.Type(ast.u64_type_idx) } + // just guessing this order + if ast.i64_type_idx in lr { + return ast.Type(ast.i64_type_idx) + } + if ast.u32_type_idx in lr { + return ast.Type(ast.u32_type_idx) + } + if ast.int_type_idx in lr { + return ast.Type(ast.int_type_idx) + } + if ast.u16_type_idx in lr { + return ast.Type(ast.u16_type_idx) + } + if ast.i16_type_idx in lr { + return ast.Type(ast.i16_type_idx) + } + if ast.byte_type_idx in lr { + return ast.Type(ast.byte_type_idx) + } + if ast.i8_type_idx in lr { + return ast.Type(ast.i8_type_idx) + } + return ast.Type(ast.int_literal_type_idx) + } + return ast.Type(l) +} + +fn (mut g JsGen) gen_map_init_expr(it ast.MapInit) { + // key_typ_sym := g.table.get_type_symbol(it.key_type) + // value_typ_sym := g.table.get_type_symbol(it.value_type) + // key_typ_str := util.no_dots(key_typ_sym.name) + // value_typ_str := util.no_dots(value_typ_sym.name) + g.writeln('new map(') + g.inc_indent() + if it.vals.len > 0 { + g.writeln('new Map([') + g.inc_indent() + for i, key in it.keys { + val := it.vals[i] + g.write('[') + g.expr(key) + g.write(', ') + g.expr(val) + g.write(']') + if i < it.keys.len - 1 { + g.write(',') + } + g.writeln('') + } + g.dec_indent() + g.write('])') + } else { + g.write('new Map()') + } + g.dec_indent() + g.write(')') +} + +fn (mut g JsGen) gen_selector_expr(it ast.SelectorExpr) { + g.expr(it.expr) + if it.expr_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.$it.field_name') +} + +fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) { + should_cast := !(g.cast_stack.len > 0 && g.cast_stack.last() == ast.string_type_idx) + if should_cast { + if g.file.mod.name == 'builtin' { + g.write('new ') + } + g.write('string(') + } + g.write('`') + for i, val in it.vals { + escaped_val := val.replace('`', '\\`') + g.write(escaped_val) + if i >= it.exprs.len { + continue + } + expr := it.exprs[i] + fmt := it.fmts[i] + fwidth := it.fwidths[i] + precision := it.precisions[i] + g.write('\${') + if fmt != `_` || fwidth != 0 || precision != 987698 { + // TODO: Handle formatting + g.expr(expr) + } else { + sym := g.table.get_type_symbol(it.expr_types[i]) + g.expr(expr) + if sym.kind == .struct_ && sym.has_method('str') { + g.write('.str()') + } + } + g.write('}') + } + g.write('`') + if should_cast { + g.write(')') + } +} + +fn (mut g JsGen) gen_string_literal(it ast.StringLiteral) { + text := it.val.replace("'", "\\'") + should_cast := !(g.cast_stack.len > 0 && g.cast_stack.last() == ast.string_type_idx) + if true || should_cast { + if g.file.mod.name == 'builtin' { + g.write('new ') + } + g.write('string(') + } + g.write("'$text'") + if true || should_cast { + g.write(')') + } +} + +fn (mut g JsGen) gen_struct_init(it ast.StructInit) { + type_sym := g.table.get_type_symbol(it.typ) + name := type_sym.name + if it.fields.len == 0 { + g.write('new ${g.js_name(name)}({})') + } else { + g.writeln('new ${g.js_name(name)}({') + g.inc_indent() + for i, field in it.fields { + g.write('$field.name: ') + g.expr(field.expr) + if i < it.fields.len - 1 { + g.write(',') + } + g.writeln('') + } + g.dec_indent() + g.write('})') + } +} + +fn (mut g JsGen) gen_typeof_expr(it ast.TypeOf) { + sym := g.table.get_type_symbol(it.expr_type) + if sym.kind == .sum_type { + // TODO: JS sumtypes not implemented yet + } else if sym.kind == .array_fixed { + fixed_info := sym.info as ast.ArrayFixed + typ_name := g.table.get_type_name(fixed_info.elem_type) + g.write('"[$fixed_info.size]$typ_name"') + } else if sym.kind == .function { + info := sym.info as ast.FnType + fn_info := info.func + mut repr := 'fn (' + for i, arg in fn_info.params { + if i > 0 { + repr += ', ' + } + repr += g.table.get_type_name(arg.typ) + } + repr += ')' + if fn_info.return_type != ast.void_type { + repr += ' ${g.table.get_type_name(fn_info.return_type)}' + } + g.write('"$repr"') + } else { + g.write('"$sym.name"') + } +} + +fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) { + is_literal := ((it.expr is ast.IntegerLiteral && it.typ in ast.integer_type_idxs) + || (it.expr is ast.FloatLiteral && it.typ in ast.float_type_idxs)) + // Skip cast if type is the same as the parrent caster + if g.cast_stack.len > 0 && is_literal { + if it.typ == g.cast_stack[g.cast_stack.len - 1] { + return + } + } + g.cast_stack << it.typ + typ := g.typ(it.typ) + if !is_literal { + if typ !in js.v_types || g.ns.name == 'builtin' { + g.write('new ') + } + g.write('${typ}(') + } + g.expr(it.expr) + if typ == 'string' && it.expr !is ast.StringLiteral { + g.write('.toString()') + } + if !is_literal { + g.write(')') + } + g.cast_stack.delete_last() +} + +fn (mut g JsGen) gen_integer_literal_expr(it ast.IntegerLiteral) { + typ := ast.Type(ast.int_type) + + // Don't wrap integers for use in JS.foo functions. + // TODO: call.language always seems to be "v", parser bug? + if g.call_stack.len > 0 { + call := g.call_stack[g.call_stack.len - 1] + if call.language == .js { + for t in call.args { + if t.expr is ast.IntegerLiteral { + if t.expr == it { + g.write(it.val) + return + } + } + } + } + } + + // Skip cast if type is the same as the parrent caster + if g.cast_stack.len > 0 { + if g.cast_stack[g.cast_stack.len - 1] in ast.integer_type_idxs { + g.write('new int($it.val)') + return + } + } + + if g.ns.name == 'builtin' { + g.write('new ') + } + + g.write('${g.typ(typ)}($it.val)') +} + +fn (mut g JsGen) gen_float_literal_expr(it ast.FloatLiteral) { + typ := ast.Type(ast.f32_type) + + // Don't wrap integers for use in JS.foo functions. + // TODO: call.language always seems to be "v", parser bug? + if g.call_stack.len > 0 { + call := g.call_stack[g.call_stack.len - 1] + // if call.language == .js { + for i, t in call.args { + if t.expr is ast.FloatLiteral { + if t.expr == it { + if call.expected_arg_types[i] in ast.integer_type_idxs { + g.write(int(it.val.f64()).str()) + } else { + g.write(it.val) + } + return + } + } + } + //} + } + + // Skip cast if type is the same as the parrent caster + if g.cast_stack.len > 0 { + if g.cast_stack[g.cast_stack.len - 1] in ast.float_type_idxs { + g.write('new f32($it.val)') + return + } else if g.cast_stack[g.cast_stack.len - 1] in ast.integer_type_idxs { + g.write(int(it.val.f64()).str()) + return + } + } + + if g.ns.name == 'builtin' { + g.write('new ') + } + + g.write('${g.typ(typ)}($it.val)') +} + +fn (mut g JsGen) unwrap_generic(typ ast.Type) ast.Type { + if typ.has_flag(.generic) { + if t_typ := g.table.resolve_generic_to_concrete(typ, g.table.cur_fn.generic_names, + g.table.cur_concrete_types) + { + return t_typ + } + } + return typ +} diff --git a/v_windows/v/old/vlib/v/gen/js/jsdoc.v b/v_windows/v/old/vlib/v/gen/js/jsdoc.v new file mode 100644 index 0000000..359687e --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/jsdoc.v @@ -0,0 +1,96 @@ +module js + +import v.ast + +struct JsDoc { +mut: + gen &JsGen +} + +fn new_jsdoc(gen &JsGen) &JsDoc { + return &JsDoc{ + gen: gen + } +} + +fn (mut d JsDoc) write(s string) { + if !d.gen.enable_doc { + return + } + d.gen.write(s) +} + +fn (mut d JsDoc) writeln(s string) { + if !d.gen.enable_doc { + return + } + d.gen.writeln(s) +} + +fn (mut d JsDoc) gen_typ(typ string) { + d.writeln('/** @type {$typ} */') +} + +fn (mut d JsDoc) gen_const(typ string) { + d.writeln('/** @constant {$typ} */') +} + +fn (mut d JsDoc) gen_enum() { + // Enum values can only be ints for now + typ := 'number' + d.writeln('/** @enum {$typ} */') +} + +fn (mut d JsDoc) gen_fac_fn(fields []ast.StructField) { + d.writeln('/**') + d.writeln(' * @constructor') + d.write(' * @param {{') + for i, field in fields { + // Marked as optional: structs have default default values, + // so all struct members don't have to be initialized. + d.write('$field.name?: ${d.gen.typ(field.typ)}') + if i < fields.len - 1 { + d.write(', ') + } + } + d.writeln('}} init') + d.writeln('*/') +} + +fn (mut d JsDoc) gen_fn(it ast.FnDecl) { + type_name := d.gen.typ(it.return_type) + d.writeln('/**') + d.writeln(' * @function') + if it.is_deprecated { + d.writeln(' * @deprecated') + } + for i, arg in it.params { + if (it.is_method || it.receiver.typ == 0) && i == 0 { + continue + } + arg_type_name := d.gen.typ(arg.typ) + is_varg := i == it.params.len - 1 && it.is_variadic + name := d.gen.js_name(arg.name) + if is_varg { + d.writeln(' * @param {...$arg_type_name} $name') + } else { + d.writeln(' * @param {$arg_type_name} $name') + } + } + d.writeln(' * @returns {$type_name}') + d.writeln('*/') +} + +fn (mut d JsDoc) gen_interface(it ast.InterfaceDecl) { + name := d.gen.js_name(it.name) + d.writeln('/**') + d.writeln(' * @interface $name') + d.writeln(' * @typedef $name') + for method in it.methods { + // Skip receiver + typ := d.gen.fn_typ(method.params[1..], method.return_type) + method_name := d.gen.js_name(method.name) + d.writeln(' * @property {$typ} $method_name') + } + d.writeln(' */\n') +} diff --git a/v_windows/v/old/vlib/v/gen/js/jsgen_test.v b/v_windows/v/old/vlib/v/gen/js/jsgen_test.v new file mode 100644 index 0000000..06316e8 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/jsgen_test.v @@ -0,0 +1,86 @@ +import os + +const ( + test_dir = os.join_path('vlib', 'v', 'gen', 'js', 'tests') + output_dir = os.join_path(os.temp_dir(), '_js_tests/') + v_options = '-b js -w' + node_options = '' +) + +fn testsuite_end() { + os.rmdir_all(output_dir) or {} +} + +const there_is_node_available = is_nodejs_working() + +const there_is_grep_available = is_grep_working() + +fn test_example_compilation() { + vexe := os.getenv('VEXE') + os.chdir(os.dir(vexe)) + os.mkdir_all(output_dir) or { panic(err) } + files := find_test_files() + for file in files { + path := os.join_path(test_dir, file) + println('Testing $file') + mut v_options_file := v_options + mut node_options_file := node_options + should_create_source_map := file.ends_with('_sourcemap.v') + if should_create_source_map { + println('activate -sourcemap creation') + v_options_file += ' -sourcemap' // activate souremap generation + + println('add node option: --enable-source-maps') // requieres node >=12.12.0 + node_options_file += ' --enable-source-maps' // activate souremap generation + } + v_code := os.system('$vexe $v_options_file -o $output_dir${file}.js $path') + if v_code != 0 { + assert false + } + // Compilation failed + assert v_code == 0 + if !there_is_node_available { + println(' ... skipping running $file, there is no NodeJS present') + continue + } + js_code := os.system('node $output_dir${file}.js') + if js_code != 0 { + assert false + } + // Running failed + assert js_code == 0 + if should_create_source_map { + if there_is_grep_available { + grep_code_sourcemap_found := os.system('grep -q -E "//#\\ssourceMappingURL=data:application/json;base64,[-A-Za-z0-9+/=]+$" $output_dir${file}.js') + assert grep_code_sourcemap_found == 0 + println('file has a source map embeded') + } else { + println(' ... skipping testing for sourcemap $file, there is no grep present') + } + } + } +} + +fn find_test_files() []string { + files := os.ls(test_dir) or { panic(err) } + // The life example never exits, so tests would hang with it, skip + mut tests := files.filter(it.ends_with('.v')).filter(it != 'life.v') + tests.sort() + return tests +} + +fn is_nodejs_working() bool { + node_res := os.execute('node --version') + if node_res.exit_code != 0 { + return false + } + return true +} + +fn is_grep_working() bool { + node_res := os.execute('grep --version') + if node_res.exit_code != 0 { + return false + } + return true +} diff --git a/v_windows/v/old/vlib/v/gen/js/program_test.v b/v_windows/v/old/vlib/v/gen/js/program_test.v new file mode 100644 index 0000000..8ba06ed --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/program_test.v @@ -0,0 +1,98 @@ +import os +import term +import rand +import v.util.vtest +import v.util.diff + +const vexe = @VEXE + +const vroot = @VMODROOT + +const diff_cmd = find_diff_cmd() + +fn find_diff_cmd() string { + return diff.find_working_diff_command() or { '' } +} + +[noreturn] +fn exit_because(msg string) { + eprintln('$msg, tests will not run') + exit(0) +} + +fn test_node_exists() { + res := os.execute('node --version') + if res.exit_code != 0 { + exit_because('node does not exist') + } + if !res.output.starts_with('v') { + exit_because('invalid node version') + } + version := res.output.trim_left('v').int() + if version < 10 { + exit_because('node should be at least version 10, but is currently version: $version') + } + println('Using node version: $version') +} + +fn test_running_programs_compiled_with_the_js_backend() ? { + os.setenv('VCOLORS', 'never', true) + os.chdir(vroot) + test_dir := 'vlib/v/gen/js/tests/testdata' + main_files := get_main_files_in_dir(test_dir) + fails := check_path(test_dir, main_files) ? + assert fails == 0 +} + +fn get_main_files_in_dir(dir string) []string { + mut mfiles := os.walk_ext(dir, '.v') + mfiles.sort() + return mfiles +} + +fn check_path(dir string, tests []string) ?int { + mut nb_fail := 0 + paths := vtest.filter_vtest_only(tests, basepath: vroot) + for path in paths { + program := path.replace(vroot + os.path_separator, '') + program_out := program.replace('.v', '.out') + if !os.exists(program_out) { + os.write_file(program_out, '') ? + } + print(program + ' ') + res := os.execute('$vexe -b js_node run $program') + if res.exit_code < 0 { + panic(res.output) + } + mut expected := os.read_file(program_out) ? + expected = clean_line_endings(expected) + found := clean_line_endings(res.output) + if expected != found { + println(term.red('FAIL')) + println('============') + println('expected $program_out content:') + println(expected) + println('============') + println('found:') + println(found) + println('============\n') + println('diff:') + println(diff.color_compare_strings(diff_cmd, rand.ulid(), found, expected)) + println('============\n') + nb_fail++ + } else { + println(term.green('OK')) + assert true + } + } + return nb_fail +} + +fn clean_line_endings(s string) string { + mut res := s.trim_space() + res = res.replace(' \n', '\n') + res = res.replace(' \r\n', '\n') + res = res.replace('\r\n', '\n') + res = res.trim('\n') + return res +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/basic_test.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/basic_test.v new file mode 100644 index 0000000..fc61b55 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/basic_test.v @@ -0,0 +1,158 @@ +module sourcemap + +fn test_simple() { + mut sg := generate_empty_map() + mut sm := sg.add_map('hello.js', '/', true, 0, 0) + sm.set_source_content('hello.v', "fn main(){nprintln('Hello World! Helo \$a')\n}") + + mlist := [ + MappingInput{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 0 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 2 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 9 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 7 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 10 + } + name: 'hello_name' + source_position: SourcePosition{ + source_line: 1 + source_column: 8 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 13 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 14 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 12 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 27 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 28 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 29 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 3 + gen_column: 0 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + ] + sm.add_mapping_list('hello.v', mlist) or { panic('x') } + + json_data := sm.to_json() + + expected := '{"version":3,"file":"hello.js","sourceRoot":"\\/","sources":["hello.v"],"sourcesContent":["fn main(){nprintln(\'Hello World! Helo \$a\')\\n}"],"names":["hello_name"],"mappings":"AAAA;AAAA,EAAA,OAAO,CAACA,GAAR,CAAY,aAAZ,CAAA,CAAA;AAAA"}' + assert json_data.str() == expected +} + +fn test_source_null() { + mut sg := generate_empty_map() + mut sm := sg.add_map('hello.js', '/', true, 0, 0) + sm.add_mapping('hello.v', SourcePosition{ + source_line: 0 + source_column: 0 + }, 1, 1, '') + sm.add_mapping('hello_lib1.v', SourcePosition{ + source_line: 0 + source_column: 0 + }, 2, 1, '') + sm.add_mapping('hello_lib2.v', SourcePosition{ + source_line: 0 + source_column: 0 + }, 3, 1, '') + json_data := sm.to_json() + + expected := '{"version":3,"file":"hello.js","sourceRoot":"\\/","sources":["hello.v","hello_lib1.v","hello_lib2.v"],"sourcesContent":[null,null,null],"names":[],"mappings":"CA+\\/\\/\\/\\/\\/HA;CCAA;CCAA"}' + assert json_data.str() == expected +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/compare_test.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/compare_test.v new file mode 100644 index 0000000..0a755be --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/compare_test.v @@ -0,0 +1,322 @@ +module sourcemap + +fn test_cmp_eq() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: IndexNumber(3) + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: IndexNumber(3) + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert !compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_name() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: IndexNumber(3) + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: IndexNumber(4) + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_name_empty() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: IndexNumber(3) + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_name_empty_empty() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert !compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_source_position_empty_eq() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: Empty{} + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: Empty{} + } + + assert !compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_source_position_empty_diff() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: Empty{} + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_source_position_column_diff() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 99 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_source_position_line_diff() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 88 + source_column: 99 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_sources() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 99 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_gen_column() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 99 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_gen_line() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 99 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/mappings.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/mappings.v new file mode 100644 index 0000000..d7aff13 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/mappings.v @@ -0,0 +1,170 @@ +module sourcemap + +import v.gen.js.sourcemap.vlq +import io + +struct Empty {} + +pub struct SourcePosition { + source_line u32 + source_column u32 +} + +type IndexNumber = u32 +type SourcePositionType = Empty | SourcePosition +type NameIndexType = Empty | IndexNumber + +struct GenPosition { + gen_line u32 + gen_column u32 +} + +struct MappingInput { + GenPosition + name string + source_position SourcePositionType +} + +struct Mapping { + GenPosition + sources_ind u32 + names_ind NameIndexType + source_position SourcePositionType +} + +struct Mappings { +mut: + sorted bool + last Mapping + values []Mapping +} + +fn new_mappings() Mappings { + return Mappings{ + last: Mapping{ + GenPosition: GenPosition{ + gen_column: 0 + gen_line: 0 + } + } + sorted: true + } +} + +// Add the given source mapping +fn (mut m Mappings) add_mapping(gen_line u32, gen_column u32, sources_ind u32, source_position SourcePositionType, names_ind NameIndexType) { + if !(gen_line > m.last.gen_line + || (gen_line == m.last.gen_line && gen_column >= m.last.gen_column)) { + m.sorted = false + } + m.values << Mapping{ + GenPosition: GenPosition{ + gen_line: gen_line + gen_column: gen_column + } + sources_ind: sources_ind + names_ind: names_ind + source_position: source_position + } +} + +// Returns the flat, sorted array of mappings. The mappings are sorted by generated position. + +fn (mut m Mappings) get_sorted_array() []Mapping { + if !m.sorted { + panic('not implemented') + } + return m.values +} + +fn (mut m Mappings) export_mappings(mut output io.Writer) ? { + mut previous_generated_line := u32(1) + mut previous_generated_column := u32(0) + mut previous_source_index := i64(0) + mut previous_source_line := i64(0) + mut previous_source_column := i64(0) + mut previous_name_index := i64(0) + + line_mappings := m.get_sorted_array() + len := line_mappings.len + for i := 0; i < len; i++ { + mapping := line_mappings[i] + + cloned_generated_line := mapping.gen_line + if cloned_generated_line > 0 { + // Write a ';' for each line between this and last line, way more efficient than storing empty lines or looping... + output.write(';'.repeat(int(cloned_generated_line - previous_generated_line)).bytes()) or { + panic('Writing vql failed!') + } + } + if cloned_generated_line != previous_generated_line { + previous_generated_column = 0 + previous_generated_line = cloned_generated_line + } else { + if i > 0 { + if !compare_by_generated_positions_inflated(mapping, line_mappings[i - 1]) { + continue + } + output.write(','.bytes()) or { panic('Writing vql failed!') } + } + } + + vlq.encode(i64(mapping.gen_column - previous_generated_column), mut &output) ? + previous_generated_column = mapping.gen_column + match mapping.source_position { + Empty {} + SourcePosition { + vlq.encode(i64(mapping.sources_ind - previous_source_index), mut &output) ? + previous_source_index = mapping.sources_ind + // lines are stored 0-based in SourceMap spec version 3 + vlq.encode(i64(mapping.source_position.source_line - 1 - previous_source_line), mut + output) ? + previous_source_line = mapping.source_position.source_line - 1 + vlq.encode(i64(mapping.source_position.source_column - previous_source_column), mut + output) ? + previous_source_column = mapping.source_position.source_column + + match mapping.names_ind { + Empty {} + IndexNumber { + vlq.encode(i64(mapping.names_ind - previous_name_index), mut &output) ? + previous_name_index = mapping.names_ind + } + } + } + } + } +} + +fn compare_by_generated_positions_inflated(mapping_a Mapping, mapping_b Mapping) bool { + if mapping_a.gen_line != mapping_b.gen_line { + return true + } + if mapping_a.gen_column != mapping_b.gen_column { + return true + } + + if mapping_a.sources_ind != mapping_b.sources_ind { + return true + } + + if mapping_a.source_position.type_name() == mapping_b.source_position.type_name() + && mapping_a.source_position is SourcePosition + && mapping_b.source_position is SourcePosition { + if mapping_a.source_position.source_line != mapping_b.source_position.source_line + || mapping_a.source_position.source_column != mapping_b.source_position.source_column { + return true + } + } else { + if mapping_a.source_position.type_name() != mapping_b.source_position.type_name() { + return true + } + } + + if mapping_a.names_ind.type_name() == mapping_b.names_ind.type_name() + && mapping_a.names_ind is IndexNumber && mapping_b.names_ind is IndexNumber { + return mapping_a.names_ind != mapping_b.names_ind + } else { + return mapping_a.names_ind.type_name() != mapping_b.names_ind.type_name() + } +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/sets.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/sets.v new file mode 100644 index 0000000..70f7482 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/sets.v @@ -0,0 +1,16 @@ +module sourcemap + +struct Sets { +mut: + value map[string]u32 +} + +// adds a new element to a Set if new and returns index position of new or existing element +fn (mut s Sets) add(element string) u32 { + index := s.value[element] or { + index := u32(s.value.len) + s.value[element] = index + return index + } + return index +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/source_map.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/source_map.v new file mode 100644 index 0000000..44f79d1 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/source_map.v @@ -0,0 +1,131 @@ +module sourcemap + +import io +import os +import x.json2 + +const ( + source_map_version = 3 +) + +type SourceMapJson = map[string]json2.Any + +struct SourceMap { +pub mut: + version int [json: version] + file string [json: file] + source_root string [json: source_root] + sources Sets [json: sources] + sources_content map[string]string + names Sets + mappings Mappings + sources_content_inline bool +} + +struct StringWriter { +pub mut: + bytes []byte +} + +pub fn new_sourcemap(file string, source_root string, sources_content_inline bool) SourceMap { + return SourceMap{ + version: sourcemap.source_map_version + file: file + source_root: source_root + mappings: new_mappings() + sources_content_inline: sources_content_inline + } +} + +// Add a single mapping from original source line and column to the generated source's line and column for this source map being created. +pub fn (mut sm SourceMap) add_mapping(source_name string, source_position SourcePositionType, gen_line u32, gen_column u32, name string) { + assert source_name.len != 0 + + sources_ind := sm.sources.add(source_name) + + names_ind := if name.len != 0 { + NameIndexType(IndexNumber(sm.names.add(name))) + } else { + NameIndexType(Empty{}) + } + sm.mappings.add_mapping(gen_line, gen_column, sources_ind, source_position, names_ind) +} + +// Add multiple mappings from the same source +pub fn (mut sm SourceMap) add_mapping_list(source_name string, mapping_list []MappingInput) ? { + assert source_name.len != 0 + + sources_ind := sm.sources.add(source_name) + + for mapping in mapping_list { + names_ind := if mapping.name.len != 0 { + NameIndexType(IndexNumber(sm.names.add(mapping.name))) + } else { + NameIndexType(Empty{}) + } + sm.mappings.add_mapping(mapping.gen_line, mapping.gen_column, sources_ind, mapping.source_position, + names_ind) + } +} + +// Set the source content for a source file. +pub fn (mut sm SourceMap) set_source_content(source_name string, source_content string) { + sm.sources_content[source_name] = source_content +} + +fn (mut sm SourceMap) export_mappings(mut writer io.Writer) { + sm.mappings.export_mappings(mut writer) or { panic('export failed') } +} + +fn (mut sm SourceMap) export_mappings_string() string { + mut output := StringWriter{} + + sm.mappings.export_mappings(mut output) or { panic('export failed') } + return output.bytes.bytestr() +} + +// create a JSON representing the sourcemap +// Sourcemap Specs http://sourcemaps.info/spec.html +pub fn (mut sm SourceMap) to_json() SourceMapJson { + mut source_map_json := map[string]json2.Any{} + source_map_json['version'] = sm.version + if sm.file != '' { + source_map_json['file'] = json2.Any(sm.file) + } + if sm.source_root != '' { + source_map_json['sourceRoot'] = json2.Any(sm.source_root) + } + mut sources_json := []json2.Any{} + mut sources_content_json := []json2.Any{} + for source_file, _ in sm.sources.value { + sources_json << source_file + if source_file in sm.sources_content { + sources_content_json << sm.sources_content[source_file] + } else { + if sm.sources_content_inline { + if source_file_content := os.read_file(source_file) { + sources_content_json << source_file_content + } else { + sources_content_json << json2.null + } + } else { + sources_content_json << json2.null + } + } + } + source_map_json['sources'] = json2.Any(sources_json) + source_map_json['sourcesContent'] = json2.Any(sources_content_json) + + mut names_json := []json2.Any{} + for name, _ in sm.names.value { + names_json << name + } + source_map_json['names'] = json2.Any(names_json) + source_map_json['mappings'] = sm.export_mappings_string() + return source_map_json +} + +fn (mut w StringWriter) write(buf []byte) ?int { + w.bytes << buf + return buf.len +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/source_map_generator.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/source_map_generator.v new file mode 100644 index 0000000..f943286 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/source_map_generator.v @@ -0,0 +1,46 @@ +module sourcemap + +struct V3 { + SourceMap +pub: + sections []Section [json: sections] +} + +struct Offset { +pub mut: + line int [json: line] + column int [json: column] +} + +struct Section { +pub mut: + offset Offset [json: offset] + source_map SourceMap [json: map] +} + +struct Generator { +mut: + file string + // source_root string + sections []Section +} + +pub fn generate_empty_map() &Generator { + return &Generator{} +} + +pub fn (mut g Generator) add_map(file string, source_root string, sources_content_inline bool, line_offset int, column_offset int) &SourceMap { + source_map := new_sourcemap(file, source_root, sources_content_inline) + + offset := Offset{ + line: line_offset + column: column_offset + } + + g.sections << Section{ + offset: offset + source_map: source_map + } + + return &source_map +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq.v new file mode 100644 index 0000000..7dfef05 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq.v @@ -0,0 +1,115 @@ +module vlq + +import io + +const ( + shift = byte(5) + mask = byte((1 << shift) - 1) + continued = byte(1 << shift) + max_i64 = u64(9223372036854775807) + + // index start is: byte - vlq.enc_char_special_plus + enc_index = [62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]! + + enc_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + + enc_char_start_au = 65 + enc_char_end_zu = 90 + enc_char_start_al = 97 + enc_char_end_zl = 122 + enc_char_start_zero = 48 + enc_char_end_nine = 57 + enc_char_special_plus = 43 + enc_char_special_slash = 47 +) + +[inline] +fn abs64(x i64) u64 { + return if x < 0 { u64(-x) } else { u64(x) } +} + +// Decode a single base64 digit. +[inline] +fn decode64(input byte) byte { + $if debug { + assert input >= vlq.enc_char_special_plus + assert input <= vlq.enc_char_end_zl + } + return byte(vlq.enc_index[input - vlq.enc_char_special_plus]) +} + +// Decode a single VLQ value from the input stream, returning the value. +// +// # Range +// +// Supports all numbers that can be represented by a sign bit and a 63 bit +// absolute value: `[-(2^63 - 1), 2^63 - 1]`. +// +// Note that `i64::MIN = -(2^63)` cannot be represented in that form, and this +// NOT IMPLEMENTED: function will return `Error::Overflowed` when attempting to decode it. +pub fn decode(mut input io.Reader) ?i64 { + mut buf := []byte{len: 1} + + mut accum := u64(0) + mut shifter := 0 + mut digit := byte(0) + + mut keep_going := true + for keep_going { + len := input.read(mut buf) or { return error('Unexpected EOF') } + if len == 0 { + return error('no content') + } + digit = decode64(buf[0]) + keep_going = (digit & vlq.continued) != 0 + + digit_value := u64(digit & vlq.mask) << u32(shifter) // TODO: check Overflow + + accum += digit_value + shifter += vlq.shift + } + + abs_value := accum / 2 + if abs_value > vlq.max_i64 { + return error('Overflow') + } + + // The low bit holds the sign. + return if (accum & 1) != 0 { (-i64(abs_value)) } else { i64(abs_value) } +} + +[inline] +fn encode64(input byte) byte { + $if debug { + assert input < 64 + } + return vlq.enc_table[input] +} + +// Encode a value as Base64 VLQ, sending it to the writer +pub fn encode(value i64, mut output io.Writer) ? { + signed := value < 0 + mut value_u64 := abs64(value) << 1 + if signed { + if value_u64 == 0 { + // Wrapped + value_u64 = vlq.max_i64 + 1 + } + value_u64 |= 1 + } + for { + mut digit := byte(value_u64) & vlq.mask + value_u64 >>= vlq.shift + if value_u64 > 0 { + digit |= vlq.continued + } + bytes := [encode64(digit)] + output.write(bytes) or { return error('Write failed') } + if value_u64 == 0 { + break + } + } +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_decode_test.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_decode_test.v new file mode 100644 index 0000000..fdb4acf --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_decode_test.v @@ -0,0 +1,52 @@ +module vlq + +import io + +struct TestReader { +pub: + bytes []byte +mut: + i int +} + +struct TestData { + decode_val string + expected i64 +} + +type TestDataList = []TestData + +fn test_decode_a() ? { + decode_values := [ + TestData{'A', 0}, + TestData{'C', 1}, + TestData{'D', -1}, + TestData{'2H', 123}, + TestData{'qxmvrH', 123456789}, + TestData{'+/////B', 1073741823} /* 2^30-1 */, + // TestData{'hgggggggggggI', 9_223_372_036_854_775_808} /* 2^63 */, + ] + + for _, test_data in decode_values { + mut input := make_test_reader(test_data.decode_val) + + res := decode(mut &input) ? + assert res == test_data.expected + } +} + +fn (mut b TestReader) read(mut buf []byte) ?int { + if !(b.i < b.bytes.len) { + return none + } + n := copy(buf, b.bytes[b.i..]) + b.i += n + return n +} + +fn make_test_reader(data string) io.Reader { + buf := &TestReader{ + bytes: data.bytes() + } + return io.new_buffered_reader(reader: buf) +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_encode_test.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_encode_test.v new file mode 100644 index 0000000..ad2db3b --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_encode_test.v @@ -0,0 +1,35 @@ +module vlq + +struct TestData { + expected string + data_val i64 +} + +struct TestWriter { +pub mut: + bytes []byte +} + +fn test_encode_a() ? { + decode_values := [ + TestData{'A', 0}, + TestData{'C', 1}, + TestData{'D', -1}, + TestData{'2H', 123}, + TestData{'qxmvrH', 123456789}, + TestData{'+/////B', 1073741823} /* 2^30-1 */, + // TestData{'hgggggggggggI', 9_223_372_036_854_775_808} /* 2^63 */, + ] + for _, test_data in decode_values { + mut output := TestWriter{} + + encode(test_data.data_val, mut &output) ? + // dump(output.bytes) + assert output.bytes == test_data.expected.bytes() + } +} + +fn (mut w TestWriter) write(buf []byte) ?int { + w.bytes << buf + return buf.len +} diff --git a/v_windows/v/old/vlib/v/gen/js/temp_fast_deep_equal.v b/v_windows/v/old/vlib/v/gen/js/temp_fast_deep_equal.v new file mode 100644 index 0000000..bca6e61 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/temp_fast_deep_equal.v @@ -0,0 +1,78 @@ +module js + +// TODO: Fix msvc bug that's preventing $embed_file('fast_deep_equal.js') +const ( + fast_deep_eq_fn = "// https://www.npmjs.com/package/fast-deep-equal - 3/3/2021 +const envHasBigInt64Array = typeof BigInt64Array !== 'undefined'; +function vEq(a, b) { + if (a === b) return true; + + if (a && b && typeof a == 'object' && typeof b == 'object') { + if (a.constructor !== b.constructor) return false; + // we want to convert all V types to JS for comparison. + if ('\$toJS' in a) + a = a.\$toJS(); + + if ('\$toJS' in b) + b = b.\$toJS(); + + var length, i, keys; + if (Array.isArray(a)) { + length = a.length; + if (length != b.length) return false; + for (i = length; i-- !== 0;) + if (!vEq(a[i], b[i])) return false; + return true; + } + + + if ((a instanceof Map) && (b instanceof Map)) { + if (a.size !== b.size) return false; + for (i of a.entries()) + if (!b.has(i[0])) return false; + for (i of a.entries()) + if (!vEq(i[1], b.get(i[0]))) return false; + return true; + } + + if ((a instanceof Set) && (b instanceof Set)) { + if (a.size !== b.size) return false; + for (i of a.entries()) + if (!b.has(i[0])) return false; + return true; + } + + if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) { + length = a.length; + if (length != b.length) return false; + for (i = length; i-- !== 0;) + if (a[i] !== b[i]) return false; + return true; + } + + + if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags; + if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf(); + if (a.toString !== Object.prototype.toString) return a.toString() === b.toString(); + + keys = Object.keys(a); + length = keys.length; + if (length !== Object.keys(b).length) return false; + + for (i = length; i-- !== 0;) + if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; + + for (i = length; i-- !== 0;) { + var key = keys[i]; + + if (!vEq(a[key], b[key])) return false; + } + + return true; + } + + // true if both NaN, false otherwise + return a!==a && b!==b; +}; +" +) diff --git a/v_windows/v/old/vlib/v/gen/js/tests/.gitignore b/v_windows/v/old/vlib/v/gen/js/tests/.gitignore new file mode 100644 index 0000000..4c43fe6 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/.gitignore @@ -0,0 +1 @@ +*.js
\ No newline at end of file diff --git a/v_windows/v/old/vlib/v/gen/js/tests/array.v b/v_windows/v/old/vlib/v/gen/js/tests/array.v new file mode 100644 index 0000000..f07e1b6 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/array.v @@ -0,0 +1,138 @@ +fn map_cb(s string) string { + return 'CB: $s' +} + +fn filter_cb(n int) bool { + return n < 4 +} + +fn variadic(args ...int) { + println(args) + println(args[0]) + println(args[1]) +} + +fn vararg_test() { + variadic(1, 2, 3) +} + +// TODO Remove `fn main` once vet supports scripts +fn main() { + vararg_test() + + arr1 := ['Hello', 'JS', 'Backend'] + mut arr2 := [1, 2, 3, 4, 5] + + // Array slices + slice1 := arr1[1..3] + slice2 := arr2[..3] + slice3 := arr2[3..] + + // Array indexes + idx1 := slice1[1] + arr2[0] = 1 + arr2[0 + 1] = 2 + println(arr2) + + // TODO: This does not work for now + // arr2[0..1] = arr2[3..4] + // println(arr2) + + // Array push operator + arr2 << 6 + arr2 << [7, 8, 9] + println(arr2) + println('\n\n') + + // String slices + mut slice4 := idx1[..4] + print('Back\t=> ') + println(slice4) // 'Back' + + // String indexes + idx2 := slice4[0] + print('66\t=> ') + println(idx2) + // TODO: + // slice4[3] = `c` + + // Maps + mut m := map[string]string{} + key := 'key' + m[key] = 'value' + val := m['key'] + print('value\t=> ') + println(val) + + // 'in' / '!in' + print('true\t=> ') + println('JS' in arr1) + print('false\t=> ') + println(3 !in arr2) + print('true\t=> ') + println('key' in m) + print('true\t=> ') + println('badkey' !in m) + print('true\t=> ') + println('o' in 'hello') + + // for in + for _ in arr1 {} + println('0 to 8\t=>') + for i, _ in arr2 { + println(i) + } + println('\n\n4 to 5\t=> ') + for _, v in slice3 { + println(v) + } + + println(int(1.5)) + + println('\n\n') + + // map + a := arr1.map('VAL: $it') + b := arr1.map(map_cb) + c := arr1.map(map_cb(it)) + d := arr1.map(fn (a string) string { + return 'ANON: $a' + }) + // I don't know when this would ever be used, + // but it's what the C backend does ¯\_(ツ)_/¯ + e := arr1.map(456) + + println(a) + println(b) + println(c) + println(d) + println(e) + + println('\n\n') + + // filter + aa := arr2.filter(it < 4) + bb := arr2.filter(filter_cb) + cc := arr2.filter(filter_cb(it)) + dd := arr2.filter(fn (a int) bool { + return a < 4 + }) + + println(aa) + println(bb) + println(cc) + println(dd) + + // fixed arrays: implemented as normal arrays + f1 := [1, 2, 3, 4, 5]! + mut f2 := [8]f32{} + f2[0] = f32(1.23) + f3 := ['foo', 'bar']! + f4 := [u64(0xffffffffffffffff), 0xdeadface]! + + println(' +$f1 +$f2 +$f3 +$f4') +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/auto_deref_args.v b/v_windows/v/old/vlib/v/gen/js/tests/auto_deref_args.v new file mode 100644 index 0000000..a9775e7 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/auto_deref_args.v @@ -0,0 +1,13 @@ +struct Foo { + field &int +} + +fn bar(x int) { + println(x) +} + +fn main() { + x := 4 + foo := Foo{&x} + bar(foo.field) +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/enum.v b/v_windows/v/old/vlib/v/gen/js/tests/enum.v new file mode 100644 index 0000000..2d1bfc0 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/enum.v @@ -0,0 +1,19 @@ +import v.gen.js.tests.hello + +enum Test { + foo = 2 + bar = 5 + baz +} + +// TODO Remove `fn main` once vet supports scripts +fn main() { + mut a := hello.Ccc.a + a = .b + a = .c + println(a) + + mut b := Test.foo + b = .bar + println(b) +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/hello/hello.js.v b/v_windows/v/old/vlib/v/gen/js/tests/hello/hello.js.v new file mode 100644 index 0000000..0b3fe40 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/hello/hello.js.v @@ -0,0 +1,5 @@ +module hello + +pub fn raw_js_log() { + #console.log('hello') +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/hello/hello.v b/v_windows/v/old/vlib/v/gen/js/tests/hello/hello.v new file mode 100644 index 0000000..9b75511 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/hello/hello.v @@ -0,0 +1,33 @@ +module hello + +import v.gen.js.tests.hello.hello1 + +pub const ( + hello = 'Hello' +) + +pub struct Aaa { +pub mut: + foo string +} + +pub fn (mut a Aaa) update(s string) { + a.foo = s +} + +struct Bbb {} + +pub enum Ccc { + a + b = 5 + c +} + +pub fn debugger() string { + v := Bbb{} + return hello.hello +} + +pub fn excited() string { + return '$hello1.nested() $debugger()!' +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/hello/hello1/hello1.v b/v_windows/v/old/vlib/v/gen/js/tests/hello/hello1/hello1.v new file mode 100644 index 0000000..030e704 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/hello/hello1/hello1.v @@ -0,0 +1,5 @@ +module hello1 + +pub fn nested() string { + return 'Nested' +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/interface.v b/v_windows/v/old/vlib/v/gen/js/tests/interface.v new file mode 100644 index 0000000..a43cadf --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/interface.v @@ -0,0 +1,47 @@ +struct Dog { + name string + age int +} + +struct Cat { + name string + age int +} + +interface Animal { + say(s string) + greet() int +} + +fn (d Dog) say(s string) { + println('Dog $d.name: "$s"') +} + +fn (c Cat) say(s string) { + println('Cat $c.name: "$s"') +} + +fn (d Dog) greet() int { + d.say('Hello!') + return d.age +} + +fn (c Cat) greet() int { + c.say('Hello!') + return c.age +} + +fn use(a Animal) { + if a is Dog { + println('dog') + } else if a is Cat { + println('cat') + } else { + println('its a bug!') + } +} + +fn main() { + use(Dog{'Doggo', 5}) + use(Cat{'Nyancat', 6}) +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/interp.v b/v_windows/v/old/vlib/v/gen/js/tests/interp.v new file mode 100644 index 0000000..5b00aa8 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/interp.v @@ -0,0 +1,188 @@ +fn test_fn(s1 string, s2 string) { + print(if s1 == s2 { 'true' } else { 'false' }) + print('\t=> ') + println('"$s1", "$s2"') +} + +fn simple_string_interpolation() { + a := 'Hello' + b := 'World' + res := '$a $b' + test_fn(res, 'Hello World') +} + +fn mixed_string_interpolation() { + num := 7 + str := 'abc' + s1 := 'number=$num' + test_fn(s1, 'number=7') + s2 := 'string=$str' + test_fn(s2, 'string=abc') + s3 := 'a: $num | b: $str' + test_fn(s3, 'a: 7 | b: abc') +} + +fn formatted_string_interpolation() { + x := 'abc' + axb := 'a:$x:b' + test_fn(axb, 'a:abc:b') + x_10 := 'a:${x:10s}:b' + x10_ := 'a:${x:-10s}:b' + test_fn(x_10, 'a: abc:b') + test_fn(x10_, 'a:abc :b') + i := 23 + si_right := '${i:10d}' + si__left := '${i:-10d}' + test_fn(si_right, ' 23') + test_fn(si__left, '23 ') +} + +/* +excape_dollar_in_string() +fn excape_dollar_in_string() { + i := 42 + test_fn('($i)', '(42)') + println('(\$i)'.contains('i') && !'(\$i)'.contains('42')) + println(!'(\\$i)'.contains('i') && '(\\$i)'.contains('42') && '(\\$i)'.contains('\\')) + println('(\\\$i)'.contains('i') && !'(\\\$i)'.contains('42') && '(\\$i)'.contains('\\')) + println(!'(\\\\$i)'.contains('i') && '(\\\\$i)'.contains('42') && '(\\\\$i)'.contains('\\\\')) + test_fn('(${i})', '(42)') + println('(\${i})'.contains('i') && !'(\${i})'.contains('42')) + println(!'(\\${i})'.contains('i') && '(\\${i})'.contains('42') && '(\\${i})'.contains('\\')) + println('(\\\${i})'.contains('i') && !'(\\\${i})'.contains('42') && '(\\${i})'.contains('\\')) + println(!'(\\\\${i})'.contains('i') && '(\\\\${i})'.contains('42') && '(\\\\${i})'.contains('\\\\')) + test_fn(i, 42) +} +*/ + +fn implicit_str() { + i := 42 + test_fn('int $i', 'int 42') + test_fn('$i', '42') + check := '$i' == '42' + // println(check) + text := '$i' + '42' + test_fn(text, '4242') +} + +fn string_interpolation_percent_escaping() { + test := 'hello' + hello := 'world' + x := '%.*s$hello$test |${hello:-30s}|' + test_fn(x, '%.*sworldhello |world |') +} + +fn string_interpolation_string_prefix() { + // `r`, `c` and `js` are also used as a string prefix. + r := 'r' + rr := '$r$r' + test_fn(rr, 'rr') + c := 'c' + cc := '$c$c' + test_fn(cc, 'cc') + js := 'js' + jsjs := '$js$js' + test_fn(jsjs, 'jsjs') +} + +fn interpolation_string_prefix_expr() { + r := 1 + c := 2 + js := 1 + test_fn('>${3 + r}<', '>4<') + test_fn('${r == js} $js', 'true 1') + test_fn('>${js + c} ${js + r == c}<', '>3 true<') +} + +/* +inttypes_string_interpolation() +fn inttypes_string_interpolation() { + c := i8(-103) + uc := byte(217) + uc2 := byte(13) + s := i16(-23456) + us := u16(54321) + i := -1622999040 + ui := u32(3421958087) + vp := voidptr(ui) + bp := byteptr(15541149836) + l := i64(-7694555558525237396) + ul := u64(17234006112912956370) + test_fn('$s $us', '-23456 54321') + test_fn('$ui $i', '3421958087 -1622999040') + test_fn('$l $ul', '-7694555558525237396 17234006112912956370') + test_fn('>${s:11}:${us:-13}<', '> -23456:54321 <') + test_fn('0x${ul:-19x}:${l:22d}', '0xef2b7d4001165bd2 : -7694555558525237396') + test_fn('${c:5}${uc:-7}x', ' -103217 x') + test_fn('${c:x}:${uc:x}:${uc2:02X}', '99:d9:0D') + test_fn('${s:X}:${us:x}:${u16(uc):04x}', 'A460:d431:00d9') + test_fn('${i:x}:${ui:X}:${int(s):x}', '9f430000:CBF6EFC7:ffffa460') + test_fn('${l:x}:${ul:X}', '9537727cad98876c:EF2B7D4001165BD2') + // default pointer format is platform dependent, so try a few + println("platform pointer format: '${vp:p}:$bp'") + test_fn('${vp:p}:$bp', '0xcbf6efc7:0x39e53208c' || + '${vp:p}:$bp' == 'CBF6EFC7:39E53208C' || + '${vp:p}:$bp' == 'cbf6efc7:39e53208c' || + '${vp:p}:$bp' == '00000000CBF6EFC7:000000039E53208C') +} +*/ + +fn utf8_string_interpolation() { + a := 'à-côté' + st := 'Sträßle' + m := '10€' + test_fn('$a $st $m', 'à-côté Sträßle 10€') + zz := '>${a:10}< >${st:-8}< >${m:5}<-' + zz_expected := '> à-côté< >Sträßle < > 10€<-' + // println(' zz: $zz') + // println('zz_expected: $zz_expected') + test_fn(zz, zz_expected) + // e := '\u20AC' // Eurosign doesn' work with MSVC and tcc + e := '€' + test_fn('100.00 $e', '100.00 €') + m2 := 'Москва́' // cyrillic а́: combination of U+0430 and U+0301, UTF-8: d0 b0 cc 81 + d := 'Antonín Dvořák' // latin á: U+00E1, UTF-8: c3 a1 + test_fn(':${m2:7}:${d:-15}:', ': Москва́:Antonín Dvořák :') + g := 'Πελοπόννησος' + test_fn('>${g:-13}<', '>Πελοπόννησος <') +} + +struct Sss { + v1 int + v2 f64 +} + +fn (s Sss) str() string { + return '[$s.v1, ${s.v2:.3f}]' +} + +fn string_interpolation_str_evaluation() { + mut x := Sss{17, 13.455893} + test_fn('$x', '[17, 13.456]') +} + +/* +string_interpolation_with_negative_format_width_should_compile_and_run_without_segfaulting() +fn string_interpolation_with_negative_format_width_should_compile_and_run_without_segfaulting() { + // discovered during debugging VLS + i := 3 + input := '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' + println('---------------------------------------------------------------------------------------------') + println('+60 ${i:10} | input.len: ${input.len:10} | ${input.bytes().hex():60} | $input') + println('-60 ${i:10} | input.len: ${input.len:10} | ${input.bytes().hex():-60} | $input') + println('---------------------------------------------------------------------------------------------') + println(true) +} +*/ + +fn main() { + simple_string_interpolation() + mixed_string_interpolation() + formatted_string_interpolation() + implicit_str() + string_interpolation_percent_escaping() + string_interpolation_string_prefix() + interpolation_string_prefix_expr() + utf8_string_interpolation() + string_interpolation_str_evaluation() +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/js.v b/v_windows/v/old/vlib/v/gen/js/tests/js.v new file mode 100644 index 0000000..ee7ba08 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/js.v @@ -0,0 +1,141 @@ +import v.gen.js.tests.hello as hl +import v.gen.js.tests.hello.hello1 as hl1 + +const ( + i_am_a_const = 21214 + super = 'amazing keyword' +) + +struct Foo { +mut: + a hl.Aaa +} + +struct Companies { + google int + amazon bool + yahoo string +} + +enum POSITION { + go_back + dont_go_back +} + +fn class(extends string, instanceof int) { + delete := instanceof + _ = delete +} + +fn main() { + println('Hello from V.js!') + println(JS.Math.atan2(1, 0)) + println(JS.eval("console.log('Hello!')")) + mut a := 1 + a *= 2 + a += 3 + println(a) + mut b := hl.Aaa{} + b.update('an update') + println(b) + mut c := Foo{hl.Aaa{}} + c.a.update('another update') + println(c) + println('int(1.5) == "${int(1.5)}"') + d := int(10) + f32(127) + println('typeof (int + f32) == "${typeof(d)}"') + _ = 'done' + { + _ = 'block' + } + _ = POSITION.go_back + _ = hl.Ccc.a + debugger := 'JS keywords' + // TODO: Implement interpolation + await := '$super: $debugger' + mut finally := 'implemented' + println('$await $finally') + dun := i_am_a_const * 20 + 2 + dunn := hl.hello // External constant + _ = hl1.nested() + for i := 0; i < 10; i++ { + } + for i, x in 'hello' { + } + mut evens := []int{} + for x in 1 .. 10 { + y := error_if_even(x) or { x + 1 } + evens << y + } + println(evens) + arr := [1, 2, 3, 4, 5] + for i in arr { + } + ma := map{ + 'str': 'done' + 'ddo': 'baba' + } + // panic('foo') + for m, n in ma { + iss := m + } + go async(0, 'hello') + fn_in_var := fn (number int) { + println('number: $number') + } + hl.debugger() + anon_consumer(hl.excited(), fn (message string) { + println(message) + }) + hl.raw_js_log() + propagation() or { println(err) } +} + +fn anon_consumer(greeting string, anon fn (string)) { + anon(greeting) +} + +fn async(num int, def string) { +} + +[deprecated; inline] +fn hello(game_on int, dummy ...string) (int, int) { + defer { + do := 'not' + } + for dd in dummy { + l := dd + } + return game_on + 2, 221 +} + +fn (it Companies) method() int { + ss := Companies{ + google: 2 + amazon: true + yahoo: 'hello' + } + a, b := hello(2, 'google', 'not google') + glue := if a > 2 { + 'more_glue' + } else if a > 5 { + 'more glueee' + } else { + 'less glue' + } + if a != 2 { + } + return 0 +} + +fn error_if_even(num int) ?int { + if num % 2 == 0 { + return error('number is even') + } + return num +} + +fn propagation() ? { + println('Propagation test:') + return error('"Task failed successfully" - Windows XP') +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/life.v b/v_windows/v/old/vlib/v/gen/js/tests/life.v new file mode 100644 index 0000000..a89da5a --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/life.v @@ -0,0 +1,98 @@ +fn clear() { + JS.console.clear() +} + +const ( + w = 30 + h = 30 +) + +fn get(game [][]bool, x int, y int) bool { + if y < 0 || x < 0 { + return false + } + if y >= h || x >= w { + return false + } + + return game[y][x] +} + +fn neighbours(game [][]bool, x int, y int) int { + mut count := 0 + if get(game, x - 1, y - 1) { + count++ + } + if get(game, x, y - 1) { + count++ + } + if get(game, x + 1, y - 1) { + count++ + } + if get(game, x - 1, y) { + count++ + } + if get(game, x + 1, y) { + count++ + } + if get(game, x - 1, y + 1) { + count++ + } + if get(game, x, y + 1) { + count++ + } + if get(game, x + 1, y + 1) { + count++ + } + return count +} + +fn step(game [][]bool) [][]bool { + mut new_game := [][]bool{} + for y, row in game { + mut new_row := []bool{} + new_game[y] = new_row + for x, cell in row { + count := neighbours(game, x, y) + new_row[x] = (cell && count in [2, 3]) || count == 3 + } + } + return new_game +} + +fn row_str(row []bool) string { + mut str := '' + for cell in row { + if cell { + str += '◼ ' + } else { + str += '◻ ' + } + } + return str +} + +fn show(game [][]bool) { + clear() + for row in game { + println(row_str(row)) + } +} + +// TODO Remove `fn main` once vet supports scripts +fn main() { + mut game := [][]bool{len: h, init: []bool{len: w}} + + game[11][15] = true + game[11][16] = true + game[12][16] = true + game[10][21] = true + game[12][20] = true + game[12][21] = true + game[12][22] = true + + JS.setInterval(fn () { + show(game) + game = step(game) + }, 500) +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/optional.v b/v_windows/v/old/vlib/v/gen/js/tests/optional.v new file mode 100644 index 0000000..6d52eac --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/optional.v @@ -0,0 +1,33 @@ +module main + +fn main() { + try_propagation() or { println('captured: $err') } +} + +fn try_propagation() ? { + try_numbers() ? +} + +fn try_numbers() ? { + for x in 1 .. 10 { + y := error_if_even(x) or { x + 1 } + println('$x rounded to $y') + error_if_prime(y) ? + } +} + +fn error_if_even(num int) ?int { + if num % 2 == 0 { + return error('number is even') + } + return num +} + +fn error_if_prime(num int) ?int { + for i in 2 .. num { + if num % i == 0 { + return error('$num is prime') + } + } + return num +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/simple.v b/v_windows/v/old/vlib/v/gen/js/tests/simple.v new file mode 100644 index 0000000..ec46286 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/simple.v @@ -0,0 +1,5 @@ +module main + +fn main() { + println('hello world') +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/simple_sourcemap.v b/v_windows/v/old/vlib/v/gen/js/tests/simple_sourcemap.v new file mode 100644 index 0000000..0dd8a64 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/simple_sourcemap.v @@ -0,0 +1,23 @@ +module main + +fn main() { + e := JS.Error{} + s := e.stack + node_version := js_node_process().version + node_main := get_node_main_version(node_version) + if node_main >= 12 { + if s.contains('simple_sourcemap.v:') { + panic('node found no source map!') + } else { + println('source map is working') + } + } else { + println('skiping test! node version >=12.12.0 required. Current Version is $node_version') + } +} + +fn get_node_main_version(str string) int { + a := str.slice(1, int(str.len)) + b := a.split('.') + return b[0].int() +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/struct.v b/v_windows/v/old/vlib/v/gen/js/tests/struct.v new file mode 100644 index 0000000..306d46a --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/struct.v @@ -0,0 +1,40 @@ +module main + +struct Int { +mut: + value int + test map[string]int + hello []int +} + +fn (mut i Int) add(value int) { + i.value += value +} + +fn (i Int) get() int { + return i.value +} + +struct Config { + foo int + bar string +} + +fn use_config(c Config) { +} + +fn main() { + mut a := Int{ + value: 10 + } + a.add(5) + println(a) // 15 + mut b := Int{} + b.add(10) + println(b.get()) // 10 + use_config(Config{2, 'bar'}) + use_config( + foo: 2 + bar: 'bar' + ) +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/array.out b/v_windows/v/old/vlib/v/gen/js/tests/testdata/array.out new file mode 100644 index 0000000..b375fcb --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/array.out @@ -0,0 +1,231 @@ +3 +1 +2 +4 +255 +256 +2 +4 +131 +4 +1 +2 +3 +5 +4 +4 +[1,5,2,3,4] +5 +4 +5 +[1,5,2,3,4] +[5,2,3,4] +4 +[5,3,4] +3 +[5,3] +2 +[2.5,3.25,4.5,5.75] +true +true +true +true +true +true +true +true +true +true +true +1 +2 +3 +10000 +234 +0 +1 +0 +1 +3 +[1,3] +3 +2 +3 +4 +1 +4 +5 +2 +5 +0 +1 +1.1 +[1,2,3,4] +[1,5,6,2,3,4] +0 +1 +1 +0 +1 +1.1 +[1,2,3,4] +[5,6,1,2,3,4] +5 +true +1.1 +1.1 +1.1 +-123 +-123 +-123 +123 +123 +123 +1.1 +1.1 +1.1 +1 +2 +1 +2 +1 +abc +1 +abc +0 +abc +2 +3 +2 +3 +1 +2 +1 +2 +2 +1 +4 +6 +1 +4 +6 +[4,3,2,1] +true +true +true +true +true +true +true +0 +[0,0,0,0] +[0,7,0,0] +0 +[2,4,6,8,10,12,14,16,18,20] +[2,4,6,8,10,12,14,16,18,20] +[2,4,6,8,10] +2 +[1,2] +0 +1 +-1 +0 +3 +-1 +0 +2 +-1 +1 +2 +-1 +2 +3 +1 +3 +6 +true +true +true +true +true +true +true +true +true +0 +0 +0 +0 +0 +[2,4,6] +["is","awesome"] +[2,3,4,6,8,9,10] +[4,5,6] +[5,10] +[2,4] +[2,4] +[1,2,3,4,5,6] +["v","is","awesome"] +[0,0,0,0,0,0] +0 +[10,20,30,40,50,60] +[1,4,9,16,25,36] +["1","2","3","4","5","6"] +[false,true,false,true,false,true] +["V","IS","AWESOME"] +[false,false,true] +[true,true,false] +[7,7,7] +[1,4,9,16,25,36] +[3,4,5,6,7,8] +[3,9,4,6,12,7] +[] +[true,true,true,true,true,true] +["1a","2a","3a","4a","5a","6a"] +[2,3,4,5,6,7] +[2,3,8] +["1v","2is","3awesome"] +[1,4,9,16,25,36] +[1,25,100] +[1,2,3,4,5,6] +["v","is","awesome"] +[2,3,4] +[2,3,4] +[3,4,5] +[2,3,4] +["1","2","3"] +["1","2","3"] +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +["1","3","5","hi"] +[-3,7,42,67,108] +["a","b","c","d","e","f"] +0 +1 +79 diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/array.v b/v_windows/v/old/vlib/v/gen/js/tests/testdata/array.v new file mode 100644 index 0000000..8ef56bc --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/array.v @@ -0,0 +1,771 @@ +struct Chunk { + val string +} + +struct Kkk { + q []Chunk +} + +const ( + c_n = 5 +) + +struct User { + age int + name string +} + +fn map_test_helper_1(i int) int { + return i * i +} + +fn map_test_helper_2(i int, b string) int { + return i + b.len +} + +fn map_test_helper_3(i int, b []string) int { + return i + b.map(it.len)[i % b.len] +} + +fn filter_test_helper_1(a int) bool { + return a > 3 +} + +fn sum(prev int, curr int) int { + return prev + curr +} + +fn sub(prev int, curr int) int { + return prev - curr +} + +struct Foooj { + a [5]int // c_n +} + +fn double_up(mut a []int) { + for i := 0; i < a.len; i++ { + a[i] = a[i] * 2 + } +} + +fn double_up_v2(mut a []int) { + for i, _ in a { + a[i] = a[i] * 2 // or val*2, doesn't matter + } +} + +fn modify(mut numbers []int) { + numbers[0] = 777 +} + +fn main() { + { + // test pointer + mut arr := []&int{} + a := 1 + b := 2 + c := 3 + arr << &a + arr << &b + arr << &c + assert *arr[0] == 1 + arr[1] = &c + assert *arr[1] == 3 + mut d_arr := [arr] // [][]&int + d_arr << arr + println(*d_arr[0][1]) // 3 + println(*d_arr[1][0]) // 1 + } + { + // test assign + mut arr := [2, 4, 8, 16, 32, 64, 128] + arr[0] = 2 + arr[1] &= 255 + arr[2] |= 255 + arr[3] <<= 4 + arr[4] >>= 4 + arr[5] %= 5 + arr[6] ^= 3 + println(arr[0]) + println(arr[1]) + println(arr[2]) + println(arr[3]) + println(arr[4]) + println(arr[5]) + println(arr[6]) + } + { + // test ints + mut a := [1, 5, 2, 3] + println(a.len) // 4 + println(a[0]) + println(a[2]) + println(a.last()) + + a << 4 + println(a.len) + println(a[4]) + println(a.last()) + + s := a.str() + println(s) + println(a[1]) + println(a.last()) + } + { + // test deleting + mut a := [1, 5, 2, 3, 4] + + println(a.len) + println(a.str()) + + a.delete(0) + + println(a.str()) + println(a.len) // 4 + + a.delete(1) + + println(a.str()) + println(a.len) + a.delete(a.len - 1) + + println(a.str()) + println(a.len) + } + { + // test slice delete + mut a := [1.5, 2.5, 3.25, 4.5, 5.75] + b := a[2..4] + a.delete(0) + // assert a == [2.5, 3.25, 4.5, 5.75] + // assert b == [3.25, 4.5] + println(a) + println(a == [2.5, 3.25, 4.5, 5.75]) + println(b == [3.25, 4.5]) + a = [3.75, 4.25, -1.5, 2.25, 6.0] + c := a[..3] + a.delete(2) + println(a == [3.75, 4.25, 2.25, 6.0]) + println(c == [3.75, 4.25, -1.5]) + } + { + // test delete many + mut a := [1, 2, 3, 4, 5, 6, 7, 8, 9] + b := a[2..6] + a.delete_many(4, 3) + println(a == [1, 2, 3, 4, 8, 9]) + println(b == [3, 4, 5, 6]) + + c := a[..a.len] + a.delete_many(2, 0) // this should just clone + a[1] = 17 + + println(a == [1, 17, 3, 4, 8, 9]) + println(c == [1, 2, 3, 4, 8, 9]) + a.delete_many(0, a.len) + println(a == []int{}) + } + { + // test short + a := [1, 2, 3] + println(a.len == 3) + println(a.cap == 3) + println(a[0]) + println(a[1]) + println(a[2]) + } + { + // test large + mut a := [0].repeat(0) + for i in 0 .. 10000 { + a << i + } + println(a.len) + println(a[234]) + } + { + // test empty + mut chunks := []Chunk{} + a := Chunk{} + println(chunks.len) + chunks << a + println(chunks.len) + chunks = [] + println(chunks.len) + chunks << a + println(chunks.len) + } + { + // test push + mut a := []int{} + a << 1 + a << 3 + println(a[1]) + println(a.str()) + } + { + // test insert + mut a := [1, 2] + a.insert(0, 3) + println(a[0]) + println(a[2]) + println(a.len) + a.insert(1, 4) + println(a[1]) + println(a[2]) + println(a.len) + a.insert(4, 5) + println(a[4]) + println(a[3]) + println(a.len) + mut b := []f64{} + println(b.len) + b.insert(0, f64(1.1)) + println(b.len) + println(b[0]) + } + { + // test insert many + mut a := [3, 4] + a.insert(0, [1, 2]) + println(a) + + b := [5, 6] + a.insert(1, b) + println(a) + } + { + // test prepend + mut a := []int{} + println(a.len) + a.prepend(1) + println(a.len) + println(a[0]) + mut b := []f64{} + + println(b.len) + + b.prepend(f64(1.1)) + + println(b.len) + + println(b[0]) + } + { + // test prepend many + mut a := [3, 4] + a.prepend([1, 2]) + println(a) + b := [5, 6] + a.prepend(b) + println(a) + } + { + // test repeat + { + a := [0].repeat(5) + println(a.len) + println(a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0 && a[4] == 0) + } + { + a := [1.1].repeat(10) + println(a[0]) + println(a[5]) + println(a[9]) + } + { + a := [i64(-123)].repeat(10) + println(a[0]) + println(a[5]) + println(a[9]) + } + { + a := [u64(123)].repeat(10) + println(a[0]) + println(a[5]) + println(a[9]) + } + { + a := [1.1].repeat(10) + println(a[0]) + println(a[5]) + println(a[9]) + } + { + a := [1, 2].repeat(2) + println(a[0]) + println(a[1]) + println(a[2]) + println(a[3]) + } + { + a := ['1', 'abc'].repeat(2) + println(a[0]) + println(a[1]) + println(a[2]) + println(a[3]) + } + { + mut a := ['1', 'abc'].repeat(0) + println(a.len) + a << 'abc' + println(a[0]) + } + } + { + // todo(playX): deep repeat does not yet work. + /* + // test deep repeat + mut a3 := [[[1, 1], [2, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]] + r := a3.repeat(3) + a3[1][1][0] = 17 + print(r) + assert r == [ + [[1, 1], [2, 2], [3, 3]], + [[4, 4], [5, 5], [6, 6]], + [[1, 1], [2, 2], [3, 3]], + [[4, 4], [5, 5], [6, 6]], + [[1, 1], [2, 2], [3, 3]], + [[4, 4], [5, 5], [6, 6]], + ] + assert a3 == [[[1, 1], [2, 2], [3, 3]], [[4, 4], [17, 5], + [6, 6], + ]] + */ + } + { + // test right + a := [1, 2, 3, 4] + c := a[1..a.len] + d := a[1..] + println(c[0]) + println(c[1]) + println(d[0]) + println(d[1]) + } + { + // test left + a := [1, 2, 3] + c := a[0..2] + d := a[..2] + println(c[0]) + println(c[1]) + println(d[0]) + println(d[1]) + } + { + // test slice + a := [1, 2, 3, 4] + b := a[2..4] + println(b.len) + println(a[1..2].len) + println(a.len) + } + { + // test push many + mut a := [1, 2, 3] + b := [4, 5, 6] + a << b + println(a.len) + println(a[0]) + println(a[3]) + println(a[5]) + } + { + // test reverse + a := [1, 2, 3, 4] + b := ['test', 'array', 'reverse'] + c := a.reverse() + println(c) + d := b.reverse() + for i, _ in c { + println(c[i] == a[a.len - i - 1]) + } + for i, _ in d { + println(d[i] == b[b.len - i - 1]) + } + e := []int{} + f := e.reverse() + println(f.len) + } + { + // test fixed + mut nums := [4]int{} + // x := nums[1..3] + // assert x.len == 2 + + println(nums) + nums[1] = 7 + println(nums) + nums2 := [5]int{} // c_n + println(nums2[c_n - 1]) + } + { + // test mut slice + /* + todo(playX): slices do not work yet. We have to implement custom wrapper for them. + mut n := [1, 2, 3] + // modify(mut n) + modify(mut n[..2]) + assert n[0] == 777 + modify(mut n[2..]) + assert n[2] == 777 + println(n) + */ + } + { + // test mut arg + mut arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + double_up(mut arr) + println(arr.str()) + arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + double_up_v2(mut arr) + println(arr.str()) + } + { + // test doubling + mut nums := [1, 2, 3, 4, 5] + for i in 0 .. nums.len { + nums[i] *= 2 + } + println(nums.str()) + } + { + // test single element + + mut a := [1] + a << 2 + println(a.len) + assert a[0] == 1 + assert a[1] == 2 + println(a) + } + { + // test find index + + // string + a := ['v', 'is', 'great'] + println(a.index('v')) + println(a.index('is')) + println(a.index('gre')) + // int + b := [1, 2, 3, 4] + println(b.index(1)) + println(b.index(4)) + println(b.index(5)) + // byte + c := [0x22, 0x33, 0x55] + println(c.index(0x22)) + println(c.index(0x55)) + println(c.index(0x99)) + // char + d := [`a`, `b`, `c`] + println(d.index(`b`)) + println(d.index(`c`)) + println(d.index(`u`)) + } + { + // test multi + + a := [[1, 2, 3], [4, 5, 6]] + println(a.len) + println(a[0].len) + println(a[0][0]) + println(a[0][2]) + println(a[1][2]) + } + { + // test in + a := [1, 2, 3] + println(1 in a) + println(2 in a) + println(3 in a) + println(!(4 in a)) + println(!(0 in a)) + println(0 !in a) + println(4 !in a) + b := [1, 4, 0] + c := [3, 6, 2, 0] + println(0 in b) + println(0 in c) + } + { + // test reduce + a := [1, 2, 3, 4, 5] + b := a.reduce(sum, 0) + c := a.reduce(sum, 5) + d := a.reduce(sum, -1) + println(b) + println(c) + println(d) + e := [1, 2, 3] + f := e.reduce(sub, 0) + g := e.reduce(sub, -1) + println(f) + println(g) + } + { + a := [1, 2, 3, 4, 5, 6] + b := a.filter(it % 2 == 0) + println(b) + + c := ['v', 'is', 'awesome'] + d := c.filter(it.len > 1) + println(d) + assert d[0] == 'is' + assert d[1] == 'awesome' + //////// + arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + println(arr.filter(it % 2 == 0 || it % 3 == 0)) + + mut mut_arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + mut_arr = mut_arr.filter(it < 4) + assert mut_arr.len == 3 + println(a.filter(filter_test_helper_1)) + println([1, 5, 10].filter(filter_test_helper_1)) + } + { + // test anon fn filter + filter_num := fn (i int) bool { + return i % 2 == 0 + } + println([1, 2, 3, 4, 5].filter(filter_num)) + } + { + a := [1, 2, 3, 4].filter(fn (i int) bool { + return i % 2 == 0 + }) + println(a) + } + { + // test map + nums := [1, 2, 3, 4, 5, 6] + strs := ['v', 'is', 'awesome'] + // assert nums.map() == <error> + // assert nums.map(it, 'excessive') == <error> + // identity + println(nums.map(it)) + println(strs.map(it)) + println(nums.map(it - it)) + println(nums.map(it - it)[0]) + // type switch + println(nums.map(it * 10)) + println(nums.map(it * it)) + println(nums.map('$it')) + println(nums.map(it % 2 == 0)) + println(strs.map(it.to_upper())) + println(strs.map(it == 'awesome')) + println(strs.map(it.len in nums)) + println(strs.map(int(7))) + // external func + println(nums.map(map_test_helper_1(it))) + println(nums.map(map_test_helper_2(it, 'bb'))) + println(nums.map(map_test_helper_3(it, strs))) + // empty array as input + println([]int{len: 0}.map(it * 2)) + // nested maps (where it is of same type) + println(nums.map(strs.map(int(7)) == [7, 7, 7])) + println(nums.map('$it' + strs.map('a')[0])) + // assert nums.map(it + strs.map(int(7))[0]) == [8, 9, 10, 11, 12, 13] + println(nums.map(it + strs.map(it.len)[0])) + println(strs.map(it.len + strs.map(it.len)[0])) + // nested (different it types) + // todo(playX): this one produces invalid JS code. + // assert strs.map(it[nums.map(it - it)[0]]) == [byte(`v`), `i`, `a`] + println(nums[0..3].map('$it' + strs.map(it)[it - 1])) + println(nums.map(map_test_helper_1)) + println([1, 5, 10].map(map_test_helper_1)) + println(nums) + println(strs) + } + { + // test anon fn map + add_num := fn (i int) int { + return i + 1 + } + println([1, 2, 3].map(add_num)) + } + { + // test multi anon fn map + a := [1, 2, 3].map(fn (i int) int { + return i + 1 + }) + b := [1, 2, 3].map(fn (i int) int { + return i + 2 + }) + println(a) + println(b) + } + { + // test anon fn arg map + a := [1, 2, 3].map(fn (i int) int { + return i + 1 + }) + println(a) + } + { + // test anon fn arg different type map + i_to_str := fn (i int) string { + return i.str() + } + a := [1, 2, 3].map(i_to_str) + println(a) + } + { + // test anon fn inline different type map + a := [1, 2, 3].map(fn (i int) string { + return i.str() + }) + println(a) + } + { + // test array str + // todo(playX): JS array formatting should match what default builtin impl has. + /* + numbers := [1, 2, 3] + assert numbers == [1, 2, 3] + numbers2 := [numbers, [4, 5, 6]] // dup str() bug + println(numbers2) + assert true + assert numbers.str() == '[1, 2, 3]' + */ + } + { + // test eq + println([5, 6, 7] != [6, 7]) + println([`a`, `b`] == [`a`, `b`]) + println([User{ + age: 22 + name: 'bob' + }] == [User{ + age: 22 + name: 'bob' + }]) + // todo(playX): map cmp does not work yet + /* + assert [map{ + 'bob': 22 + }, map{ + 'tom': 33 + }] == [map{ + 'bob': 22 + }, map{ + 'tom': 33 + }]*/ + println([[1, 2, 3], [4]] == [[1, 2, 3], [4]]) + } + { + // test fixed array eq + a1 := [1, 2, 3]! + println(a1 == [1, 2, 3]!) + println(a1 != [2, 3, 4]!) + + a2 := [[1, 2]!, [3, 4]!]! + println(a2 == [[1, 2]!, [3, 4]!]!) + println(a2 != [[3, 4]!, [1, 2]!]!) + + a3 := [[1, 2], [3, 4]]! + println(a3 == [[1, 2], [3, 4]]!) + println(a3 != [[1, 1], [2, 2]]!) + + a4 := [[`a`, `b`], [`c`, `d`]]! + println(a4 == [[`a`, `b`], [`c`, `d`]]!) + println(a4 != [[`c`, `a`], [`a`, `b`]]!) + + a5 := [['aaa', 'bbb'], ['ccc', 'ddd']]! + println(a5 == [['aaa', 'bbb'], ['ccc', 'ddd']]!) + println(a5 != [['abc', 'def'], ['ccc', 'ddd']]!) + + a6 := [['aaa', 'bbb']!, ['ccc', 'ddd']!]! + println(a6 == [['aaa', 'bbb']!, ['ccc', 'ddd']!]!) + println(a6 != [['aaa', 'bbb']!, ['aaa', 'ddd']!]!) + + a7 := [[1, 2]!, [3, 4]!] + println(a7 == [[1, 2]!, [3, 4]!]) + println(a7 != [[2, 3]!, [1, 2]!]) + + a8 := [['aaa', 'bbb']!, ['ccc', 'ddd']!] + println(a8 == [['aaa', 'bbb']!, ['ccc', 'ddd']!]) + println(a8 != [['bbb', 'aaa']!, ['cccc', 'dddd']!]) + } + { + // test fixed array literal eq + println([1, 2, 3]! == [1, 2, 3]!) + println([1, 1, 1]! != [1, 2, 3]!) + + println([[1, 2], [3, 4]]! == [[1, 2], [3, 4]]!) + println([[1, 1], [2, 2]]! != [[1, 2], [3, 4]]!) + + println([[1, 1]!, [2, 2]!]! == [[1, 1]!, [2, 2]!]!) + println([[1, 1]!, [2, 2]!]! != [[1, 2]!, [2, 3]!]!) + + println([[1, 1]!, [2, 2]!] == [[1, 1]!, [2, 2]!]) + println([[1, 1]!, [2, 2]!] != [[1, 2]!, [2, 3]!]) + } + { + // test sort + mut a := ['hi', '1', '5', '3'] + a.sort() + println(a) + + mut nums := [67, -3, 108, 42, 7] + nums.sort() + println(nums) + assert nums[0] == -3 + assert nums[1] == 7 + assert nums[2] == 42 + assert nums[3] == 67 + assert nums[4] == 108 + // todo(playX): add codegen for comparator fn passed + /* + nums.sort(a < b) + assert nums[0] == -3 + assert nums[1] == 7 + assert nums[2] == 42 + assert nums[3] == 67 + assert nums[4] == 108 + + mut users := [User{22, 'Peter'}, User{20, 'Bob'}, User{25, 'Alice'}] + users.sort(a.age < b.age) + assert users[0].age == 20 + assert users[1].age == 22 + assert users[2].age == 25 + assert users[0].name == 'Bob' + assert users[1].name == 'Peter' + assert users[2].name == 'Alice' + + users.sort(a.age > b.age) + assert users[0].age == 25 + assert users[1].age == 22 + assert users[2].age == 20 + + users.sort(a.name < b.name) // Test sorting by string fields + */ + } + { + // test rune sort + mut bs := [`f`, `e`, `d`, `b`, `c`, `a`] + bs.sort() + println(bs) + + /* + bs.sort(a > b) + println(bs) + assert '$bs' == '[`f`, `e`, `d`, `c`, `b`, `a`]' + + bs.sort(a < b) + println(bs) + assert '$bs' == '[`a`, `b`, `c`, `d`, `e`, `f`]' + */ + } + { + // test f32 sort + mut f := [f32(50.0), 15, 1, 79, 38, 0, 27] + f.sort() + println(f[0]) + println(f[1]) + println(f[6]) + } +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.out b/v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.out new file mode 100644 index 0000000..d252328 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.out @@ -0,0 +1,2 @@ +true +false
\ No newline at end of file diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.v b/v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.v new file mode 100644 index 0000000..8d99196 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.v @@ -0,0 +1,4 @@ +x := ' x' + +println(x[0].is_space()) +println(x[1].is_space()) diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.out b/v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.out new file mode 100644 index 0000000..2db62ac --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.out @@ -0,0 +1 @@ +2 > 1
\ No newline at end of file diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.v b/v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.v new file mode 100644 index 0000000..f214466 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.v @@ -0,0 +1,15 @@ +if 2 > 1 { + println('2 > 1') +} + +if 2 < 1 { + println('2 < 1') +} + +if 2 == 1 { + println('2 == 1') +} + +if 2 == 0 { + println('2 == 0') +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.out b/v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.out new file mode 100644 index 0000000..95d09f2 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.out @@ -0,0 +1 @@ +hello world
\ No newline at end of file diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.v b/v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.v new file mode 100644 index 0000000..2a082f9 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.v @@ -0,0 +1 @@ +println('hello world') diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.out b/v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.out new file mode 100644 index 0000000..de2a355 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.out @@ -0,0 +1,3 @@ +Hello V developer + Hello V +Hello V
\ No newline at end of file diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.v b/v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.v new file mode 100644 index 0000000..1594b48 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.v @@ -0,0 +1,3 @@ +println('d Hello V developer'.trim_left(' d')) +println(' Hello V d'.trim_right(' d')) +println('WorldHello V'.trim_prefix('World')) |