jsgen: class -> factory

pull/4984/head
Leah Lundqvist 2020-05-21 22:36:06 +02:00 committed by GitHub
parent 1633675c11
commit f2ea8ca62c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 204 additions and 29 deletions

View File

@ -7,10 +7,10 @@ module builtin
fn JS.console.log(arg ...string) fn JS.console.log(arg ...string)
fn JS.process.stdout.write(arg string) fn JS.process.stdout.write(arg string)
pub fn println(s string) { pub fn println(s any) {
JS.console.log(s) JS.console.log(s)
} }
pub fn print(s string) { pub fn print(s any) {
JS.process.stdout.write(s) JS.process.stdout.write(s)
} }

View File

@ -17,6 +17,9 @@ const (
'var', 'void', 'while', 'with', 'yield'] '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'] 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'] builtin_globals = ['println', 'print']
type_values = {
}
) )
struct JsGen { 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] { for pub_var in g.namespaces_pub[node.name] {
out += '\n\t\t$pub_var,' out += '\n\t\t$pub_var,'
} }
out += '\n\t};' if g.namespaces_pub[node.name].len > 0 { out += '\n\t' }
out += '};'
out += '\n})(' out += '\n})('
for i, key in imports.keys() { for i, key in imports.keys() {
if i > 0 { out += ', ' } 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_', '')) + '[]' styp = g.to_js_typ(typ.replace('array_', '')) + '[]'
} else if typ.starts_with('map_') { } else if typ.starts_with('map_') {
tokens := typ.split('_') tokens := typ.split('_')
styp = 'Map<${tokens[1]}, ${tokens[2]}>' styp = 'Map<${g.to_js_typ(tokens[1])}, ${g.to_js_typ(tokens[2])}>'
} else { } else {
styp = typ styp = typ
} }
@ -259,6 +263,42 @@ fn (mut g JsGen) to_js_typ(typ string) string {
return styp 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 (g &JsGen) save() {}
pub fn (mut g JsGen) gen_indent() { pub fn (mut g JsGen) gen_indent() {
@ -828,7 +868,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) {
if is_main { if is_main {
g.write(')();') g.write(')();')
} }
if !it.is_anon { if !it.is_anon && !it.is_method {
g.writeln('') 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) { fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
g.writeln('class ${g.js_name(node.name)} {') g.writeln(g.doc.gen_fac_fn(node.fields))
g.inc_indent() g.write('function ${g.js_name(node.name)}({ ')
g.writeln(g.doc.gen_ctor(node.fields)) for i, field in node.fields {
g.writeln('constructor(values) {') 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() g.inc_indent()
for field in node.fields { for field in node.fields {
// TODO: Generate default struct init values g.writeln('this.$field.name = $field.name')
g.writeln('this.$field.name = values.$field.name')
} }
g.dec_indent() 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] 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 // TODO: Move cast to the entire array whenever it's possible
it := cfn as ast.FnDecl it := cfn as ast.FnDecl
g.writeln('')
g.gen_method_decl(it) g.gen_method_decl(it)
if i < fns.len - 1 { g.writeln(',') } else { g.writeln('') }
} }
g.dec_indent() g.dec_indent()
g.writeln('}\n') g.writeln('};\n')
if node.is_pub { if node.is_pub {
g.push_pub_var(node.name) 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) { fn (mut g JsGen) gen_struct_init(it ast.StructInit) {
type_sym := g.table.get_type_symbol(it.typ) type_sym := g.table.get_type_symbol(it.typ)
name := type_sym.name name := type_sym.name
g.writeln('new ${g.js_name(name)}({') if it.fields.len == 0 {
g.inc_indent() g.write('new ${g.js_name(name)}({})')
for i, field in it.fields { } else {
g.write('$field.name: ') g.writeln('new ${g.js_name(name)}({')
g.expr(field.expr) g.inc_indent()
if i < it.fields.len - 1 { for i, field in it.fields {
g.write(', ') 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) { fn (mut g JsGen) gen_ident(node ast.Ident) {

View File

@ -53,7 +53,7 @@ fn (mut d JsDoc) gen_typ(typ, name string) string {
return d.out.str() 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.reset()
d.writeln('/**') d.writeln('/**')
d.write(' * @param {{') 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. // so all struct members don't have to be initialized.
// TODO: Actually generate default struct init values :P // TODO: Actually generate default struct init values :P
d.write('$field.name?: ${d.gen.typ(field.typ)}') d.write('$field.name?: ${d.gen.typ(field.typ)}')
if i < fields.len - 1 { if i < fields.len - 1 { d.write(', ') }
d.write(', ')
}
} }
d.writeln('}} values - values for this class fields') d.writeln('}} values - values for this class fields')
d.writeln(' * @constructor') d.writeln(' * @constructor')
@ -93,6 +91,7 @@ fn (mut d JsDoc) gen_fn(it ast.FnDecl) string {
} }
} }
d.writeln(' * @returns {$type_name}') d.writeln(' * @returns {$type_name}')
d.writeln(' * @function')
d.write('*/') d.write('*/')
return d.out.str() return d.out.str()
} }

View File

@ -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<string, number>, 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<string, number>} - 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 {};
})();

View File

@ -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
}