From 11e6734912a6c894bd870a1901b9bf077d157ade Mon Sep 17 00:00:00 2001 From: spaceface777 Date: Sun, 31 May 2020 20:48:31 +0200 Subject: [PATCH] jsgen: more fixes and improvements --- vlib/v/gen/js/js.v | 393 ++++++++++++++++-------------- vlib/v/gen/js/jsdoc.v | 66 ++--- vlib/v/gen/js/tests/array.js | 47 ++-- vlib/v/gen/js/tests/array.v | 13 +- vlib/v/gen/js/tests/enum.js | 139 +++++++++++ vlib/v/gen/js/tests/enum.v | 17 ++ vlib/v/gen/js/tests/hello/hello.v | 11 +- vlib/v/gen/js/tests/js.js | 123 +++++----- vlib/v/gen/js/tests/js.v | 2 + vlib/v/gen/js/tests/life.js | 62 +++-- vlib/v/gen/js/tests/simple.js | 17 +- vlib/v/gen/js/tests/struct.js | 53 +++- vlib/v/gen/js/tests/struct.v | 10 + 13 files changed, 595 insertions(+), 358 deletions(-) create mode 100644 vlib/v/gen/js/tests/enum.js create mode 100644 vlib/v/gen/js/tests/enum.v diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index f2608d8e05..728aa880ae 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -33,11 +33,11 @@ mut: namespace string doc &JsDoc enable_doc bool - constants strings.Builder // all global V constants file ast.File tmp_count int inside_ternary bool inside_loop bool + inside_map_set bool // map.set(key, value) is_test bool indents map[string]int // indentations mapped to namespaces stmt_start_pos int @@ -52,7 +52,6 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string mut g := &JsGen{ out: strings.new_builder(100) definitions: strings.new_builder(100) - constants: strings.new_builder(100) table: table pref: pref fn_decl: 0 @@ -98,11 +97,13 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string // resolve imports deps_resolved := graph.resolve() - g.finish() - mut out := g.hashes() + g.definitions.str() + g.constants.str() + mut out := g.hashes() + g.definitions.str() for node in deps_resolved.nodes { - out += g.doc.gen_namespace(node.name) - out += 'const $node.name = (function (' + name := g.js_name(node.name) + if g.enable_doc { + out += '/** @namespace $name */\n' + } + out += 'const $name = (function (' imports := g.namespace_imports[node.name] for i, key in imports.keys() { if i > 0 { out += ', ' } @@ -112,7 +113,10 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string // private scope out += g.namespaces[node.name].str().trim_space() // public scope - out += '\n\n\t/* module exports */' + out += '\n' + if g.enable_doc { + out += '\n\t/* module exports */' + } out += '\n\treturn {' for pub_var in g.namespaces_pub[node.name] { out += '\n\t\t$pub_var,' @@ -175,17 +179,6 @@ pub fn (mut g JsGen) init() { g.definitions.writeln('') } -pub fn (mut g JsGen) finish() { - if g.constants.len > 0 { - constants := g.constants.str() - g.constants = strings.new_builder(100) - g.constants.writeln('const _CONSTS = Object.freeze({') - g.constants.write(constants) - g.constants.writeln('});') - g.constants.writeln('') - } -} - pub fn (g JsGen) hashes() string { mut res := '// V_COMMIT_HASH ${util.vhash()}\n' res += '// V_CURRENT_COMMIT_HASH ${util.githash(g.pref.building_v)}\n' @@ -195,109 +188,162 @@ pub fn (g JsGen) hashes() string { // V type to JS type pub fn (mut g JsGen) typ(t table.Type) string { sym := g.table.get_type_symbol(t) - mut styp := sym.name - if styp.starts_with('JS.') { - styp = styp[3..] - } - // 'multi_return_int_int' => '[number, number]' - if styp.starts_with('multi_return_') { - tokens := styp.replace('multi_return_', '').split('_') - return '[' + tokens.map(g.to_js_typ(it)).join(', ') + ']' - } - // 'anon_fn_7_7_1' => '(a number, b number) => void' - if styp.starts_with('anon_') { - info := sym.info as table.FnType - mut res := '(' - for i, arg in info.func.args { - res += '$arg.name: ${g.typ(arg.typ)}' - if i < info.func.args.len - 1 { res += ', ' } - } - return res + ') => ' + g.typ(info.func.return_type) - } - // Struct instance => ns["class"]["prototype"] - if sym.kind == .struct_ && get_ns(styp).len > 0 { - return g.to_js_typ(g.get_alias(styp)) + '["prototype"]' - } - return g.to_js_typ(styp) -} - -fn (mut g JsGen) to_js_typ(typ string) string { mut styp := '' - match typ { - 'int' { + + match sym.kind { + .placeholder { + // This should never happen: means checker bug + styp = 'any' + } + .void { + styp = 'void' + } + .voidptr { + styp = 'any' + } + .byteptr, .charptr { + styp = 'string' + } + .i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, .any_int, .any_float, .size_t { + // TODO: Should u64 and i64 use BigInt rather than number? styp = 'number' } - 'bool' { + .bool { styp = 'boolean' } - 'voidptr' { - styp = 'Object' + .none_ { + styp = 'undefined' } - 'byteptr' { + .string, .ustring, .char { styp = 'string' } - 'charptr' { - styp = 'string' + // 'array_array_int' => 'number[][]' + .array { + info := sym.info as table.Array + styp = g.typ(info.elem_type) + '[]' } - else { - if typ.starts_with('array_') { - styp = g.to_js_typ(typ.replace('array_', '')) + '[]' - } else if typ.starts_with('map_') { - tokens := typ.split('_') - styp = 'Map<${g.to_js_typ(tokens[1])}, ${g.to_js_typ(tokens[2])}>' - } else { - styp = typ + .array_fixed { + info := sym.info as table.ArrayFixed + styp = g.array_fixed_typ(info.elem_type) or { g.typ(info.elem_type) + '[]' } + } + // 'map[string]int' => 'Map' + .map { + info := sym.info as table.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) + } + // 'multi_return_int_int' => '[number, number]' + .multi_return { + info := sym.info as table.MultiReturn + types := info.types.map(g.typ(it)) + joined := types.join(', ') + styp = '[$joined]' + } + .sum_type { + // TODO: Implement sumtypes + styp = '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 table.FnType + mut res := '(' + for i, arg in info.func.args { + res += '$arg.name: ${g.typ(arg.typ)}' + if i < info.func.args.len - 1 { res += ', ' } } + styp = res + ') => ' + g.typ(info.func.return_type) } - } - // ns.export => ns["export"] - for i, v in styp.split('.') { - if i == 0 { - styp = v - continue + .interface_ { + // TODO: Implement interfaces + styp = 'interface' } - styp += '["$v"]' + /* else { + println('jsgen.typ: Unhandled type $t') + styp = sym.name + } */ } + if styp.starts_with('JS.') { return styp[3..] } return styp } -fn (mut g JsGen) to_js_typ_val(typ string) string { +fn (mut g JsGen) struct_typ(s string) string { + ns := get_ns(s) + mut name := if ns == g.namespace { s.split('.').last() } else { g.get_alias(s) } mut styp := '' - match typ { - 'number' { + for i, v in name.split('.') { + if i == 0 { styp = v } + else { styp += '["$v"]' } + } + if ns in ['', g.namespace] { return styp } + return styp + '["prototype"]' +} + +fn (mut g JsGen) to_js_typ_val(t table.Type) string { + sym := g.table.get_type_symbol(t) + mut styp := '' + + match sym.kind { + .i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, .any_int, .any_float, .size_t { styp = '0' } - 'boolean' { + .bool { styp = 'false' } - 'Object' { - styp = '{}' - } - 'string' { + .string { styp = '""' } + .map { + styp = 'new Map()' + } + .array { + styp = '[]' + } + .struct_ { + styp = 'new ${g.js_name(sym.name)}({})' + } else { - if typ.starts_with('Map') { - styp = 'new Map()' - } else if typ.ends_with('[]') { - styp = '[]' - } else { - styp = '{}' - } + // TODO + styp = 'undefined' } } - // ns.export => ns["export"] - for i, v in styp.split('.') { - if i == 0 { - styp = v - continue - } - styp += '["$v"]' - } return styp } -pub fn (g &JsGen) save() {} +fn (mut g JsGen) array_fixed_typ(t table.Type) ?string { + sym := g.table.get_type_symbol(t) + match sym.kind { + .i8 { return 'Int8Array' } + .i16 { return 'Int16Array' } + .int { return 'Int32Array' } + .i64 { return 'BigInt64Array' } + .byte { return 'Uint8Array' } + .u16 { return 'Uint16Array' } + .u32 { return 'Uint32Array' } + .u64 { return 'BigUint64Array' } + .f32 { return 'Float32Array' } + .f64 { return 'Float64Array' } + else { return none } + } +} pub fn (mut g JsGen) gen_indent() { if g.indents[g.namespace] > 0 && g.empty_line { @@ -334,28 +380,19 @@ pub fn (mut g JsGen) new_tmp_var() string { // 'fn' => '' [inline] fn get_ns(s string) string { - parts := s.split('.') - mut res := '' - for i, p in parts { - if i == parts.len - 1 { break } // Last part (fn/struct/var name): skip - res += p - if i < parts.len - 2 { res += '.' } // Avoid trailing dot - } - return res + idx := s.last_index('.') or { return '' } + return s.substr(0, idx) } fn (mut g JsGen) get_alias(name string) string { - // TODO: This is a hack; find a better way to do this - split := name.split('.') - if split.len > 1 { - imports := g.namespace_imports[g.namespace] - alias := imports[split[0]] + ns := get_ns(name) + if ns == '' { return name } - if alias != '' { - return alias + '.' + split[1..].join('.') - } - } - return name // No dot == no alias + imports := g.namespace_imports[g.namespace] + alias := imports[ns] + if alias == '' { return name } + + return alias + '.' + name.split('.').last() } fn (mut g JsGen) js_name(name_ string) string { @@ -519,7 +556,8 @@ fn (mut g JsGen) expr(node ast.Expr) { // TODO } ast.EnumVal { - styp := g.typ(it.typ) + sym := g.table.get_type_symbol(it.typ) + styp := g.js_name(sym.name) g.write('${styp}.${it.val}') } ast.FloatLiteral { @@ -631,26 +669,14 @@ fn (mut g JsGen) gen_assert_stmt(a ast.AssertStmt) { fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) { if it.left.len > it.right.len { // multi return - jsdoc := strings.new_builder(50) - jsdoc.write('[') - stmt := strings.new_builder(50) - stmt.write('const [') + g.write('const [') for i, ident in it.left { - ident_var_info := ident.var_info() - styp := g.typ(ident_var_info.typ) - jsdoc.write(styp) - - stmt.write(g.js_name(ident.name)) - + g.write(g.js_name(ident.name)) if i < it.left.len - 1 { - jsdoc.write(', ') - stmt.write(', ') + g.write(', ') } } - jsdoc.write(']') - stmt.write('] = ') - g.writeln(g.doc.gen_typ(jsdoc.str(), '')) - g.write(stmt.str()) + g.write('] = ') g.expr(it.right[0]) g.writeln(';') } else { @@ -660,16 +686,8 @@ fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) { ident_var_info := ident.var_info() mut styp := g.typ(ident_var_info.typ) - if val is ast.EnumVal { - // we want the type of the enum value not the enum - styp = 'number' - } else if val is ast.StructInit { - // no need to print jsdoc for structs - styp = '' - } - if !g.inside_loop && styp.len > 0 { - g.writeln(g.doc.gen_typ(styp, ident.name)) + g.doc.gen_typ(styp) } if g.inside_loop || ident.is_mut { @@ -707,25 +725,16 @@ fn (mut g JsGen) gen_branch_stmt(it ast.BranchStmt) { } fn (mut g JsGen) gen_const_decl(it ast.ConstDecl) { - // old_indent := g.indents[g.namespace] - for i, field in it.fields { - // TODO hack. Cut the generated value and paste it into definitions. - pos := g.out.len + 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) - val := g.out.after(pos) - g.out.go_back(val.len) - if g.enable_doc { - typ := g.typ(field.typ) - g.constants.write('\t') - g.constants.writeln(g.doc.gen_typ(typ, field.name)) - } - g.constants.write('\t') - g.constants.write('${g.js_name(field.name)}: $val') - if i < it.fields.len - 1 { - g.constants.writeln(',') - } + g.writeln(';') } - g.constants.writeln('') + g.writeln('') } fn (mut g JsGen) gen_defer_stmts() { @@ -738,23 +747,20 @@ fn (mut g JsGen) gen_defer_stmts() { } fn (mut g JsGen) gen_enum_decl(it ast.EnumDecl) { - g.writeln('const ${g.js_name(it.name)} = Object.freeze({') + g.doc.gen_enum() + g.writeln('const ${g.js_name(it.name)} = {') g.inc_indent() - for i, field in it.fields { + mut i := 0 + for field in it.fields { g.write('$field.name: ') - if field.has_expr { - pos := g.out.len - g.expr(field.expr) - expr_str := g.out.after(pos) - g.out.go_back(expr_str.len) - g.write('$expr_str') - } else { - g.write('$i') + if field.has_expr && field.expr is ast.IntegerLiteral { + e := field.expr as ast.IntegerLiteral + i = e.val.int() } - g.writeln(',') + g.writeln('${i++},') } g.dec_indent() - g.writeln('});') + g.writeln('};') if it.is_pub { g.push_pub_var(it.name) } @@ -810,7 +816,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) { // type_name := g.typ(it.return_type) // generate jsdoc for the function - g.writeln(g.doc.gen_fn(it)) + g.doc.gen_fn(it) if has_go { g.write('async ') @@ -1015,14 +1021,14 @@ fn (mut g JsGen) gen_hash_stmt(it ast.HashStmt) { } fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) { - g.writeln(g.doc.gen_fac_fn(node.fields)) + g.doc.gen_fac_fn(node.fields) g.write('function ${g.js_name(node.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(g.typ(field.typ))}') + g.write('${g.to_js_typ_val(field.typ)}') } if i < node.fields.len - 1 { g.write(', ') } } @@ -1040,8 +1046,9 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) { fns := g.method_fn_decls[node.name] for i, field in node.fields { - g.writeln(g.doc.gen_typ(g.typ(field.typ), field.name)) - g.write('$field.name: ${g.to_js_typ_val(g.typ(field.typ))}') + typ := g.typ(field.typ) + g.doc.gen_typ(typ) + g.write('$field.name: ${g.to_js_typ_val(field.typ)}') if i < node.fields.len - 1 || fns.len > 0 { g.writeln(',') } else { g.writeln('') } } @@ -1074,8 +1081,15 @@ fn (mut g JsGen) gen_array_init_expr(it ast.ArrayInit) { fn (mut g JsGen) gen_assign_expr(it ast.AssignExpr) { g.expr(it.left) - g.write(' $it.op ') - g.expr(it.val) + if g.inside_map_set && it.op == .assign { + g.inside_map_set = false + g.write(', ') + g.expr(it.val) + g.write(')') + } else { + g.write(' $it.op ') + g.expr(it.val) + } } fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { @@ -1105,11 +1119,6 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { } fn (mut g JsGen) gen_ident(node ast.Ident) { - if node.kind == .constant { - // TODO: Handle const namespacing: only consts in the main module are handled rn - g.write('_CONSTS.') - } - name := g.js_name(node.name) // TODO `is` // TODO handle optionals @@ -1172,6 +1181,7 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { } fn (mut g JsGen) gen_index_expr(it ast.IndexExpr) { + left_typ := g.table.get_type_symbol(it.left_type) // TODO: Handle splice setting if it's implemented if it.index is ast.RangeExpr { range := it.index as ast.RangeExpr @@ -1190,8 +1200,28 @@ fn (mut g JsGen) gen_index_expr(it ast.IndexExpr) { g.write('.length') } g.write(')') + } else if left_typ.kind == .map { + g.expr(it.left) + if it.is_setter { + g.inside_map_set = true + g.write('.set(') + } else { + g.write('.get(') + } + g.expr(it.index) + if !it.is_setter { g.write(')') } + } else if left_typ.kind == .string { + if it.is_setter { + // TODO: What's the best way to do this? + // 'string'[3] = `o` + } else { + g.expr(it.left) + g.write('.charCodeAt(') + g.expr(it.index) + g.write(')') + } } else { - // TODO Does this work in all cases? + // TODO Does this cover all cases? g.expr(it.left) g.write('[') g.expr(it.index) @@ -1209,6 +1239,13 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { if r_sym.kind == .array { g.write('...') } // arr << [1, 2] g.expr(it.right) g.write(')') + } else if r_sym.kind in [.array, .map] && it.op in [.key_in, .not_in] { + if it.op == .not_in { g.write('!(') } + g.expr(it.right) + g.write(if r_sym.kind == .map { '.has(' } else { '.includes(' }) + g.expr(it.left) + g.write(')') + if it.op == .not_in { g.write(')') } } else if it.op == .key_is { // foo is Foo g.write('/*') g.expr(it.left) diff --git a/vlib/v/gen/js/jsdoc.v b/vlib/v/gen/js/jsdoc.v index 4519af3747..ccbaece18a 100644 --- a/vlib/v/gen/js/jsdoc.v +++ b/vlib/v/gen/js/jsdoc.v @@ -1,79 +1,59 @@ module js -import strings import v.ast struct JsDoc { gen &JsGen -mut: - out strings.Builder - empty_line bool } fn new_jsdoc(gen &JsGen) &JsDoc { return &JsDoc{ - out: strings.new_builder(20) gen: gen } } -fn (mut d JsDoc) gen_indent() { - if d.gen.indents[d.gen.namespace] > 0 && d.empty_line { - d.out.write(tabs[d.gen.indents[d.gen.namespace]]) - } - d.empty_line = false -} - fn (mut d JsDoc) write(s string) { if !d.gen.enable_doc { return } - d.gen_indent() - d.out.write(s) + d.gen.write(s) } fn (mut d JsDoc) writeln(s string) { if !d.gen.enable_doc { return } - d.gen_indent() - d.out.writeln(s) - d.empty_line = true + d.gen.writeln(s) } -fn (mut d JsDoc) reset() { - d.out = strings.new_builder(20) - d.empty_line = false +fn (mut d JsDoc) gen_typ(typ string) { + d.writeln('/** @type {$typ} */') } -fn (mut d JsDoc) gen_typ(typ, name string) string { - d.reset() - d.write('/**') - d.write(' @type {$typ}') - if name.len > 0 { - d.write(' - ${d.gen.js_name(name)}') - } - d.write(' */') - return d.out.str() +fn (mut d JsDoc) gen_const(typ string) { + d.writeln('/** @constant {$typ} */') } -fn (mut d JsDoc) gen_fac_fn(fields []ast.StructField) string { - d.reset() +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. - // TODO: Actually generate default struct init values :P d.write('$field.name?: ${d.gen.typ(field.typ)}') if i < fields.len - 1 { d.write(', ') } } - d.writeln('}} values - values for this class fields') - d.writeln(' * @constructor') - d.write('*/') - return d.out.str() + d.writeln('}} init') + d.writeln('*/') } -fn (mut d JsDoc) gen_fn(it ast.FnDecl) string { - d.reset() +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') } @@ -91,13 +71,5 @@ fn (mut d JsDoc) gen_fn(it ast.FnDecl) string { } } d.writeln(' * @returns {$type_name}') - d.writeln(' * @function') - d.write('*/') - return d.out.str() -} - -fn (mut d JsDoc) gen_namespace(ns string) string { - d.reset() - d.writeln('/** @namespace ${ns} */') - return d.out.str() + d.writeln('*/') } diff --git a/vlib/v/gen/js/tests/array.js b/vlib/v/gen/js/tests/array.js index ce43b3aa7c..7b37a7f376 100644 --- a/vlib/v/gen/js/tests/array.js +++ b/vlib/v/gen/js/tests/array.js @@ -1,5 +1,5 @@ -// V_COMMIT_HASH 0de70e8 -// V_CURRENT_COMMIT_HASH 2eac2a5 +// V_COMMIT_HASH 2943bdc +// V_CURRENT_COMMIT_HASH ad5deef // Generated by the V compiler "use strict"; @@ -7,18 +7,18 @@ /** @namespace builtin */ const builtin = (function () { /** + * @function * @param {any} s * @returns {void} - * @function */ function println(s) { console.log(s); } /** + * @function * @param {any} s * @returns {void} - * @function */ function print(s) { process.stdout.write(s); @@ -34,9 +34,9 @@ const builtin = (function () { /** @namespace main */ const main = (function () { /** + * @function * @param {...number} args * @returns {void} - * @function */ function variadic(...args) { builtin.println(args); @@ -45,8 +45,8 @@ const main = (function () { } /** - * @returns {void} * @function + * @returns {void} */ function vararg_test() { variadic(1, 2, 3); @@ -55,17 +55,17 @@ const main = (function () { /* program entry point */ (function() { vararg_test(); - /** @type {string[]} - arr1 */ + /** @type {string[]} */ const arr1 = ["Hello", "JS", "Backend"]; - /** @type {number[]} - arr2 */ + /** @type {number[]} */ let arr2 = [1, 2, 3, 4, 5]; - /** @type {string[]} - slice1 */ + /** @type {string[]} */ const slice1 = arr1.slice(1, 3); - /** @type {number[]} - slice2 */ + /** @type {number[]} */ const slice2 = arr2.slice(0, 3); - /** @type {number[]} - slice3 */ + /** @type {number[]} */ const slice3 = arr2.slice(3, arr2.length); - /** @type {string} - idx1 */ + /** @type {string} */ const idx1 = slice1[1]; arr2[0] = 1; arr2[0 + 1] = 2; @@ -73,19 +73,24 @@ const main = (function () { arr2.push(6); arr2.push(...[7, 8, 9]); builtin.println(arr2); - /** @type {string} - slice4 */ - const slice4 = idx1.slice(0, 4); + /** @type {string} */ + let slice4 = idx1.slice(0, 4); builtin.println(slice4); - /** @type {byte} - idx2 */ - const idx2 = slice4[0]; - /** @type {Map} - m */ + /** @type {number} */ + const idx2 = slice4.charCodeAt(0); + builtin.println(idx2); + /** @type {Map} */ let m = new Map(); - /** @type {string} - key */ + /** @type {string} */ const key = "key"; - m[key] = "value"; - /** @type {string} - val */ - const val = m["key"]; + m.set(key, "value"); + /** @type {string} */ + const val = m.get("key"); builtin.println(val); + builtin.println(arr1.includes("JS")); + builtin.println(!(arr2.includes(3))); + builtin.println(m.has("key")); + builtin.println(!(m.has("badkey"))); })(); /* module exports */ diff --git a/vlib/v/gen/js/tests/array.v b/vlib/v/gen/js/tests/array.v index 32e68109b7..797e1fb5e0 100644 --- a/vlib/v/gen/js/tests/array.v +++ b/vlib/v/gen/js/tests/array.v @@ -34,15 +34,24 @@ arr2 << [7, 8, 9] println(arr2) // String slices -slice4 := idx1[..4] +mut slice4 := idx1[..4] println(slice4) // 'Back' // String indexes idx2 := slice4[0] +println(idx2) +// TODO: +// slice4[3] = `c` // Maps mut m := map[string]string key := 'key' m[key] = 'value' val := m['key'] -println(val) \ No newline at end of file +println(val) + +// 'in' / '!in' +println('JS' in arr1) +println(3 !in arr2) +println('key' in m) +println('badkey' !in m) diff --git a/vlib/v/gen/js/tests/enum.js b/vlib/v/gen/js/tests/enum.js new file mode 100644 index 0000000000..bcb38d04fc --- /dev/null +++ b/vlib/v/gen/js/tests/enum.js @@ -0,0 +1,139 @@ +// V_COMMIT_HASH 2943bdc +// V_CURRENT_COMMIT_HASH ad5deef +// Generated by the V compiler + +"use strict"; + +/** @namespace builtin */ +const builtin = (function () { + /** + * @function + * @param {any} s + * @returns {void} + */ + function println(s) { + console.log(s); + } + + /** + * @function + * @param {any} s + * @returns {void} + */ + function print(s) { + process.stdout.write(s); + } + + /* module exports */ + return { + println, + print, + }; +})(); + +/** @namespace hello */ +const hello = (function () { + /** + * @function + * @returns {void} + */ + function raw_js_log() { + console.log('hello') + } + + /** @constant {string} */ + const hello = "Hello"; + + /** + * @constructor + * @param {{foo?: string}} init + */ + function Aaa({ foo = "" }) { + this.foo = foo + }; + Aaa.prototype = { + /** @type {string} */ + foo: "", + /** + * @function + * @param {string} s + * @returns {void} + */ + update(s) { + const a = this; + a.foo = s; + } + }; + + + /** + * @constructor + * @param {{}} init + */ + function Bbb({ }) { + }; + Bbb.prototype = { + }; + + /** @enum {number} */ + const Ccc = { + a: 0, + b: 5, + c: 6, + }; + + /** + * @function + * @returns {string} + */ + function v_debugger() { + /** @type {Bbb} */ + const v = new Bbb({}); + return hello; + } + + /** + * @function + * @returns {string} + */ + function excited() { + return v_debugger() + "!"; + } + + /* module exports */ + return { + raw_js_log, + Aaa, + Ccc, + v_debugger, + excited, + }; +})(); + +/** @namespace main */ +const main = (function (hello) { + /** @enum {number} */ + const Test = { + foo: 2, + bar: 5, + baz: 6, + }; + + /* program entry point */ + (function() { + /** @type {number} */ + let a = hello.Ccc.a; + a = hello.Ccc.b; + a = hello.Ccc.c; + builtin.println(a); + /** @type {number} */ + let b = Test.foo; + b = Test.bar; + builtin.println(b); + })(); + + /* module exports */ + return {}; +})(hello); + + diff --git a/vlib/v/gen/js/tests/enum.v b/vlib/v/gen/js/tests/enum.v new file mode 100644 index 0000000000..da78d69483 --- /dev/null +++ b/vlib/v/gen/js/tests/enum.v @@ -0,0 +1,17 @@ +import hello + +enum Test { + foo = 2 + bar = 5 + baz +} + +mut a := hello.Ccc.a +a = .b +a = .c +println(a) + + +mut b := Test.foo +b = .bar +println(b) diff --git a/vlib/v/gen/js/tests/hello/hello.v b/vlib/v/gen/js/tests/hello/hello.v index d825694a7a..8950384c6f 100644 --- a/vlib/v/gen/js/tests/hello/hello.v +++ b/vlib/v/gen/js/tests/hello/hello.v @@ -1,11 +1,8 @@ module hello -// TODO: Fix const namespacing, uncomment once it works -/* pub const ( hello = 'Hello' ) -*/ pub struct Aaa { pub mut: @@ -18,11 +15,15 @@ pub fn (mut a Aaa) update(s string) { struct Bbb {} -pub enum Ccc {} +pub enum Ccc { + a + b = 5 + c +} pub fn debugger() string { v := Bbb{} - return 'Hello' + return hello } pub fn excited() string { diff --git a/vlib/v/gen/js/tests/js.js b/vlib/v/gen/js/tests/js.js index 34f496bc9f..05d9ad08a2 100644 --- a/vlib/v/gen/js/tests/js.js +++ b/vlib/v/gen/js/tests/js.js @@ -1,31 +1,24 @@ -// V_COMMIT_HASH 5423a15 -// V_CURRENT_COMMIT_HASH 941404d +// V_COMMIT_HASH 2943bdc +// V_CURRENT_COMMIT_HASH ad5deef // Generated by the V compiler "use strict"; -const _CONSTS = Object.freeze({ - /** @type {number} - i_am_a_const */ - i_am_a_const: 21214, - /** @type {string} - v_super */ - v_super: "amazing keyword" -}); - /** @namespace builtin */ const builtin = (function () { /** + * @function * @param {any} s * @returns {void} - * @function */ function println(s) { console.log(s); } /** + * @function * @param {any} s * @returns {void} - * @function */ function print(s) { process.stdout.write(s); @@ -41,27 +34,30 @@ const builtin = (function () { /** @namespace hello */ const hello = (function () { /** - * @returns {void} * @function + * @returns {void} */ function raw_js_log() { console.log('hello') } + /** @constant {string} */ + const hello = "Hello"; + /** - * @param {{foo?: string}} values - values for this class fields * @constructor + * @param {{foo?: string}} init */ function Aaa({ foo = "" }) { this.foo = foo }; Aaa.prototype = { - /** @type {string} - foo */ + /** @type {string} */ foo: "", /** + * @function * @param {string} s * @returns {void} - * @function */ update(s) { const a = this; @@ -71,29 +67,34 @@ const hello = (function () { /** - * @param {{}} values - values for this class fields * @constructor + * @param {{}} init */ function Bbb({ }) { }; Bbb.prototype = { }; - const Ccc = Object.freeze({ - }); + /** @enum {number} */ + const Ccc = { + a: 0, + b: 5, + c: 6, + }; /** - * @returns {string} * @function + * @returns {string} */ function v_debugger() { + /** @type {Bbb} */ const v = new Bbb({}); - return "Hello"; + return hello; } /** - * @returns {string} * @function + * @returns {string} */ function excited() { return v_debugger() + "!"; @@ -111,21 +112,26 @@ const hello = (function () { /** @namespace main */ const main = (function (hl) { + /** @constant {number} */ + const i_am_a_const = 21214; + /** @constant {string} */ + const v_super = "amazing keyword"; + /** - * @param {{a?: hl["Aaa"]["prototype"]}} values - values for this class fields * @constructor + * @param {{a?: hl["Aaa"]["prototype"]}} init */ - function Foo({ a = {} }) { + function Foo({ a = new hl.Aaa({}) }) { this.a = a }; Foo.prototype = { - /** @type {hl["Aaa"]["prototype"]} - a */ - a: {} + /** @type {hl["Aaa"]["prototype"]} */ + a: new hl.Aaa({}) }; /** - * @param {{google?: number, amazon?: boolean, yahoo?: string}} values - values for this class fields * @constructor + * @param {{google?: number, amazon?: boolean, yahoo?: string}} init */ function Companies({ google = 0, amazon = false, yahoo = "" }) { this.google = google @@ -133,26 +139,26 @@ const main = (function (hl) { this.yahoo = yahoo }; Companies.prototype = { - /** @type {number} - google */ + /** @type {number} */ google: 0, - /** @type {boolean} - amazon */ + /** @type {boolean} */ amazon: false, - /** @type {string} - yahoo */ + /** @type {string} */ yahoo: "", /** - * @returns {number} * @function + * @returns {number} */ method() { const it = this; + /** @type {Companies} */ const ss = new Companies({ google: 2, amazon: true, yahoo: "hello" }); - /** @type {[number, number]} */ const [a, b] = hello(2, "google", "not google"); - /** @type {string} - glue */ + /** @type {string} */ const glue = (a > 2 ? "more_glue" : a > 5 ? "more glueee" : "less glue"); if (a !== 2) { } @@ -161,56 +167,63 @@ const main = (function (hl) { } }; - const POSITION = Object.freeze({ + /** @enum {number} */ + const POSITION = { go_back: 0, dont_go_back: 1, - }); + }; /** + * @function * @param {string} v_extends * @param {number} v_instanceof * @returns {void} - * @function */ function v_class(v_extends, v_instanceof) { - /** @type {number} - v_delete */ + /** @type {number} */ const v_delete = v_instanceof; } /* program entry point */ (async function() { builtin.println("Hello from V.js!"); - /** @type {number} - a */ + /** @type {number} */ let a = 1; a *= 2; a += 3; builtin.println(a); + /** @type {hl["Aaa"]["prototype"]} */ const b = new hl.Aaa({}); b.update("an update"); builtin.println(b); + /** @type {Foo} */ const c = new Foo({ a: new hl.Aaa({}) }); c.a.update("another update"); builtin.println(c); - /** @type {string} - v */ + /** @type {string} */ const v = "done"; { - /** @type {string} - _ */ + /** @type {string} */ const _ = "block"; } - /** @type {number} - pos */ + /** @type {number} */ const pos = POSITION.go_back; - /** @type {string} - v_debugger */ + /** @type {number} */ + const enum2 = hl.Ccc.a; + /** @type {string} */ const v_debugger = "JS keywords"; - /** @type {string} - v_await */ - const v_await = _CONSTS.v_super + ": " + v_debugger; - /** @type {string} - v_finally */ + /** @type {string} */ + const v_await = v_super + ": " + v_debugger; + /** @type {string} */ let v_finally = "implemented"; console.log(v_await, v_finally); - /** @type {number} - dun */ - const dun = _CONSTS.i_am_a_const * 20; + /** @type {number} */ + const dun = i_am_a_const * 20; + /** @type {string} */ + const dunn = hl.hello; for (let i = 0; i < 10; i++) { } @@ -221,19 +234,19 @@ const main = (function (hl) { for (let x = 1; x < 10; ++x) { } - /** @type {number[]} - arr */ + /** @type {number[]} */ const arr = [1, 2, 3, 4, 5]; for (let _tmp1 = 0; _tmp1 < arr.length; ++_tmp1) { let i = arr[_tmp1]; } - /** @type {Map} - ma */ + /** @type {Map} */ const ma = new Map([ ["str", "done"], ["ddo", "baba"] ]); for (let [m, n] of ma) { - /** @type {string} - iss */ + /** @type {string} */ const iss = m; } @@ -242,7 +255,7 @@ const main = (function (hl) { resolve(); }); - /** @type {(number: number) => void} - fn_in_var */ + /** @type {(number: number) => void} */ const fn_in_var = function (number) { builtin.println(tos3(`number: ${number}`)); }; @@ -254,20 +267,20 @@ const main = (function (hl) { })(); /** + * @function * @param {string} greeting * @param {(message: string) => void} anon * @returns {void} - * @function */ function anon_consumer(greeting, anon) { anon(greeting); } /** + * @function * @param {number} num * @param {string} def * @returns {void} - * @function */ function async(num, def) { } @@ -275,21 +288,21 @@ const main = (function (hl) { /* [inline] */ /* [deprecated] */ /** + * @function * @deprecated * @param {number} game_on * @param {...string} dummy * @returns {[number, number]} - * @function */ function hello(game_on, ...dummy) { for (let _tmp2 = 0; _tmp2 < dummy.length; ++_tmp2) { let dd = dummy[_tmp2]; - /** @type {string} - l */ + /** @type {string} */ const l = dd; } (function defer() { - /** @type {string} - v_do */ + /** @type {string} */ const v_do = "not"; })(); return [game_on + 2, 221]; diff --git a/vlib/v/gen/js/tests/js.v b/vlib/v/gen/js/tests/js.v index b68d5b90ec..680c482317 100644 --- a/vlib/v/gen/js/tests/js.v +++ b/vlib/v/gen/js/tests/js.v @@ -49,6 +49,7 @@ fn main() { } pos := POSITION.go_back + enum2 := hl.Ccc.a debugger := 'JS keywords' // TODO: Implement interpolation @@ -58,6 +59,7 @@ fn main() { JS.console.log(await, finally) dun := i_am_a_const * 20 + dunn := hl.hello // External constant for i := 0; i < 10; i++ {} diff --git a/vlib/v/gen/js/tests/life.js b/vlib/v/gen/js/tests/life.js index cf06263c67..df9c068cbd 100644 --- a/vlib/v/gen/js/tests/life.js +++ b/vlib/v/gen/js/tests/life.js @@ -1,31 +1,24 @@ -// V_COMMIT_HASH 0de70e8 -// V_CURRENT_COMMIT_HASH 1c2dbea +// V_COMMIT_HASH 2943bdc +// V_CURRENT_COMMIT_HASH ad5deef // Generated by the V compiler "use strict"; -const _CONSTS = Object.freeze({ - /** @type {number} - w */ - w: 30, - /** @type {number} - h */ - h: 30 -}); - /** @namespace builtin */ const builtin = (function () { /** + * @function * @param {any} s * @returns {void} - * @function */ function println(s) { console.log(s); } /** + * @function * @param {any} s * @returns {void} - * @function */ function print(s) { process.stdout.write(s); @@ -41,26 +34,31 @@ const builtin = (function () { /** @namespace main */ const main = (function () { /** - * @returns {void} * @function + * @returns {void} */ function clear() { console.clear(); } -/** - * @param {boolean[]} game + /** @constant {number} */ + const w = 30; + /** @constant {number} */ + const h = 30; + + /** + * @function + * @param {boolean[][]} game * @param {number} x * @param {number} y * @returns {boolean} - * @function */ function get(game, x, y) { if (y < 0 || x < 0) { return false; } - if (y >= _CONSTS.h || x >= _CONSTS.w) { + if (y >= h || x >= w) { return false; } @@ -68,14 +66,14 @@ const main = (function () { } /** - * @param {boolean[]} game + * @function + * @param {boolean[][]} game * @param {number} x * @param {number} y * @returns {number} - * @function */ function neighbours(game, x, y) { - /** @type {number} - count */ + /** @type {number} */ let count = 0; if (get(game, x - 1, y - 1)) { count++; @@ -113,21 +111,21 @@ const main = (function () { } /** - * @param {boolean[]} game - * @returns {boolean[]} * @function + * @param {boolean[][]} game + * @returns {boolean[][]} */ function step(game) { - /** @type {boolean[]} - new_game */ + /** @type {boolean[][]} */ let new_game = [[]]; for (let y = 0; y < game.length; ++y) { let row = game[y]; - /** @type {boolean[]} - new_row */ + /** @type {boolean[]} */ let new_row = []; new_game[y] = new_row; for (let x = 0; x < row.length; ++x) { let cell = row[x]; - /** @type {number} - count */ + /** @type {number} */ const count = neighbours(game, x, y); new_row[x] = cell && count === 2 || count === 3; } @@ -138,12 +136,12 @@ const main = (function () { } /** + * @function * @param {boolean[]} row * @returns {string} - * @function */ function row_str(row) { - /** @type {string} - str */ + /** @type {string} */ let str = ""; for (let _tmp1 = 0; _tmp1 < row.length; ++_tmp1) { let cell = row[_tmp1]; @@ -159,9 +157,9 @@ const main = (function () { } /** - * @param {boolean[]} game - * @returns {void} * @function + * @param {boolean[][]} game + * @returns {void} */ function show(game) { clear(); @@ -174,12 +172,12 @@ const main = (function () { /* program entry point */ (function() { - /** @type {boolean[]} - game */ + /** @type {boolean[][]} */ let game = [[]]; - for (let y = 0; y < _CONSTS.h; ++y) { - /** @type {boolean[]} - row */ + for (let y = 0; y < h; ++y) { + /** @type {boolean[]} */ let row = []; - for (let x = 0; x < _CONSTS.w; ++x) { + for (let x = 0; x < w; ++x) { row[x] = false; } diff --git a/vlib/v/gen/js/tests/simple.js b/vlib/v/gen/js/tests/simple.js index 2ef3a34dd3..301ccee213 100644 --- a/vlib/v/gen/js/tests/simple.js +++ b/vlib/v/gen/js/tests/simple.js @@ -1,13 +1,14 @@ -// V_COMMIT_HASH 0de70e8 -// V_CURRENT_COMMIT_HASH 0de70e8 +// V_COMMIT_HASH 2943bdc +// V_CURRENT_COMMIT_HASH ad5deef // Generated by the V compiler "use strict"; -/* namespace: builtin */ +/** @namespace builtin */ const builtin = (function () { /** - * @param {string} s + * @function + * @param {any} s * @returns {void} */ function println(s) { @@ -15,7 +16,8 @@ const builtin = (function () { } /** - * @param {string} s + * @function + * @param {any} s * @returns {void} */ function print(s) { @@ -29,7 +31,7 @@ const builtin = (function () { }; })(); -/* namespace: main */ +/** @namespace main */ const main = (function () { /* program entry point */ (function() { @@ -37,8 +39,7 @@ const main = (function () { })(); /* module exports */ - return { - }; + return {}; })(); diff --git a/vlib/v/gen/js/tests/struct.js b/vlib/v/gen/js/tests/struct.js index 0ebcef9eb9..57162dcbdc 100644 --- a/vlib/v/gen/js/tests/struct.js +++ b/vlib/v/gen/js/tests/struct.js @@ -1,5 +1,5 @@ -// V_COMMIT_HASH 7e55261 -// V_CURRENT_COMMIT_HASH 79b1f27 +// V_COMMIT_HASH 2943bdc +// V_CURRENT_COMMIT_HASH ad5deef // Generated by the V compiler "use strict"; @@ -7,18 +7,18 @@ /** @namespace builtin */ const builtin = (function () { /** + * @function * @param {any} s * @returns {void} - * @function */ function println(s) { console.log(s); } /** + * @function * @param {any} s * @returns {void} - * @function */ function print(s) { process.stdout.write(s); @@ -34,8 +34,8 @@ const builtin = (function () { /** @namespace main */ const main = (function () { /** - * @param {{value?: number, test?: Map, hello?: number[]}} values - values for this class fields * @constructor + * @param {{value?: number, test?: Map, hello?: number[]}} init */ function Int({ value = 0, test = new Map(), hello = [] }) { this.value = value @@ -43,24 +43,24 @@ const main = (function () { this.hello = hello }; Int.prototype = { - /** @type {number} - value */ + /** @type {number} */ value: 0, - /** @type {Map} - test */ + /** @type {Map} */ test: new Map(), - /** @type {number[]} - hello */ + /** @type {number[]} */ hello: [], /** + * @function * @param {number} value * @returns {void} - * @function */ add(value) { const i = this; i.value += value; }, /** - * @returns {number} * @function + * @returns {number} */ get() { const i = this; @@ -70,16 +70,49 @@ const main = (function () { + /** + * @constructor + * @param {{foo?: number, bar?: string}} init + */ + function Config({ foo = 0, bar = "" }) { + this.foo = foo + this.bar = bar + }; + Config.prototype = { + /** @type {number} */ + foo: 0, + /** @type {string} */ + bar: "" + }; + + /** + * @function + * @param {Config} c + * @returns {void} + */ + function use_config(c) { + } + /* program entry point */ (function() { + /** @type {Int} */ const a = new Int({ value: 10 }); a.add(5); builtin.println(a); + /** @type {Int} */ const b = new Int({}); b.add(10); builtin.println(b.get()); + use_config(new Config({ + foo: 2, + bar: "bar" + })); + use_config(new Config({ + foo: 2, + bar: "bar" + })); })(); /* module exports */ diff --git a/vlib/v/gen/js/tests/struct.v b/vlib/v/gen/js/tests/struct.v index 861456c2d8..d3fbf78534 100644 --- a/vlib/v/gen/js/tests/struct.v +++ b/vlib/v/gen/js/tests/struct.v @@ -15,6 +15,13 @@ fn (i Int) get() int { return i.value } +struct Config { + foo int + bar string +} + +fn use_config(c Config) {} + fn main() { a := Int { value: 10 } a.add(5) @@ -23,4 +30,7 @@ fn main() { b := Int{} b.add(10) println(b.get()) // 10 + + use_config(Config{ 2, 'bar' }) + use_config(foo: 2, bar: 'bar') }