From 1cb752f722dac37ebb57500f29c6e6053151af4e Mon Sep 17 00:00:00 2001 From: Hunam Date: Fri, 1 Jul 2022 14:52:43 +0200 Subject: [PATCH 1/2] preliminary support for builtin functions, various fixes, various tweaks --- cmd/tools/builders/go_builder.v | 7 ++++++ cmd/tools/builders/golang_builder.v | 7 ------ cmd/v/v.v | 4 ++-- vlib/builtin/go/builtin.go.v | 5 +++++ vlib/v/ast/ast.v | 2 +- vlib/v/ast/types.v | 1 + vlib/v/builder/cc.v | 3 ++- vlib/v/builder/compile.v | 7 ++++-- .../golangbuilder.v => gobuilder/gobuilder.v} | 22 +++++++++++++------ vlib/v/checker/checker.v | 4 ++-- vlib/v/checker/fn.v | 7 +++--- vlib/v/gen/golang/golang.v | 18 +++++++++++---- vlib/v/parser/parser.v | 12 +++++++++- vlib/v/pref/pref.v | 4 ++-- 14 files changed, 71 insertions(+), 32 deletions(-) create mode 100644 cmd/tools/builders/go_builder.v delete mode 100644 cmd/tools/builders/golang_builder.v create mode 100644 vlib/builtin/go/builtin.go.v rename vlib/v/builder/{golangbuilder/golangbuilder.v => gobuilder/gobuilder.v} (63%) diff --git a/cmd/tools/builders/go_builder.v b/cmd/tools/builders/go_builder.v new file mode 100644 index 00000000000000..43a7a435923478 --- /dev/null +++ b/cmd/tools/builders/go_builder.v @@ -0,0 +1,7 @@ +module main + +import v.builder.gobuilder + +fn main() { + gobuilder.start() +} diff --git a/cmd/tools/builders/golang_builder.v b/cmd/tools/builders/golang_builder.v deleted file mode 100644 index d2b7edf1a69933..00000000000000 --- a/cmd/tools/builders/golang_builder.v +++ /dev/null @@ -1,7 +0,0 @@ -module main - -import v.builder.golangbuilder - -fn main() { - golangbuilder.start() -} diff --git a/cmd/v/v.v b/cmd/v/v.v index 5b3cfbd936eefe..4a304156ddb944 100644 --- a/cmd/v/v.v +++ b/cmd/v/v.v @@ -176,9 +176,9 @@ fn rebuild(prefs &pref.Preferences) { .interpret { util.launch_tool(prefs.is_verbose, 'builders/interpret_builder', os.args[1..]) } - .golang { + .@go { println('using Go WIP backend...') - util.launch_tool(prefs.is_verbose, 'builders/golang_builder', os.args[1..]) + util.launch_tool(prefs.is_verbose, 'builders/go_builder', os.args[1..]) } } } diff --git a/vlib/builtin/go/builtin.go.v b/vlib/builtin/go/builtin.go.v new file mode 100644 index 00000000000000..d4504c503f89b8 --- /dev/null +++ b/vlib/builtin/go/builtin.go.v @@ -0,0 +1,5 @@ +module builtin + +pub fn println(s string) { + #fmt.Println(s.str) +} diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 5b2d122f6cf6d7..d738e6758cc5a6 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -523,7 +523,7 @@ pub: method_idx int rec_mut bool // is receiver mutable rec_share ShareType - language Language // V, C, JS + language Language // V, C, JS, Go file_mode Language // whether *the file*, where a function was a '.c.v', '.js.v' etc. no_body bool // just a definition `fn C.malloc()` is_builtin bool // this function is defined in builtin/strconv diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index d82d1bd1676202..71b4eb552cc374 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -35,6 +35,7 @@ pub enum Language { v c js + @go amd64 // aka x86_64 i386 arm64 // 64-bit arm diff --git a/vlib/v/builder/cc.v b/vlib/v/builder/cc.v index f3a487eb5188ac..30a1c80bf9d150 100644 --- a/vlib/v/builder/cc.v +++ b/vlib/v/builder/cc.v @@ -523,7 +523,8 @@ pub fn (mut v Builder) cc() { // whether to just create a .c or .js file and exit, for example: `v -o v.c cmd.v` ends_with_c := v.pref.out_name.ends_with('.c') ends_with_js := v.pref.out_name.ends_with('.js') - if ends_with_c || ends_with_js { + ends_with_go := v.pref.out_name.ends_with('.go') + if ends_with_c || ends_with_js || ends_with_go { v.pref.skip_running = true msg_mv := 'os.mv_by_cp $v.out_name_c => $v.pref.out_name' util.timing_start(msg_mv) diff --git a/vlib/v/builder/compile.v b/vlib/v/builder/compile.v index 828e397704313c..d0afb65ced7166 100644 --- a/vlib/v/builder/compile.v +++ b/vlib/v/builder/compile.v @@ -88,7 +88,7 @@ fn (mut b Builder) run_compiled_executable_and_exit() { os.find_abs_path_of_executable(node_basename) or { panic('Could not find `$node_basename` in system path. Do you have Node.js installed?') } - } else if b.pref.backend == .golang { + } else if b.pref.backend == .@go { go_basename := $if windows { 'go.exe' } $else { 'go' } os.find_abs_path_of_executable(go_basename) or { panic('Could not find `$go_basename` in system path. Do you have Go installed?') @@ -99,7 +99,7 @@ fn (mut b Builder) run_compiled_executable_and_exit() { mut run_args := []string{cap: b.pref.run_args.len + 1} if b.pref.backend.is_js() { run_args << compiled_file - } else if b.pref.backend == .golang { + } else if b.pref.backend == .@go { run_args << ['run', compiled_file] } run_args << b.pref.run_args @@ -195,6 +195,9 @@ pub fn (v Builder) get_builtin_files() []string { if v.pref.backend.is_js() { builtin_files << v.v_files_from_dir(os.join_path(location, 'builtin', 'js')) + } else if v.pref.backend == .@go { + builtin_files << v.v_files_from_dir(os.join_path(location, 'builtin', + 'go')) } else { builtin_files << v.v_files_from_dir(os.join_path(location, 'builtin')) } diff --git a/vlib/v/builder/golangbuilder/golangbuilder.v b/vlib/v/builder/gobuilder/gobuilder.v similarity index 63% rename from vlib/v/builder/golangbuilder/golangbuilder.v rename to vlib/v/builder/gobuilder/gobuilder.v index 79cae393e91b3f..ce47b36a725a54 100644 --- a/vlib/v/builder/golangbuilder/golangbuilder.v +++ b/vlib/v/builder/gobuilder/gobuilder.v @@ -1,4 +1,4 @@ -module golangbuilder +module gobuilder import os import v.pref @@ -9,17 +9,25 @@ import v.gen.golang pub fn start() { mut args_and_flags := util.join_env_vflags_and_os_args()[1..] prefs, _ := pref.parse_args([], args_and_flags) - builder.compile('build', prefs, compile_golang) + builder.compile('build', prefs, compile_go) } -pub fn compile_golang(mut b builder.Builder) { - // v.files << v.v_files_from_dir(os.join_path(v.pref.vlib_path,'builtin','bare')) - files := [b.pref.path] +pub fn compile_go(mut b builder.Builder) { + mut files := b.get_builtin_files() + files << b.get_user_files() b.set_module_lookup_paths() - build_golang(mut b, files, b.pref.out_name) + if b.pref.is_verbose { + println('all .v files:') + println(files) + } + mut name := b.pref.out_name + if !name.ends_with('.go') { + name += '.go' + } + build_go(mut b, files, name) } -pub fn build_golang(mut b builder.Builder, v_files []string, out_file string) { +pub fn build_go(mut b builder.Builder, v_files []string, out_file string) { if b.pref.os == .windows { if !b.pref.is_shared && b.pref.build_mode != .build_module && !b.pref.out_name.ends_with('.exe') { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 7f372a7e509c7e..eea829fce92678 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1765,14 +1765,14 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { if c.ct_cond_stack.len > 0 { node.ct_conds = c.ct_cond_stack.clone() } - if c.pref.backend.is_js() || c.pref.backend == .golang { + if c.pref.backend.is_js() || c.pref.backend == .@go { // consider the the best way to handle the .go.vv files if !c.file.path.ends_with('.js.v') && !c.file.path.ends_with('.go.v') && !c.file.path.ends_with('.go.vv') { c.error('hash statements are only allowed in backend specific files such "x.js.v" and "x.go.v"', node.pos) } - if c.mod == 'main' && c.pref.backend != .golang { + if c.mod == 'main' && c.pref.backend != .@go { c.error('hash statements are not allowed in the main module. Place them in a separate module.', node.pos) } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index bbb503408a3990..55b25c2b3a1e88 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -438,7 +438,7 @@ pub fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type { pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.Type { fn_name := node.name - if fn_name == 'main' { + if fn_name == 'main' && node.language != .@go { c.error('the `main` function cannot be called in the program', node.pos) } mut has_generic := false // foo() instead of foo() @@ -1292,8 +1292,9 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } else { c.fail_if_unreadable(node.left, left_type, 'receiver') } - if left_sym.language != .js && (!left_sym.is_builtin() && method.mod != 'builtin') - && method.language == .v && method.no_body { + if left_sym.language != .js && left_sym.language != .@go + && (!left_sym.is_builtin() && method.mod != 'builtin') && method.language == .v + && method.no_body { c.error('cannot call a method that does not have a body', node.pos) } if node.concrete_types.len > 0 && method.generic_names.len > 0 diff --git a/vlib/v/gen/golang/golang.v b/vlib/v/gen/golang/golang.v index b7ac957c1b73c3..e990c67d777b6e 100644 --- a/vlib/v/gen/golang/golang.v +++ b/vlib/v/gen/golang/golang.v @@ -10,9 +10,10 @@ import v.pref import os const ( - bs = '\\' + bs = '\\' // when to break a line dependant on penalty - max_len = [0, 35, 60, 85, 93, 100] + max_len = [0, 35, 60, 85, 93, 100] + v_builtin_fns = ['println', 'print', 'eprintln', 'eprint', 'exit', 'panic'] ) pub struct Gen { @@ -867,6 +868,7 @@ pub fn (mut f Gen) expr_stmt(node ast.ExprStmt) { } pub fn (mut f Gen) enum_decl(node ast.EnumDecl) { + // TODO(hunam6): transform to custom type + iota f.attrs(node.attrs) if node.is_pub { f.write('pub ') @@ -889,8 +891,15 @@ pub fn (mut f Gen) enum_decl(node ast.EnumDecl) { } pub fn (mut f Gen) fn_decl(node ast.FnDecl) { - f.attrs(node.attrs) - f.write(node.stringify(f.table, f.cur_mod, f.mod2alias).replace('fn ', 'func ')) + v_fn_signature := node.stringify(f.table, f.cur_mod, f.mod2alias) + before_name_idx := v_fn_signature.index('fn') or { 0 } + 3 // will never equal 0 + after_name_idx := v_fn_signature.index('(') or { 0 } // will never equal 0 + mut name := v_fn_signature[before_name_idx..after_name_idx] + if v_fn_signature[..3] == 'pub' && name !in golang.v_builtin_fns { + name = name.capitalize() + } + + f.write('func $name${v_fn_signature[after_name_idx..]}') f.fn_body(node) } @@ -1124,6 +1133,7 @@ pub fn (mut f Gen) interface_method(method ast.FnDecl) { } pub fn (mut f Gen) module_stmt(mod ast.Module) { + dump(mod) f.set_current_module_name(mod.name) if mod.is_skipped { return diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index f7122bb45f1dbd..9d1acc9428d302 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -203,6 +203,9 @@ pub fn (mut p Parser) set_path(path string) { 'js' { p.file_backend_mode = .js } + 'go' { + p.file_backend_mode = .@go + } else { arch := pref.arch_from_string(actual_language) or { pref.Arch._auto } p.file_backend_mode = ast.pref_arch_to_table_language(arch) @@ -3096,7 +3099,7 @@ fn (mut p Parser) module_decl() ast.Module { mut module_pos := token.Pos{} mut name_pos := token.Pos{} mut mod_node := ast.Module{} - is_skipped := p.tok.kind != .key_module + mut is_skipped := p.tok.kind != .key_module if is_skipped { // the attributes were for something else != module, like a struct/fn/type etc. module_attrs = [] @@ -3133,6 +3136,13 @@ fn (mut p Parser) module_decl() ast.Module { full_name := util.qualify_module(p.pref, name, p.file_name) p.mod = full_name p.builtin_mod = p.mod == 'builtin' + dump(p.builtin_mod) + dump(p.file_backend_mode) + dump(p.language) + // NOTE: Not so sure about that + if p.builtin_mod && p.file_backend_mode == .@go { + is_skipped = true + } mod_node = ast.Module{ name: full_name short_name: name diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index 5b2440957a65a4..de602eb8b2c460 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -51,7 +51,7 @@ pub enum Backend { js_freestanding // The JavaScript freestanding backend native // The Native backend interpret // Interpret the ast - golang // Go backend + @go // Go backend } pub fn (b Backend) is_js() bool { @@ -922,7 +922,7 @@ pub fn backend_from_string(s string) ?Backend { match s { 'c' { return .c } 'js' { return .js_node } - 'go' { return .golang } + 'go' { return .@go } 'js_node' { return .js_node } 'js_browser' { return .js_browser } 'js_freestanding' { return .js_freestanding } From c78d98c0bae8381811438de001f009fbe5e881e8 Mon Sep 17 00:00:00 2001 From: Hunam Date: Fri, 1 Jul 2022 16:07:43 +0200 Subject: [PATCH 2/2] `@go` -> `golang` --- cmd/v/v.v | 2 +- vlib/v/ast/types.v | 2 +- vlib/v/builder/compile.v | 6 +++--- vlib/v/checker/checker.v | 4 ++-- vlib/v/checker/fn.v | 4 ++-- vlib/v/parser/parser.v | 7 ++----- vlib/v/pref/pref.v | 4 ++-- 7 files changed, 13 insertions(+), 16 deletions(-) diff --git a/cmd/v/v.v b/cmd/v/v.v index 4a304156ddb944..e3e10020f09d0b 100644 --- a/cmd/v/v.v +++ b/cmd/v/v.v @@ -176,7 +176,7 @@ fn rebuild(prefs &pref.Preferences) { .interpret { util.launch_tool(prefs.is_verbose, 'builders/interpret_builder', os.args[1..]) } - .@go { + .golang { println('using Go WIP backend...') util.launch_tool(prefs.is_verbose, 'builders/go_builder', os.args[1..]) } diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index 71b4eb552cc374..44ba74266dd28d 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -35,7 +35,7 @@ pub enum Language { v c js - @go + golang amd64 // aka x86_64 i386 arm64 // 64-bit arm diff --git a/vlib/v/builder/compile.v b/vlib/v/builder/compile.v index d0afb65ced7166..4aa0d5e77b4c20 100644 --- a/vlib/v/builder/compile.v +++ b/vlib/v/builder/compile.v @@ -88,7 +88,7 @@ fn (mut b Builder) run_compiled_executable_and_exit() { os.find_abs_path_of_executable(node_basename) or { panic('Could not find `$node_basename` in system path. Do you have Node.js installed?') } - } else if b.pref.backend == .@go { + } else if b.pref.backend == .golang { go_basename := $if windows { 'go.exe' } $else { 'go' } os.find_abs_path_of_executable(go_basename) or { panic('Could not find `$go_basename` in system path. Do you have Go installed?') @@ -99,7 +99,7 @@ fn (mut b Builder) run_compiled_executable_and_exit() { mut run_args := []string{cap: b.pref.run_args.len + 1} if b.pref.backend.is_js() { run_args << compiled_file - } else if b.pref.backend == .@go { + } else if b.pref.backend == .golang { run_args << ['run', compiled_file] } run_args << b.pref.run_args @@ -195,7 +195,7 @@ pub fn (v Builder) get_builtin_files() []string { if v.pref.backend.is_js() { builtin_files << v.v_files_from_dir(os.join_path(location, 'builtin', 'js')) - } else if v.pref.backend == .@go { + } else if v.pref.backend == .golang { builtin_files << v.v_files_from_dir(os.join_path(location, 'builtin', 'go')) } else { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index eea829fce92678..7f372a7e509c7e 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1765,14 +1765,14 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { if c.ct_cond_stack.len > 0 { node.ct_conds = c.ct_cond_stack.clone() } - if c.pref.backend.is_js() || c.pref.backend == .@go { + if c.pref.backend.is_js() || c.pref.backend == .golang { // consider the the best way to handle the .go.vv files if !c.file.path.ends_with('.js.v') && !c.file.path.ends_with('.go.v') && !c.file.path.ends_with('.go.vv') { c.error('hash statements are only allowed in backend specific files such "x.js.v" and "x.go.v"', node.pos) } - if c.mod == 'main' && c.pref.backend != .@go { + if c.mod == 'main' && c.pref.backend != .golang { c.error('hash statements are not allowed in the main module. Place them in a separate module.', node.pos) } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 55b25c2b3a1e88..7e8fef265d329a 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -438,7 +438,7 @@ pub fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type { pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.Type { fn_name := node.name - if fn_name == 'main' && node.language != .@go { + if fn_name == 'main' && node.language != .golang { c.error('the `main` function cannot be called in the program', node.pos) } mut has_generic := false // foo() instead of foo() @@ -1292,7 +1292,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } else { c.fail_if_unreadable(node.left, left_type, 'receiver') } - if left_sym.language != .js && left_sym.language != .@go + if left_sym.language != .js && left_sym.language != .golang && (!left_sym.is_builtin() && method.mod != 'builtin') && method.language == .v && method.no_body { c.error('cannot call a method that does not have a body', node.pos) diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 9d1acc9428d302..6af2a219b86260 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -204,7 +204,7 @@ pub fn (mut p Parser) set_path(path string) { p.file_backend_mode = .js } 'go' { - p.file_backend_mode = .@go + p.file_backend_mode = .golang } else { arch := pref.arch_from_string(actual_language) or { pref.Arch._auto } @@ -3136,11 +3136,8 @@ fn (mut p Parser) module_decl() ast.Module { full_name := util.qualify_module(p.pref, name, p.file_name) p.mod = full_name p.builtin_mod = p.mod == 'builtin' - dump(p.builtin_mod) - dump(p.file_backend_mode) - dump(p.language) // NOTE: Not so sure about that - if p.builtin_mod && p.file_backend_mode == .@go { + if p.builtin_mod && p.file_backend_mode == .golang { is_skipped = true } mod_node = ast.Module{ diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index de602eb8b2c460..5b2440957a65a4 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -51,7 +51,7 @@ pub enum Backend { js_freestanding // The JavaScript freestanding backend native // The Native backend interpret // Interpret the ast - @go // Go backend + golang // Go backend } pub fn (b Backend) is_js() bool { @@ -922,7 +922,7 @@ pub fn backend_from_string(s string) ?Backend { match s { 'c' { return .c } 'js' { return .js_node } - 'go' { return .@go } + 'go' { return .golang } 'js_node' { return .js_node } 'js_browser' { return .js_browser } 'js_freestanding' { return .js_freestanding }