From f2ea8ca62c2ea7dd401bb23625b37b087f43d262 Mon Sep 17 00:00:00 2001 From: Leah Lundqvist Date: Thu, 21 May 2020 22:36:06 +0200 Subject: [PATCH] jsgen: class -> factory --- vlib/builtin/js/builtin.v | 4 +- vlib/v/gen/js/js.v | 107 ++++++++++++++++++++++++++-------- vlib/v/gen/js/jsdoc.v | 7 +-- vlib/v/gen/js/tests/struct.js | 89 ++++++++++++++++++++++++++++ vlib/v/gen/js/tests/struct.v | 26 +++++++++ 5 files changed, 204 insertions(+), 29 deletions(-) create mode 100644 vlib/v/gen/js/tests/struct.js create mode 100644 vlib/v/gen/js/tests/struct.v diff --git a/vlib/builtin/js/builtin.v b/vlib/builtin/js/builtin.v index 2ee5f6ff3e..130777bf83 100644 --- a/vlib/builtin/js/builtin.v +++ b/vlib/builtin/js/builtin.v @@ -7,10 +7,10 @@ module builtin fn JS.console.log(arg ...string) fn JS.process.stdout.write(arg string) -pub fn println(s string) { +pub fn println(s any) { JS.console.log(s) } -pub fn print(s string) { +pub fn print(s any) { JS.process.stdout.write(s) } \ No newline at end of file diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index cbe5ed09d5..2cd15d0922 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -17,6 +17,9 @@ const ( 'var', 'void', 'while', 'with', 'yield'] tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t'] builtin_globals = ['println', 'print'] + type_values = { + + } ) struct JsGen { @@ -115,7 +118,8 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string for pub_var in g.namespaces_pub[node.name] { out += '\n\t\t$pub_var,' } - out += '\n\t};' + if g.namespaces_pub[node.name].len > 0 { out += '\n\t' } + out += '};' out += '\n})(' for i, key in imports.keys() { if i > 0 { out += ', ' } @@ -242,7 +246,7 @@ fn (mut g JsGen) to_js_typ(typ string) string { styp = g.to_js_typ(typ.replace('array_', '')) + '[]' } else if typ.starts_with('map_') { tokens := typ.split('_') - styp = 'Map<${tokens[1]}, ${tokens[2]}>' + styp = 'Map<${g.to_js_typ(tokens[1])}, ${g.to_js_typ(tokens[2])}>' } else { styp = typ } @@ -259,6 +263,42 @@ fn (mut g JsGen) to_js_typ(typ string) string { return styp } +fn (mut g JsGen) to_js_typ_val(typ string) string { + mut styp := '' + match typ { + 'number' { + styp = '0' + } + 'boolean' { + styp = 'false' + } + 'Object' { + styp = '{}' + } + 'string' { + styp = '""' + } + else { + if typ.starts_with('Map') { + styp = 'new Map()' + } else if typ.ends_with('[]') { + styp = '[]' + } else { + styp = '{}' + } + } + } + // 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() {} pub fn (mut g JsGen) gen_indent() { @@ -828,7 +868,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) { if is_main { g.write(')();') } - if !it.is_anon { + if !it.is_anon && !it.is_method { g.writeln('') } @@ -1021,27 +1061,44 @@ fn (mut g JsGen) enum_expr(node ast.Expr) { } fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) { - g.writeln('class ${g.js_name(node.name)} {') - g.inc_indent() - g.writeln(g.doc.gen_ctor(node.fields)) - g.writeln('constructor(values) {') + g.writeln(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))}') + } + if i < node.fields.len - 1 { g.write(', ') } + } + g.writeln(' }) {') g.inc_indent() for field in node.fields { - // TODO: Generate default struct init values - g.writeln('this.$field.name = values.$field.name') + g.writeln('this.$field.name = $field.name') } g.dec_indent() - g.writeln('}') + g.writeln('};') + + g.writeln('${g.js_name(node.name)}.prototype = {') + g.inc_indent() fns := g.method_fn_decls[node.name] - for cfn in fns { + + 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))}') + if i < node.fields.len - 1 || fns.len > 0 { g.writeln(',') } else { g.writeln('') } + } + + for i, cfn in fns { // TODO: Move cast to the entire array whenever it's possible it := cfn as ast.FnDecl - g.writeln('') g.gen_method_decl(it) + if i < fns.len - 1 { g.writeln(',') } else { g.writeln('') } } g.dec_indent() - g.writeln('}\n') + g.writeln('};\n') if node.is_pub { g.push_pub_var(node.name) } @@ -1050,18 +1107,22 @@ 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) name := type_sym.name - 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(', ') + 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.writeln('') + g.dec_indent() + g.write('})') } - g.dec_indent() - g.write('})') } fn (mut g JsGen) gen_ident(node ast.Ident) { diff --git a/vlib/v/gen/js/jsdoc.v b/vlib/v/gen/js/jsdoc.v index 2edfd8c476..4519af3747 100644 --- a/vlib/v/gen/js/jsdoc.v +++ b/vlib/v/gen/js/jsdoc.v @@ -53,7 +53,7 @@ fn (mut d JsDoc) gen_typ(typ, name string) string { return d.out.str() } -fn (mut d JsDoc) gen_ctor(fields []ast.StructField) string { +fn (mut d JsDoc) gen_fac_fn(fields []ast.StructField) string { d.reset() d.writeln('/**') d.write(' * @param {{') @@ -62,9 +62,7 @@ fn (mut d JsDoc) gen_ctor(fields []ast.StructField) string { // 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(', ') - } + if i < fields.len - 1 { d.write(', ') } } d.writeln('}} values - values for this class fields') d.writeln(' * @constructor') @@ -93,6 +91,7 @@ fn (mut d JsDoc) gen_fn(it ast.FnDecl) string { } } d.writeln(' * @returns {$type_name}') + d.writeln(' * @function') d.write('*/') return d.out.str() } diff --git a/vlib/v/gen/js/tests/struct.js b/vlib/v/gen/js/tests/struct.js new file mode 100644 index 0000000000..0ebcef9eb9 --- /dev/null +++ b/vlib/v/gen/js/tests/struct.js @@ -0,0 +1,89 @@ +// V_COMMIT_HASH 7e55261 +// V_CURRENT_COMMIT_HASH 79b1f27 +// Generated by the V compiler + +"use strict"; + +/** @namespace builtin */ +const builtin = (function () { + /** + * @param {any} s + * @returns {void} + * @function + */ + function println(s) { + console.log(s); + } + + /** + * @param {any} s + * @returns {void} + * @function + */ + function print(s) { + process.stdout.write(s); + } + + /* module exports */ + return { + println, + print, + }; +})(); + +/** @namespace main */ +const main = (function () { + /** + * @param {{value?: number, test?: Map, hello?: number[]}} values - values for this class fields + * @constructor + */ + function Int({ value = 0, test = new Map(), hello = [] }) { + this.value = value + this.test = test + this.hello = hello + }; + Int.prototype = { + /** @type {number} - value */ + value: 0, + /** @type {Map} - test */ + test: new Map(), + /** @type {number[]} - hello */ + hello: [], + /** + * @param {number} value + * @returns {void} + * @function + */ + add(value) { + const i = this; + i.value += value; + }, + /** + * @returns {number} + * @function + */ + get() { + const i = this; + return i.value; + } + }; + + + + /* program entry point */ + (function() { + const a = new Int({ + value: 10 + }); + a.add(5); + builtin.println(a); + const b = new Int({}); + b.add(10); + builtin.println(b.get()); + })(); + + /* module exports */ + return {}; +})(); + + diff --git a/vlib/v/gen/js/tests/struct.v b/vlib/v/gen/js/tests/struct.v new file mode 100644 index 0000000000..861456c2d8 --- /dev/null +++ b/vlib/v/gen/js/tests/struct.v @@ -0,0 +1,26 @@ +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 +} + +fn main() { + a := Int { value: 10 } + a.add(5) + println(a) // 15 + + b := Int{} + b.add(10) + println(b.get()) // 10 +}