diff --git a/assign_stmt.v b/assign_stmt.v index e25e7c0..2616170 100644 --- a/assign_stmt.v +++ b/assign_stmt.v @@ -1,5 +1,6 @@ // Copyright (c) 2024 Alexander Medvednikov. All rights reserved. // Use of this source code is governed by a GPL license that can be found in the LICENSE file. +import rand fn (mut app App) assign_stmt(assign AssignStmt, no_mut bool) { for l_idx, lhs_expr in assign.lhs { @@ -19,7 +20,22 @@ fn (mut app App) assign_stmt(assign AssignStmt, no_mut bool) { } else { app.gen(', ') } - app.expr(lhs_expr) + if lhs_expr is Ident { + // Handle shadowing + mut n := lhs_expr.name + if assign.tok == ':=' && n != '_' && n in app.cur_fn_names { + n += rand.intn(10000) or { 0 }.str() // LOL fix this + } + + app.cur_fn_names[n] = true + new_ident := Ident{ + ...lhs_expr + name: n + } + app.ident(new_ident) // lhs_expr) + } else { + app.expr(lhs_expr) + } } // Special case for 'append()' => '<<' if app.check_and_handle_append(assign) { diff --git a/ast.v b/ast.v index 5850e62..0fba3fa 100644 --- a/ast.v +++ b/ast.v @@ -37,6 +37,7 @@ type Stmt = AssignStmt | RangeStmt | ReturnStmt | SwitchStmt + | GoStmt struct InvalidExpr {} @@ -295,6 +296,7 @@ struct ReturnStmt { struct BranchStmt { node_type string @[json: '_type'] tok string @[json: 'Tok'] + label Ident @[json: 'Label'] // only for `goto` } struct FuncLit { @@ -303,6 +305,11 @@ struct FuncLit { body BlockStmt @[json: 'Body'] } +struct GoStmt { + node_type string @[json: '_type'] + call Expr @[json: 'Call'] +} + fn parse_go_ast(file_path string) !GoFile { data := os.read_file(file_path)! return json.decode(GoFile, data)! diff --git a/complex_tests/esbuild/api_impl/api_impl.vv b/complex_tests/esbuild/api_impl/api_impl.vv index 8b0b452..ebb9c74 100644 --- a/complex_tests/esbuild/api_impl/api_impl.vv +++ b/complex_tests/esbuild/api_impl/api_impl.vv @@ -318,9 +318,7 @@ fn validate_loader(value Loader) config.Loader { } } -__global versionRegex STRUCT -TYPE - +__global versionRegex = regexp.mustcompile(r'^([0-9]+)(?:\.([0-9]+))?(?:\.([0-9]+))?(-[A-Za-z0-9]+(?:\.[A-Za-z0-9]+)*)?$') fn validate_features(log logger.Log, target Target, engines []Engine) (compat.JSFeature, compat.CSSFeature, map[css_ast.D]compat.CSSPrefix, string) { if target == default_target && engines.len == 0 { return 0, 0, nil, '' @@ -1183,7 +1181,8 @@ fn validate_build_options(buildOpts BuildOptions, log logger.Log, realFS fs.FS) mut out_js, out_css := validate_output_extensions(log, build_opts.out_extension) mut banner_js, banner_css := validate_banner_or_footer(log, 'banner', build_opts.banner) mut footer_js, footer_css := validate_banner_or_footer(log, 'footer', build_opts.footer) - mut minify := build_opts.minify_whitespace && build_opts.minify_identifiers && build_opts.minify_syntax + mut minify := build_opts.minify_whitespace && build_opts.minify_identifiers + && build_opts.minify_syntax mut platform := validate_platform(build_opts.platform) mut defines, injected_defines := validate_defines(log, build_opts.define, build_opts.pure, platform, true, minify, build_opts.drop) @@ -1312,7 +1311,8 @@ fn validate_build_options(buildOpts BuildOptions, log logger.Log, realFS fs.FS) options.abs_output_dir = real_fs.dir(options.abs_output_file) } else if options.abs_output_dir == '' { options.write_to_stdout = true - if options.source_map != config.source_map_none && options.source_map != config.source_map_inline { + if options.source_map != config.source_map_none + && options.source_map != config.source_map_inline { log.adderror(nil, logger.Range{}, 'Cannot use an external source map without an output path') } if options.legal_comments.hasexternalfile() { @@ -1781,7 +1781,8 @@ fn (impl &pluginImpl) on_resolve(options OnResolveOptions, callback fn () (OnRes result.plugin_name = response.plugin_name result.abs_watch_files = impl.validatepathsarray(response.watch_files, 'watch file') result.abs_watch_dirs = impl.validatepathsarray(response.watch_dirs, 'watch directory') - if err == nil && response.suffix != '' && response.suffix[0] != `?` && response.suffix[0] != `#` { + if err == nil && response.suffix != '' && response.suffix[0] != `?` + && response.suffix[0] != `#` { err = strconv.errorf('Invalid path suffix %q returned from plugin (must start with "?" or "#")', response.suffix) } diff --git a/expr.v b/expr.v index 41aa557..62615cb 100644 --- a/expr.v +++ b/expr.v @@ -90,6 +90,7 @@ fn quoted_lit(s string, quote string) string { fn (mut app App) selector_expr(s SelectorExpr) { force_upper := app.force_upper // save force upper for `mod.ForceUpper` + app.force_upper = false app.expr(s.x) app.gen('.') app.force_upper = force_upper @@ -156,6 +157,10 @@ fn (mut app App) array_type(node ArrayType) { app.gen('[]') app.func_type(node.elt) } + ArrayType { + app.gen('[]') + app.array_type(node.elt) + } else { app.gen('UNKNOWN ELT ${node.elt.type_name()}') } @@ -165,6 +170,7 @@ fn (mut app App) array_type(node ArrayType) { fn (mut app App) map_type(node MapType) { app.force_upper = true app.gen('map[') + app.force_upper = true app.expr(node.key) app.gen(']') app.force_upper = true diff --git a/fn_call.v b/fn_call.v index 143276c..9166da6 100644 --- a/fn_call.v +++ b/fn_call.v @@ -62,11 +62,11 @@ fn (mut app App) call_expr(call CallExpr) { elt := fun.elt if elt is Ident && elt.name == 'byte' { x := call.args[0] - if x is BasicLit { - app.expr(x) - app.gen('.bytes()') - return - } + // TODO not every expr/type can be handled like this? + // if x is BasicLit { + app.expr(x) + app.gen('.bytes()') + return } } @@ -130,10 +130,14 @@ fn (mut app App) selector_expr_fn_call(call CallExpr, sel SelectorExpr) { // app.genln('///selector_expr_fn_call') if sel.x is Ident { if sel.x.name in nonexistent_modules { - app.handle_nonexistent_module_call(sel.x.name, sel.sel.name, call) + app.handle_nonexistent_module_call(sel, sel.x.name, sel.sel.name, call) return } } + app.selector_xxx(sel) +} + +fn (mut app App) selector_xxx(sel SelectorExpr) { app.expr(sel.x) app.gen('.') mut sel_name := sel.sel.name.to_lower() @@ -145,9 +149,16 @@ fn (mut app App) selector_expr_fn_call(call CallExpr, sel SelectorExpr) { fn (mut app App) make_call(call CallExpr) { // app.genln('//make ${call.fun.type_name()} ar0=${call.args[0].type_name()}') + app.force_upper = true app.expr(call.args[0]) + // len only + if call.args.len == 2 { + app.gen('{ len: ') + app.expr(call.args[1]) + app.gen(' }') + } // cap + len - if call.args.len == 3 { + else if call.args.len == 3 { app.gen('{ len: ') app.expr(call.args[1]) app.gen(', cap: ') @@ -158,13 +169,13 @@ fn (mut app App) make_call(call CallExpr) { } } -fn (mut app App) handle_nonexistent_module_call(mod_name string, fn_name string, node CallExpr) { +fn (mut app App) handle_nonexistent_module_call(sel SelectorExpr, mod_name string, fn_name string, node CallExpr) { match mod_name { 'strings' { app.handle_strings_call(app.go2v_ident(fn_name), node.args) } 'path' { - app.handle_path_call(app.go2v_ident(fn_name), node.args) + app.handle_path_call(sel, app.go2v_ident(fn_name), node.args) } 'fmt' { app.handle_fmt_call(app.go2v_ident(fn_name), node.args) @@ -181,10 +192,15 @@ fn (mut app App) handle_strings_call(fn_name string, args []Expr) { app.skip_first_arg = true } -fn (mut app App) handle_path_call(fn_name string, _ []Expr) { +fn (mut app App) handle_path_call(sel SelectorExpr, fn_name string, x []Expr) { if fn_name == 'base' { app.gen('os.base') } + // Go allows module name shadowing, so we can have a variable + // `path` + else { + app.selector_xxx(sel) + } } fn (mut app App) handle_fmt_call(fn_name string, _ []Expr) { diff --git a/fn_decl.v b/fn_decl.v index b87f54b..879c213 100644 --- a/fn_decl.v +++ b/fn_decl.v @@ -2,6 +2,7 @@ // Use of this source code is governed by a GPL license that can be found in the LICENSE file. fn (mut app App) func_decl(decl FuncDecl) { + app.cur_fn_names.clear() app.genln('') app.comments(decl.doc) method_name := app.go2v_ident(decl.name.name) // decl.name.name.to_lower() @@ -53,17 +54,25 @@ fn (mut app App) func_return_type(results FieldList) { // app.genln(results) // Return types return_types := results.list - if return_types.len > 1 { + needs_pars := return_types.len > 1 + //|| (return_types.len > 0 && return_types[0].names.len > 0 && return_types[0].names[0].name != '') + if needs_pars { app.gen('(') } for i, res in return_types { + /* + if res.names.len > 0 && res.names[0].name != '' { + app.gen(app.go2v_ident(res.names[0].name)) + app.gen(' ') + } + */ app.typ(res.typ) if i < return_types.len - 1 { app.gen(',') } //' ${decl.typ.results.list.map(type_or_ident(it.typ)).join(', ')}' } - if return_types.len > 1 { + if needs_pars { app.gen(')') } } diff --git a/go2v_test.v b/go2v_test.v index 375471a..6d3e0a6 100644 --- a/go2v_test.v +++ b/go2v_test.v @@ -13,6 +13,9 @@ fn test_all() { mut test_names := os.ls('tests') or { return } test_names.sort() mut tests_failures := []string{} + complex_names := os.ls('complex_tests/esbuild') or { return } + assert complex_names.len > 0 + test_names << complex_names for test_name in test_names { println('='.repeat(44)) create_json(subdir, test_name) diff --git a/main.v b/main.v index 922be5c..c5cb70e 100644 --- a/main.v +++ b/main.v @@ -17,7 +17,8 @@ mut: force_upper bool // for `field Type` in struct decl, `mod.UpperCase` types etc type_decl_name string is_enum_decl bool - is_mut_recv bool // so that `mut f Foo` is generated instead of `mut f &Foo` + is_mut_recv bool // so that `mut f Foo` is generated instead of `mut f &Foo` + cur_fn_names map[string]bool // for fixing shadowing } fn (mut app App) genln(s string) { @@ -146,6 +147,7 @@ fn (mut app App) run_test(subdir string, test_name string) ! { res := os.execute('v fmt -w ${v_path}') if res.exit_code != 0 { println(res) + app.tests_ok = false } mut formatted_v_code := os.read_file(v_path) or { panic(err) } diff --git a/stmt.v b/stmt.v index 834042d..5b9c15b 100644 --- a/stmt.v +++ b/stmt.v @@ -51,6 +51,9 @@ fn (mut app App) stmt(stmt Stmt) { DeferStmt { app.defer_stmt(stmt) } + GoStmt { + app.go_stmt(stmt) + } else { app.genln('\t// unhandled in stmt: ${stmt}') } // Add additional handlers as needed @@ -61,6 +64,11 @@ fn (mut app App) expr_stmt(stmt ExprStmt) { app.expr(stmt.x) } +fn (mut app App) go_stmt(stmt GoStmt) { + app.gen('go ') + app.expr(stmt.call) +} + fn (mut app App) block_stmt(body BlockStmt) { app.genln('{') // println('LIST=') @@ -126,12 +134,12 @@ fn (mut app App) range_stmt(node RangeStmt) { if node.key.name == '' { app.gen('_ ') } else { - app.gen(node.key.name) + app.gen(app.go2v_ident(node.key.name)) app.gen(', ') if node.value.name == '' { app.gen(' _ ') } else { - app.gen(node.value.name) + app.gen(app.go2v_ident(node.value.name)) } } app.gen(' in ') @@ -227,5 +235,9 @@ fn (mut app App) return_stmt(node ReturnStmt) { // continue break etc fn (mut app App) branch_stmt(node BranchStmt) { - app.genln(node.tok) + app.gen(node.tok) + if node.label.name != '' { + app.gen(' ' + node.label.name) + } + app.genln('') } diff --git a/struct.v b/struct.v index 9e88dfb..78ca025 100644 --- a/struct.v +++ b/struct.v @@ -136,6 +136,7 @@ fn (mut app App) struct_decl(struct_name string, spec StructType) { app.genln('pub mut:') } for field in spec.fields.list { + app.comments(field.doc) for n in field.names { app.gen('\t') app.gen(app.go2v_ident(n.name)) @@ -202,7 +203,9 @@ fn (mut app App) struct_init(c CompositeLit) { typ := c.typ match typ { Ident { - app.gen('${typ.name}{') + app.force_upper = true + n := app.go2v_ident(typ.name) + app.gen('${n}{') if c.elts.len > 0 { app.genln('') } diff --git a/todo.txt b/todo.txt new file mode 100644 index 0000000..45207b4 --- /dev/null +++ b/todo.txt @@ -0,0 +1,5 @@ +- Fix shadowing ("redefinition of xxx") +- Fix `else if x := .... ; x {` +- Implement `if obj, ok := json.Data.(*js_ast.EObject); ok {` +- []u8(byteptr) cast ??? + diff --git a/util.v b/util.v index 3ee28f8..edeb69e 100644 --- a/util.v +++ b/util.v @@ -54,7 +54,13 @@ fn (mut app App) go2v_ident(ident string) string { // println('ident=${ident} force_upper=${app.force_upper}') if app.force_upper { app.force_upper = false - return ident // go2v_ident2(ident) + if ident in v_keywords_which_are_not_go_keywords { + return ident + '_' + } + if ident in ['string', 'int', 'float64'] { + return ident + } + return ident.capitalize() } return go2v_ident2(ident) /*