jsgen: more fixes and improvements

pull/5153/head
spaceface777 2020-05-31 20:48:31 +02:00 committed by GitHub
parent d7bb887c2a
commit 11e6734912
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 595 additions and 358 deletions

View File

@ -33,11 +33,11 @@ mut:
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
inside_map_set bool // map.set(key, value)
is_test bool
indents map[string]int // indentations mapped to namespaces
stmt_start_pos int
@ -52,7 +52,6 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
mut g := &JsGen{
out: strings.new_builder(100)
definitions: strings.new_builder(100)
constants: strings.new_builder(100)
table: table
pref: pref
fn_decl: 0
@ -98,11 +97,13 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
// resolve imports
deps_resolved := graph.resolve()
g.finish()
mut out := g.hashes() + g.definitions.str() + g.constants.str()
mut out := g.hashes() + g.definitions.str()
for node in deps_resolved.nodes {
out += g.doc.gen_namespace(node.name)
out += 'const $node.name = (function ('
name := g.js_name(node.name)
if g.enable_doc {
out += '/** @namespace $name */\n'
}
out += 'const $name = (function ('
imports := g.namespace_imports[node.name]
for i, key in imports.keys() {
if i > 0 { out += ', ' }
@ -112,7 +113,10 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
// private scope
out += g.namespaces[node.name].str().trim_space()
// public scope
out += '\n\n\t/* module exports */'
out += '\n'
if g.enable_doc {
out += '\n\t/* module exports */'
}
out += '\n\treturn {'
for pub_var in g.namespaces_pub[node.name] {
out += '\n\t\t$pub_var,'
@ -175,17 +179,6 @@ pub fn (mut g JsGen) init() {
g.definitions.writeln('')
}
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 _CONSTS = Object.freeze({')
g.constants.write(constants)
g.constants.writeln('});')
g.constants.writeln('')
}
}
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'
@ -195,109 +188,162 @@ 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
if styp.starts_with('JS.') {
styp = styp[3..]
mut styp := ''
match sym.kind {
.placeholder {
// This should never happen: means checker bug
styp = 'any'
}
.void {
styp = 'void'
}
.voidptr {
styp = 'any'
}
.byteptr, .charptr {
styp = 'string'
}
.i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, .any_int, .any_float, .size_t {
// TODO: Should u64 and i64 use BigInt rather than number?
styp = 'number'
}
.bool {
styp = 'boolean'
}
.none_ {
styp = 'undefined'
}
.string, .ustring, .char {
styp = 'string'
}
// 'array_array_int' => 'number[][]'
.array {
info := sym.info as table.Array
styp = g.typ(info.elem_type) + '[]'
}
.array_fixed {
info := sym.info as table.ArrayFixed
styp = g.array_fixed_typ(info.elem_type) or { g.typ(info.elem_type) + '[]' }
}
// 'map[string]int' => 'Map<string, number>'
.map {
info := sym.info as table.Map
key := g.typ(info.key_type)
val := g.typ(info.value_type)
styp = 'Map<$key, $val>'
}
.any {
styp = 'any'
}
// ns.Foo => alias["Foo"]["prototype"]
.struct_ {
styp = g.struct_typ(sym.name)
}
// '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(', ') + ']'
.multi_return {
info := sym.info as table.MultiReturn
types := info.types.map(g.typ(it))
joined := types.join(', ')
styp = '[$joined]'
}
.sum_type {
// TODO: Implement sumtypes
styp = 'sym_type'
}
.alias {
// TODO: Implement aliases
styp = 'alias'
}
.enum_ {
// NB: We could declare them as TypeScript enums but TS doesn't like
// our namespacing so these break if declared in a different module.
// Until this is fixed, We need to use the type of an enum's members
// rather than the enum itself, and this can only be 'number' for now
styp = 'number'
}
// 'anon_fn_7_7_1' => '(a number, b number) => void'
if styp.starts_with('anon_') {
.function {
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)
styp = res + ') => ' + g.typ(info.func.return_type)
}
// Struct instance => ns["class"]["prototype"]
if sym.kind == .struct_ && get_ns(styp).len > 0 {
return g.to_js_typ(g.get_alias(styp)) + '["prototype"]'
.interface_ {
// TODO: Implement interfaces
styp = 'interface'
}
return g.to_js_typ(styp)
}
fn (mut g JsGen) to_js_typ(typ string) string {
mut styp := ''
match typ {
'int' {
styp = 'number'
}
'bool' {
styp = 'boolean'
}
'voidptr' {
styp = 'Object'
}
'byteptr' {
styp = 'string'
}
'charptr' {
styp = 'string'
}
else {
if typ.starts_with('array_') {
styp = g.to_js_typ(typ.replace('array_', '')) + '[]'
} else if typ.starts_with('map_') {
tokens := typ.split('_')
styp = 'Map<${g.to_js_typ(tokens[1])}, ${g.to_js_typ(tokens[2])}>'
} else {
styp = typ
}
}
}
// ns.export => ns["export"]
for i, v in styp.split('.') {
if i == 0 {
styp = v
continue
}
styp += '["$v"]'
/* else {
println('jsgen.typ: Unhandled type $t')
styp = sym.name
} */
}
if styp.starts_with('JS.') { return styp[3..] }
return styp
}
fn (mut g JsGen) to_js_typ_val(typ string) string {
fn (mut g JsGen) struct_typ(s string) string {
ns := get_ns(s)
mut name := if ns == g.namespace { s.split('.').last() } else { g.get_alias(s) }
mut styp := ''
match typ {
'number' {
for i, v in name.split('.') {
if i == 0 { styp = v }
else { styp += '["$v"]' }
}
if ns in ['', g.namespace] { return styp }
return styp + '["prototype"]'
}
fn (mut g JsGen) to_js_typ_val(t table.Type) string {
sym := g.table.get_type_symbol(t)
mut styp := ''
match sym.kind {
.i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, .any_int, .any_float, .size_t {
styp = '0'
}
'boolean' {
.bool {
styp = 'false'
}
'Object' {
styp = '{}'
}
'string' {
.string {
styp = '""'
}
else {
if typ.starts_with('Map') {
.map {
styp = 'new Map()'
} else if typ.ends_with('[]') {
}
.array {
styp = '[]'
} else {
styp = '{}'
}
.struct_ {
styp = 'new ${g.js_name(sym.name)}({})'
}
else {
// TODO
styp = 'undefined'
}
// 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() {}
fn (mut g JsGen) array_fixed_typ(t table.Type) ?string {
sym := g.table.get_type_symbol(t)
match sym.kind {
.i8 { return 'Int8Array' }
.i16 { return 'Int16Array' }
.int { return 'Int32Array' }
.i64 { return 'BigInt64Array' }
.byte { return 'Uint8Array' }
.u16 { return 'Uint16Array' }
.u32 { return 'Uint32Array' }
.u64 { return 'BigUint64Array' }
.f32 { return 'Float32Array' }
.f64 { return 'Float64Array' }
else { return none }
}
}
pub fn (mut g JsGen) gen_indent() {
if g.indents[g.namespace] > 0 && g.empty_line {
@ -334,28 +380,19 @@ pub fn (mut g JsGen) new_tmp_var() string {
// 'fn' => ''
[inline]
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 res
idx := s.last_index('.') or { return '' }
return s.substr(0, idx)
}
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]]
ns := get_ns(name)
if ns == '' { return name }
if alias != '' {
return alias + '.' + split[1..].join('.')
}
}
return name // No dot == no alias
imports := g.namespace_imports[g.namespace]
alias := imports[ns]
if alias == '' { return name }
return alias + '.' + name.split('.').last()
}
fn (mut g JsGen) js_name(name_ string) string {
@ -519,7 +556,8 @@ fn (mut g JsGen) expr(node ast.Expr) {
// TODO
}
ast.EnumVal {
styp := g.typ(it.typ)
sym := g.table.get_type_symbol(it.typ)
styp := g.js_name(sym.name)
g.write('${styp}.${it.val}')
}
ast.FloatLiteral {
@ -631,26 +669,14 @@ fn (mut g JsGen) gen_assert_stmt(a ast.AssertStmt) {
fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) {
if it.left.len > it.right.len {
// multi return
jsdoc := strings.new_builder(50)
jsdoc.write('[')
stmt := strings.new_builder(50)
stmt.write('const [')
g.write('const [')
for i, ident in it.left {
ident_var_info := ident.var_info()
styp := g.typ(ident_var_info.typ)
jsdoc.write(styp)
stmt.write(g.js_name(ident.name))
g.write(g.js_name(ident.name))
if i < it.left.len - 1 {
jsdoc.write(', ')
stmt.write(', ')
g.write(', ')
}
}
jsdoc.write(']')
stmt.write('] = ')
g.writeln(g.doc.gen_typ(jsdoc.str(), ''))
g.write(stmt.str())
g.write('] = ')
g.expr(it.right[0])
g.writeln(';')
} else {
@ -660,16 +686,8 @@ fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) {
ident_var_info := ident.var_info()
mut styp := g.typ(ident_var_info.typ)
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 {
g.writeln(g.doc.gen_typ(styp, ident.name))
g.doc.gen_typ(styp)
}
if g.inside_loop || ident.is_mut {
@ -707,25 +725,16 @@ fn (mut g JsGen) gen_branch_stmt(it ast.BranchStmt) {
}
fn (mut g JsGen) gen_const_decl(it ast.ConstDecl) {
// old_indent := g.indents[g.namespace]
for i, field in it.fields {
// TODO hack. Cut the generated value and paste it into definitions.
pos := g.out.len
for field in it.fields {
g.doc.gen_const(g.typ(field.typ))
if field.is_pub { g.push_pub_var(field.name) }
g.write('const ${g.js_name(field.name)} = ')
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.writeln(';')
}
g.constants.write('\t')
g.constants.write('${g.js_name(field.name)}: $val')
if i < it.fields.len - 1 {
g.constants.writeln(',')
}
}
g.constants.writeln('')
g.writeln('')
}
fn (mut g JsGen) gen_defer_stmts() {
@ -738,23 +747,20 @@ fn (mut g JsGen) gen_defer_stmts() {
}
fn (mut g JsGen) gen_enum_decl(it ast.EnumDecl) {
g.writeln('const ${g.js_name(it.name)} = Object.freeze({')
g.doc.gen_enum()
g.writeln('const ${g.js_name(it.name)} = {')
g.inc_indent()
for i, field in it.fields {
mut i := 0
for field in it.fields {
g.write('$field.name: ')
if field.has_expr {
pos := g.out.len
g.expr(field.expr)
expr_str := g.out.after(pos)
g.out.go_back(expr_str.len)
g.write('$expr_str')
} else {
g.write('$i')
if field.has_expr && field.expr is ast.IntegerLiteral {
e := field.expr as ast.IntegerLiteral
i = e.val.int()
}
g.writeln(',')
g.writeln('${i++},')
}
g.dec_indent()
g.writeln('});')
g.writeln('};')
if it.is_pub {
g.push_pub_var(it.name)
}
@ -810,7 +816,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) {
// type_name := g.typ(it.return_type)
// generate jsdoc for the function
g.writeln(g.doc.gen_fn(it))
g.doc.gen_fn(it)
if has_go {
g.write('async ')
@ -1015,14 +1021,14 @@ fn (mut g JsGen) gen_hash_stmt(it ast.HashStmt) {
}
fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
g.writeln(g.doc.gen_fac_fn(node.fields))
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))}')
g.write('${g.to_js_typ_val(field.typ)}')
}
if i < node.fields.len - 1 { g.write(', ') }
}
@ -1040,8 +1046,9 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
fns := g.method_fn_decls[node.name]
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))}')
typ := g.typ(field.typ)
g.doc.gen_typ(typ)
g.write('$field.name: ${g.to_js_typ_val(field.typ)}')
if i < node.fields.len - 1 || fns.len > 0 { g.writeln(',') } else { g.writeln('') }
}
@ -1074,9 +1081,16 @@ fn (mut g JsGen) gen_array_init_expr(it ast.ArrayInit) {
fn (mut g JsGen) gen_assign_expr(it ast.AssignExpr) {
g.expr(it.left)
if g.inside_map_set && it.op == .assign {
g.inside_map_set = false
g.write(', ')
g.expr(it.val)
g.write(')')
} else {
g.write(' $it.op ')
g.expr(it.val)
}
}
fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
mut name := ''
@ -1105,11 +1119,6 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
}
fn (mut g JsGen) gen_ident(node ast.Ident) {
if node.kind == .constant {
// TODO: Handle const namespacing: only consts in the main module are handled rn
g.write('_CONSTS.')
}
name := g.js_name(node.name)
// TODO `is`
// TODO handle optionals
@ -1172,6 +1181,7 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) {
}
fn (mut g JsGen) gen_index_expr(it ast.IndexExpr) {
left_typ := g.table.get_type_symbol(it.left_type)
// TODO: Handle splice setting if it's implemented
if it.index is ast.RangeExpr {
range := it.index as ast.RangeExpr
@ -1190,8 +1200,28 @@ fn (mut g JsGen) gen_index_expr(it ast.IndexExpr) {
g.write('.length')
}
g.write(')')
} else if left_typ.kind == .map {
g.expr(it.left)
if it.is_setter {
g.inside_map_set = true
g.write('.set(')
} else {
// TODO Does this work in all cases?
g.write('.get(')
}
g.expr(it.index)
if !it.is_setter { g.write(')') }
} else if left_typ.kind == .string {
if it.is_setter {
// TODO: What's the best way to do this?
// 'string'[3] = `o`
} else {
g.expr(it.left)
g.write('.charCodeAt(')
g.expr(it.index)
g.write(')')
}
} else {
// TODO Does this cover all cases?
g.expr(it.left)
g.write('[')
g.expr(it.index)
@ -1209,6 +1239,13 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
if r_sym.kind == .array { g.write('...') } // arr << [1, 2]
g.expr(it.right)
g.write(')')
} else if r_sym.kind in [.array, .map] && it.op in [.key_in, .not_in] {
if it.op == .not_in { g.write('!(') }
g.expr(it.right)
g.write(if r_sym.kind == .map { '.has(' } else { '.includes(' })
g.expr(it.left)
g.write(')')
if it.op == .not_in { g.write(')') }
} else if it.op == .key_is { // foo is Foo
g.write('/*')
g.expr(it.left)

View File

@ -1,79 +1,59 @@
module js
import strings
import v.ast
struct JsDoc {
gen &JsGen
mut:
out strings.Builder
empty_line bool
}
fn new_jsdoc(gen &JsGen) &JsDoc {
return &JsDoc{
out: strings.new_builder(20)
gen: gen
}
}
fn (mut d JsDoc) gen_indent() {
if d.gen.indents[d.gen.namespace] > 0 && d.empty_line {
d.out.write(tabs[d.gen.indents[d.gen.namespace]])
}
d.empty_line = false
}
fn (mut d JsDoc) write(s string) {
if !d.gen.enable_doc { return }
d.gen_indent()
d.out.write(s)
d.gen.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
d.gen.writeln(s)
}
fn (mut d JsDoc) reset() {
d.out = strings.new_builder(20)
d.empty_line = false
fn (mut d JsDoc) gen_typ(typ string) {
d.writeln('/** @type {$typ} */')
}
fn (mut d JsDoc) gen_typ(typ, name string) string {
d.reset()
d.write('/**')
d.write(' @type {$typ}')
if name.len > 0 {
d.write(' - ${d.gen.js_name(name)}')
}
d.write(' */')
return d.out.str()
fn (mut d JsDoc) gen_const(typ string) {
d.writeln('/** @constant {$typ} */')
}
fn (mut d JsDoc) gen_fac_fn(fields []ast.StructField) string {
d.reset()
fn (mut d JsDoc) gen_enum() {
// Enum values can only be ints for now
typ := 'number'
d.writeln('/** @enum {$typ} */')
}
fn (mut d JsDoc) gen_fac_fn(fields []ast.StructField) {
d.writeln('/**')
d.writeln(' * @constructor')
d.write(' * @param {{')
for i, field in fields {
// 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.write('*/')
return d.out.str()
d.writeln('}} init')
d.writeln('*/')
}
fn (mut d JsDoc) gen_fn(it ast.FnDecl) string {
d.reset()
fn (mut d JsDoc) gen_fn(it ast.FnDecl) {
type_name := d.gen.typ(it.return_type)
d.writeln('/**')
d.writeln(' * @function')
if it.is_deprecated {
d.writeln(' * @deprecated')
}
@ -91,13 +71,5 @@ fn (mut d JsDoc) gen_fn(it ast.FnDecl) string {
}
}
d.writeln(' * @returns {$type_name}')
d.writeln(' * @function')
d.write('*/')
return d.out.str()
}
fn (mut d JsDoc) gen_namespace(ns string) string {
d.reset()
d.writeln('/** @namespace ${ns} */')
return d.out.str()
d.writeln('*/')
}

View File

@ -1,5 +1,5 @@
// V_COMMIT_HASH 0de70e8
// V_CURRENT_COMMIT_HASH 2eac2a5
// V_COMMIT_HASH 2943bdc
// V_CURRENT_COMMIT_HASH ad5deef
// Generated by the V compiler
"use strict";
@ -7,18 +7,18 @@
/** @namespace builtin */
const builtin = (function () {
/**
* @function
* @param {any} s
* @returns {void}
* @function
*/
function println(s) {
console.log(s);
}
/**
* @function
* @param {any} s
* @returns {void}
* @function
*/
function print(s) {
process.stdout.write(s);
@ -34,9 +34,9 @@ const builtin = (function () {
/** @namespace main */
const main = (function () {
/**
* @function
* @param {...number} args
* @returns {void}
* @function
*/
function variadic(...args) {
builtin.println(args);
@ -45,8 +45,8 @@ const main = (function () {
}
/**
* @returns {void}
* @function
* @returns {void}
*/
function vararg_test() {
variadic(1, 2, 3);
@ -55,17 +55,17 @@ const main = (function () {
/* program entry point */
(function() {
vararg_test();
/** @type {string[]} - arr1 */
/** @type {string[]} */
const arr1 = ["Hello", "JS", "Backend"];
/** @type {number[]} - arr2 */
/** @type {number[]} */
let arr2 = [1, 2, 3, 4, 5];
/** @type {string[]} - slice1 */
/** @type {string[]} */
const slice1 = arr1.slice(1, 3);
/** @type {number[]} - slice2 */
/** @type {number[]} */
const slice2 = arr2.slice(0, 3);
/** @type {number[]} - slice3 */
/** @type {number[]} */
const slice3 = arr2.slice(3, arr2.length);
/** @type {string} - idx1 */
/** @type {string} */
const idx1 = slice1[1];
arr2[0] = 1;
arr2[0 + 1] = 2;
@ -73,19 +73,24 @@ const main = (function () {
arr2.push(6);
arr2.push(...[7, 8, 9]);
builtin.println(arr2);
/** @type {string} - slice4 */
const slice4 = idx1.slice(0, 4);
/** @type {string} */
let slice4 = idx1.slice(0, 4);
builtin.println(slice4);
/** @type {byte} - idx2 */
const idx2 = slice4[0];
/** @type {Map<string, string>} - m */
/** @type {number} */
const idx2 = slice4.charCodeAt(0);
builtin.println(idx2);
/** @type {Map<string, string>} */
let m = new Map();
/** @type {string} - key */
/** @type {string} */
const key = "key";
m[key] = "value";
/** @type {string} - val */
const val = m["key"];
m.set(key, "value");
/** @type {string} */
const val = m.get("key");
builtin.println(val);
builtin.println(arr1.includes("JS"));
builtin.println(!(arr2.includes(3)));
builtin.println(m.has("key"));
builtin.println(!(m.has("badkey")));
})();
/* module exports */

View File

@ -34,11 +34,14 @@ arr2 << [7, 8, 9]
println(arr2)
// String slices
slice4 := idx1[..4]
mut slice4 := idx1[..4]
println(slice4) // 'Back'
// String indexes
idx2 := slice4[0]
println(idx2)
// TODO:
// slice4[3] = `c`
// Maps
mut m := map[string]string
@ -46,3 +49,9 @@ key := 'key'
m[key] = 'value'
val := m['key']
println(val)
// 'in' / '!in'
println('JS' in arr1)
println(3 !in arr2)
println('key' in m)
println('badkey' !in m)

View File

@ -0,0 +1,139 @@
// V_COMMIT_HASH 2943bdc
// V_CURRENT_COMMIT_HASH ad5deef
// Generated by the V compiler
"use strict";
/** @namespace builtin */
const builtin = (function () {
/**
* @function
* @param {any} s
* @returns {void}
*/
function println(s) {
console.log(s);
}
/**
* @function
* @param {any} s
* @returns {void}
*/
function print(s) {
process.stdout.write(s);
}
/* module exports */
return {
println,
print,
};
})();
/** @namespace hello */
const hello = (function () {
/**
* @function
* @returns {void}
*/
function raw_js_log() {
console.log('hello')
}
/** @constant {string} */
const hello = "Hello";
/**
* @constructor
* @param {{foo?: string}} init
*/
function Aaa({ foo = "" }) {
this.foo = foo
};
Aaa.prototype = {
/** @type {string} */
foo: "",
/**
* @function
* @param {string} s
* @returns {void}
*/
update(s) {
const a = this;
a.foo = s;
}
};
/**
* @constructor
* @param {{}} init
*/
function Bbb({ }) {
};
Bbb.prototype = {
};
/** @enum {number} */
const Ccc = {
a: 0,
b: 5,
c: 6,
};
/**
* @function
* @returns {string}
*/
function v_debugger() {
/** @type {Bbb} */
const v = new Bbb({});
return hello;
}
/**
* @function
* @returns {string}
*/
function excited() {
return v_debugger() + "!";
}
/* module exports */
return {
raw_js_log,
Aaa,
Ccc,
v_debugger,
excited,
};
})();
/** @namespace main */
const main = (function (hello) {
/** @enum {number} */
const Test = {
foo: 2,
bar: 5,
baz: 6,
};
/* program entry point */
(function() {
/** @type {number} */
let a = hello.Ccc.a;
a = hello.Ccc.b;
a = hello.Ccc.c;
builtin.println(a);
/** @type {number} */
let b = Test.foo;
b = Test.bar;
builtin.println(b);
})();
/* module exports */
return {};
})(hello);

View File

@ -0,0 +1,17 @@
import hello
enum Test {
foo = 2
bar = 5
baz
}
mut a := hello.Ccc.a
a = .b
a = .c
println(a)
mut b := Test.foo
b = .bar
println(b)

View File

@ -1,11 +1,8 @@
module hello
// TODO: Fix const namespacing, uncomment once it works
/*
pub const (
hello = 'Hello'
)
*/
pub struct Aaa {
pub mut:
@ -18,11 +15,15 @@ pub fn (mut a Aaa) update(s string) {
struct Bbb {}
pub enum Ccc {}
pub enum Ccc {
a
b = 5
c
}
pub fn debugger() string {
v := Bbb{}
return 'Hello'
return hello
}
pub fn excited() string {

View File

@ -1,31 +1,24 @@
// V_COMMIT_HASH 5423a15
// V_CURRENT_COMMIT_HASH 941404d
// V_COMMIT_HASH 2943bdc
// V_CURRENT_COMMIT_HASH ad5deef
// Generated by the V compiler
"use strict";
const _CONSTS = Object.freeze({
/** @type {number} - i_am_a_const */
i_am_a_const: 21214,
/** @type {string} - v_super */
v_super: "amazing keyword"
});
/** @namespace builtin */
const builtin = (function () {
/**
* @function
* @param {any} s
* @returns {void}
* @function
*/
function println(s) {
console.log(s);
}
/**
* @function
* @param {any} s
* @returns {void}
* @function
*/
function print(s) {
process.stdout.write(s);
@ -41,27 +34,30 @@ const builtin = (function () {
/** @namespace hello */
const hello = (function () {
/**
* @returns {void}
* @function
* @returns {void}
*/
function raw_js_log() {
console.log('hello')
}
/** @constant {string} */
const hello = "Hello";
/**
* @param {{foo?: string}} values - values for this class fields
* @constructor
* @param {{foo?: string}} init
*/
function Aaa({ foo = "" }) {
this.foo = foo
};
Aaa.prototype = {
/** @type {string} - foo */
/** @type {string} */
foo: "",
/**
* @function
* @param {string} s
* @returns {void}
* @function
*/
update(s) {
const a = this;
@ -71,29 +67,34 @@ const hello = (function () {
/**
* @param {{}} values - values for this class fields
* @constructor
* @param {{}} init
*/
function Bbb({ }) {
};
Bbb.prototype = {
};
const Ccc = Object.freeze({
});
/** @enum {number} */
const Ccc = {
a: 0,
b: 5,
c: 6,
};
/**
* @returns {string}
* @function
* @returns {string}
*/
function v_debugger() {
/** @type {Bbb} */
const v = new Bbb({});
return "Hello";
return hello;
}
/**
* @returns {string}
* @function
* @returns {string}
*/
function excited() {
return v_debugger() + "!";
@ -111,21 +112,26 @@ const hello = (function () {
/** @namespace main */
const main = (function (hl) {
/** @constant {number} */
const i_am_a_const = 21214;
/** @constant {string} */
const v_super = "amazing keyword";
/**
* @param {{a?: hl["Aaa"]["prototype"]}} values - values for this class fields
* @constructor
* @param {{a?: hl["Aaa"]["prototype"]}} init
*/
function Foo({ a = {} }) {
function Foo({ a = new hl.Aaa({}) }) {
this.a = a
};
Foo.prototype = {
/** @type {hl["Aaa"]["prototype"]} - a */
a: {}
/** @type {hl["Aaa"]["prototype"]} */
a: new hl.Aaa({})
};
/**
* @param {{google?: number, amazon?: boolean, yahoo?: string}} values - values for this class fields
* @constructor
* @param {{google?: number, amazon?: boolean, yahoo?: string}} init
*/
function Companies({ google = 0, amazon = false, yahoo = "" }) {
this.google = google
@ -133,26 +139,26 @@ const main = (function (hl) {
this.yahoo = yahoo
};
Companies.prototype = {
/** @type {number} - google */
/** @type {number} */
google: 0,
/** @type {boolean} - amazon */
/** @type {boolean} */
amazon: false,
/** @type {string} - yahoo */
/** @type {string} */
yahoo: "",
/**
* @returns {number}
* @function
* @returns {number}
*/
method() {
const it = this;
/** @type {Companies} */
const ss = new Companies({
google: 2,
amazon: true,
yahoo: "hello"
});
/** @type {[number, number]} */
const [a, b] = hello(2, "google", "not google");
/** @type {string} - glue */
/** @type {string} */
const glue = (a > 2 ? "more_glue" : a > 5 ? "more glueee" : "less glue");
if (a !== 2) {
}
@ -161,56 +167,63 @@ const main = (function (hl) {
}
};
const POSITION = Object.freeze({
/** @enum {number} */
const POSITION = {
go_back: 0,
dont_go_back: 1,
});
};
/**
* @function
* @param {string} v_extends
* @param {number} v_instanceof
* @returns {void}
* @function
*/
function v_class(v_extends, v_instanceof) {
/** @type {number} - v_delete */
/** @type {number} */
const v_delete = v_instanceof;
}
/* program entry point */
(async function() {
builtin.println("Hello from V.js!");
/** @type {number} - a */
/** @type {number} */
let a = 1;
a *= 2;
a += 3;
builtin.println(a);
/** @type {hl["Aaa"]["prototype"]} */
const b = new hl.Aaa({});
b.update("an update");
builtin.println(b);
/** @type {Foo} */
const c = new Foo({
a: new hl.Aaa({})
});
c.a.update("another update");
builtin.println(c);
/** @type {string} - v */
/** @type {string} */
const v = "done";
{
/** @type {string} - _ */
/** @type {string} */
const _ = "block";
}
/** @type {number} - pos */
/** @type {number} */
const pos = POSITION.go_back;
/** @type {string} - v_debugger */
/** @type {number} */
const enum2 = hl.Ccc.a;
/** @type {string} */
const v_debugger = "JS keywords";
/** @type {string} - v_await */
const v_await = _CONSTS.v_super + ": " + v_debugger;
/** @type {string} - v_finally */
/** @type {string} */
const v_await = v_super + ": " + v_debugger;
/** @type {string} */
let v_finally = "implemented";
console.log(v_await, v_finally);
/** @type {number} - dun */
const dun = _CONSTS.i_am_a_const * 20;
/** @type {number} */
const dun = i_am_a_const * 20;
/** @type {string} */
const dunn = hl.hello;
for (let i = 0; i < 10; i++) {
}
@ -221,19 +234,19 @@ const main = (function (hl) {
for (let x = 1; x < 10; ++x) {
}
/** @type {number[]} - arr */
/** @type {number[]} */
const arr = [1, 2, 3, 4, 5];
for (let _tmp1 = 0; _tmp1 < arr.length; ++_tmp1) {
let i = arr[_tmp1];
}
/** @type {Map<string, string>} - ma */
/** @type {Map<string, string>} */
const ma = new Map([
["str", "done"],
["ddo", "baba"]
]);
for (let [m, n] of ma) {
/** @type {string} - iss */
/** @type {string} */
const iss = m;
}
@ -242,7 +255,7 @@ const main = (function (hl) {
resolve();
});
/** @type {(number: number) => void} - fn_in_var */
/** @type {(number: number) => void} */
const fn_in_var = function (number) {
builtin.println(tos3(`number: ${number}`));
};
@ -254,20 +267,20 @@ const main = (function (hl) {
})();
/**
* @function
* @param {string} greeting
* @param {(message: string) => void} anon
* @returns {void}
* @function
*/
function anon_consumer(greeting, anon) {
anon(greeting);
}
/**
* @function
* @param {number} num
* @param {string} def
* @returns {void}
* @function
*/
function async(num, def) {
}
@ -275,21 +288,21 @@ const main = (function (hl) {
/* [inline] */
/* [deprecated] */
/**
* @function
* @deprecated
* @param {number} game_on
* @param {...string} dummy
* @returns {[number, number]}
* @function
*/
function hello(game_on, ...dummy) {
for (let _tmp2 = 0; _tmp2 < dummy.length; ++_tmp2) {
let dd = dummy[_tmp2];
/** @type {string} - l */
/** @type {string} */
const l = dd;
}
(function defer() {
/** @type {string} - v_do */
/** @type {string} */
const v_do = "not";
})();
return [game_on + 2, 221];

View File

@ -49,6 +49,7 @@ fn main() {
}
pos := POSITION.go_back
enum2 := hl.Ccc.a
debugger := 'JS keywords'
// TODO: Implement interpolation
@ -58,6 +59,7 @@ fn main() {
JS.console.log(await, finally)
dun := i_am_a_const * 20
dunn := hl.hello // External constant
for i := 0; i < 10; i++ {}

View File

@ -1,31 +1,24 @@
// V_COMMIT_HASH 0de70e8
// V_CURRENT_COMMIT_HASH 1c2dbea
// V_COMMIT_HASH 2943bdc
// V_CURRENT_COMMIT_HASH ad5deef
// Generated by the V compiler
"use strict";
const _CONSTS = Object.freeze({
/** @type {number} - w */
w: 30,
/** @type {number} - h */
h: 30
});
/** @namespace builtin */
const builtin = (function () {
/**
* @function
* @param {any} s
* @returns {void}
* @function
*/
function println(s) {
console.log(s);
}
/**
* @function
* @param {any} s
* @returns {void}
* @function
*/
function print(s) {
process.stdout.write(s);
@ -41,26 +34,31 @@ const builtin = (function () {
/** @namespace main */
const main = (function () {
/**
* @returns {void}
* @function
* @returns {void}
*/
function clear() {
console.clear();
}
/** @constant {number} */
const w = 30;
/** @constant {number} */
const h = 30;
/**
* @param {boolean[]} game
* @function
* @param {boolean[][]} game
* @param {number} x
* @param {number} y
* @returns {boolean}
* @function
*/
function get(game, x, y) {
if (y < 0 || x < 0) {
return false;
}
if (y >= _CONSTS.h || x >= _CONSTS.w) {
if (y >= h || x >= w) {
return false;
}
@ -68,14 +66,14 @@ const main = (function () {
}
/**
* @param {boolean[]} game
* @function
* @param {boolean[][]} game
* @param {number} x
* @param {number} y
* @returns {number}
* @function
*/
function neighbours(game, x, y) {
/** @type {number} - count */
/** @type {number} */
let count = 0;
if (get(game, x - 1, y - 1)) {
count++;
@ -113,21 +111,21 @@ const main = (function () {
}
/**
* @param {boolean[]} game
* @returns {boolean[]}
* @function
* @param {boolean[][]} game
* @returns {boolean[][]}
*/
function step(game) {
/** @type {boolean[]} - new_game */
/** @type {boolean[][]} */
let new_game = [[]];
for (let y = 0; y < game.length; ++y) {
let row = game[y];
/** @type {boolean[]} - new_row */
/** @type {boolean[]} */
let new_row = [];
new_game[y] = new_row;
for (let x = 0; x < row.length; ++x) {
let cell = row[x];
/** @type {number} - count */
/** @type {number} */
const count = neighbours(game, x, y);
new_row[x] = cell && count === 2 || count === 3;
}
@ -138,12 +136,12 @@ const main = (function () {
}
/**
* @function
* @param {boolean[]} row
* @returns {string}
* @function
*/
function row_str(row) {
/** @type {string} - str */
/** @type {string} */
let str = "";
for (let _tmp1 = 0; _tmp1 < row.length; ++_tmp1) {
let cell = row[_tmp1];
@ -159,9 +157,9 @@ const main = (function () {
}
/**
* @param {boolean[]} game
* @returns {void}
* @function
* @param {boolean[][]} game
* @returns {void}
*/
function show(game) {
clear();
@ -174,12 +172,12 @@ const main = (function () {
/* program entry point */
(function() {
/** @type {boolean[]} - game */
/** @type {boolean[][]} */
let game = [[]];
for (let y = 0; y < _CONSTS.h; ++y) {
/** @type {boolean[]} - row */
for (let y = 0; y < h; ++y) {
/** @type {boolean[]} */
let row = [];
for (let x = 0; x < _CONSTS.w; ++x) {
for (let x = 0; x < w; ++x) {
row[x] = false;
}

View File

@ -1,13 +1,14 @@
// V_COMMIT_HASH 0de70e8
// V_CURRENT_COMMIT_HASH 0de70e8
// V_COMMIT_HASH 2943bdc
// V_CURRENT_COMMIT_HASH ad5deef
// Generated by the V compiler
"use strict";
/* namespace: builtin */
/** @namespace builtin */
const builtin = (function () {
/**
* @param {string} s
* @function
* @param {any} s
* @returns {void}
*/
function println(s) {
@ -15,7 +16,8 @@ const builtin = (function () {
}
/**
* @param {string} s
* @function
* @param {any} s
* @returns {void}
*/
function print(s) {
@ -29,7 +31,7 @@ const builtin = (function () {
};
})();
/* namespace: main */
/** @namespace main */
const main = (function () {
/* program entry point */
(function() {
@ -37,8 +39,7 @@ const main = (function () {
})();
/* module exports */
return {
};
return {};
})();

View File

@ -1,5 +1,5 @@
// V_COMMIT_HASH 7e55261
// V_CURRENT_COMMIT_HASH 79b1f27
// V_COMMIT_HASH 2943bdc
// V_CURRENT_COMMIT_HASH ad5deef
// Generated by the V compiler
"use strict";
@ -7,18 +7,18 @@
/** @namespace builtin */
const builtin = (function () {
/**
* @function
* @param {any} s
* @returns {void}
* @function
*/
function println(s) {
console.log(s);
}
/**
* @function
* @param {any} s
* @returns {void}
* @function
*/
function print(s) {
process.stdout.write(s);
@ -34,8 +34,8 @@ const builtin = (function () {
/** @namespace main */
const main = (function () {
/**
* @param {{value?: number, test?: Map<string, number>, hello?: number[]}} values - values for this class fields
* @constructor
* @param {{value?: number, test?: Map<string, number>, hello?: number[]}} init
*/
function Int({ value = 0, test = new Map(), hello = [] }) {
this.value = value
@ -43,24 +43,24 @@ const main = (function () {
this.hello = hello
};
Int.prototype = {
/** @type {number} - value */
/** @type {number} */
value: 0,
/** @type {Map<string, number>} - test */
/** @type {Map<string, number>} */
test: new Map(),
/** @type {number[]} - hello */
/** @type {number[]} */
hello: [],
/**
* @function
* @param {number} value
* @returns {void}
* @function
*/
add(value) {
const i = this;
i.value += value;
},
/**
* @returns {number}
* @function
* @returns {number}
*/
get() {
const i = this;
@ -70,16 +70,49 @@ const main = (function () {
/**
* @constructor
* @param {{foo?: number, bar?: string}} init
*/
function Config({ foo = 0, bar = "" }) {
this.foo = foo
this.bar = bar
};
Config.prototype = {
/** @type {number} */
foo: 0,
/** @type {string} */
bar: ""
};
/**
* @function
* @param {Config} c
* @returns {void}
*/
function use_config(c) {
}
/* program entry point */
(function() {
/** @type {Int} */
const a = new Int({
value: 10
});
a.add(5);
builtin.println(a);
/** @type {Int} */
const b = new Int({});
b.add(10);
builtin.println(b.get());
use_config(new Config({
foo: 2,
bar: "bar"
}));
use_config(new Config({
foo: 2,
bar: "bar"
}));
})();
/* module exports */

View File

@ -15,6 +15,13 @@ fn (i Int) get() int {
return i.value
}
struct Config {
foo int
bar string
}
fn use_config(c Config) {}
fn main() {
a := Int { value: 10 }
a.add(5)
@ -23,4 +30,7 @@ fn main() {
b := Int{}
b.add(10)
println(b.get()) // 10
use_config(Config{ 2, 'bar' })
use_config(foo: 2, bar: 'bar')
}