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.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)
}

View File

@ -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) {

View File

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

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
}