js: fix printing, make builtins for result and option types behave correctly (#11336)
parent
f33f216698
commit
a9b705bfd8
|
@ -136,7 +136,7 @@ pub fn (a array) str() string {
|
|||
}
|
||||
|
||||
#array.prototype[Symbol.iterator] = function () { return this.arr[Symbol.iterator](); }
|
||||
#array.prototype.entries = function () { return this.arr.entries(); }
|
||||
#array.prototype.entries = function () { let result = []; for (const [key,val] of this.arr.entries()) { result.push([new int(key), val]); } return result[Symbol.iterator](); }
|
||||
#array.prototype.map = function(callback) { return new builtin.array(this.arr.map(callback)); }
|
||||
#array.prototype.filter = function(callback) { return new array(this.arr.filter( function (it) { return (+callback(it)) != 0; } )); }
|
||||
#Object.defineProperty(array.prototype,'cap',{ get: function () { return this.len; } })
|
||||
|
|
|
@ -2,36 +2,36 @@ module builtin
|
|||
|
||||
// used to generate JS throw statements.
|
||||
pub fn js_throw(s any) {
|
||||
#throw (s instanceof Error ? s : new Error(s))
|
||||
#throw s
|
||||
}
|
||||
|
||||
pub fn println(s any) {
|
||||
pub fn println(s string) {
|
||||
$if js_freestanding {
|
||||
#print(s.toString())
|
||||
#print(s.str)
|
||||
} $else {
|
||||
#console.log(s.toString())
|
||||
#console.log(s.str)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(s any) {
|
||||
pub fn print(s string) {
|
||||
$if js_node {
|
||||
#$process.stdout.write(s.toString())
|
||||
#$process.stdout.write(s.str)
|
||||
} $else {
|
||||
panic('Cannot `print` in a browser, use `println` instead')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eprintln(s any) {
|
||||
pub fn eprintln(s string) {
|
||||
$if js_freestanding {
|
||||
#print(s.toString())
|
||||
#print(s.str)
|
||||
} $else {
|
||||
#console.error(s.toString())
|
||||
#console.error(s.str)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eprint(s any) {
|
||||
pub fn eprint(s string) {
|
||||
$if js_node {
|
||||
#$process.stderr.write(s.toString())
|
||||
#$process.stderr.write(s.str)
|
||||
} $else {
|
||||
panic('Cannot `eprint` in a browser, use `println` instead')
|
||||
}
|
||||
|
@ -50,3 +50,12 @@ fn opt_ok(data voidptr, option Option) {
|
|||
#option.err = none__
|
||||
#option.data = data
|
||||
}
|
||||
|
||||
pub fn unwrap(opt string) string {
|
||||
mut o := Option{}
|
||||
#o = opt
|
||||
if o.state != 0 {
|
||||
js_throw(o.err)
|
||||
}
|
||||
return opt
|
||||
}
|
||||
|
|
|
@ -6,14 +6,6 @@ module builtin
|
|||
|
||||
fn (a any) toString()
|
||||
|
||||
pub fn unwrap(opt any) any {
|
||||
o := &Option(opt)
|
||||
if o.state != 0 {
|
||||
js_throw(o.err)
|
||||
}
|
||||
return opt
|
||||
}
|
||||
|
||||
pub fn panic(s string) {
|
||||
eprintln('V panic: $s')
|
||||
exit(1)
|
||||
|
@ -32,13 +24,6 @@ pub:
|
|||
code int
|
||||
}
|
||||
|
||||
pub const none__ = IError(&None__{})
|
||||
|
||||
pub struct Option {
|
||||
state byte
|
||||
err IError = none__
|
||||
}
|
||||
|
||||
struct None__ {
|
||||
msg string
|
||||
code int
|
||||
|
@ -48,6 +33,13 @@ fn (_ None__) str() string {
|
|||
return 'none'
|
||||
}
|
||||
|
||||
pub const none__ = IError(None__{'', 0})
|
||||
|
||||
pub struct Option {
|
||||
state byte
|
||||
err IError = none__
|
||||
}
|
||||
|
||||
pub fn (err IError) str() string {
|
||||
return match err {
|
||||
None__ { 'none' }
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
module js
|
||||
|
||||
import v.ast
|
||||
|
||||
fn (mut g JsGen) gen_method_call(it ast.CallExpr) bool {
|
||||
g.call_stack << it
|
||||
|
||||
mut name := g.js_name(it.name)
|
||||
call_return_is_optional := it.return_type.has_flag(.optional)
|
||||
if call_return_is_optional {
|
||||
g.writeln('(function(){')
|
||||
g.inc_indent()
|
||||
g.writeln('try {')
|
||||
g.inc_indent()
|
||||
g.write('return builtin.unwrap(')
|
||||
}
|
||||
sym := g.table.get_type_symbol(it.receiver_type)
|
||||
if sym.kind == .array {
|
||||
if sym.kind == .array && it.name in ['map', 'filter'] {
|
||||
g.expr(it.left)
|
||||
mut ltyp := it.left_type
|
||||
for ltyp.is_ptr() {
|
||||
g.write('.val')
|
||||
ltyp = ltyp.deref()
|
||||
}
|
||||
g.write('.')
|
||||
// Prevent 'it' from getting shadowed inside the match
|
||||
node := it
|
||||
g.write(it.name)
|
||||
g.write('(')
|
||||
expr := node.args[0].expr
|
||||
match expr {
|
||||
ast.AnonFn {
|
||||
g.gen_fn_decl(expr.decl)
|
||||
g.write(')')
|
||||
return true
|
||||
}
|
||||
ast.Ident {
|
||||
if expr.kind == .function {
|
||||
g.write(g.js_name(expr.name))
|
||||
g.write(')')
|
||||
return true
|
||||
} else if expr.kind == .variable {
|
||||
v_sym := g.table.get_type_symbol(expr.var_info().typ)
|
||||
if v_sym.kind == .function {
|
||||
g.write(g.js_name(expr.name))
|
||||
g.write(')')
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
||||
g.write('it => ')
|
||||
g.expr(node.args[0].expr)
|
||||
g.write(')')
|
||||
return true
|
||||
}
|
||||
|
||||
left_sym := g.table.get_type_symbol(it.left_type)
|
||||
if left_sym.kind == .array {
|
||||
if it.name in special_array_methods {
|
||||
g.expr(it.left)
|
||||
mut ltyp := it.left_type
|
||||
for ltyp.is_ptr() {
|
||||
g.write('.val')
|
||||
ltyp = ltyp.deref()
|
||||
}
|
||||
g.write('.')
|
||||
|
||||
g.gen_array_method_call(it)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mut ltyp := it.left_type
|
||||
mut lsym := g.table.get_type_symbol(ltyp)
|
||||
if lsym.kind == .interface_ {
|
||||
g.write(g.js_name(lsym.name))
|
||||
g.write('.${name}.call(')
|
||||
g.expr(it.left)
|
||||
g.write(',')
|
||||
for i, arg in it.args {
|
||||
g.expr(arg.expr)
|
||||
if i != it.args.len - 1 {
|
||||
g.write(', ')
|
||||
}
|
||||
}
|
||||
// end method call
|
||||
g.write(')')
|
||||
} else {
|
||||
g.write('Object.getPrototypeOf(')
|
||||
g.expr(it.left)
|
||||
|
||||
for ltyp.is_ptr() {
|
||||
g.write('.val')
|
||||
ltyp = ltyp.deref()
|
||||
}
|
||||
g.write(').$name .call(')
|
||||
g.expr(it.left)
|
||||
g.write(',')
|
||||
for i, arg in it.args {
|
||||
g.expr(arg.expr)
|
||||
if i != it.args.len - 1 {
|
||||
g.write(', ')
|
||||
}
|
||||
}
|
||||
// end method call
|
||||
g.write(')')
|
||||
}
|
||||
|
||||
if call_return_is_optional {
|
||||
// end unwrap
|
||||
g.writeln(')')
|
||||
g.dec_indent()
|
||||
// begin catch block
|
||||
g.writeln('} catch(err) {')
|
||||
g.inc_indent()
|
||||
// gen or block contents
|
||||
match it.or_block.kind {
|
||||
.block {
|
||||
if it.or_block.stmts.len > 1 {
|
||||
g.stmts(it.or_block.stmts[..it.or_block.stmts.len - 1])
|
||||
}
|
||||
// g.write('return ')
|
||||
g.stmt(it.or_block.stmts.last())
|
||||
}
|
||||
.propagate {
|
||||
panicstr := '`optional not set (\${err})`'
|
||||
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
||||
g.writeln('return builtin.panic($panicstr)')
|
||||
} else {
|
||||
g.writeln('builtin.js_throw(err)')
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
// end catch
|
||||
g.dec_indent()
|
||||
g.writeln('}')
|
||||
// end anon fn
|
||||
g.dec_indent()
|
||||
g.write('})()')
|
||||
}
|
||||
g.call_stack.delete_last()
|
||||
return true
|
||||
}
|
||||
|
||||
fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
|
||||
if it.is_method {
|
||||
if g.gen_method_call(it) {
|
||||
return
|
||||
}
|
||||
}
|
||||
node := it
|
||||
g.call_stack << it
|
||||
mut name := g.js_name(it.name)
|
||||
is_print := name in ['print', 'println', 'eprint', 'eprintln', 'panic']
|
||||
print_method := name
|
||||
ret_sym := g.table.get_type_symbol(it.return_type)
|
||||
if it.language == .js && ret_sym.name in 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(){')
|
||||
g.inc_indent()
|
||||
g.writeln('try {')
|
||||
g.inc_indent()
|
||||
g.write('return builtin.unwrap(')
|
||||
}
|
||||
if is_print {
|
||||
mut typ := node.args[0].typ
|
||||
|
||||
expr := node.args[0].expr
|
||||
g.write('builtin.$print_method (')
|
||||
g.gen_expr_to_string(expr, typ)
|
||||
g.write(')')
|
||||
return
|
||||
}
|
||||
g.expr(it.left)
|
||||
|
||||
if name in g.builtin_fns {
|
||||
g.write('builtin.')
|
||||
}
|
||||
|
||||
g.write('${name}(')
|
||||
for i, arg in it.args {
|
||||
g.expr(arg.expr)
|
||||
if i != it.args.len - 1 {
|
||||
g.write(', ')
|
||||
}
|
||||
}
|
||||
// end method call
|
||||
g.write(')')
|
||||
if call_return_is_optional {
|
||||
// end unwrap
|
||||
g.writeln(')')
|
||||
g.dec_indent()
|
||||
// begin catch block
|
||||
g.writeln('} catch(err) {')
|
||||
g.inc_indent()
|
||||
// gen or block contents
|
||||
match it.or_block.kind {
|
||||
.block {
|
||||
if it.or_block.stmts.len > 1 {
|
||||
g.stmts(it.or_block.stmts[..it.or_block.stmts.len - 1])
|
||||
}
|
||||
|
||||
// g.write('return ')
|
||||
g.stmt(it.or_block.stmts.last())
|
||||
}
|
||||
.propagate {
|
||||
panicstr := '`optional not set (\${err})`'
|
||||
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
||||
g.writeln('return builtin.panic($panicstr)')
|
||||
} else {
|
||||
g.writeln('builtin.js_throw(err)')
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
// end catch
|
||||
g.dec_indent()
|
||||
g.writeln('}')
|
||||
// end anon fn
|
||||
g.dec_indent()
|
||||
g.write('})()')
|
||||
}
|
||||
if it.language == .js && ret_sym.name in v_types && ret_sym.name != 'void' {
|
||||
g.write(')')
|
||||
}
|
||||
g.call_stack.delete_last()
|
||||
}
|
|
@ -406,6 +406,12 @@ pub fn (mut g JsGen) init() {
|
|||
g.definitions.writeln('const \$process = process;')
|
||||
}
|
||||
g.definitions.writeln('function alias(value) { return value; } ')
|
||||
|
||||
g.definitions.writeln('function \$v_fmt(value) { let res = "";
|
||||
if (Object.getPrototypeOf(s).hasOwnProperty("str") && typeof s.str == "function") res = s.str().str
|
||||
else res = s.toString()
|
||||
return res
|
||||
} ')
|
||||
}
|
||||
|
||||
pub fn (g JsGen) hashes() string {
|
||||
|
@ -1323,11 +1329,14 @@ enum FnGenType {
|
|||
function
|
||||
struct_method
|
||||
alias_method
|
||||
iface_method
|
||||
}
|
||||
|
||||
fn (g &JsGen) fn_gen_type(it &ast.FnDecl) FnGenType {
|
||||
if it.is_method && g.table.get_type_symbol(it.params[0].typ).kind == .alias {
|
||||
return .alias_method
|
||||
} else if it.is_method && g.table.get_type_symbol(it.params[0].typ).kind == .interface_ {
|
||||
return .iface_method
|
||||
} else if it.is_method || it.no_body {
|
||||
return .struct_method
|
||||
} else {
|
||||
|
@ -1366,7 +1375,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) {
|
|||
unsafe {
|
||||
g.fn_decl = &it
|
||||
}
|
||||
if typ == .alias_method {
|
||||
if typ == .alias_method || typ == .iface_method {
|
||||
sym := g.table.get_final_type_symbol(it.params[0].typ.set_nr_muls(0))
|
||||
name := g.js_name(sym.name)
|
||||
if name in js.v_types {
|
||||
|
@ -1443,13 +1452,17 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) {
|
|||
}
|
||||
|
||||
g.stmts(it.stmts)
|
||||
g.write('}')
|
||||
g.writeln('}')
|
||||
|
||||
if is_main {
|
||||
g.write(')();')
|
||||
} else if typ != .struct_method {
|
||||
// g.write(';')
|
||||
}
|
||||
if typ == .struct_method || typ == .alias_method {
|
||||
if typ == .struct_method || typ == .alias_method || typ == .iface_method {
|
||||
g.writeln('\n')
|
||||
}
|
||||
|
||||
g.fn_decl = voidptr(0)
|
||||
}
|
||||
|
||||
|
@ -1620,7 +1633,7 @@ fn (mut g JsGen) gen_interface_decl(it ast.InterfaceDecl) {
|
|||
// This is a hack to make the interface's type accessible outside its namespace
|
||||
// TODO: interfaces are always `pub`?
|
||||
name := g.js_name(it.name)
|
||||
g.push_pub_var('/** @type $name */\n\t\t$name: undefined')
|
||||
g.push_pub_var('/** @type $name */\n\t\t$name')
|
||||
g.writeln('function ${g.js_name(it.name)} (arg) { return arg; }')
|
||||
}
|
||||
|
||||
|
@ -1828,217 +1841,6 @@ fn (mut g JsGen) gen_array_init_values(exprs []ast.Expr) {
|
|||
g.write(']')
|
||||
}
|
||||
|
||||
fn (mut g JsGen) gen_method_call(it ast.CallExpr) bool {
|
||||
g.call_stack << it
|
||||
|
||||
mut name := g.js_name(it.name)
|
||||
call_return_is_optional := it.return_type.has_flag(.optional)
|
||||
if call_return_is_optional {
|
||||
g.writeln('(function(){')
|
||||
g.inc_indent()
|
||||
g.writeln('try {')
|
||||
g.inc_indent()
|
||||
g.write('return builtin.unwrap(')
|
||||
}
|
||||
sym := g.table.get_type_symbol(it.receiver_type)
|
||||
if sym.kind == .array {
|
||||
if sym.kind == .array && it.name in ['map', 'filter'] {
|
||||
g.expr(it.left)
|
||||
mut ltyp := it.left_type
|
||||
for ltyp.is_ptr() {
|
||||
g.write('.val')
|
||||
ltyp = ltyp.deref()
|
||||
}
|
||||
g.write('.')
|
||||
// Prevent 'it' from getting shadowed inside the match
|
||||
node := it
|
||||
g.write(it.name)
|
||||
g.write('(')
|
||||
expr := node.args[0].expr
|
||||
match expr {
|
||||
ast.AnonFn {
|
||||
g.gen_fn_decl(expr.decl)
|
||||
g.write(')')
|
||||
return true
|
||||
}
|
||||
ast.Ident {
|
||||
if expr.kind == .function {
|
||||
g.write(g.js_name(expr.name))
|
||||
g.write(')')
|
||||
return true
|
||||
} else if expr.kind == .variable {
|
||||
v_sym := g.table.get_type_symbol(expr.var_info().typ)
|
||||
if v_sym.kind == .function {
|
||||
g.write(g.js_name(expr.name))
|
||||
g.write(')')
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
||||
g.write('it => ')
|
||||
g.expr(node.args[0].expr)
|
||||
g.write(')')
|
||||
return true
|
||||
}
|
||||
|
||||
left_sym := g.table.get_type_symbol(it.left_type)
|
||||
if left_sym.kind == .array {
|
||||
if it.name in special_array_methods {
|
||||
g.expr(it.left)
|
||||
mut ltyp := it.left_type
|
||||
for ltyp.is_ptr() {
|
||||
g.write('.val')
|
||||
ltyp = ltyp.deref()
|
||||
}
|
||||
g.write('.')
|
||||
|
||||
g.gen_array_method_call(it)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// interfaces require dynamic dispatch. To obtain method table we use getPrototypeOf
|
||||
g.write('Object.getPrototypeOf(')
|
||||
g.expr(it.left)
|
||||
mut ltyp := it.left_type
|
||||
for ltyp.is_ptr() {
|
||||
g.write('.val')
|
||||
ltyp = ltyp.deref()
|
||||
}
|
||||
g.write(').$name .call(')
|
||||
g.expr(it.left)
|
||||
g.write(',')
|
||||
for i, arg in it.args {
|
||||
g.expr(arg.expr)
|
||||
if i != it.args.len - 1 {
|
||||
g.write(', ')
|
||||
}
|
||||
}
|
||||
// end method call
|
||||
g.write(')')
|
||||
|
||||
if call_return_is_optional {
|
||||
// end unwrap
|
||||
g.writeln(')')
|
||||
g.dec_indent()
|
||||
// begin catch block
|
||||
g.writeln('} catch(err) {')
|
||||
g.inc_indent()
|
||||
// gen or block contents
|
||||
match it.or_block.kind {
|
||||
.block {
|
||||
if it.or_block.stmts.len > 1 {
|
||||
g.stmts(it.or_block.stmts[..it.or_block.stmts.len - 1])
|
||||
}
|
||||
// g.write('return ')
|
||||
g.stmt(it.or_block.stmts.last())
|
||||
}
|
||||
.propagate {
|
||||
panicstr := '`optional not set (\${err})`'
|
||||
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
||||
g.writeln('return builtin.panic($panicstr)')
|
||||
} else {
|
||||
g.writeln('builtin.js_throw(err)')
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
// end catch
|
||||
g.dec_indent()
|
||||
g.writeln('}')
|
||||
// end anon fn
|
||||
g.dec_indent()
|
||||
g.write('})()')
|
||||
}
|
||||
g.call_stack.delete_last()
|
||||
return true
|
||||
}
|
||||
|
||||
fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
|
||||
if it.is_method {
|
||||
if g.gen_method_call(it) {
|
||||
return
|
||||
}
|
||||
}
|
||||
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(){')
|
||||
g.inc_indent()
|
||||
g.writeln('try {')
|
||||
g.inc_indent()
|
||||
g.write('return builtin.unwrap(')
|
||||
}
|
||||
|
||||
g.expr(it.left)
|
||||
|
||||
if name in g.builtin_fns {
|
||||
g.write('builtin.')
|
||||
}
|
||||
|
||||
g.write('${name}(')
|
||||
for i, arg in it.args {
|
||||
g.expr(arg.expr)
|
||||
if i != it.args.len - 1 {
|
||||
g.write(', ')
|
||||
}
|
||||
}
|
||||
// end method call
|
||||
g.write(')')
|
||||
if call_return_is_optional {
|
||||
// end unwrap
|
||||
g.writeln(')')
|
||||
g.dec_indent()
|
||||
// begin catch block
|
||||
g.writeln('} catch(err) {')
|
||||
g.inc_indent()
|
||||
// gen or block contents
|
||||
match it.or_block.kind {
|
||||
.block {
|
||||
if it.or_block.stmts.len > 1 {
|
||||
g.stmts(it.or_block.stmts[..it.or_block.stmts.len - 1])
|
||||
}
|
||||
|
||||
// g.write('return ')
|
||||
g.stmt(it.or_block.stmts.last())
|
||||
}
|
||||
.propagate {
|
||||
panicstr := '`optional not set (\${err})`'
|
||||
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
||||
g.writeln('return builtin.panic($panicstr)')
|
||||
} else {
|
||||
g.writeln('builtin.js_throw(err)')
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
// end catch
|
||||
g.dec_indent()
|
||||
g.writeln('}')
|
||||
// end anon fn
|
||||
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()
|
||||
}
|
||||
|
||||
fn (mut g JsGen) gen_ident(node ast.Ident) {
|
||||
mut name := g.js_name(node.name)
|
||||
if node.kind == .blank_ident || name in ['', '_'] {
|
||||
|
@ -2337,8 +2139,17 @@ fn (mut g JsGen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var M
|
|||
if sym.kind == .sum_type {
|
||||
g.write(' instanceof ')
|
||||
g.expr(branch.exprs[sumtype_index])
|
||||
} else if sym.kind == .interface_ {
|
||||
if branch.exprs[sumtype_index] is ast.TypeNode {
|
||||
g.write(' instanceof ')
|
||||
g.expr(branch.exprs[sumtype_index])
|
||||
} else {
|
||||
panic('TODO: Generate match for interfaces')
|
||||
g.write(' instanceof ')
|
||||
if g.ns.name != 'builtin' {
|
||||
g.write('builtin.')
|
||||
}
|
||||
g.write('None__')
|
||||
}
|
||||
}
|
||||
if is_expr && tmp_var.len == 0 {
|
||||
g.write(') ? ')
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
module js
|
||||
|
||||
import v.ast
|
||||
|
||||
fn (mut g JsGen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
|
||||
mut typ := etype
|
||||
if etype.has_flag(.shared_f) {
|
||||
typ = typ.clear_flag(.shared_f).set_nr_muls(0)
|
||||
}
|
||||
|
||||
mut sym := g.table.get_type_symbol(typ)
|
||||
|
||||
if mut sym.info is ast.Alias {
|
||||
parent_sym := g.table.get_type_symbol(sym.info.parent_type)
|
||||
if parent_sym.has_method('str') {
|
||||
typ = sym.info.parent_type
|
||||
sym = parent_sym
|
||||
}
|
||||
}
|
||||
|
||||
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
|
||||
if typ.has_flag(.variadic) {
|
||||
// todo(playX): generate str method just like in the C backend
|
||||
g.write('new string(')
|
||||
g.expr(expr)
|
||||
g.write('.valueOf()')
|
||||
g.write('.toString())')
|
||||
} else if typ == ast.string_type {
|
||||
g.expr(expr)
|
||||
} else if typ == ast.bool_type {
|
||||
g.write('new string(')
|
||||
g.expr(expr)
|
||||
g.write('.valueOf() ? "true" : "false")')
|
||||
} else if sym.kind == .none_ {
|
||||
g.write('new string("<none>")')
|
||||
} else if sym.kind == .enum_ {
|
||||
g.write('new string(')
|
||||
if expr !is ast.EnumVal {
|
||||
g.expr(expr)
|
||||
g.write('.valueOf()')
|
||||
g.write('.toString()')
|
||||
} else {
|
||||
g.write('"')
|
||||
g.expr(expr)
|
||||
g.write('"')
|
||||
}
|
||||
g.write(')')
|
||||
} else if sym.kind == .interface_ && sym_has_str_method {
|
||||
is_ptr := typ.is_ptr()
|
||||
g.write(sym.mod.replace_once('${g.ns.name}.', ''))
|
||||
g.write('.')
|
||||
g.write(sym.name)
|
||||
g.write('.prototype.str.call(')
|
||||
g.expr(expr)
|
||||
if !str_method_expects_ptr && is_ptr {
|
||||
g.gen_deref_ptr(typ)
|
||||
}
|
||||
g.write(')')
|
||||
}
|
||||
//|| sym.kind in [.array, .array_fixed, .map, .struct_, .multi_return,.sum_type, .interface_]
|
||||
else if sym_has_str_method {
|
||||
g.write('new string(')
|
||||
g.write('Object.getPrototypeOf(/*str exists*/')
|
||||
g.expr(expr)
|
||||
is_ptr := typ.is_ptr()
|
||||
g.gen_deref_ptr(typ)
|
||||
g.write(').str.call(')
|
||||
g.expr(expr)
|
||||
if !str_method_expects_ptr && is_ptr {
|
||||
g.gen_deref_ptr(typ)
|
||||
}
|
||||
|
||||
g.write('))')
|
||||
} else if sym.kind == .struct_ && !sym_has_str_method {
|
||||
g.write('new string(')
|
||||
g.expr(expr)
|
||||
g.gen_deref_ptr(typ)
|
||||
g.write('.toString())')
|
||||
} else {
|
||||
g.write('new string(')
|
||||
g.expr(expr)
|
||||
g.gen_deref_ptr(typ)
|
||||
g.write('.valueOf().toString())')
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue