From d78e7e3b2b7f9a040360fa1932ce0be916b78b63 Mon Sep 17 00:00:00 2001 From: playX Date: Mon, 23 Aug 2021 14:25:02 +0300 Subject: [PATCH] js: codegen fixes, W.I.P `os` availability for JS backend (#11281) --- vlib/builtin/js/array.js.v | 4 + vlib/math/pow.js.v | 4 + vlib/os/environment.js.v | 37 +++++ vlib/os/os.js.v | 97 +++++++++++++ vlib/os/{os_c.v => os_c.c.v} | 0 vlib/os/os_js.js.v | 47 +++++++ vlib/os/process.c.v | 248 ++++++++++++++++++++++++++++++++++ vlib/os/process.js.v | 117 ++++++++++++++++ vlib/os/process.v | 247 --------------------------------- vlib/v/gen/js/builtin_types.v | 2 +- vlib/v/gen/js/js.v | 106 +++++++++++---- 11 files changed, 635 insertions(+), 274 deletions(-) create mode 100644 vlib/os/environment.js.v create mode 100644 vlib/os/os.js.v rename vlib/os/{os_c.v => os_c.c.v} (100%) create mode 100644 vlib/os/os_js.js.v create mode 100644 vlib/os/process.c.v create mode 100644 vlib/os/process.js.v diff --git a/vlib/builtin/js/array.js.v b/vlib/builtin/js/array.js.v index 7fc4084540..51d762ad06 100644 --- a/vlib/builtin/js/array.js.v +++ b/vlib/builtin/js/array.js.v @@ -239,3 +239,7 @@ pub fn (a array) contains(key voidptr) bool { pub fn (mut a array) delete_last() { #a.val.arr.pop(); } + +[unsafe] +pub fn (a array) free() { +} diff --git a/vlib/math/pow.js.v b/vlib/math/pow.js.v index e10cf358ab..c8a5129016 100644 --- a/vlib/math/pow.js.v +++ b/vlib/math/pow.js.v @@ -1,3 +1,7 @@ module math fn JS.Math.pow(x f64, y f64) f64 + +pub fn pow(x f64, y f64) f64 { + return JS.Math.pow(x, y) +} diff --git a/vlib/os/environment.js.v b/vlib/os/environment.js.v new file mode 100644 index 0000000000..0e7961f56d --- /dev/null +++ b/vlib/os/environment.js.v @@ -0,0 +1,37 @@ +module os + +$if js_node { + #const $ENV = $process.env +} $else { + #const $ENV = {} +} + +// setenv sets the value of an environment variable with `name` to `value`. +pub fn setenv(key string, val string, overwrite bool) { + #if ($ENV[key] && !(overwrite.valueOf())) return; + #$ENV[key] = val + ''; +} + +// `getenv` returns the value of the environment variable named by the key. +pub fn getenv(key string) string { + mut res := '' + #if ($ENV[key]) res = new builtin.string($ENV[key]) + + return res +} + +// unsetenv clears an environment variable with `name`. +pub fn unsetenv(name string) int { + #$ENV[name] = "" + + return 1 +} + +pub fn environ() map[string]string { + mut res := map[string]string{} + #for (const key in $ENV) { + #res.map.set(key,$ENV[key]) + #} + + return res +} diff --git a/vlib/os/os.js.v b/vlib/os/os.js.v new file mode 100644 index 0000000000..a1342f466d --- /dev/null +++ b/vlib/os/os.js.v @@ -0,0 +1,97 @@ +module os + +#const $fs = require('fs'); +#const $path = require('path'); + +pub const ( + args = []string{} + path_delimiter = '/' + path_separator = '/' +) + +$if js_node { + #$process.argv.forEach(function(val,index) { args.arr[index] = new string(val); }) +} + +// real_path returns the full absolute path for fpath, with all relative ../../, symlinks and so on resolved. +// See http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html +// Also https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html +// and https://insanecoding.blogspot.com/2007/11/implementing-realpath-in-c.html +// NB: this particular rabbit hole is *deep* ... +pub fn real_path(fpath string) string { + $if js_node { + mut res := '' + #res = new string( $fs.realpathSync(fpath)) + + return res + } $else { + return fpath + } +} + +// flush will flush the stdout buffer. +pub fn flush() { + $if js_node { + #$process.stdout.write('') + } +} + +// chmod change file access attributes of `path` to `mode`. +// Octals like `0o600` can be used. +pub fn chmod(path string, mode int) { + $if js_node { + #$fs.chmodSync(''+path,mode.valueOf()) + } +} + +// chown changes the owner and group attributes of `path` to `owner` and `group`. +// Octals like `0o600` can be used. +pub fn chown(path string, owner int, group int) { + $if js_node { + #$fs.chownSync(''+path,owner.valueOf(),group.valueOf()) + } +} + +pub fn temp_dir() string { + mut res := '' + $if js_node { + #res = new builtin.string($os.tmpdir()) + } + return res +} + +pub fn home_dir() string { + mut res := '' + $if js_node { + #res = new builtin.string($os.homedir()) + } + return res +} + +// join_path returns a path as string from input string parameter(s). +pub fn join_path(base string, dirs ...string) string { + mut result := []string{} + result << base.trim_right('\\/') + for d in dirs { + result << d + } + mut path_sep := '' + #path_sep = $path.sep; + + res := result.join(path_sep) + return res +} + +pub fn execute(cmd string) Result { + mut exit_code := 0 + mut stdout := '' + #let commands = cmd.str.split(' '); + #let output = $child_process.spawnSync(commands[0],commands.slice(1,commands.length)); + #exit_code = new builtin.int(output.status) + #stdout = new builtin.string(output.stdout + '') + + return Result{ + exit_code: exit_code + output: stdout + } +} diff --git a/vlib/os/os_c.v b/vlib/os/os_c.c.v similarity index 100% rename from vlib/os/os_c.v rename to vlib/os/os_c.c.v diff --git a/vlib/os/os_js.js.v b/vlib/os/os_js.js.v new file mode 100644 index 0000000000..c198ce95c4 --- /dev/null +++ b/vlib/os/os_js.js.v @@ -0,0 +1,47 @@ +module os + +pub fn mkdir(path string) ?bool { + $if js_node { + if path == '.' { + return true + } + #$fs.mkdirSync(path.valueOf()) + + return true + } $else { + return false + } +} + +pub fn is_dir(path string) bool { + res := false + #res.val = $fs.existsSync(path,str) && $fs.lstatSync(path.str).isDirectory() + + return res +} + +pub fn is_link(path string) bool { + res := false + #res.val = $fs.existsSync(path.str) && $fs.lstatSync(path.str).isSymbolicLink() + + return res +} + +pub fn exists(path string) bool { + res := false + #res.val = $fs.existsSync(path.str) + + return res +} + +pub fn ls(path string) ?[]string { + if !is_dir(path) { + return error('ls(): cannot open dir $dir') + } + + result := []string{} + #let i = 0 + #$fs.readdirSync(path.str).forEach((path) => result.arr[i++] = new builtin.string(path)) + + return result +} diff --git a/vlib/os/process.c.v b/vlib/os/process.c.v new file mode 100644 index 0000000000..c7e2b981e3 --- /dev/null +++ b/vlib/os/process.c.v @@ -0,0 +1,248 @@ +module os + +// signal_kill - kills the process, after that it is no longer running +pub fn (mut p Process) signal_kill() { + if p.status !in [.running, .stopped] { + return + } + p._signal_kill() + p.status = .aborted + return +} + +// signal_pgkill - kills the whole process group +pub fn (mut p Process) signal_pgkill() { + if p.status !in [.running, .stopped] { + return + } + p._signal_pgkill() + return +} + +// signal_stop - stops the process, you can resume it with p.signal_continue() +pub fn (mut p Process) signal_stop() { + if p.status != .running { + return + } + p._signal_stop() + p.status = .stopped + return +} + +// signal_continue - tell a stopped process to continue/resume its work +pub fn (mut p Process) signal_continue() { + if p.status != .stopped { + return + } + p._signal_continue() + p.status = .running + return +} + +// wait - wait for a process to finish. +// NB: You have to call p.wait(), otherwise a finished process +// would get to a zombie state, and its resources will not get +// released fully, until its parent process exits. +// NB: This call will block the calling process until the child +// process is finished. +pub fn (mut p Process) wait() { + if p.status == .not_started { + p._spawn() + } + if p.status !in [.running, .stopped] { + return + } + p._wait() + return +} + +// close - free the OS resources associated with the process. +// Can be called multiple times, but will free the resources just once. +// This sets the process state to .closed, which is final. +pub fn (mut p Process) close() { + if p.status in [.not_started, .closed] { + return + } + p.status = .closed + $if !windows { + for i in 0 .. 3 { + if p.stdio_fd[i] != 0 { + fd_close(p.stdio_fd[i]) + } + } + } +} + +[unsafe] +pub fn (mut p Process) free() { + p.close() + unsafe { + p.filename.free() + p.err.free() + p.args.free() + p.env.free() + } +} + +// +// _spawn - should not be called directly, but only by p.run()/p.wait() . +// It encapsulates the fork/execve mechanism that allows the +// asynchronous starting of the new child process. +fn (mut p Process) _spawn() int { + if !p.env_is_custom { + p.env = []string{} + current_environment := environ() + for k, v in current_environment { + p.env << '$k=$v' + } + } + mut pid := 0 + $if windows { + pid = p.win_spawn_process() + } $else { + pid = p.unix_spawn_process() + } + p.pid = pid + p.status = .running + return 0 +} + +// is_alive - query whether the process p.pid is still alive +pub fn (mut p Process) is_alive() bool { + if p.status in [.running, .stopped] { + return p._is_alive() + } + return false +} + +// +pub fn (mut p Process) set_redirect_stdio() { + p.use_stdio_ctl = true + return +} + +pub fn (mut p Process) stdin_write(s string) { + p._check_redirection_call('stdin_write') + $if windows { + p.win_write_string(0, s) + } $else { + fd_write(p.stdio_fd[0], s) + } +} + +// will read from stdout pipe, will only return when EOF (end of file) or data +// means this will block unless there is data +pub fn (mut p Process) stdout_slurp() string { + p._check_redirection_call('stdout_slurp') + $if windows { + return p.win_slurp(1) + } $else { + return fd_slurp(p.stdio_fd[1]).join('') + } +} + +// read from stderr pipe, wait for data or EOF +pub fn (mut p Process) stderr_slurp() string { + p._check_redirection_call('stderr_slurp') + $if windows { + return p.win_slurp(2) + } $else { + return fd_slurp(p.stdio_fd[2]).join('') + } +} + +// read from stdout, return if data or not +pub fn (mut p Process) stdout_read() string { + p._check_redirection_call('stdout_read') + $if windows { + s, _ := p.win_read_string(1, 4096) + return s + } $else { + s, _ := fd_read(p.stdio_fd[1], 4096) + return s + } +} + +pub fn (mut p Process) stderr_read() string { + p._check_redirection_call('stderr_read') + $if windows { + s, _ := p.win_read_string(2, 4096) + return s + } $else { + s, _ := fd_read(p.stdio_fd[2], 4096) + return s + } +} + +// _check_redirection_call - should be called just by stdxxx methods +fn (mut p Process) _check_redirection_call(fn_name string) { + if !p.use_stdio_ctl { + panic('Call p.set_redirect_stdio() before calling p.$fn_name') + } + if p.status == .not_started { + panic('Call p.${fn_name}() after you have called p.run()') + } +} + +// _signal_stop - should not be called directly, except by p.signal_stop +fn (mut p Process) _signal_stop() { + $if windows { + p.win_stop_process() + } $else { + p.unix_stop_process() + } +} + +// _signal_continue - should not be called directly, just by p.signal_continue +fn (mut p Process) _signal_continue() { + $if windows { + p.win_resume_process() + } $else { + p.unix_resume_process() + } +} + +// _signal_kill - should not be called directly, except by p.signal_kill +fn (mut p Process) _signal_kill() { + $if windows { + p.win_kill_process() + } $else { + p.unix_kill_process() + } +} + +// _signal_pgkill - should not be called directly, except by p.signal_pgkill +fn (mut p Process) _signal_pgkill() { + $if windows { + p.win_kill_pgroup() + } $else { + p.unix_kill_pgroup() + } +} + +// _wait - should not be called directly, except by p.wait() +fn (mut p Process) _wait() { + $if windows { + p.win_wait() + } $else { + p.unix_wait() + } +} + +// _is_alive - should not be called directly, except by p.is_alive() +fn (mut p Process) _is_alive() bool { + $if windows { + return p.win_is_alive() + } $else { + return p.unix_is_alive() + } +} + +// run - starts the new process +pub fn (mut p Process) run() { + if p.status != .not_started { + return + } + p._spawn() + return +} diff --git a/vlib/os/process.js.v b/vlib/os/process.js.v new file mode 100644 index 0000000000..dc15c8b9f9 --- /dev/null +++ b/vlib/os/process.js.v @@ -0,0 +1,117 @@ +module os + +#const $child_process = require('child_process') + +// new_process - create a new process descriptor +// NB: new does NOT start the new process. +// That is done because you may want to customize it first, +// by calling different set_ methods on it. +// In order to start it, call p.run() or p.wait() +pub fn new_process(filename string) &Process { + return &Process{ + filename: filename + stdio_fd: [-1, -1, -1]! + } +} + +fn (mut p Process) spawn_internal() { + #p.val.pid = $child_process.spawn( + #p.val.filename+'', + #p.val.args.arr.map((x) => x.valueOf() + ''), + #{ + #env: (p.val.env_is_custom ? p.val.env : $process.env), + #}) + #p.val.pid.on('error', function (err) { builtin.panic('Failed to start subprocess') }) + + p.status = .running + // todo(playX): stderr,stdin + if p.use_stdio_ctl { + #p.val.pid.stdout.pipe(process.stdout) + #p.val.pid.stdin.pipe(process.stdin) + #p.val.pid.stderr.pipe(process.stderr) + } +} + +pub fn (mut p Process) run() { + if p.status != .not_started { + return + } + p.spawn_internal() + return +} + +pub fn (mut p Process) signal_kill() { + if p.status !in [.running, .stopped] { + return + } + #p.val.pid.kill('SIGKILL'); + + p.status = .aborted +} + +pub fn (mut p Process) signal_stop() { + if p.status !in [.running, .stopped] { + return + } + #p.val.pid.kill('SIGSTOP'); + + p.status = .aborted +} + +pub fn (mut p Process) signal_continue() { + if p.status != .stopped { + return + } + #p.val.pid.kill('SIGCONT'); + + p.status = .running + return +} + +pub fn (mut p Process) wait() { + if p.status == .not_started { + p.spawn_internal() + } + if p.status !in [.running, .stopped] { + return + } + + p.wait_internal() + return +} + +fn (mut p Process) wait_internal() { + #p.val.pid.on('exit', function (code) { console.log(code) }) +} + +pub fn (mut p Process) set_redirect_stdio() { + p.use_stdio_ctl = true + return +} + +pub fn (mut p Process) stdin_write(s string) { + p.check_redirection_call('stdin_write') + #p.val.pid.stdin.write(s) +} + +// todo(playX): probably does not work + +// will read from stdout pipe, will only return when EOF (end of file) or data +// means this will block unless there is data +pub fn (mut p Process) stdout_slurp() string { + p.check_redirection_call('stdout_slurp') + mut res := '' + #p.val.pid.stdout.on('data', function (data) { res = new builtin.string(data) }) + + return res +} + +// _check_redirection_call - should be called just by stdxxx methods +fn (mut p Process) check_redirection_call(fn_name string) { + if !p.use_stdio_ctl { + panic('Call p.set_redirect_stdio() before calling p.$fn_name') + } + if p.status == .not_started { + panic('Call p.${fn_name}() after you have called p.run()') + } +} diff --git a/vlib/os/process.v b/vlib/os/process.v index 8fa5e765b4..3f883987d3 100644 --- a/vlib/os/process.v +++ b/vlib/os/process.v @@ -68,250 +68,3 @@ pub fn (mut p Process) set_environment(envs map[string]string) { } return } - -// run - starts the new process -pub fn (mut p Process) run() { - if p.status != .not_started { - return - } - p._spawn() - return -} - -// signal_kill - kills the process, after that it is no longer running -pub fn (mut p Process) signal_kill() { - if p.status !in [.running, .stopped] { - return - } - p._signal_kill() - p.status = .aborted - return -} - -// signal_pgkill - kills the whole process group -pub fn (mut p Process) signal_pgkill() { - if p.status !in [.running, .stopped] { - return - } - p._signal_pgkill() - return -} - -// signal_stop - stops the process, you can resume it with p.signal_continue() -pub fn (mut p Process) signal_stop() { - if p.status != .running { - return - } - p._signal_stop() - p.status = .stopped - return -} - -// signal_continue - tell a stopped process to continue/resume its work -pub fn (mut p Process) signal_continue() { - if p.status != .stopped { - return - } - p._signal_continue() - p.status = .running - return -} - -// wait - wait for a process to finish. -// NB: You have to call p.wait(), otherwise a finished process -// would get to a zombie state, and its resources will not get -// released fully, until its parent process exits. -// NB: This call will block the calling process until the child -// process is finished. -pub fn (mut p Process) wait() { - if p.status == .not_started { - p._spawn() - } - if p.status !in [.running, .stopped] { - return - } - p._wait() - return -} - -// close - free the OS resources associated with the process. -// Can be called multiple times, but will free the resources just once. -// This sets the process state to .closed, which is final. -pub fn (mut p Process) close() { - if p.status in [.not_started, .closed] { - return - } - p.status = .closed - $if !windows { - for i in 0 .. 3 { - if p.stdio_fd[i] != 0 { - fd_close(p.stdio_fd[i]) - } - } - } -} - -[unsafe] -pub fn (mut p Process) free() { - p.close() - unsafe { - p.filename.free() - p.err.free() - p.args.free() - p.env.free() - } -} - -// -// _spawn - should not be called directly, but only by p.run()/p.wait() . -// It encapsulates the fork/execve mechanism that allows the -// asynchronous starting of the new child process. -fn (mut p Process) _spawn() int { - if !p.env_is_custom { - p.env = []string{} - current_environment := environ() - for k, v in current_environment { - p.env << '$k=$v' - } - } - mut pid := 0 - $if windows { - pid = p.win_spawn_process() - } $else { - pid = p.unix_spawn_process() - } - p.pid = pid - p.status = .running - return 0 -} - -// is_alive - query whether the process p.pid is still alive -pub fn (mut p Process) is_alive() bool { - if p.status in [.running, .stopped] { - return p._is_alive() - } - return false -} - -// -pub fn (mut p Process) set_redirect_stdio() { - p.use_stdio_ctl = true - return -} - -pub fn (mut p Process) stdin_write(s string) { - p._check_redirection_call('stdin_write') - $if windows { - p.win_write_string(0, s) - } $else { - fd_write(p.stdio_fd[0], s) - } -} - -// will read from stdout pipe, will only return when EOF (end of file) or data -// means this will block unless there is data -pub fn (mut p Process) stdout_slurp() string { - p._check_redirection_call('stdout_slurp') - $if windows { - return p.win_slurp(1) - } $else { - return fd_slurp(p.stdio_fd[1]).join('') - } -} - -// read from stderr pipe, wait for data or EOF -pub fn (mut p Process) stderr_slurp() string { - p._check_redirection_call('stderr_slurp') - $if windows { - return p.win_slurp(2) - } $else { - return fd_slurp(p.stdio_fd[2]).join('') - } -} - -// read from stdout, return if data or not -pub fn (mut p Process) stdout_read() string { - p._check_redirection_call('stdout_read') - $if windows { - s, _ := p.win_read_string(1, 4096) - return s - } $else { - s, _ := fd_read(p.stdio_fd[1], 4096) - return s - } -} - -pub fn (mut p Process) stderr_read() string { - p._check_redirection_call('stderr_read') - $if windows { - s, _ := p.win_read_string(2, 4096) - return s - } $else { - s, _ := fd_read(p.stdio_fd[2], 4096) - return s - } -} - -// _check_redirection_call - should be called just by stdxxx methods -fn (mut p Process) _check_redirection_call(fn_name string) { - if !p.use_stdio_ctl { - panic('Call p.set_redirect_stdio() before calling p.$fn_name') - } - if p.status == .not_started { - panic('Call p.${fn_name}() after you have called p.run()') - } -} - -// _signal_stop - should not be called directly, except by p.signal_stop -fn (mut p Process) _signal_stop() { - $if windows { - p.win_stop_process() - } $else { - p.unix_stop_process() - } -} - -// _signal_continue - should not be called directly, just by p.signal_continue -fn (mut p Process) _signal_continue() { - $if windows { - p.win_resume_process() - } $else { - p.unix_resume_process() - } -} - -// _signal_kill - should not be called directly, except by p.signal_kill -fn (mut p Process) _signal_kill() { - $if windows { - p.win_kill_process() - } $else { - p.unix_kill_process() - } -} - -// _signal_pgkill - should not be called directly, except by p.signal_pgkill -fn (mut p Process) _signal_pgkill() { - $if windows { - p.win_kill_pgroup() - } $else { - p.unix_kill_pgroup() - } -} - -// _wait - should not be called directly, except by p.wait() -fn (mut p Process) _wait() { - $if windows { - p.win_wait() - } $else { - p.unix_wait() - } -} - -// _is_alive - should not be called directly, except by p.is_alive() -fn (mut p Process) _is_alive() bool { - $if windows { - return p.win_is_alive() - } $else { - return p.unix_is_alive() - } -} diff --git a/vlib/v/gen/js/builtin_types.v b/vlib/v/gen/js/builtin_types.v index 80a96a30aa..ea792ad90b 100644 --- a/vlib/v/gen/js/builtin_types.v +++ b/vlib/v/gen/js/builtin_types.v @@ -348,7 +348,7 @@ fn (mut g JsGen) gen_builtin_type_defs() { 'f32', 'f64', 'float_literal' { g.gen_builtin_prototype( typ_name: typ_name - constructor: 'this.val = +val' + constructor: 'this.val = Number(val)' default_value: 'new Number(0)' to_jsval: '+this' ) diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 76f66fd797..eb2a2bc4c3 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -1595,6 +1595,7 @@ fn (mut g JsGen) gen_method_call(it ast.CallExpr) bool { } } } + // interfaces require dynamic dispatch. To obtain method table we use getPrototypeOf g.write('Object.getPrototypeOf(') g.expr(it.left) @@ -1660,6 +1661,15 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { } g.call_stack << it mut name := g.js_name(it.name) + ret_sym := g.table.get_type_symbol(it.return_type) + if it.language == .js && ret_sym.name in js.v_types && ret_sym.name != 'void' { + g.write('new ') + if g.ns.name != 'builtin' { + g.write('builtin.') + } + g.write(ret_sym.name) + g.write('(') + } call_return_is_optional := it.return_type.has_flag(.optional) if call_return_is_optional { g.writeln('(function(){') @@ -1717,6 +1727,9 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { g.dec_indent() g.write('})()') } + if it.language == .js && ret_sym.name in js.v_types && ret_sym.name != 'void' { + g.write(')') + } g.call_stack.delete_last() } @@ -1761,7 +1774,7 @@ fn (mut g JsGen) need_tmp_var_in_match(node ast.MatchExpr) bool { return false } -fn (mut g JsGen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string) { +fn (mut g JsGen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var MatchCond, tmp_var string) { type_sym := g.table.get_type_symbol(node.cond_type) for j, branch in node.branches { is_last := j == node.branches.len - 1 @@ -1801,27 +1814,37 @@ fn (mut g JsGen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var s } match type_sym.kind { .array { - g.write('vEq($cond_var, ') + g.write('vEq(') + g.match_cond(cond_var) + g.write(',') g.expr(expr) g.write(')') } .array_fixed { - g.write('vEq($cond_var, ') + g.write('vEq(') + g.match_cond(cond_var) + g.write(',') g.expr(expr) g.write(')') } .map { - g.write('vEq($cond_var, ') + g.write('vEq(') + g.match_cond(cond_var) + g.write(',') g.expr(expr) g.write(')') } .string { - g.write('vEq($cond_var, ') + g.write('vEq(') + g.match_cond(cond_var) + g.write(',') g.expr(expr) g.write(')') } .struct_ { - g.write('vEq($cond_var, ') + g.write('vEq(') + g.match_cond(cond_var) + g.write(',') g.expr(expr) g.write(')') } @@ -1837,15 +1860,19 @@ fn (mut g JsGen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var s } g.write('(') if !skip_low { - g.write('$cond_var >= ') + g.match_cond(cond_var) + g.write(' >= ') g.expr(expr.low) g.write(' && ') } - g.write('$cond_var <= ') + g.match_cond(cond_var) + g.write(' <= ') g.expr(expr.high) g.write(')') } else { - g.write('vEq($cond_var,') + g.write('vEq(') + g.match_cond(cond_var) + g.write(',') g.expr(expr) g.write(')') } @@ -1865,6 +1892,27 @@ fn (mut g JsGen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var s } } +type MatchCond = CondExpr | CondString + +struct CondString { + s string +} + +struct CondExpr { + expr ast.Expr +} + +fn (mut g JsGen) match_cond(cond MatchCond) { + match cond { + CondString { + g.writeln(cond.s) + } + CondExpr { + g.expr(cond.expr) + } + } +} + fn (mut g JsGen) match_expr(node ast.MatchExpr) { if node.cond_type == 0 { g.writeln('// match 0') @@ -1873,16 +1921,22 @@ fn (mut g JsGen) match_expr(node ast.MatchExpr) { prev := g.inside_ternary need_tmp_var := g.need_tmp_var_in_match(node) is_expr := (node.is_expr && node.return_type != ast.void_type) || g.inside_ternary - mut cond_var := '' + mut cond_var := MatchCond(CondString{''}) mut tmp_var := '' if is_expr && !need_tmp_var { g.inside_ternary = true } - cond_var = g.new_tmp_var() - g.write('let $cond_var = ') - g.expr(node.cond) - g.write(';') + if node.cond is ast.Ident || node.cond is ast.SelectorExpr || node.cond is ast.IntegerLiteral + || node.cond is ast.StringLiteral || node.cond is ast.FloatLiteral { + cond_var = CondExpr{node.cond} + } else { + s := g.new_tmp_var() + cond_var = CondString{s} + g.write('let $s = ') + g.expr(node.cond) + g.writeln(';') + } if need_tmp_var { tmp_var = g.new_tmp_var() g.writeln('let $tmp_var = undefined;') @@ -1927,7 +1981,7 @@ fn (mut g JsGen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) { } } -fn (mut g JsGen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string) { +fn (mut g JsGen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var MatchCond, tmp_var string) { for j, branch in node.branches { mut sumtype_index := 0 for { @@ -1954,7 +2008,7 @@ fn (mut g JsGen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var s } else { g.write('if (') } - g.write(cond_var) + g.match_cond(cond_var) if sym.kind == .sum_type { g.write(' instanceof ') g.expr(branch.exprs[sumtype_index]) @@ -2669,20 +2723,20 @@ fn (mut g JsGen) gen_float_literal_expr(it ast.FloatLiteral) { // TODO: call.language always seems to be "v", parser bug? if g.call_stack.len > 0 { call := g.call_stack[g.call_stack.len - 1] - // if call.language == .js { - for i, t in call.args { - if t.expr is ast.FloatLiteral { - if t.expr == it { - if call.expected_arg_types[i] in ast.integer_type_idxs { - g.write(int(it.val.f64()).str()) - } else { - g.write(it.val) + if call.language == .js { + for i, t in call.args { + if t.expr is ast.FloatLiteral { + if t.expr == it { + if call.expected_arg_types[i] in ast.integer_type_idxs { + g.write(int(it.val.f64()).str()) + } else { + g.write(it.val) + } + return } - return } } } - //} } // Skip cast if type is the same as the parrent caster