From dd534fde57582c1c62b21c8e1fb24c5b8da68199 Mon Sep 17 00:00:00 2001 From: spaceface777 Date: Wed, 20 May 2020 16:57:42 +0200 Subject: [PATCH] jsgen: many fixes and updates --- vlib/v/gen/js/js.v | 314 ++++++++++++---------- vlib/v/gen/js/jsdoc.v | 24 +- vlib/v/gen/js/tests/hello/Hello1/hello1.v | 7 + vlib/v/gen/js/tests/hello/hello.v | 27 +- vlib/v/gen/js/tests/js.js | 140 +++++++--- vlib/v/gen/js/tests/js.v | 29 +- vlib/v/gen/js/tests/simple.js | 105 ++++++++ vlib/v/gen/js/tests/simple.v | 25 ++ 8 files changed, 474 insertions(+), 197 deletions(-) create mode 100644 vlib/v/gen/js/tests/hello/Hello1/hello1.v create mode 100644 vlib/v/gen/js/tests/simple.js create mode 100644 vlib/v/gen/js/tests/simple.v diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 2cd648f1f0..f8261459c5 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -19,29 +19,30 @@ const ( ) struct JsGen { - table &table.Table - definitions strings.Builder - pref &pref.Preferences + table &table.Table + definitions strings.Builder + pref &pref.Preferences mut: - out strings.Builder - namespaces map[string]strings.Builder - namespaces_pub map[string][]string - namespace_imports map[string]map[string]string - namespace string - doc &JsDoc - constants strings.Builder // all global V constants - file ast.File - tmp_count int - inside_ternary bool - inside_loop bool - is_test bool - indents map[string]int // indentations mapped to namespaces - 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.Stmt - empty_line bool + out strings.Builder + namespaces map[string]strings.Builder + namespaces_pub map[string][]string + namespace_imports map[string]map[string]string + 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 + is_test bool + indents map[string]int // indentations mapped to namespaces + 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.Stmt + empty_line bool } pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string { @@ -54,8 +55,13 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string fn_decl: 0 empty_line: true doc: 0 + enable_doc: true } g.doc = new_jsdoc(g) + // TODO: Add '[-no]-jsdoc' flag + if pref.is_prod { + g.enable_doc = false + } g.init() mut graph := depgraph.new_dep_graph() @@ -92,18 +98,18 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string g.finish() mut out := g.hashes() + g.definitions.str() + g.constants.str() for node in deps_resolved.nodes { - out += '\n/* namespace: $node.name */\n' + out += '/* namespace: $node.name */\n' out += 'const $node.name = (function (' imports := g.namespace_imports[node.name] for i, key in imports.keys() { if i > 0 { out += ', ' } out += imports[key] } - out += ') {' + out += ') {\n\t' // private scope - out += g.namespaces[node.name].str() + out += g.namespaces[node.name].str().trim_space() // public scope - out += '\n\t/* module exports */' + out += '\n\n\t/* module exports */' out += '\n\treturn {' for pub_var in g.namespaces_pub[node.name] { out += '\n\t\t$pub_var,' @@ -114,7 +120,7 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string if i > 0 { out += ', ' } out += key } - out += ');' + out += ');\n\n' } return out } @@ -125,20 +131,19 @@ pub fn (mut g JsGen) enter_namespace(n string) { // create a new namespace g.out = strings.new_builder(100) g.indents[g.namespace] = 0 - } - else { + } else { g.out = g.namespaces[g.namespace] } } pub fn (mut g JsGen) escape_namespace() { g.namespaces[g.namespace] = g.out - g.namespace = "" + g.namespace = '' } pub fn (mut g JsGen) push_pub_var(s string) { mut arr := g.namespaces_pub[g.namespace] - arr << s + arr << g.js_name(s) g.namespaces_pub[g.namespace] = arr } @@ -148,7 +153,7 @@ pub fn (mut g JsGen) find_class_methods(stmts []ast.Stmt) { ast.FnDecl { if it.is_method { // Found struct method, store it to be generated along with the class. - class_name := g.table.get_type_symbol(it.receiver.typ).name + class_name := g.table.get_type_name(it.receiver.typ) // Workaround until `map[key] << val` works. mut arr := g.method_fn_decls[class_name] arr << stmt @@ -161,7 +166,7 @@ pub fn (mut g JsGen) find_class_methods(stmts []ast.Stmt) { } pub fn (mut g JsGen) init() { - g.definitions.writeln('// Generated by the V compiler') + g.definitions.writeln('// Generated by the V compiler\n') g.definitions.writeln('"use strict";') g.definitions.writeln('') } @@ -170,7 +175,7 @@ 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 CONSTANTS = Object.freeze({') + g.constants.writeln('const _CONSTS = Object.freeze({') g.constants.write(constants) g.constants.writeln('});') g.constants.writeln('') @@ -179,7 +184,7 @@ pub fn (mut g JsGen) finish() { 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\n' + res += '// V_CURRENT_COMMIT_HASH ${util.githash(g.pref.building_v)}\n' return res } @@ -187,9 +192,28 @@ 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.replace('.', '__') - if styp.starts_with('JS__') { - styp = styp[4..] + 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_ { + return g.to_js_typ(styp) + '["prototype"]' } return g.to_js_typ(styp) } @@ -223,6 +247,14 @@ fn (mut g JsGen) to_js_typ(typ string) string { } } } + // ns.export => ns["export"] + for i, v in styp.split('.') { + if i == 0 { + styp = v + continue + } + styp += '["$v"]' + } return styp } @@ -259,13 +291,42 @@ pub fn (mut g JsGen) new_tmp_var() string { return '_tmp$g.tmp_count' } +// 'mod1.mod2.fn' => 'mod1.mod2' +// 'fn' => '' [inline] -fn js_name(name string) string { - // name := name_.replace('.', '__') - if name in js_reserved { - return 'v_$name' +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 name + return res +} + +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]] + + if alias != '' { + return alias + '.' + split[1..].join('.') + } + } + return name // No dot == no alias +} + +fn (mut g JsGen) js_name(name_ string) string { + ns := get_ns(name_) + mut name := if ns == g.namespace { name_.split('.').last() } else { g.get_alias(name_) } + mut parts := name.split('.') + for i, p in parts { + if p in js_reserved { parts[i] = 'v_$p' } + } + return parts.join('.') } fn (mut g JsGen) stmts(stmts []ast.Stmt) { @@ -337,7 +398,7 @@ fn (mut g JsGen) stmt(node ast.Stmt) { g.writeln('') } ast.GotoLabel { - g.writeln('${js_name(it.name)}:') + g.writeln('${g.js_name(it.name)}:') } ast.GotoStmt { // skip: JS has no goto @@ -377,11 +438,13 @@ fn (mut g JsGen) expr(node ast.Expr) { ast.ArrayInit { g.gen_array_init_expr(it) } + ast.AssignExpr { + g.gen_assign_expr(it) + } ast.BoolLiteral { if it.val == true { g.write('true') - } - else { + } else { g.write('false') } } @@ -389,30 +452,18 @@ fn (mut g JsGen) expr(node ast.Expr) { g.write("'$it.val'") } ast.CallExpr { - mut name := "" + mut name := '' if it.name.starts_with('JS.') { name = it.name[3..] } else { - name = it.name - // TODO: Ugly fix until `it.is_method` and `it.left` gets fixed. - // `it.left` should be the name of the module in this case. - // TODO: This should be in `if it.is_method` instead but is_method seems to be broken. - dot_idx := name.index('.') or {-1} // is there a way to do `if optional()`? - if dot_idx > -1 { - split := name.split('.') - imports := g.namespace_imports[g.namespace] - alias := imports[split.first()] - if alias != "" { - name = alias + "." + split[1..].join(".") - } - } + name = g.js_name(it.name) } g.expr(it.left) if it.is_method { // example: foo.bar.baz() g.write('.') } - g.write('${js_name(name)}(') + g.write('${g.js_name(name)}(') for i, arg in it.args { g.expr(arg.expr) if i != it.args.len - 1 { @@ -479,8 +530,8 @@ fn (mut g JsGen) expr(node ast.Expr) { g.gen_selector_expr(it) } ast.AnonFn { - g.gen_anon_fn_decl(it) - } + g.gen_anon_fn_decl(it) + } else { println(term.red('jsgen.expr(): bad node "${typeof(node)}"')) } @@ -514,7 +565,7 @@ fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) { // `expr ? "true" : "false"` g.expr(expr) g.write(' ? "true" : "false"') - } else { + } else { sym := g.table.get_type_symbol(it.expr_types[i]) match sym.kind { @@ -538,7 +589,7 @@ fn (mut g JsGen) gen_import_stmt(it ast.Import) { mut imports := g.namespace_imports[g.namespace] imports[it.mod] = it.alias g.namespace_imports[g.namespace] = imports - } +} fn (mut g JsGen) gen_array_init_expr(it ast.ArrayInit) { type_sym := g.table.get_type_symbol(it.typ) @@ -589,7 +640,7 @@ fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) { styp := g.typ(ident_var_info.typ) jsdoc.write(styp) - stmt.write(js_name(ident.name)) + stmt.write(g.js_name(ident.name)) if i < it.left.len - 1 { jsdoc.write(', ') @@ -602,23 +653,19 @@ fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) { g.write(stmt.str()) g.expr(it.right[0]) g.writeln(';') - } - else { + } else { // `a := 1` | `a,b := 1,2` for i, ident in it.left { val := it.right[i] ident_var_info := ident.var_info() mut styp := g.typ(ident_var_info.typ) - match val { - ast.EnumVal { - // we want the type of the enum value not the enum - styp = 'number' - } - ast.StructInit { - // no need to print jsdoc for structs - styp = '' - } else {} + 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 { @@ -631,11 +678,11 @@ fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) { g.write('const ') } - g.write('${js_name(ident.name)} = ') + g.write('${g.js_name(ident.name)} = ') g.expr(val) if g.inside_loop { - g.write("; ") + g.write('; ') } else { g.writeln(';') } @@ -643,6 +690,12 @@ fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) { } } +fn (mut g JsGen) gen_assign_expr(it ast.AssignExpr) { + g.expr(it.left) + g.write(' $it.op ') + g.expr(it.val) +} + fn (mut g JsGen) gen_attr(it ast.Attr) { g.writeln('/* [$it.name] */') } @@ -667,11 +720,13 @@ fn (mut g JsGen) gen_const_decl(it ast.ConstDecl) { g.expr(field.expr) val := g.out.after(pos) g.out.go_back(val.len) - typ := g.typ(field.typ) + 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.writeln(g.doc.gen_typ(typ, field.name)) - g.constants.write('\t') - g.constants.write('${js_name(field.name)}: $val') + g.constants.write('${g.js_name(field.name)}: $val') if i < it.fields.len - 1 { g.constants.writeln(',') } @@ -689,7 +744,7 @@ fn (mut g JsGen) gen_defer_stmts() { } fn (mut g JsGen) gen_enum_decl(it ast.EnumDecl) { - g.writeln('const ${js_name(it.name)} = Object.freeze({') + g.writeln('const ${g.js_name(it.name)} = Object.freeze({') g.inc_indent() for i, field in it.fields { g.write('$field.name: ') @@ -714,16 +769,8 @@ fn (mut g JsGen) gen_enum_decl(it ast.EnumDecl) { fn (mut g JsGen) gen_expr_stmt(it ast.ExprStmt) { g.expr(it.expr) expr := it.expr - match expr { - ast.IfExpr { - // no ; after an if expression - } - else { - if !g.inside_ternary { - g.writeln(';') - } - } - } + if expr is ast.IfExpr { } // no ; after an if expression + else if !g.inside_ternary { g.writeln(';') } } fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) { @@ -738,8 +785,8 @@ fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) { } fn (mut g JsGen) gen_anon_fn_decl(it ast.AnonFn) { - g.gen_method_decl(it.decl) - } + g.gen_method_decl(it.decl) +} fn (mut g JsGen) gen_method_decl(it ast.FnDecl) { g.fn_decl = &it @@ -756,7 +803,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) { } else if it.is_anon { g.write('function (') } else { - mut name := js_name(it.name.split('.').last()) + mut name := g.js_name(it.name) c := name[0] if c in [`+`, `-`, `*`, `/`] { name = util.replace_op(name) @@ -775,7 +822,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) { } g.write('${name}(') - if it.is_pub { + if it.is_pub && !it.is_method { g.push_pub_var(name) } } @@ -894,7 +941,7 @@ fn (mut g JsGen) gen_for_stmt(it ast.ForStmt) { fn (mut g JsGen) fn_args(args []table.Arg, is_variadic bool) { // no_names := args.len > 0 && args[0].name == 'arg_1' for i, arg in args { - name := js_name(arg.name) + name := g.js_name(arg.name) is_varg := i == args.len - 1 && is_variadic if is_varg { g.write('...$name') @@ -931,7 +978,7 @@ fn (mut g JsGen) gen_go_stmt(node ast.GoStmt) { g.dec_indent() g.writeln('});') } - else { } + else {} } } @@ -965,14 +1012,12 @@ fn (mut g JsGen) gen_map_init_expr(it ast.MapInit) { fn (mut g JsGen) gen_return_stmt(it ast.Return) { g.write('return ') - if g.fn_decl.name == 'main' { - // we can't return anything in main - g.writeln('void;') - return - } - - // multiple returns - if it.exprs.len > 1 { + if it.exprs.len == 0 { + // Returns nothing + } else if it.exprs.len == 1 { + g.expr(it.exprs[0]) + } else { + // Multi return g.write('[') for i, expr in it.exprs { g.expr(expr) @@ -982,53 +1027,38 @@ fn (mut g JsGen) gen_return_stmt(it ast.Return) { } g.write(']') } - else { - g.expr(it.exprs[0]) - } g.writeln(';') } fn (mut g JsGen) enum_expr(node ast.Expr) { match node { - ast.EnumVal { - g.write(it.val) - } - else { - g.expr(node) - } + ast.EnumVal { g.write(it.val) } + else { g.expr(node) } } } fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) { - g.writeln('class ${js_name(node.name)} {') + g.writeln('class ${g.js_name(node.name)} {') g.inc_indent() g.writeln(g.doc.gen_ctor(node.fields)) g.writeln('constructor(values) {') g.inc_indent() for field in node.fields { - g.writeln('this.$field.name = values.$field.name') + // TODO: Generate default struct init values + g.writeln('this.$field.name = values.$field.name') } g.dec_indent() g.writeln('}') - g.writeln('') fns := g.method_fn_decls[node.name] for cfn in fns { - // TODO: Fix this hack for type conversion - // Directly converting to FnDecl gives - // error: conversion to non-scalar type requested - match cfn { - ast.FnDecl { - g.gen_method_decl(it) - } - else {} - } - + // TODO: Move cast to the entire array whenever it's possible + it := cfn as ast.FnDecl + g.writeln('') + g.gen_method_decl(it) } - g.dec_indent() - g.writeln('}') - + g.writeln('}\n') if node.is_pub { g.push_pub_var(node.name) } @@ -1036,7 +1066,8 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) { fn (mut g JsGen) gen_struct_init(it ast.StructInit) { type_sym := g.table.get_type_symbol(it.typ) - g.writeln('new ${type_sym.name}({') + name := type_sym.name + g.writeln('new ${g.js_name(name)}({') g.inc_indent() for i, field in it.fields { g.write('$field.name: ') @@ -1052,10 +1083,11 @@ fn (mut g JsGen) gen_struct_init(it ast.StructInit) { fn (mut g JsGen) gen_ident(node ast.Ident) { if node.kind == .constant { - g.write('CONSTANTS.') + // TODO: Handle const namespacing: only consts in the main module are handled rn + g.write('_CONSTS.') } - name := js_name(node.name) + name := g.js_name(node.name) // TODO `is` // TODO handle optionals g.write(name) @@ -1087,7 +1119,7 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { g.inside_ternary = false g.write(')') } else { - //mut is_guard = false + // mut is_guard = false for i, branch in node.branches { if i == 0 { match branch.cond { @@ -1109,7 +1141,7 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { //g.writeln('} if (!$guard_ok) { /* else */') } else { */ g.writeln('} else {') - //} + // } } g.stmts(branch.stmts) } @@ -1128,11 +1160,7 @@ fn verror(s string) { fn fn_has_go(it ast.FnDecl) bool { mut has_go := false for stmt in it.stmts { - match stmt { - ast.GoStmt { - has_go = true - } else {} - } + if stmt is ast.GoStmt { has_go = true } } return has_go } diff --git a/vlib/v/gen/js/jsdoc.v b/vlib/v/gen/js/jsdoc.v index 1135434cbb..c4f48db6ba 100644 --- a/vlib/v/gen/js/jsdoc.v +++ b/vlib/v/gen/js/jsdoc.v @@ -25,11 +25,13 @@ fn (mut d JsDoc) gen_indent() { } fn (mut d JsDoc) write(s string) { + if !d.gen.enable_doc { return } d.gen_indent() d.out.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 @@ -45,7 +47,7 @@ fn (mut d JsDoc) gen_typ(typ, name string) string { d.write('/**') d.write(' @type {$typ}') if name.len > 0 { - d.write(' - ${js_name(name)}') + d.write(' - ${d.gen.js_name(name)}') } d.write(' */') return d.out.str() @@ -54,15 +56,18 @@ fn (mut d JsDoc) gen_typ(typ, name string) string { fn (mut d JsDoc) gen_ctor(fields []ast.StructField) string { d.reset() d.writeln('/**') - d.write('* @param {{') + d.write(' * @param {{') for i, field in fields { - d.write('$field.name: ${d.gen.typ(field.typ)}') + // 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.writeln(' * @constructor') d.write('*/') return d.out.str() } @@ -71,20 +76,23 @@ fn (mut d JsDoc) gen_fn(it ast.FnDecl) string { d.reset() type_name := d.gen.typ(it.return_type) d.writeln('/**') + if it.is_deprecated { + d.writeln(' * @deprecated') + } for i, arg in it.args { if it.is_method && i == 0 { continue } arg_type_name := d.gen.typ(arg.typ) is_varg := i == it.args.len - 1 && it.is_variadic - name := js_name(arg.name) + name := d.gen.js_name(arg.name) if is_varg { - d.writeln('* @param {...$arg_type_name} $name') + d.writeln(' * @param {...$arg_type_name} $name') } else { - d.writeln('* @param {$arg_type_name} $name') + d.writeln(' * @param {$arg_type_name} $name') } } - d.writeln('* @return {$type_name}') + d.writeln(' * @returns {$type_name}') d.write('*/') return d.out.str() } diff --git a/vlib/v/gen/js/tests/hello/Hello1/hello1.v b/vlib/v/gen/js/tests/hello/Hello1/hello1.v new file mode 100644 index 0000000000..6e017dde69 --- /dev/null +++ b/vlib/v/gen/js/tests/hello/Hello1/hello1.v @@ -0,0 +1,7 @@ +module hello1 + +// Unused for now: nested modules do not work yet + +pub fn nested() string { + return 'Nested' +} diff --git a/vlib/v/gen/js/tests/hello/hello.v b/vlib/v/gen/js/tests/hello/hello.v index 2339441a4e..0369c062fa 100644 --- a/vlib/v/gen/js/tests/hello/hello.v +++ b/vlib/v/gen/js/tests/hello/hello.v @@ -1,9 +1,30 @@ module hello -pub fn standard() string { - return "Hello" +// TODO: Fix const namespacing, uncomment once it works +/* +pub const ( + hello = 'Hello' +) +*/ + +pub struct A { +pub mut: + foo string +} + +pub fn (mut a A) update(s string) { + a.foo = s +} + +struct B {} + +pub enum C {} + +pub fn debugger() string { + v := B{} + return 'Hello' } pub fn excited() string { - return standard() + "!" + return debugger() + "!" } \ No newline at end of file diff --git a/vlib/v/gen/js/tests/js.js b/vlib/v/gen/js/tests/js.js index 935cdf389a..86a9cacc2d 100644 --- a/vlib/v/gen/js/tests/js.js +++ b/vlib/v/gen/js/tests/js.js @@ -1,46 +1,91 @@ -// V_COMMIT_HASH d697b28 -// V_CURRENT_COMMIT_HASH 11c06ec - +// V_COMMIT_HASH 74686d0 +// V_CURRENT_COMMIT_HASH 577d252 // Generated by the V compiler + "use strict"; -const CONSTANTS = Object.freeze({ +const _CONSTS = Object.freeze({ /** @type {number} - i_am_a_const */ i_am_a_const: 21214, /** @type {string} - v_super */ v_super: "amazing keyword" }); - /* namespace: hello */ -const hello = (function () { /** - * @return {string} +const hello = (function () { + class A { + /** + * @param {{foo?: string}} values - values for this class fields + * @constructor + */ + constructor(values) { + this.foo = values.foo + } + + /** + * @param {string} s + * @returns {void} + */ + update(s) { + const a = this; + a.foo = s; + } + } + + + class B { + /** + * @param {{}} values - values for this class fields + * @constructor + */ + constructor(values) { + } + } + + const C = Object.freeze({ + }); + + /** + * @returns {string} */ - function standard() { + function v_debugger() { + const v = new B({ + }); return "Hello"; } /** - * @return {string} + * @returns {string} */ function excited() { - return hello.standard() + "!"; + return v_debugger() + "!"; } - /* module exports */ return { - standard, + A, + C, + v_debugger, excited, }; })(); + /* namespace: main */ -const main = (function (greeting) { - -class Companies { +const main = (function (hl) { + class Foo { /** - * @param {{google: number, amazon: boolean, yahoo: string}} values - values for this class fields - * @constructor + * @param {{a?: hello["A"]["prototype"]}} values - values for this class fields + * @constructor + */ + constructor(values) { + this.a = values.a + } + } + + class Companies { + /** + * @param {{google?: number, amazon?: boolean, yahoo?: string}} values - values for this class fields + * @constructor */ constructor(values) { this.google = values.google @@ -49,7 +94,7 @@ class Companies { } /** - * @return {number} + * @returns {number} */ method() { const it = this; @@ -68,15 +113,16 @@ class Companies { return 0; } } + const POSITION = Object.freeze({ go_back: 0, dont_go_back: 1, }); /** - * @param {string} v_extends - * @param {number} v_instanceof - * @return {void} + * @param {string} v_extends + * @param {number} v_instanceof + * @returns {void} */ function v_class(v_extends, v_instanceof) { /** @type {number} - v_delete */ @@ -86,6 +132,21 @@ class Companies { /* program entry point */ (async function() { console.log("Hello from V.js!"); + /** @type {number} - a */ + let a = 1; + a *= 2; + a += 3; + console.log(a, " == 5"); + const b = new hl.A({ + }); + b.update("an update"); + console.log(b); + const c = new Foo({ + a: new hl.A({ + }) + }); + c.a.update("another update"); + console.log(c); /** @type {string} - v */ const v = "done"; { @@ -96,14 +157,14 @@ class Companies { /** @type {number} - pos */ const pos = POSITION.go_back; /** @type {string} - v_debugger */ - const v_debugger = "JS keyword"; + const v_debugger = "JS keywords"; /** @type {string} - v_await */ - const v_await = CONSTANTS.v_super + v_debugger; + const v_await = _CONSTS.v_super + ": " + v_debugger; /** @type {string} - v_finally */ let v_finally = "implemented"; console.log(v_await, v_finally); /** @type {number} - dun */ - const dun = CONSTANTS.i_am_a_const * 20; + const dun = _CONSTS.i_am_a_const * 20; for (let i = 0; i < 10; i++) { } @@ -117,7 +178,7 @@ class Companies { /** @type {number[]} - arr */ const arr = [1, 2, 3, 4, 5]; for (let _tmp1 = 0; _tmp1 < arr.length; ++_tmp1) { - let a = arr[_tmp1]; + let i = arr[_tmp1]; } /** @type {Map} - ma */ @@ -135,37 +196,40 @@ class Companies { resolve(); }); - /** @type {anon_1011_7_1} - fn_in_var */ + /** @type {(number: number) => void} - fn_in_var */ const fn_in_var = function (number) { console.log(tos3(`number: ${number}`)); }; - anon_consumer(greeting.excited(), function (message) { + hl.v_debugger(); + anon_consumer(hl.excited(), function (message) { console.log(message); }); })(); /** - * @param {string} greeting - * @param {anon_fn_18_1} anon - * @return {void} + * @param {string} greeting + * @param {(message: string) => void} anon + * @returns {void} */ function anon_consumer(greeting, anon) { anon(greeting); } /** - * @param {number} num - * @param {string} def - * @return {void} + * @param {number} num + * @param {string} def + * @returns {void} */ function async(num, def) { } /* [inline] */ + /* [deprecated] */ /** - * @param {number} game_on - * @param {...string} dummy - * @return {multi_return_int_int} + * @deprecated + * @param {number} game_on + * @param {...string} dummy + * @returns {[number, number]} */ function hello(game_on, ...dummy) { for (let _tmp2 = 0; _tmp2 < dummy.length; ++_tmp2) { @@ -180,10 +244,10 @@ class Companies { })(); return [game_on + 2, 221]; } - - /* module exports */ return { }; })(hello); + + diff --git a/vlib/v/gen/js/tests/js.v b/vlib/v/gen/js/tests/js.v index b508dbf35e..26aab2a389 100644 --- a/vlib/v/gen/js/tests/js.v +++ b/vlib/v/gen/js/tests/js.v @@ -1,4 +1,4 @@ -import hello as greeting +import hello as hl fn JS.alert(arg string) fn JS.console.log(arg string) @@ -8,6 +8,10 @@ const ( super = 'amazing keyword' ) +struct Foo { + a hl.A +} + struct Companies { google int amazon bool @@ -27,6 +31,19 @@ fn class(extends string, instanceof int) { fn main() { JS.console.log('Hello from V.js!') + mut a := 1 + a *= 2 + a += 3 + JS.console.log(a, ' == 5') // TODO: Handle string interpolation + + b := hl.A{} + b.update('an update') + JS.console.log(b) + + c := Foo{ hl.A{} } + c.a.update('another update') + JS.console.log(c) + v := "done" { _ := "block" @@ -34,9 +51,9 @@ fn main() { pos := POSITION.go_back - debugger := 'JS keyword' + debugger := 'JS keywords' // TODO: Implement interpolation - await := super + debugger + await := super + ': ' + debugger mut finally := 'implemented' JS.console.log(await, finally) @@ -50,7 +67,7 @@ fn main() { for x in 1..10 {} arr := [1,2,3,4,5] - for a in arr {} + for i in arr {} ma := { 'str': "done" @@ -67,7 +84,8 @@ fn main() { JS.console.log("number: $number") } - anon_consumer(greeting.excited(), fn (message string) { + hl.debugger() + anon_consumer(hl.excited(), fn (message string) { JS.console.log(message) }) } @@ -79,6 +97,7 @@ fn anon_consumer (greeting string, anon fn(message string)) { fn async(num int, def string) {} [inline] +[deprecated] fn hello(game_on int, dummy ...string) (int, int) { defer { do := "not" diff --git a/vlib/v/gen/js/tests/simple.js b/vlib/v/gen/js/tests/simple.js new file mode 100644 index 0000000000..b82664cd6f --- /dev/null +++ b/vlib/v/gen/js/tests/simple.js @@ -0,0 +1,105 @@ +// V_COMMIT_HASH 74686d0 +// V_CURRENT_COMMIT_HASH 577d252 +// Generated by the V compiler + +"use strict"; + +/* namespace: hello */ +const hello = (function () { + class A { + /** + * @param {{foo?: string}} values - values for this class fields + * @constructor + */ + constructor(values) { + this.foo = values.foo + } + + /** + * @param {string} s + * @returns {void} + */ + update(s) { + const a = this; + a.foo = s; + } + } + + + class B { + /** + * @param {{}} values - values for this class fields + * @constructor + */ + constructor(values) { + } + } + + const C = Object.freeze({ + }); + + /** + * @returns {string} + */ + function v_debugger() { + const v = new B({ + }); + return "Hello"; + } + + /** + * @returns {string} + */ + function excited() { + return v_debugger() + "!"; + } + + /* module exports */ + return { + A, + C, + v_debugger, + excited, + }; +})(); + +/* namespace: main */ +const main = (function (hello) { + class D { + /** + * @param {{a?: hello["A"]["prototype"]}} values - values for this class fields + * @constructor + */ + constructor(values) { + this.a = values.a + } + } + + /** + * @param {hello["A"]["prototype"]} arg + * @returns {void} + */ + function struct_arg(arg) { + console.log(arg); + } + + /* program entry point */ + (function() { + struct_arg(new hello.A({ + foo: "hello" + })); + /** @type {number} - a */ + let a = 1; + a += 2; + console.log(a); + const b = new hello.A({ + }); + console.log(b); + })(); + + /* module exports */ + return { + }; +})(hello); + + diff --git a/vlib/v/gen/js/tests/simple.v b/vlib/v/gen/js/tests/simple.v new file mode 100644 index 0000000000..e10aeebbf3 --- /dev/null +++ b/vlib/v/gen/js/tests/simple.v @@ -0,0 +1,25 @@ +import hello +// import hello.hello1 +// TODO: Uncomment once nested modules work + +fn JS.console.log() + +struct D { + a hello.A +} + +fn struct_arg (arg hello.A) { + JS.console.log(arg) +} + +fn main() { + + struct_arg(hello.A{ 'hello' }) + + mut a := 1 + a += 2 + JS.console.log(a) + b := hello.A{} + JS.console.log(b) + // hello1.nested() +}