js: codegen fixes, W.I.P `os` availability for JS backend (#11281)

pull/11289/head
playX 2021-08-23 14:25:02 +03:00 committed by GitHub
parent 47278b4a7d
commit d78e7e3b2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 635 additions and 274 deletions

View File

@ -239,3 +239,7 @@ pub fn (a array) contains(key voidptr) bool {
pub fn (mut a array) delete_last() { pub fn (mut a array) delete_last() {
#a.val.arr.pop(); #a.val.arr.pop();
} }
[unsafe]
pub fn (a array) free() {
}

View File

@ -1,3 +1,7 @@
module math module math
fn JS.Math.pow(x f64, y f64) f64 fn JS.Math.pow(x f64, y f64) f64
pub fn pow(x f64, y f64) f64 {
return JS.Math.pow(x, y)
}

View File

@ -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
}

97
vlib/os/os.js.v 100644
View File

@ -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
}
}

47
vlib/os/os_js.js.v 100644
View File

@ -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
}

248
vlib/os/process.c.v 100644
View File

@ -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
}

View File

@ -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()')
}
}

View File

@ -68,250 +68,3 @@ pub fn (mut p Process) set_environment(envs map[string]string) {
} }
return 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()
}
}

View File

@ -348,7 +348,7 @@ fn (mut g JsGen) gen_builtin_type_defs() {
'f32', 'f64', 'float_literal' { 'f32', 'f64', 'float_literal' {
g.gen_builtin_prototype( g.gen_builtin_prototype(
typ_name: typ_name typ_name: typ_name
constructor: 'this.val = +val' constructor: 'this.val = Number(val)'
default_value: 'new Number(0)' default_value: 'new Number(0)'
to_jsval: '+this' to_jsval: '+this'
) )

View File

@ -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 // interfaces require dynamic dispatch. To obtain method table we use getPrototypeOf
g.write('Object.getPrototypeOf(') g.write('Object.getPrototypeOf(')
g.expr(it.left) g.expr(it.left)
@ -1660,6 +1661,15 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
} }
g.call_stack << it g.call_stack << it
mut name := g.js_name(it.name) 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) call_return_is_optional := it.return_type.has_flag(.optional)
if call_return_is_optional { if call_return_is_optional {
g.writeln('(function(){') g.writeln('(function(){')
@ -1717,6 +1727,9 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
g.dec_indent() g.dec_indent()
g.write('})()') g.write('})()')
} }
if it.language == .js && ret_sym.name in js.v_types && ret_sym.name != 'void' {
g.write(')')
}
g.call_stack.delete_last() g.call_stack.delete_last()
} }
@ -1761,7 +1774,7 @@ fn (mut g JsGen) need_tmp_var_in_match(node ast.MatchExpr) bool {
return false 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) type_sym := g.table.get_type_symbol(node.cond_type)
for j, branch in node.branches { for j, branch in node.branches {
is_last := j == node.branches.len - 1 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 { match type_sym.kind {
.array { .array {
g.write('vEq($cond_var, ') g.write('vEq(')
g.match_cond(cond_var)
g.write(',')
g.expr(expr) g.expr(expr)
g.write(')') g.write(')')
} }
.array_fixed { .array_fixed {
g.write('vEq($cond_var, ') g.write('vEq(')
g.match_cond(cond_var)
g.write(',')
g.expr(expr) g.expr(expr)
g.write(')') g.write(')')
} }
.map { .map {
g.write('vEq($cond_var, ') g.write('vEq(')
g.match_cond(cond_var)
g.write(',')
g.expr(expr) g.expr(expr)
g.write(')') g.write(')')
} }
.string { .string {
g.write('vEq($cond_var, ') g.write('vEq(')
g.match_cond(cond_var)
g.write(',')
g.expr(expr) g.expr(expr)
g.write(')') g.write(')')
} }
.struct_ { .struct_ {
g.write('vEq($cond_var, ') g.write('vEq(')
g.match_cond(cond_var)
g.write(',')
g.expr(expr) g.expr(expr)
g.write(')') g.write(')')
} }
@ -1837,15 +1860,19 @@ fn (mut g JsGen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var s
} }
g.write('(') g.write('(')
if !skip_low { if !skip_low {
g.write('$cond_var >= ') g.match_cond(cond_var)
g.write(' >= ')
g.expr(expr.low) g.expr(expr.low)
g.write(' && ') g.write(' && ')
} }
g.write('$cond_var <= ') g.match_cond(cond_var)
g.write(' <= ')
g.expr(expr.high) g.expr(expr.high)
g.write(')') g.write(')')
} else { } else {
g.write('vEq($cond_var,') g.write('vEq(')
g.match_cond(cond_var)
g.write(',')
g.expr(expr) g.expr(expr)
g.write(')') 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) { fn (mut g JsGen) match_expr(node ast.MatchExpr) {
if node.cond_type == 0 { if node.cond_type == 0 {
g.writeln('// match 0') g.writeln('// match 0')
@ -1873,16 +1921,22 @@ fn (mut g JsGen) match_expr(node ast.MatchExpr) {
prev := g.inside_ternary prev := g.inside_ternary
need_tmp_var := g.need_tmp_var_in_match(node) need_tmp_var := g.need_tmp_var_in_match(node)
is_expr := (node.is_expr && node.return_type != ast.void_type) || g.inside_ternary 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 := '' mut tmp_var := ''
if is_expr && !need_tmp_var { if is_expr && !need_tmp_var {
g.inside_ternary = true g.inside_ternary = true
} }
cond_var = g.new_tmp_var() if node.cond is ast.Ident || node.cond is ast.SelectorExpr || node.cond is ast.IntegerLiteral
g.write('let $cond_var = ') || node.cond is ast.StringLiteral || node.cond is ast.FloatLiteral {
g.expr(node.cond) cond_var = CondExpr{node.cond}
g.write(';') } else {
s := g.new_tmp_var()
cond_var = CondString{s}
g.write('let $s = ')
g.expr(node.cond)
g.writeln(';')
}
if need_tmp_var { if need_tmp_var {
tmp_var = g.new_tmp_var() tmp_var = g.new_tmp_var()
g.writeln('let $tmp_var = undefined;') 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 { for j, branch in node.branches {
mut sumtype_index := 0 mut sumtype_index := 0
for { for {
@ -1954,7 +2008,7 @@ fn (mut g JsGen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var s
} else { } else {
g.write('if (') g.write('if (')
} }
g.write(cond_var) g.match_cond(cond_var)
if sym.kind == .sum_type { if sym.kind == .sum_type {
g.write(' instanceof ') g.write(' instanceof ')
g.expr(branch.exprs[sumtype_index]) 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? // TODO: call.language always seems to be "v", parser bug?
if g.call_stack.len > 0 { if g.call_stack.len > 0 {
call := g.call_stack[g.call_stack.len - 1] call := g.call_stack[g.call_stack.len - 1]
// if call.language == .js { if call.language == .js {
for i, t in call.args { for i, t in call.args {
if t.expr is ast.FloatLiteral { if t.expr is ast.FloatLiteral {
if t.expr == it { if t.expr == it {
if call.expected_arg_types[i] in ast.integer_type_idxs { if call.expected_arg_types[i] in ast.integer_type_idxs {
g.write(int(it.val.f64()).str()) g.write(int(it.val.f64()).str())
} else { } else {
g.write(it.val) g.write(it.val)
}
return
} }
return
} }
} }
} }
//}
} }
// Skip cast if type is the same as the parrent caster // Skip cast if type is the same as the parrent caster