jsgen: many fixes and updates

pull/4962/head
spaceface777 2020-05-20 16:57:42 +02:00 committed by GitHub
parent a0ed1e2878
commit dd534fde57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 474 additions and 197 deletions

View File

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

View File

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

View File

@ -0,0 +1,7 @@
module hello1
// Unused for now: nested modules do not work yet
pub fn nested() string {
return 'Nested'
}

View File

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

View File

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

View File

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

View File

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

View File

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