jsgen: many fixes and updates
parent
a0ed1e2878
commit
dd534fde57
|
@ -29,6 +29,7 @@ mut:
|
|||
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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
if val is ast.EnumVal {
|
||||
// we want the type of the enum value not the enum
|
||||
styp = 'number'
|
||||
}
|
||||
ast.StructInit {
|
||||
} else if val is ast.StructInit {
|
||||
// no need to print jsdoc for structs
|
||||
styp = ''
|
||||
} else {}
|
||||
}
|
||||
|
||||
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)
|
||||
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.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) {
|
||||
|
@ -739,7 +786,7 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
// 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 {
|
||||
// TODO: Move cast to the entire array whenever it's possible
|
||||
it := cfn as ast.FnDecl
|
||||
g.writeln('')
|
||||
g.gen_method_decl(it)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
module hello1
|
||||
|
||||
// Unused for now: nested modules do not work yet
|
||||
|
||||
pub fn nested() string {
|
||||
return 'Nested'
|
||||
}
|
|
@ -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() + "!"
|
||||
}
|
|
@ -1,45 +1,90 @@
|
|||
// 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
|
||||
*/
|
||||
function standard() {
|
||||
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";
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
/* namespace: main */
|
||||
const main = (function (hl) {
|
||||
class Foo {
|
||||
/**
|
||||
* @param {{google: number, amazon: boolean, yahoo: string}} values - values for this class fields
|
||||
* @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) {
|
||||
|
@ -49,7 +94,7 @@ class Companies {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
* @returns {number}
|
||||
*/
|
||||
method() {
|
||||
const it = this;
|
||||
|
@ -68,6 +113,7 @@ class Companies {
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const POSITION = Object.freeze({
|
||||
go_back: 0,
|
||||
dont_go_back: 1,
|
||||
|
@ -76,7 +122,7 @@ class Companies {
|
|||
/**
|
||||
* @param {string} v_extends
|
||||
* @param {number} v_instanceof
|
||||
* @return {void}
|
||||
* @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,19 +196,20 @@ 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 {(message: string) => void} anon
|
||||
* @returns {void}
|
||||
*/
|
||||
function anon_consumer(greeting, anon) {
|
||||
anon(greeting);
|
||||
|
@ -156,16 +218,18 @@ class Companies {
|
|||
/**
|
||||
* @param {number} num
|
||||
* @param {string} def
|
||||
* @return {void}
|
||||
* @returns {void}
|
||||
*/
|
||||
function async(num, def) {
|
||||
}
|
||||
|
||||
/* [inline] */
|
||||
/* [deprecated] */
|
||||
/**
|
||||
* @deprecated
|
||||
* @param {number} game_on
|
||||
* @param {...string} dummy
|
||||
* @return {multi_return_int_int}
|
||||
* @returns {[number, number]}
|
||||
*/
|
||||
function hello(game_on, ...dummy) {
|
||||
for (let _tmp2 = 0; _tmp2 < dummy.length; ++_tmp2) {
|
||||
|
@ -181,9 +245,9 @@ class Companies {
|
|||
return [game_on + 2, 221];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* module exports */
|
||||
return {
|
||||
};
|
||||
})(hello);
|
||||
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
@ -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()
|
||||
}
|
Loading…
Reference in New Issue