js: use prefixed names for functions and global symbols (#11387)

pull/11450/head
playX 2021-09-08 20:30:46 +03:00 committed by GitHub
parent 96d4a0777f
commit 72089c4feb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1895 additions and 357 deletions

View File

@ -119,7 +119,7 @@ pub fn (mut a array) insert_many(i int, val voidptr, size int) {
pub fn (mut a array) join(separator string) string {
mut res := ''
#res = new builtin.string(a.val.arr.join(separator +''));
#res = new string(a.val.arr.join(separator +''));
return res
}
@ -128,16 +128,9 @@ fn (a array) push(val voidptr) {
#a.arr.push(val)
}
pub fn (a array) str() string {
mut res := ''
#res = new builtin.string(a + '')
return res
}
#array.prototype[Symbol.iterator] = function () { return this.arr[Symbol.iterator](); }
#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.map = function(callback) { return new 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; } })
#array.prototype.any = function (value) {
@ -251,3 +244,21 @@ pub fn (a array) bytestr() string {
return res
}
/*
pub fn (a []string) str() string {
mut sb := strings.new_builder(a.len * 3)
sb.write_string('[')
for i in 0 .. a.len {
val := a[i]
sb.write_string("'")
sb.write_string(val)
sb.write_string("'")
if i < a.len - 1 {
sb.write_string(', ')
}
}
sb.write_string(']')
res := sb.str()
return res
}*/

View File

@ -1,6 +1,8 @@
module builtin
import strings
// used to generate JS throw statements.
pub fn js_throw(s any) {
#throw s
}
@ -68,3 +70,12 @@ pub fn unwrap(opt string) string {
return res
}
pub fn (r rune) str() string {
res := ''
mut sb := strings.new_builder(5)
#res.str = r.valueOf().toString()
sb.write_string(res)
return sb.str()
}

View File

@ -14,3 +14,10 @@ pub fn (c byte) is_letter() bool {
return result
}
pub fn (c byte) str() string {
res := ''
#res.str = c.val.toString()
return res
}

View File

@ -0,0 +1,15 @@
module builtin
pub fn (x f32) str() string {
res := ''
#res.str = x.val + ''
return res
}
pub fn (x f64) str() string {
res := ''
#res.str = x.val + ''
return res
}

View File

@ -2,7 +2,49 @@ module builtin
pub fn (i int) str() string {
mut res := ''
#res = new builtin.string( i )
#res = new string( i )
return res
}
pub fn (i i64) str() string {
mut res := ''
#res = new string( i )
return res
}
pub fn (i u32) str() string {
mut res := ''
#res = new string( i )
return res
}
pub fn (i u64) str() string {
mut res := ''
#res = new string( i )
return res
}
pub fn (i bool) str() string {
mut res := ''
#res = new string( i )
return res
}
pub fn (i any) str() string {
mut res := ''
#res = new string( i.toString() )
return res
}
pub fn (i int_literal) str() string {
res := ''
#res.str = i.val.toString()
return res
}

View File

@ -718,3 +718,11 @@ pub fn (s string) split_into_lines() []string {
return res
}
// replace_once replaces the first occurence of `rep` with the string passed in `with`.
pub fn (s string) replace_once(rep string, with_ string) string {
s2 := ''
#s2.val = s.str.replace(rep.str,with_.str)
return s2
}

View File

@ -15,7 +15,7 @@ pub fn setenv(key string, val string, overwrite bool) {
// `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])
#if ($ENV[key]) res = new string($ENV[key])
return res
}

View File

@ -10,7 +10,7 @@ pub const (
)
$if js_node {
#$process.argv.forEach(function(val,index) { args.arr[index] = new string(val); })
#$process.argv.forEach(function(val,index) { os__args.arr[index] = new string(val); })
}
// real_path returns the full absolute path for fpath, with all relative ../../, symlinks and so on resolved.
@ -55,7 +55,7 @@ pub fn chown(path string, owner int, group int) {
pub fn temp_dir() string {
mut res := ''
$if js_node {
#res = new builtin.string($os.tmpdir())
#res = new string($os.tmpdir())
}
return res
}
@ -63,7 +63,7 @@ pub fn temp_dir() string {
pub fn home_dir() string {
mut res := ''
$if js_node {
#res = new builtin.string($os.homedir())
#res = new string($os.homedir())
}
return res
}
@ -87,8 +87,8 @@ pub fn execute(cmd string) Result {
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 + '')
#exit_code = new int(output.status)
#stdout = newstring(output.stdout + '')
return Result{
exit_code: exit_code

View File

@ -45,7 +45,7 @@ pub fn ls(path string) ?[]string {
result := []string{}
$if js_node {
#let i = 0
#$fs.readdirSync(path.str).forEach((path) => result.arr[i++] = new builtin.string(path))
#$fs.readdirSync(path.str).forEach((path) => result.arr[i++] = new string(path))
}
return result
}

View File

@ -101,7 +101,7 @@ pub fn (mut p Process) stdin_write(s string) {
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) })
#p.val.pid.stdout.on('data', function (data) { res = new string(data) })
return res
}

View File

@ -50,7 +50,6 @@ pub fn (mut b Builder) writeln(s string) {
}
pub fn (mut b Builder) str() string {
b.buf << byte(0)
s := ''
#for (const c of b.val.buf.arr)

View File

@ -25,8 +25,8 @@ pub fn (mut b Builder) build_js(v_files []string, out_file string) {
}
pub fn (mut b Builder) compile_js() {
mut files := b.get_user_files()
files << b.get_builtin_files()
mut files := b.get_builtin_files()
files << b.get_user_files()
b.set_module_lookup_paths()
if b.pref.is_verbose {
println('all .v files:')

View File

@ -1,19 +1,81 @@
module js
import v.ast
import strings
const (
special_array_methods = [
'sort',
'insert',
'prepend',
'index',
'contains',
]
)
fn (mut g JsGen) gen_array_index_method(left_type ast.Type) string {
unwrap_left_type := g.unwrap_generic(left_type)
mut left_sym := g.table.get_type_symbol(unwrap_left_type)
mut left_type_str := g.typ(unwrap_left_type).trim('*')
fn_name := '${left_type_str}_index'
if !left_sym.has_method('index') {
info := left_sym.info as ast.Array
elem_sym := g.table.get_type_symbol(info.elem_type)
if elem_sym.kind == .function {
left_type_str = 'Array_voidptr'
}
mut fn_builder := strings.new_builder(512)
fn_builder.writeln('function ${fn_name}(a, v) {')
fn_builder.writeln('\tlet pelem = a.arr;')
fn_builder.writeln('\tfor (let i = 0; i < pelem.length; ++i) {')
if elem_sym.kind == .string {
fn_builder.writeln('\t\tif (pelem[i].str == v.str) {')
} else if elem_sym.kind == .array && !info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif (vEq(pelem[i], v)) {')
} else if elem_sym.kind == .function && !info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif ( vEq(pelem[i], v)) {')
} else if elem_sym.kind == .map && !info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif (vEq(pelem[i], v)) {')
} else if elem_sym.kind == .struct_ && !info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif (vEq(pelem[i], v)) {')
} else {
fn_builder.writeln('\t\tif (pelem[i].valueOf() == v.valueOf()) {')
}
fn_builder.writeln('\t\t\treturn new int(i);')
fn_builder.writeln('\t\t}')
fn_builder.writeln('\t}')
fn_builder.writeln('\treturn new int(-1);')
fn_builder.writeln('}')
g.definitions.writeln(fn_builder.str())
left_sym.register_method(&ast.Fn{
name: 'index'
params: [ast.Param{
typ: unwrap_left_type
}, ast.Param{
typ: info.elem_type
}]
})
}
return fn_name
}
fn (mut g JsGen) gen_array_method_call(it ast.CallExpr) {
node := it
match node.name {
'index' {
g.gen_array_index(node)
return
}
'contains' {
g.gen_array_contains(node)
return
}
'insert' {
g.write('array_')
arg2_sym := g.table.get_type_symbol(node.args[1].typ)
is_arg2_array := arg2_sym.kind == .array && node.args[1].typ == node.left_type
if is_arg2_array {
@ -21,7 +83,13 @@ fn (mut g JsGen) gen_array_method_call(it ast.CallExpr) {
} else {
g.write('insert(')
}
g.expr(it.left)
mut ltyp := it.left_type
for ltyp.is_ptr() {
g.write('.val')
ltyp = ltyp.deref()
}
g.write(',')
g.expr(node.args[0].expr)
g.write(',')
if is_arg2_array {
@ -36,6 +104,7 @@ fn (mut g JsGen) gen_array_method_call(it ast.CallExpr) {
return
}
'prepend' {
g.write('array_')
arg_sym := g.table.get_type_symbol(node.args[0].typ)
is_arg_array := arg_sym.kind == .array && node.args[0].typ == node.left_type
if is_arg_array {
@ -43,7 +112,13 @@ fn (mut g JsGen) gen_array_method_call(it ast.CallExpr) {
} else {
g.write('prepend(')
}
g.expr(it.left)
mut ltyp := it.left_type
for ltyp.is_ptr() {
g.write('.val')
ltyp = ltyp.deref()
}
g.write(',')
if is_arg_array {
g.expr(node.args[0].expr)
g.write('.arr, ')
@ -56,6 +131,7 @@ fn (mut g JsGen) gen_array_method_call(it ast.CallExpr) {
return
}
'sort' {
g.write('array')
rec_sym := g.table.get_type_symbol(node.receiver_type)
if rec_sym.kind != .array {
println(node.name)
@ -67,9 +143,24 @@ fn (mut g JsGen) gen_array_method_call(it ast.CallExpr) {
// `users.sort(a.age > b.age)`
if node.args.len == 0 {
g.write('sort()')
g.write('_sort(')
g.expr(it.left)
mut ltyp := it.left_type
for ltyp.is_ptr() {
g.write('.val')
ltyp = ltyp.deref()
}
g.write(')')
return
} else {
g.expr(it.left)
mut ltyp := it.left_type
for ltyp.is_ptr() {
g.write('.val')
ltyp = ltyp.deref()
}
g.write('.')
infix_expr := node.args[0].expr as ast.InfixExpr
left_name := infix_expr.left.str()
is_reverse := (left_name.starts_with('a') && infix_expr.op == .gt)
@ -87,3 +178,73 @@ fn (mut g JsGen) gen_array_method_call(it ast.CallExpr) {
else {}
}
}
fn (mut g JsGen) gen_array_index(node ast.CallExpr) {
fn_name := g.gen_array_index_method(node.left_type)
g.write('${fn_name}(')
g.expr(node.left)
g.gen_deref_ptr(node.left_type)
g.write(',')
g.expr(node.args[0].expr)
g.write(')')
}
fn (mut g JsGen) gen_array_contains(node ast.CallExpr) {
fn_name := g.gen_array_contains_method(node.left_type)
g.write('${fn_name}(')
g.expr(node.left)
g.gen_deref_ptr(node.left_type)
g.write(',')
g.expr(node.args[0].expr)
g.write(')')
}
fn (mut g JsGen) gen_array_contains_method(left_type ast.Type) string {
mut unwrap_left_type := g.unwrap_generic(left_type)
if unwrap_left_type.share() == .shared_t {
unwrap_left_type = unwrap_left_type.clear_flag(.shared_f)
}
mut left_sym := g.table.get_type_symbol(unwrap_left_type)
left_final_sym := g.table.get_final_type_symbol(unwrap_left_type)
mut left_type_str := g.typ(unwrap_left_type).replace('*', '')
fn_name := '${left_type_str}_contains'
if !left_sym.has_method('contains') {
left_info := left_final_sym.info as ast.Array
elem_sym := g.table.get_type_symbol(left_info.elem_type)
if elem_sym.kind == .function {
left_type_str = 'Array_voidptr'
}
mut fn_builder := strings.new_builder(512)
fn_builder.writeln('function ${fn_name}(a,v) {')
fn_builder.writeln('\tfor (let i = 0; i < a.len; ++i) {')
if elem_sym.kind == .string {
fn_builder.writeln('\t\tif (a.arr[i].str == v.str) {')
} else if elem_sym.kind == .array && left_info.elem_type.nr_muls() == 0 {
fn_builder.writeln('\t\tif (vEq(a.arr[i], v)) {')
} else if elem_sym.kind == .function {
fn_builder.writeln('\t\tif (a.arr[i] == v) {')
} else if elem_sym.kind == .map && left_info.elem_type.nr_muls() == 0 {
fn_builder.writeln('\t\tif (vEq(a.arr[i], v)) {')
} else if elem_sym.kind == .struct_ && left_info.elem_type.nr_muls() == 0 {
fn_builder.writeln('\t\tif (vEq(a.arr[i],v)) {')
} else {
fn_builder.writeln('\t\tif (a.arr[i].valueOf() == v.valueOf()) {')
}
fn_builder.writeln('\t\t\treturn new bool(true);')
fn_builder.writeln('\t\t}')
fn_builder.writeln('\t}')
fn_builder.writeln('\treturn new bool(false);')
fn_builder.writeln('}')
g.definitions.writeln(fn_builder.str())
left_sym.register_method(&ast.Fn{
name: 'contains'
params: [ast.Param{
typ: unwrap_left_type
}, ast.Param{
typ: left_info.elem_type
}]
})
}
return fn_name
}

View File

@ -0,0 +1 @@
module js

View File

@ -0,0 +1,789 @@
// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
module js
import v.ast
import v.util
import strings
pub enum StrIntpType {
si_no_str = 0 // no parameter to print only fix string
si_c
si_u8
si_i8
si_u16
si_i16
si_u32
si_i32
si_u64
si_i64
si_e32
si_e64
si_f32
si_f64
si_g32
si_g64
si_s
si_p
si_vp
}
pub fn type_to_str(x StrIntpType) string {
match x {
.si_no_str { return 'no_str' }
.si_c { return 'c' }
.si_u8 { return 'u8' }
.si_i8 { return 'i8' }
.si_u16 { return 'u16' }
.si_i16 { return 'i16' }
.si_u32 { return 'u32' }
.si_i32 { return 'i32' }
.si_u64 { return 'u64' }
.si_i64 { return 'i64' }
.si_f32 { return 'f32' }
.si_f64 { return 'f64' }
.si_g32 { return 'f32' } // g32 format use f32 data
.si_g64 { return 'f64' } // g64 format use f64 data
.si_e32 { return 'f32' } // e32 format use f32 data
.si_e64 { return 'f64' } // e64 format use f64 data
.si_s { return 's' }
.si_p { return 'p' }
.si_vp { return 'vp' }
}
}
pub fn data_str(x StrIntpType) string {
match x {
.si_no_str { return 'no_str' }
.si_c { return 'd_c' }
.si_u8 { return 'd_u8' }
.si_i8 { return 'd_i8' }
.si_u16 { return 'd_u16' }
.si_i16 { return 'd_i16' }
.si_u32 { return 'd_u32' }
.si_i32 { return 'd_i32' }
.si_u64 { return 'd_u64' }
.si_i64 { return 'd_i64' }
.si_f32 { return 'd_f32' }
.si_f64 { return 'd_f64' }
.si_g32 { return 'd_f32' } // g32 format use f32 data
.si_g64 { return 'd_f64' } // g64 format use f64 data
.si_e32 { return 'd_f32' } // e32 format use f32 data
.si_e64 { return 'd_f64' } // e64 format use f64 data
.si_s { return 'd_s' }
.si_p { return 'd_p' }
.si_vp { return 'd_vp' }
}
}
const (
// BUG: this const is not released from the memory! use a const for now
// si_s_code = "0x" + int(StrIntpType.si_s).hex() // code for a simple string
si_s_code = '0xfe10'
)
fn should_use_indent_func(kind ast.Kind) bool {
return kind in [.struct_, .alias, .array, .array_fixed, .map, .sum_type, .interface_]
}
fn (mut g JsGen) gen_str_default(sym ast.TypeSymbol, styp string, str_fn_name string) {
mut convertor := ''
mut typename_ := ''
if sym.parent_idx in ast.integer_type_idxs {
convertor = 'int'
typename_ = 'int'
} else if sym.parent_idx == ast.f32_type_idx {
convertor = 'float'
typename_ = 'f32'
} else if sym.parent_idx == ast.f64_type_idx {
convertor = 'double'
typename_ = 'f64'
} else if sym.parent_idx == ast.bool_type_idx {
convertor = 'bool'
typename_ = 'bool'
} else {
panic("could not generate string method for type '$styp'")
}
g.definitions.writeln('function ${str_fn_name}(it) {')
if convertor == 'bool' {
g.definitions.writeln('\tlet tmp1 = string__plus(new string("${styp}("), it.valueOf() ? new string("true") : new string("false"));')
} else {
g.definitions.writeln('\tlet tmp1 = string__plus(new string("${styp}("), new string(${typename_}_str(($convertor)it).str));')
}
g.definitions.writeln('\tstring tmp2 = string__plus(tmp1, new string(")"));')
g.definitions.writeln('\treturn tmp2;')
g.definitions.writeln('}')
}
fn (mut g JsGen) gen_str_for_type(typ ast.Type) string {
styp := g.typ(typ).replace('*', '')
mut sym := g.table.get_type_symbol(g.unwrap_generic(typ))
mut str_fn_name := styp_to_str_fn_name(styp)
if mut sym.info is ast.Alias {
if sym.info.is_import {
sym = g.table.get_type_symbol(sym.info.parent_type)
str_fn_name = styp_to_str_fn_name(sym.name)
}
}
sym_has_str_method, str_method_expects_ptr, str_nr_args := sym.str_method_info()
already_generated_key := '$styp:$str_fn_name'
if !sym_has_str_method && already_generated_key !in g.str_types && !typ.has_flag(.optional) {
$if debugautostr ? {
eprintln('> gen_str_for_type: |typ: ${typ:5}, ${sym.name:20}|has_str: ${sym_has_str_method:5}|expects_ptr: ${str_method_expects_ptr:5}|nr_args: ${str_nr_args:1}|fn_name: ${str_fn_name:20}')
}
g.str_types << already_generated_key
match mut sym.info {
ast.Alias {
if sym.info.is_import {
g.gen_str_default(sym, styp, str_fn_name)
} else {
g.gen_str_for_alias(sym.info, styp, str_fn_name)
}
}
ast.Array {
g.gen_str_for_array(sym.info, styp, str_fn_name)
}
ast.ArrayFixed {
g.gen_str_for_array_fixed(sym.info, styp, str_fn_name)
}
ast.Enum {
g.gen_str_for_enum(sym.info, styp, str_fn_name)
}
ast.FnType {
g.gen_str_for_fn_type(sym.info, styp, str_fn_name)
}
ast.Struct {
g.gen_str_for_struct(sym.info, styp, str_fn_name)
}
ast.Map {
g.gen_str_for_map(sym.info, styp, str_fn_name)
}
ast.MultiReturn {
g.gen_str_for_multi_return(sym.info, styp, str_fn_name)
}
ast.SumType {
g.gen_str_for_union_sum_type(sym.info, styp, str_fn_name)
}
ast.Interface {
g.gen_str_for_interface(sym.info, styp, str_fn_name)
}
ast.Chan {
g.gen_str_for_chan(sym.info, styp, str_fn_name)
}
ast.Thread {
g.gen_str_for_thread(sym.info, styp, str_fn_name)
}
else {
panic("could not generate string method $str_fn_name for type '$styp'")
}
}
}
if typ.has_flag(.optional) {
option_already_generated_key := 'option_$already_generated_key'
if option_already_generated_key !in g.str_types {
g.gen_str_for_option(typ, styp, str_fn_name)
g.str_types << option_already_generated_key
}
return str_fn_name
}
return str_fn_name
}
fn (mut g JsGen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string) {
parent_type := typ.clear_flag(.optional)
sym := g.table.get_type_symbol(parent_type)
sym_has_str_method, _, _ := sym.str_method_info()
parent_str_fn_name := g.gen_str_for_type(parent_type)
g.definitions.writeln('function ${str_fn_name}(it) { return indent_${str_fn_name}(it, 0); }')
g.definitions.writeln('function indent_${str_fn_name}(it, indent_count) {')
g.definitions.writeln('\tlet res;')
g.definitions.writeln('\tif (it.state == 0) {')
if sym.kind == .string {
tmp_res := '${parent_str_fn_name}(it.data)'
g.definitions.writeln('\t\tres = ${str_intp_sq(tmp_res)};')
} else if should_use_indent_func(sym.kind) && !sym_has_str_method {
g.definitions.writeln('\t\tres = indent_${parent_str_fn_name}(it.data, indent_count);')
} else {
g.definitions.writeln('\t\tres = ${parent_str_fn_name}(it.data);')
}
g.definitions.writeln('\t} else {')
tmp_str := str_intp_sub('error: %%', 'IError_str(it.err)')
g.definitions.writeln('\t\tres = $tmp_str;')
g.definitions.writeln('\t}')
g.definitions.writeln('\treturn ${str_intp_sub('Option(%%)', 'res')};')
g.definitions.writeln('}')
}
fn (mut g JsGen) gen_str_for_alias(info ast.Alias, styp string, str_fn_name string) {
parent_str_fn_name := g.gen_str_for_type(info.parent_type)
g.definitions.writeln('function ${str_fn_name}(it) { return indent_${str_fn_name}(it, 0); }')
g.definitions.writeln('function indent_${str_fn_name}(it, indent_count) {')
g.definitions.writeln('\tlet indents = string_repeat(new string(" "), indent_count);')
g.definitions.writeln('\tlet tmp_ds = ${parent_str_fn_name}(it);')
g.definitions.writeln('\tlet res = new string("TODO");')
g.definitions.writeln('\treturn res;')
g.definitions.writeln('}')
}
fn (mut g JsGen) gen_str_for_multi_return(info ast.MultiReturn, styp string, str_fn_name string) {
mut fn_builder := strings.new_builder(512)
fn_builder.writeln('function ${str_fn_name}(a) {')
fn_builder.writeln('\tlet sb = strings__new_builder($info.types.len * 10);')
fn_builder.writeln('\tstrings__Builder_write_string(sb, new string("("));')
for i, typ in info.types {
sym := g.table.get_type_symbol(typ)
is_arg_ptr := typ.is_ptr()
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
arg_str_fn_name := g.gen_str_for_type(typ)
if should_use_indent_func(sym.kind) && !sym_has_str_method {
fn_builder.writeln('\tstrings__Builder_write_string(sb, ${arg_str_fn_name}(a.arg$i));')
} else if sym.kind in [.f32, .f64] {
if sym.kind == .f32 {
tmp_val := str_intp_g32('a.arg$i')
fn_builder.writeln('\tstrings__Builder_write_string(sb, $tmp_val);')
} else {
tmp_val := str_intp_g64('a.arg$i')
fn_builder.writeln('\tstrings__Builder_write_string(sb, $tmp_val);')
}
} else if sym.kind == .string {
tmp_str := str_intp_sq('a.arg$i')
fn_builder.writeln('\tstrings__Builder_write_string(sb, $tmp_str);')
} else if sym.kind == .function {
fn_builder.writeln('\tstrings__Builder_write_string(sb, ${arg_str_fn_name}());')
} else {
deref, deref_label := deref_kind(str_method_expects_ptr, is_arg_ptr, typ)
fn_builder.writeln('\t\tstrings__Builder_write_string(sb, new string("$deref_label"));')
fn_builder.writeln('\tstrings__Builder_write_string(sb, ${arg_str_fn_name}( $deref a.arg$i));')
}
if i != info.types.len - 1 {
fn_builder.writeln('\tstrings__Builder_write_string(sb, new string(", "));')
}
}
fn_builder.writeln('\tstrings__Builder_write_string(sb, new string(")"));')
fn_builder.writeln('\tlet res = strings__Builder_str(sb);')
fn_builder.writeln('\treturn res;')
fn_builder.writeln('}')
g.definitions.writeln(fn_builder.str())
}
fn (mut g JsGen) gen_str_for_enum(info ast.Enum, styp string, str_fn_name string) {
s := util.no_dots(styp)
g.definitions.writeln('function ${str_fn_name}(it) { /* gen_str_for_enum */')
// Enums tagged with `[flag]` are special in that they can be a combination of enum values
if info.is_flag {
clean_name := util.strip_main_name(styp.replace('__', '.'))
g.definitions.writeln('\tlet ret = new string("$clean_name{");')
g.definitions.writeln('\tlet first = 1;')
for i, val in info.vals {
g.definitions.writeln('\tif (it & (1 << $i)) {if (!first) {ret = string__plus(ret, new string(" | "));} ret = string__plus(ret, new string(".$val")); first = 0;}')
}
g.definitions.writeln('\tret = string__plus(ret, new string("}"));')
g.definitions.writeln('\treturn ret;')
} else {
g.definitions.writeln('\tswitch(it) {')
// Only use the first multi value on the lookup
mut seen := []string{len: info.vals.len}
for val in info.vals {
if info.is_multi_allowed && val in seen {
continue
} else if info.is_multi_allowed {
seen << val
}
g.definitions.writeln('\t\tcase ${s}.$val: return new string("$val");')
}
g.definitions.writeln('\t\tdefault: return new string("unknown enum value");')
g.definitions.writeln('\t}')
}
g.definitions.writeln('}')
}
fn (mut g JsGen) gen_str_for_interface(info ast.Interface, styp string, str_fn_name string) {
// _str() functions should have a single argument, the indenting ones take 2:
g.definitions.writeln('function ${str_fn_name}(x) { return indent_${str_fn_name}(x, 0); }')
mut fn_builder := strings.new_builder(512)
mut clean_interface_v_type_name := styp.replace('__', '.')
if styp.ends_with('*') {
clean_interface_v_type_name = '&' + clean_interface_v_type_name.replace('*', '')
}
if clean_interface_v_type_name.contains('_T_') {
clean_interface_v_type_name =
clean_interface_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') +
'>'
}
clean_interface_v_type_name = util.strip_main_name(clean_interface_v_type_name)
fn_builder.writeln('function indent_${str_fn_name}(x,indent_count) { /* gen_str_for_interface */')
for typ in info.types {
subtype := g.table.get_type_symbol(typ)
mut func_name := g.gen_str_for_type(typ)
sym_has_str_method, _, _ := subtype.str_method_info()
if should_use_indent_func(subtype.kind) && !sym_has_str_method {
func_name = 'indent_$func_name'
}
// str_intp
if typ == ast.string_type {
/*
mut val := '${func_name}(${deref}($subtype.cname*)x._$subtype.cname'
if should_use_indent_func(subtype.kind) && !sym_has_str_method {
val += ', indent_count'
}
val += ')'
val = val
*/
res := '"TODO"'
fn_builder.write_string('\tif (x._typ == _${styp}_${subtype.cname}_index)')
fn_builder.write_string(' return $res;')
} else {
/*
mut val := '${func_name}(${deref}($subtype.cname*)x._$subtype.cname'
if should_use_indent_func(subtype.kind) && !sym_has_str_method {
val += ', indent_count'
}
val += ')'
val = val
*/
res := '"TODO'
fn_builder.write_string('\tif (x._typ == _${styp}_${subtype.cname}_index)')
fn_builder.write_string(' return $res;\n')
}
}
fn_builder.writeln('\treturn new string("unknown interface value");')
fn_builder.writeln('}')
g.definitions.writeln(fn_builder.str())
}
fn (mut g JsGen) gen_str_for_union_sum_type(info ast.SumType, styp string, str_fn_name string) {
}
fn (mut g JsGen) fn_decl_str(info ast.FnType) string {
mut fn_str := 'fn ('
for i, arg in info.func.params {
if arg.is_mut {
fn_str += 'mut '
}
if i > 0 {
fn_str += ', '
}
fn_str += util.strip_main_name(g.table.get_type_name(g.unwrap_generic(arg.typ)))
}
fn_str += ')'
if info.func.return_type == ast.ovoid_type {
fn_str += ' ?'
} else if info.func.return_type != ast.void_type {
x := util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.func.return_type)))
if info.func.return_type.has_flag(.optional) {
fn_str += ' ?$x'
} else {
fn_str += ' $x'
}
}
return fn_str
}
fn (mut g JsGen) gen_str_for_fn_type(info ast.FnType, styp string, str_fn_name string) {
g.definitions.writeln('function ${str_fn_name}() { return new string("${g.fn_decl_str(info)}");}')
}
fn (mut g JsGen) gen_str_for_chan(info ast.Chan, styp string, str_fn_name string) {
elem_type_name := util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.elem_type)))
g.definitions.writeln('function ${str_fn_name}(x) { return sync__Channel_auto_str(x, new string("$elem_type_name")); }')
}
fn (mut g JsGen) gen_str_for_thread(info ast.Thread, styp string, str_fn_name string) {
ret_type_name := util.strip_main_name(g.table.get_type_name(info.return_type))
g.definitions.writeln('function ${str_fn_name}(_) { return new string("thread($ret_type_name)");}')
}
[inline]
fn styp_to_str_fn_name(styp string) string {
return styp.replace_each(['*', '', '.', '__', ' ', '__']) + '_str'
}
fn deref_kind(str_method_expects_ptr bool, is_elem_ptr bool, typ ast.Type) (string, string) {
if str_method_expects_ptr != is_elem_ptr {
if is_elem_ptr {
return '.val'.repeat(typ.nr_muls()), 'new \$ref('.repeat(typ.nr_muls())
} else {
return 'new \$ref', ''
}
}
return '', ''
}
fn (mut g JsGen) gen_str_for_array(info ast.Array, styp string, str_fn_name string) {
mut typ := info.elem_type
mut sym := g.table.get_type_symbol(info.elem_type)
if mut sym.info is ast.Alias {
typ = sym.info.parent_type
sym = g.table.get_type_symbol(typ)
}
is_elem_ptr := typ.is_ptr()
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
mut elem_str_fn_name := g.gen_str_for_type(typ)
if sym.kind == .byte {
elem_str_fn_name = elem_str_fn_name + '_escaped'
}
g.definitions.writeln('function ${str_fn_name}(a) { return indent_${str_fn_name}(a, 0);}')
g.definitions.writeln('function indent_${str_fn_name}(a, indent_count) {')
g.definitions.writeln('\tlet sb = strings__new_builder(a.len * 10);')
g.definitions.writeln('\tstrings__Builder_write_string(sb, new string("["));')
g.definitions.writeln('\tfor (let i = 0; i < a.len; ++i) {')
if sym.kind == .function {
g.definitions.writeln('\t\tlet it = ${elem_str_fn_name}();')
} else {
g.definitions.writeln('\t\tlet it = a.arr[i];')
if should_use_indent_func(sym.kind) && !sym_has_str_method {
if is_elem_ptr {
g.definitions.writeln('\t\tlet x = indent_${elem_str_fn_name}(it.val, indent_count);')
} else {
g.definitions.writeln('\t\tlet x = indent_${elem_str_fn_name}(it, indent_count);')
}
} else if sym.kind in [.f32, .f64] {
g.definitions.writeln('\t\tlet x = new string( it.val + "");')
} else if sym.kind == .rune {
// Rune are managed at this level as strings
// g.definitions.writeln('\t\tstring x = str_intp(2, _MOV((StrIntpData[]){{new string("\`"), $c.si_s_code, {.d_s = ${elem_str_fn_name}(it) }}, {new string("\`"), 0, {.d_c = 0 }}}));\n')
} else if sym.kind == .string {
g.definitions.writeln('\t\tlet x = new string(it);')
// g.definitions.writeln('\t\tstring x = str_intp(2, _MOV((StrIntpData[]){{new string("\'"), $c.si_s_code, {.d_s = it }}, {new string("\'"), 0, {.d_c = 0 }}}));\n')
} else {
// There is a custom .str() method, so use it.
// NB: we need to take account of whether the user has defined
// `fn (x T) str() {` or `fn (x &T) str() {`, and convert accordingly
deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ)
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string("$deref_label"));')
g.definitions.writeln('\t\tlet x = ${elem_str_fn_name}( $deref it);')
}
}
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, x);')
g.definitions.writeln('\t\tif (i < a.len-1) {')
g.definitions.writeln('\t\t\tstrings__Builder_write_string(sb, new string(", "));')
g.definitions.writeln('\t\t}')
g.definitions.writeln('\t}')
g.definitions.writeln('\tstrings__Builder_write_string(sb, new string("]"));')
g.definitions.writeln('\tlet res = strings__Builder_str(sb);')
g.definitions.writeln('\treturn res;')
g.definitions.writeln('}')
}
fn (mut g JsGen) gen_str_for_array_fixed(info ast.ArrayFixed, styp string, str_fn_name string) {
mut typ := info.elem_type
mut sym := g.table.get_type_symbol(info.elem_type)
if mut sym.info is ast.Alias {
typ = sym.info.parent_type
sym = g.table.get_type_symbol(typ)
}
is_elem_ptr := typ.is_ptr()
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
elem_str_fn_name := g.gen_str_for_type(typ)
g.definitions.writeln('function ${str_fn_name}(a) { return indent_${str_fn_name}(a, 0);}')
g.definitions.writeln('function indent_${str_fn_name}(a, indent_count) {')
g.definitions.writeln('\tlet sb = strings__new_builder($info.size * 10);')
g.definitions.writeln('\tstrings__Builder_write_string(sb, new string("["));')
g.definitions.writeln('\tfor (let i = 0; i < $info.size; ++i) {')
if sym.kind == .function {
g.definitions.writeln('\t\tstring x = ${elem_str_fn_name}();')
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, x);')
} else {
deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ)
if should_use_indent_func(sym.kind) && !sym_has_str_method {
if is_elem_ptr {
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string("$deref_label"));')
g.definitions.writeln('\t\tif ( 0 == a.arr[i] ) {')
g.definitions.writeln('\t\t\tstrings__Builder_write_string(sb, new string("0"));')
g.definitions.writeln('\t\t}else{')
g.definitions.writeln('\t\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(a.arr[i] $deref) );')
g.definitions.writeln('\t\t}')
} else {
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(a.arr[i]) );')
}
} else if sym.kind in [.f32, .f64] {
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string(a.arr[i].val.toString()) );')
} else if sym.kind == .string {
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, a.arr[i].str);')
} else if sym.kind == .rune {
// tmp_str := str_intp_rune('${elem_str_fn_name}( a[i] $deref)')
// g.definitions.writeln('\t\tstrings__Builder_write_string(sb, $tmp_str);')
} else {
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(a.arr[i] $deref));')
}
}
g.definitions.writeln('\t\tif (i < ${info.size - 1}) {')
g.definitions.writeln('\t\t\tstrings__Builder_write_string(sb, new string(", "));')
g.definitions.writeln('\t\t}')
g.definitions.writeln('\t}')
g.definitions.writeln('\tstrings__Builder_write_string(sb, new string("]"));')
g.definitions.writeln('\tlet res = strings__Builder_str(sb);')
g.definitions.writeln('\treturn res;')
g.definitions.writeln('}')
}
fn (mut g JsGen) gen_str_for_map(info ast.Map, styp string, str_fn_name string) {
mut key_typ := info.key_type
mut key_sym := g.table.get_type_symbol(key_typ)
if mut key_sym.info is ast.Alias {
key_typ = key_sym.info.parent_type
key_sym = g.table.get_type_symbol(key_typ)
}
key_styp := g.typ(key_typ)
key_str_fn_name := key_styp.replace('*', '') + '_str'
if !key_sym.has_method('str') {
g.gen_str_for_type(key_typ)
}
mut val_typ := info.value_type
mut val_sym := g.table.get_type_symbol(val_typ)
if mut val_sym.info is ast.Alias {
val_typ = val_sym.info.parent_type
val_sym = g.table.get_type_symbol(val_typ)
}
val_styp := g.typ(val_typ)
elem_str_fn_name := val_styp.replace('*', '') + '_str'
if !val_sym.has_method('str') {
g.gen_str_for_type(val_typ)
}
g.definitions.writeln('function ${str_fn_name}(m) { return indent_${str_fn_name}(m, 0);}')
g.definitions.writeln('function indent_${str_fn_name}(m, indent_count) { /* gen_str_for_map */')
g.definitions.writeln('\tlet sb = strings__new_builder(m.map.length*10);')
g.definitions.writeln('\tstrings__Builder_write_string(sb, new string("{"));')
g.definitions.writeln('\tlet i = 0;')
g.definitions.writeln('\tfor (let [key,value] of m.map) {')
if key_sym.kind == .string {
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string(key));')
} else if key_sym.kind == .rune {
// tmp_str := str_intp_rune('${key_str_fn_name}(key)')
// g.definitions.writeln('\t\tstrings__Builder_write_string(sb, $tmp_str);')
} else {
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${key_str_fn_name}(key));')
}
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string(": "));')
if val_sym.kind == .function {
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}());')
} else if val_sym.kind == .string {
// tmp_str := str_intp_sq('*($val_styp*)DenseArray_value(&m.key_values, i)')
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, value);')
} else if should_use_indent_func(val_sym.kind) && !val_sym.has_method('str') {
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, indent_${elem_str_fn_name}(value, indent_count));')
} else if val_sym.kind in [.f32, .f64] {
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, value.val + "");')
} else if val_sym.kind == .rune {
// tmp_str := str_intp_rune('${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i))')
// g.definitions.writeln('\t\tstrings__Builder_write_string(sb, $tmp_str);')
} else {
g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(value));')
}
g.definitions.writeln('\t\tif (i != m.map.size-1) {')
g.definitions.writeln('\t\t\tstrings__Builder_write_string(sb, new string(", "));')
g.definitions.writeln('\t\t}')
g.definitions.writeln('\t\ti++;')
g.definitions.writeln('\t}')
g.definitions.writeln('\tstrings__Builder_write_string(sb, new string("}"));')
g.definitions.writeln('\tlet res = strings__Builder_str(sb);')
g.definitions.writeln('\treturn res;')
g.definitions.writeln('}')
}
fn (g &JsGen) type_to_fmt1(typ ast.Type) StrIntpType {
if typ == ast.byte_type_idx {
return .si_u8
}
if typ == ast.char_type_idx {
return .si_c
}
if typ in ast.voidptr_types || typ in ast.byteptr_types {
return .si_p
}
if typ in ast.charptr_types {
// return '%C\\000' // a C string
return .si_s
}
sym := g.table.get_type_symbol(typ)
if typ.is_ptr() && (typ.is_int_valptr() || typ.is_float_valptr()) {
return .si_s
} else if sym.kind in [.struct_, .array, .array_fixed, .map, .bool, .enum_, .interface_,
.sum_type, .function, .alias, .chan] {
return .si_s
} else if sym.kind == .string {
return .si_s
// return "'%.*s\\000'"
} else if sym.kind in [.f32, .f64] {
if sym.kind == .f32 {
return .si_g32
}
return .si_g64
} else if sym.kind == .int {
return .si_i32
} else if sym.kind == .u32 {
return .si_u32
} else if sym.kind == .u64 {
return .si_u64
} else if sym.kind == .i64 {
return .si_i64
}
return .si_i32
}
fn (mut g JsGen) gen_str_for_struct(info ast.Struct, styp string, str_fn_name string) {
// _str() functions should have a single argument, the indenting ones take 2:
g.definitions.writeln('function ${str_fn_name}(it) { return indent_${str_fn_name}(it, 0);}')
mut fn_builder := strings.new_builder(512)
defer {
g.definitions.writeln(fn_builder.str())
}
fn_builder.writeln('function indent_${str_fn_name}(it, indent_count) {')
mut clean_struct_v_type_name := styp.replace('__', '.')
if clean_struct_v_type_name.contains('_T_') {
// TODO: this is a bit hacky. styp shouldn't be even parsed with _T_
// use something different than g.typ for styp
clean_struct_v_type_name =
clean_struct_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') +
'>'
}
clean_struct_v_type_name = util.strip_main_name(clean_struct_v_type_name)
// generate ident / indent length = 4 spaces
if info.fields.len == 0 {
fn_builder.writeln('\treturn new string("$clean_struct_v_type_name{}");')
fn_builder.writeln('}')
return
}
fn_builder.writeln('\tlet res = /*struct name*/new string("$clean_struct_v_type_name{\\n")')
for i, field in info.fields {
mut ptr_amp := if field.typ.is_ptr() { '&' } else { '' }
mut prefix := ''
// manage prefix and quote symbol for the filed
/*
mut quote_str := ''
if sym.kind == .string {
quote_str = "'"
} else if field.typ in ast.charptr_types {
quote_str = '\\"'
prefix = 'C'
}
quote_str = quote_str
*/
sym := g.table.get_type_symbol(g.unwrap_generic(field.typ))
// first fields doesn't need \n
if i == 0 {
fn_builder.write_string('res.str += " $field.name: $ptr_amp$prefix" + ')
} else {
fn_builder.write_string('res.str += "\\n $field.name: $ptr_amp$prefix" + ')
}
// custom methods management
has_custom_str := sym.has_method('str')
mut field_styp := g.typ(field.typ).replace('*', '')
field_styp_fn_name := if has_custom_str {
'${field_styp}_str'
} else {
g.gen_str_for_type(field.typ)
}
mut func := struct_auto_str_func1(mut g, sym, field.typ, field_styp_fn_name, field.name)
if field.typ in ast.cptr_types {
func = '(voidptr) it.$field.name'
} else if field.typ.is_ptr() {
// reference types can be "nil"
fn_builder.write_string('isnil(it.${g.js_name(field.name)})')
fn_builder.write_string(' ? new string("nil") : ')
// struct, floats and ints have a special case through the _str function
if sym.kind != .struct_ && !field.typ.is_int_valptr() && !field.typ.is_float_valptr() {
fn_builder.write_string('*')
}
}
// handle circular ref type of struct to the struct itself
if styp == field_styp {
fn_builder.write_string('res.str += new string("<circular>")')
} else {
// manage C charptr
if field.typ in ast.charptr_types {
fn_builder.write_string('tos2((byteptr)$func)')
} else {
if field.typ.is_ptr() && sym.kind == .struct_ {
fn_builder.write_string('(indent_count > 25) ? new string("<probably circular>") : ')
}
fn_builder.write_string(func)
}
}
fn_builder.writeln('')
}
fn_builder.writeln('res.str += "\\n}"')
// fn_builder.writeln('\t\t{new string("\\n"), $c.si_s_code, {.d_s=indents}}, {new string("}"), 0, {.d_c=0}},')
fn_builder.writeln('\treturn res;')
fn_builder.writeln('}')
}
fn struct_auto_str_func1(mut g JsGen, sym &ast.TypeSymbol, field_type ast.Type, fn_name string, field_name string) string {
has_custom_str, expects_ptr, _ := sym.str_method_info()
if sym.kind == .enum_ {
return '${fn_name}(it.${g.js_name(field_name)})'
} else if should_use_indent_func(sym.kind) {
mut obj := 'it.${g.js_name(field_name)}'
if field_type.is_ptr() && !expects_ptr {
obj = '*$obj'
}
if has_custom_str {
return '${fn_name}($obj)'
}
return 'indent_${fn_name}($obj, indent_count + 1)'
} else if sym.kind in [.array, .array_fixed, .map, .sum_type] {
if has_custom_str {
return '${fn_name}(it.${g.js_name(field_name)})'
}
return 'indent_${fn_name}(it.${g.js_name(field_name)}, indent_count + 1)'
} else if sym.kind == .function {
return '${fn_name}()'
} else {
if sym.kind == .chan {
return '${fn_name}(it.${g.js_name(field_name)})'
}
mut method_str := 'it.${g.js_name(field_name)}'
if sym.kind == .bool {
method_str += ' ? new string("true") : new string("false")'
} else if (field_type.is_int_valptr() || field_type.is_float_valptr())
&& field_type.is_ptr() && !expects_ptr {
// ptr int can be "nil", so this needs to be casted to a string
if sym.kind == .f32 {
return 'str_intp(1, _MOV((StrIntpData[]){
{_SLIT0, $si_g32_code, {.d_f32 = *$method_str }}
}))'
} else if sym.kind == .f64 {
return 'str_intp(1, _MOV((StrIntpData[]){
{_SLIT0, $si_g64_code, {.d_f64 = *$method_str }}
}))'
} else if sym.kind == .u64 {
fmt_type := StrIntpType.si_u64
return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_u64 = *$method_str }}}))'
}
fmt_type := StrIntpType.si_i32
return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_i32 = *$method_str }}}))'
}
return method_str
}
}

View File

@ -17,7 +17,7 @@ fn (mut g JsGen) to_js_typ_def_val(s string) string {
fn (mut g JsGen) to_js_typ_val(t ast.Type) string {
sym := g.table.get_type_symbol(t)
mut styp := ''
mut prefix := if g.file.mod.name == 'builtin' { 'new ' } else { 'new builtin.' }
mut prefix := 'new '
match sym.kind {
.i8, .i16, .int, .i64, .byte, .u8, .u16, .u32, .u64, .f32, .f64, .int_literal,
.float_literal, .size_t {
@ -114,8 +114,22 @@ fn (mut g JsGen) sym_to_js_typ(sym ast.TypeSymbol) string {
return styp
}
// V type to JS type
pub fn (mut g JsGen) base_type(t ast.Type) string {
mut styp := g.cc_type(t, true)
return styp
}
pub fn (mut g JsGen) typ(t ast.Type) string {
sym := g.table.get_type_symbol(t)
if sym.kind == .voidptr {
return 'any'
}
styp := g.base_type(t)
return styp
}
// V type to JS type
pub fn (mut g JsGen) doc_typ(t ast.Type) string {
sym := g.table.get_type_symbol(t)
mut styp := ''
match sym.kind {
@ -263,7 +277,7 @@ struct BuiltinPrototypeConfig {
constructor string = 'this.val = val'
value_of string = 'this.val'
to_string string = 'this.val.toString()'
eq string = 'this.val === other.val'
eq string = 'self.val === other.val'
to_jsval string = 'this'
extras string
has_strfn bool
@ -277,21 +291,22 @@ fn (mut g JsGen) gen_builtin_prototype(c BuiltinPrototypeConfig) {
if c.extras.len > 0 {
g.writeln('$c.extras,')
}
for method in g.method_fn_decls[c.typ_name] {
g.inside_def_typ_decl = true
g.gen_method_decl(method, .struct_method)
g.inside_def_typ_decl = false
g.writeln(',')
}
g.writeln('valueOf() { return $c.value_of },')
g.writeln('toString() { return $c.to_string },')
g.writeln('eq(other) { return $c.eq },')
// g.writeln('eq(other) { return $c.eq },')
g.writeln('\$toJS() { return $c.to_jsval }, ')
if c.has_strfn {
g.writeln('str() { return new string(this.toString()) }')
}
g.dec_indent()
g.writeln('};\n')
g.writeln('function ${c.typ_name}__eq(self,other) { return $c.eq; } ')
for method in g.method_fn_decls[c.typ_name] {
g.inside_def_typ_decl = true
g.gen_method_decl(method, .struct_method)
g.inside_def_typ_decl = false
}
}
// generate builtin type definitions, used for casting and methods.
@ -308,7 +323,7 @@ fn (mut g JsGen) gen_builtin_type_defs() {
constructor: 'this.val = Number(val)'
value_of: 'Number(this.val)'
to_string: 'this.valueOf().toString()'
eq: 'this.valueOf() === other.valueOf()'
eq: 'new bool(self.valueOf() === other.valueOf())'
to_jsval: '+this'
)
}
@ -320,7 +335,7 @@ fn (mut g JsGen) gen_builtin_type_defs() {
constructor: 'this.val = BigInt.asUintN(64,BigInt(val))'
value_of: 'this.val'
to_string: 'this.val.toString()'
eq: 'this.valueOf() === other.valueOf()'
eq: 'new bool(self.valueOf() === other.valueOf())'
to_jsval: 'this.val'
)
}
@ -331,7 +346,7 @@ fn (mut g JsGen) gen_builtin_type_defs() {
constructor: 'this.val = BigInt.asIntN(64,BigInt(val))'
value_of: 'this.val'
to_string: 'this.val.toString()'
eq: 'this.valueOf() === other.valueOf()'
eq: 'new bool(self.valueOf() === other.valueOf())'
to_jsval: 'this.val'
)
}
@ -342,7 +357,7 @@ fn (mut g JsGen) gen_builtin_type_defs() {
constructor: 'if (typeof(val) == "string") { this.val = val.charCodeAt() } else if (val instanceof string) { this.val = val.str.charCodeAt(); } else { this.val = val | 0 }'
value_of: 'this.val | 0'
to_string: 'new string(this.val + "")'
eq: 'this.valueOf() === other.valueOf()'
eq: 'new bool(self.valueOf() === other.valueOf())'
to_jsval: '+this'
)
}
@ -356,11 +371,11 @@ fn (mut g JsGen) gen_builtin_type_defs() {
}
'bool' {
g.gen_builtin_prototype(
constructor: 'this.val = +val !== 0'
constructor: 'this.val = val instanceof bool ? val.val : +val !== 0'
typ_name: typ_name
default_value: 'new Boolean(false)'
to_jsval: '+this != 0'
eq: 'this.val === other.valueOf()'
eq: 'new bool(self.val === other.valueOf())'
)
}
'string' {
@ -371,7 +386,7 @@ fn (mut g JsGen) gen_builtin_type_defs() {
constructor: 'this.str = str.toString(); this.len = this.str.length'
value_of: 'this.str'
to_string: 'this.str'
eq: 'this.str === other.str'
eq: 'new bool(self.str === other.str)'
has_strfn: false
to_jsval: 'this.str'
)
@ -384,7 +399,7 @@ fn (mut g JsGen) gen_builtin_type_defs() {
constructor: 'this.map = map'
value_of: 'this'
to_string: 'this.map.toString()'
eq: 'vEq(this, other)'
eq: 'new bool(vEq(self, other))'
to_jsval: 'this.map'
)
}
@ -396,7 +411,7 @@ fn (mut g JsGen) gen_builtin_type_defs() {
constructor: 'this.arr = arr'
value_of: 'this'
to_string: 'JSON.stringify(this.arr.map(it => it.valueOf()))'
eq: 'vEq(this, other)'
eq: 'new bool(vEq(self, other))'
to_jsval: 'this.arr'
)
}
@ -408,7 +423,7 @@ fn (mut g JsGen) gen_builtin_type_defs() {
constructor: 'this.val = any'
value_of: 'this.val'
to_string: '"&" + this.val'
eq: 'this == other' // compare by ptr
eq: 'new bool(self == other)' // compare by ptr
to_jsval: 'this.val.\$toJS()'
)
}

View File

@ -1,11 +1,99 @@
module js
import v.ast
import v.util
fn (mut g JsGen) gen_method_call(it ast.CallExpr) bool {
g.call_stack << it
fn (mut g JsGen) js_mname(name_ string) string {
mut is_js := false
is_overload := ['+', '-', '*', '/', '==', '<', '>']
mut name := name_
if name.starts_with('JS.') {
name = name[3..]
is_js = true
}
ns := get_ns(name)
name = if name in is_overload {
match name {
'+' {
'\$add'
}
'-' {
'\$sub'
}
'/' {
'\$div'
}
'*' {
'\$mul'
}
'%' {
'\$mod'
}
'==' {
'eq'
}
'>' {
'\$gt'
}
'<' {
'\$lt'
}
else {
''
}
}
} else if g.ns == 0 {
name
} else if ns == g.ns.name {
name.split('.').last()
} else {
g.get_alias(name)
}
mut parts := name.split('.')
if !is_js {
for i, p in parts {
if p in js_reserved {
parts[i] = 'v_$p'
}
}
}
return parts.join('.')
}
mut name := g.js_name(it.name)
fn (mut g JsGen) js_call(node ast.CallExpr) {
g.call_stack << node
it := node
g.write('${g.js_mname(it.name)}(')
for i, arg in it.args {
g.expr(arg.expr)
if i != it.args.len - 1 {
g.write(', ')
}
}
// end call
g.write(')')
g.call_stack.delete_last()
}
fn (mut g JsGen) js_method_call(node ast.CallExpr) {
g.call_stack << node
it := node
g.expr(it.left)
g.write('.${g.js_mname(it.name)}(')
for i, arg in it.args {
g.expr(arg.expr)
if i != it.args.len - 1 {
g.write(', ')
}
}
// end method call
g.write(')')
g.call_stack.delete_last()
}
fn (mut g JsGen) method_call(node ast.CallExpr) {
g.call_stack << node
it := node
call_return_is_optional := it.return_type.has_flag(.optional)
if call_return_is_optional {
g.writeln('(function(){')
@ -14,9 +102,45 @@ fn (mut g JsGen) gen_method_call(it ast.CallExpr) bool {
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'] {
mut unwrapped_rec_type := node.receiver_type
if g.table.cur_fn.generic_names.len > 0 {
unwrapped_rec_type = g.unwrap_generic(node.receiver_type)
} else {
sym := g.table.get_type_symbol(node.receiver_type)
match sym.info {
ast.Struct, ast.Interface, ast.SumType {
generic_names := sym.info.generic_types.map(g.table.get_type_symbol(it).name)
if utyp := g.table.resolve_generic_to_concrete(node.receiver_type, generic_names,
sym.info.concrete_types)
{
unwrapped_rec_type = utyp
}
}
else {}
}
}
mut typ_sym := g.table.get_type_symbol(unwrapped_rec_type)
rec_cc_type := g.cc_type(unwrapped_rec_type, false)
mut receiver_type_name := util.no_dots(rec_cc_type)
// alias type that undefined this method (not include `str`) need to use parent type
if typ_sym.kind == .alias && node.name != 'str' && !typ_sym.has_method(node.name) {
unwrapped_rec_type = (typ_sym.info as ast.Alias).parent_type
typ_sym = g.table.get_type_symbol(unwrapped_rec_type)
}
if typ_sym.kind == .interface_ && (typ_sym.info as ast.Interface).defines_method(node.name) {
// g.write('${g.js_name(receiver_type_name)}_name_table')
// g.expr(node.left)
g.writeln('/* TODO: Interface call */')
return
}
left_sym := g.table.get_type_symbol(node.left_type)
final_left_sym := g.table.get_final_type_symbol(node.left_type)
if final_left_sym.kind == .array {
if final_left_sym.kind == .array && it.name in ['map', 'filter'] {
g.expr(it.left)
mut ltyp := it.left_type
for ltyp.is_ptr() {
@ -25,7 +149,7 @@ fn (mut g JsGen) gen_method_call(it ast.CallExpr) bool {
}
g.write('.')
// Prevent 'it' from getting shadowed inside the match
node := it
g.write(it.name)
g.write('(')
expr := node.args[0].expr
@ -33,19 +157,19 @@ fn (mut g JsGen) gen_method_call(it ast.CallExpr) bool {
ast.AnonFn {
g.gen_fn_decl(expr.decl)
g.write(')')
return true
return
}
ast.Ident {
if expr.kind == .function {
g.write(g.js_name(expr.name))
g.write(')')
return true
return
} 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
return
}
}
}
@ -55,61 +179,36 @@ fn (mut g JsGen) gen_method_call(it ast.CallExpr) bool {
g.write('it => ')
g.expr(node.args[0].expr)
g.write(')')
return true
return
}
left_sym := g.table.get_type_symbol(it.left_type)
if left_sym.kind == .array {
if final_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
return
}
}
}
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(', ')
}
if final_left_sym.kind == .array
&& node.name in ['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', 'first', 'last', 'pop', 'clone', 'reverse', 'slice', 'pointers'] {
if !(left_sym.info is ast.Alias && typ_sym.has_method(node.name)) {
// `array_Xyz_clone` => `array_clone`
receiver_type_name = 'array'
}
// 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(')')
}
mut name := util.no_dots('${receiver_type_name}_$node.name')
name = g.generic_fn_name(node.concrete_types, name, false)
g.write('${name}(')
g.expr(it.left)
g.write(',')
for i, arg in it.args {
g.expr(arg.expr)
if i != it.args.len - 1 {
g.write(', ')
}
}
g.write(')')
if call_return_is_optional {
// end unwrap
@ -145,14 +244,19 @@ fn (mut g JsGen) gen_method_call(it ast.CallExpr) bool {
g.write('})()')
}
g.call_stack.delete_last()
return true
}
fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
if it.is_method && g.table.get_type_symbol(it.receiver_type).name.starts_with('JS.') {
g.js_method_call(it)
return
} else if it.name.starts_with('JS.') {
g.js_call(it)
return
}
if it.is_method {
if g.gen_method_call(it) {
return
}
g.method_call(it)
return
}
node := it
g.call_stack << it
@ -162,9 +266,6 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
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('(')
}
@ -174,23 +275,19 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
g.inc_indent()
g.writeln('try {')
g.inc_indent()
g.write('return builtin.unwrap(')
g.write('return unwrap(')
}
if is_print {
mut typ := node.args[0].typ
expr := node.args[0].expr
g.write('builtin.$print_method (')
g.write('$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)
@ -220,9 +317,9 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
.propagate {
panicstr := '`optional not set (\${err})`'
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
g.writeln('return builtin.panic($panicstr)')
g.writeln('return panic($panicstr)')
} else {
g.writeln('builtin.js_throw(err)')
g.writeln('js_throw(err)')
}
}
else {}

View File

@ -0,0 +1,243 @@
module js
import v.util
import v.ast
fn (mut g JsGen) gen_plain_infix_expr(node ast.InfixExpr) {
it := node
l_sym := g.table.get_final_type_symbol(it.left_type)
r_sym := g.table.get_final_type_symbol(it.right_type)
greater_typ := g.greater_typ(it.left_type, it.right_type)
g.write('new ${g.typ(greater_typ)}( ')
g.cast_stack << greater_typ
if (l_sym.kind == .i64 || l_sym.kind == .u64) || (r_sym.kind == .i64 || r_sym.kind == .u64) {
g.write('BigInt(')
g.expr(node.left)
g.gen_deref_ptr(node.left_type)
g.write('.valueOf())')
g.write(' $node.op.str() ')
g.write('BigInt(')
g.expr(node.right)
g.gen_deref_ptr(node.left_type)
g.write('.valueOf())')
} else {
g.expr(node.left)
g.gen_deref_ptr(node.left_type)
g.write('.valueOf()')
g.write(' $node.op.str() ')
g.expr(node.right)
g.gen_deref_ptr(node.left_type)
g.write('.valueOf()')
}
g.cast_stack.delete_last()
g.write(')')
}
fn (mut g JsGen) infix_expr_arithmetic_op(node ast.InfixExpr) {
left := g.unwrap(node.left_type)
right := g.unwrap(node.right_type)
method := g.table.type_find_method(left.sym, node.op.str()) or {
g.gen_plain_infix_expr(node)
return
}
left_styp := g.typ(left.typ.set_nr_muls(0))
g.write(left_styp)
g.write('_')
g.write(util.replace_op(node.op.str()))
g.write('(')
g.op_arg(node.left, method.params[0].typ, left.typ)
g.write(', ')
g.op_arg(node.right, method.params[1].typ, right.typ)
g.write(')')
}
fn (mut g JsGen) op_arg(expr ast.Expr, expected ast.Type, got ast.Type) {
mut needs_closing := 0
mut nr_muls := got.nr_muls()
if expected.is_ptr() {
if nr_muls > 0 {
nr_muls--
} else {
g.write('new \$ref(')
needs_closing++
}
}
g.expr(expr)
g.write('.val'.repeat(nr_muls))
for i := 0; i < needs_closing; i++ {
g.write(')')
}
}
fn (mut g JsGen) infix_expr_eq_op(node ast.InfixExpr) {
left := g.unwrap(node.left_type)
right := g.unwrap(node.right_type)
has_operator_overloading := g.table.type_has_method(left.sym, '==')
g.write('new bool(')
if (left.typ.is_ptr() && right.typ.is_int()) || (right.typ.is_ptr() && left.typ.is_int()) {
g.gen_plain_infix_expr(node)
} else if has_operator_overloading {
if node.op == .ne {
g.write('!')
}
g.write(g.typ(left.unaliased.set_nr_muls(0)))
g.write('__eq(')
g.expr(node.left)
g.gen_deref_ptr(node.left_type)
g.write(',')
g.expr(node.right)
g.gen_deref_ptr(node.right_type)
g.write(')')
} else if left.typ.idx() == right.typ.idx()
&& left.sym.kind in [.array, .array_fixed, .alias, .map, .struct_, .sum_type] {
// TODO: Actually generate equality methods
if node.op == .ne {
g.write('!')
}
g.write('vEq(')
g.expr(node.left)
g.gen_deref_ptr(node.left_type)
g.write(',')
g.expr(node.right)
g.gen_deref_ptr(node.right_type)
g.write(')')
} else {
g.expr(node.left)
g.gen_deref_ptr(node.left_type)
g.write('.valueOf() $node.op.str() ')
g.expr(node.right)
g.gen_deref_ptr(node.right_type)
g.write('.valueOf()')
}
g.write(')')
}
fn (mut g JsGen) infix_expr_cmp_op(node ast.InfixExpr) {
left := g.unwrap(node.left_type)
right := g.unwrap(node.right_type)
has_operator_overloading := g.table.type_has_method(left.sym, '<')
g.write('new bool(')
if left.sym.kind == right.sym.kind && has_operator_overloading {
if node.op in [.le, .ge] {
g.write('!')
}
g.write(g.typ(left.typ.set_nr_muls(0)))
g.write('__lt')
if node.op in [.lt, .ge] {
g.write('(')
g.expr(node.left)
g.gen_deref_ptr(left.typ)
g.write(', ')
g.expr(node.right)
g.gen_deref_ptr(right.typ)
g.write(')')
} else {
g.write('(')
g.expr(node.right)
g.gen_deref_ptr(left.typ)
g.write(', ')
g.expr(node.left)
g.gen_deref_ptr(right.typ)
g.write(')')
}
} else {
g.expr(node.left)
g.gen_deref_ptr(node.left_type)
g.write('.valueOf() $node.op.str() ')
g.expr(node.right)
g.gen_deref_ptr(node.right_type)
g.write('.valueOf()')
}
g.write(')')
}
fn (mut g JsGen) infix_expr_left_shift_op(node ast.InfixExpr) {
left := g.unwrap(node.left_type)
right := g.unwrap(node.right_type)
if left.unaliased_sym.kind == .array {
// arr << val
array_info := left.unaliased_sym.info as ast.Array
g.write('Array.prototype.push.call(')
//&& array_info.elem_type != g.unwrap_generic(node.right_type)
if right.unaliased_sym.kind == .array && array_info.elem_type != right.typ {
g.expr(node.left)
g.gen_deref_ptr(left.typ)
g.write('.arr,...')
g.expr(node.right)
g.gen_deref_ptr(right.typ)
g.write('.arr')
g.write(')')
} else {
g.expr(node.left)
g.gen_deref_ptr(left.typ)
g.write('.arr,')
g.expr(node.right)
g.write(')')
}
} else {
g.gen_plain_infix_expr(node)
}
}
fn (mut g JsGen) infix_in_not_in_op(node ast.InfixExpr) {
l_sym := g.table.get_final_type_symbol(node.left_type)
r_sym := g.table.get_final_type_symbol(node.right_type)
if node.op == .not_in {
g.write('!')
}
g.expr(node.right)
g.gen_deref_ptr(node.right_type)
if r_sym.kind == .map {
g.write('.map.has(')
} else if r_sym.kind == .string {
g.write('.str.includes(')
} else {
g.write('.\$includes(')
}
g.expr(node.left)
if l_sym.kind == .string {
g.write('.str')
}
g.write(')')
}
fn (mut g JsGen) infix_is_not_is_op(node ast.InfixExpr) {
g.expr(node.left)
rsym := g.table.get_type_symbol(g.unwrap(node.right_type).typ)
g.gen_deref_ptr(node.left_type)
g.write(' instanceof ')
g.write(g.js_name(rsym.name))
}
fn (mut g JsGen) infix_expr(node ast.InfixExpr) {
match node.op {
.plus, .minus, .mul, .div, .mod {
g.infix_expr_arithmetic_op(node)
}
.eq, .ne {
g.infix_expr_eq_op(node)
}
.gt, .ge, .lt, .le {
g.infix_expr_cmp_op(node)
}
.logical_or, .and {
g.gen_plain_infix_expr(node)
}
.left_shift {
g.infix_expr_left_shift_op(node)
}
.not_in, .key_in {
g.infix_in_not_in_op(node)
}
.key_is, .not_is {
g.infix_is_not_is_op(node)
}
else {
g.gen_plain_infix_expr(node)
}
}
}

View File

@ -39,7 +39,6 @@ struct SourcemapHelper {
struct Namespace {
name string
mut:
out strings.Builder = strings.new_builder(128)
pub_vars []string
imports map[string]string
indent int
@ -80,6 +79,7 @@ mut:
sourcemap sourcemap.SourceMap // maps lines in generated javascrip file to original source files and line
comptime_var_type_map map[string]ast.Type
defer_ifdef string
out strings.Builder = strings.new_builder(128)
}
fn (mut g JsGen) write_tests_definitions() {
@ -121,24 +121,27 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
g.find_class_methods(file.stmts)
g.escape_namespace()
}
for file in files {
g.file = file
g.enter_namespace(g.file.mod.name)
if g.enable_doc {
g.writeln('/** @namespace $file.mod.name */')
}
g.is_test = g.pref.is_test
// store imports
mut imports := []string{}
for imp in g.file.imports {
imports << imp.mod
}
graph.add(g.file.mod.name, imports)
// builtin types
if g.file.mod.name == 'builtin' && !g.generated_builtin {
g.gen_builtin_type_defs()
g.writeln('Object.defineProperty(array.prototype,"len", { get: function() {return new builtin.int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }); ')
g.writeln('Object.defineProperty(string.prototype,"len", { get: function() {return new builtin.int(this.str.length);}, set: function(l) {/* ignore */ } }); ')
g.writeln('Object.defineProperty(map.prototype,"len", { get: function() {return new builtin.int(this.map.length);}, set: function(l) { this.map.length = l.valueOf(); } }); ')
g.writeln('Object.defineProperty(array.prototype,"length", { get: function() {return new builtin.int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }); ')
g.writeln('Object.defineProperty(array.prototype,"len", { get: function() {return new int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }); ')
g.writeln('Object.defineProperty(string.prototype,"len", { get: function() {return new int(this.str.length);}, set: function(l) {/* ignore */ } }); ')
g.writeln('Object.defineProperty(map.prototype,"len", { get: function() {return new int(this.map.length);}, set: function(l) { this.map.length = l.valueOf(); } }); ')
g.writeln('Object.defineProperty(array.prototype,"length", { get: function() {return new int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }); ')
g.generated_builtin = true
}
if g.is_test && !tests_inited {
@ -146,7 +149,6 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
tests_inited = true
}
g.stmts(file.stmts)
g.writeln('try { init() } catch (_) {}')
// store the current namespace
g.escape_namespace()
}
@ -157,7 +159,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
deps_resolved := graph.resolve()
nodes := deps_resolved.nodes
mut out := g.hashes() + g.definitions.str()
mut out := g.definitions.str() + g.hashes()
// equality check for js objects
// TODO: Fix msvc bug that's preventing $embed_file('fast_deep_equal.js')
// unsafe {
@ -165,30 +167,32 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
// out += eq_fn.data().vstring()
//}
out += fast_deep_eq_fn
if pref.is_shared {
// Export, through CommonJS, the module of the entry file if `-shared` was passed
export := nodes[nodes.len - 1].name
out += 'if (typeof module === "object" && module.exports) module.exports = $export;\n'
}
out += '\n'
out += g.out.str()
/*
for node in nodes {
name := g.js_name(node.name).replace('.', '_')
if g.enable_doc {
out += '/** @namespace $name */\n'
}
out += 'const $name = (function ('
// out += 'const $name = (function ('
mut namespace := g.namespaces[node.name]
mut first := true
for _, val in namespace.imports {
if !first {
out += ', '
}
first = false
out += val
}
out += ') {\n\t'
namespace_code := namespace.out.str()
if g.pref.sourcemap {
// calculate current output start line
mut current_line := u32(out.count('\n') + 1)
mut sm_pos := u32(0)
for sourcemap_ns_entry in namespace.sourcemap_helper {
// calculate final generated location in output based on position
current_segment := namespace_code.substr(int(sm_pos), int(sourcemap_ns_entry.ns_pos))
current_segment := g.out.substr(int(sm_pos), int(sourcemap_ns_entry.ns_pos))
current_line += u32(current_segment.count('\n'))
current_column := if last_nl_pos := current_segment.last_index('\n') {
u32(current_segment.len - last_nl_pos - 1)
@ -202,66 +206,11 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
sm_pos = sourcemap_ns_entry.ns_pos
}
}
out += namespace_code
// public scope
out += '\n'
if g.enable_doc {
out += '\n\t/* module exports */'
}
out += '\n\treturn {'
// export builtin types
if name == 'builtin' {
for typ in js.v_types {
out += '\n\t\t$typ,'
}
}
for i, pub_var in namespace.pub_vars {
out += '\n\t\t$pub_var'
if i < namespace.pub_vars.len - 1 {
out += ','
}
}
if namespace.pub_vars.len > 0 {
out += '\n\t'
}
out += '};'
out += '\n})('
first = true
for key, _ in namespace.imports {
if !first {
out += ', '
}
first = false
out += key.replace('.', '_')
}
out += ');\n'
// generate builtin basic type casts
if name == 'builtin' {
out += '// builtin type casts\n'
out += 'const ['
for i, typ in js.v_types {
if i > 0 {
out += ', '
}
out += '$typ'
}
out += '] = ['
for i, typ in js.v_types {
if i > 0 {
out += ','
}
out += '\n\tfunction(val) { return new builtin.${typ}(val) }'
}
out += '\n]\n'
}
}
if pref.is_shared {
// Export, through CommonJS, the module of the entry file if `-shared` was passed
export := nodes[nodes.len - 1].name
out += 'if (typeof module === "object" && module.exports) module.exports = $export;\n'
}
out += '\n'
}*/
if g.pref.sourcemap {
out += g.create_sourcemap()
}
@ -429,7 +378,7 @@ fn verror(msg string) {
[inline]
pub fn (mut g JsGen) gen_indent() {
if g.ns.indent > 0 && g.empty_line {
g.ns.out.write_string(util.tabs(g.ns.indent))
g.out.write_string(util.tabs(g.ns.indent))
}
g.empty_line = false
}
@ -450,7 +399,7 @@ pub fn (mut g JsGen) write(s string) {
verror('g.write: not in a namespace')
}
g.gen_indent()
g.ns.out.write_string(s)
g.out.write_string(s)
}
[inline]
@ -459,7 +408,7 @@ pub fn (mut g JsGen) writeln(s string) {
verror('g.writeln: not in a namespace')
}
g.gen_indent()
g.ns.out.writeln(s)
g.out.writeln(s)
g.empty_line = true
}
@ -490,6 +439,7 @@ fn (mut g JsGen) get_alias(name string) string {
}
fn (mut g JsGen) js_name(name_ string) string {
/*
mut is_js := false
is_overload := ['+', '-', '*', '/', '==', '<', '>']
mut name := name_
@ -543,7 +493,18 @@ fn (mut g JsGen) js_name(name_ string) string {
}
}
}
return parts.join('.')
return parts.join('.')*/
mut name := name_
if name.starts_with('JS.') {
name = name[3..]
return name
}
name = name_.replace('.', '__')
if name in js.js_reserved {
return '_v_$name'
}
return name
}
fn (mut g JsGen) stmts(stmts []ast.Stmt) {
@ -561,11 +522,11 @@ fn (mut g JsGen) write_v_source_line_info(pos token.Position) {
g.ns.sourcemap_helper << SourcemapHelper{
src_path: util.vlines_escape_path(g.file.path, g.pref.ccompiler)
src_line: u32(pos.line_nr + 1)
ns_pos: u32(g.ns.out.len)
ns_pos: u32(g.out.len)
}
}
if g.pref.is_vlines && g.is_vlines_enabled {
g.write(' /* ${pos.line_nr + 1} $g.ns.out.len */ ')
g.write(' /* ${pos.line_nr + 1} $g.out.len */ ')
}
}
@ -609,7 +570,7 @@ fn (mut g JsGen) gen_global_decl(node ast.GlobalDecl) {
}
fn (mut g JsGen) stmt_no_semi(node ast.Stmt) {
g.stmt_start_pos = g.ns.out.len
g.stmt_start_pos = g.out.len
match node {
ast.EmptyStmt {}
ast.AsmStmt {
@ -712,7 +673,7 @@ fn (mut g JsGen) stmt_no_semi(node ast.Stmt) {
}
fn (mut g JsGen) stmt(node ast.Stmt) {
g.stmt_start_pos = g.ns.out.len
g.stmt_start_pos = g.out.len
match node {
ast.EmptyStmt {}
ast.AsmStmt {
@ -840,11 +801,13 @@ fn (mut g JsGen) expr(node ast.Expr) {
// TODO
}
ast.BoolLiteral {
g.write('new bool(')
if node.val == true {
g.write('true')
} else {
g.write('false')
}
g.write(')')
}
ast.CallExpr {
g.gen_call_expr(node)
@ -856,7 +819,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
g.gen_type_cast_expr(node)
}
ast.CharLiteral {
g.write("new builtin.byte('$node.val')")
g.write("new byte('$node.val')")
}
ast.Comment {}
ast.ConcatExpr {
@ -886,7 +849,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
g.gen_index_expr(node)
}
ast.InfixExpr {
g.gen_infix_expr(node)
g.infix_expr(node)
}
ast.IntegerLiteral {
g.gen_integer_literal_expr(node)
@ -898,7 +861,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
g.gen_map_init_expr(node)
}
ast.None {
g.write('builtin.none__')
g.write('none__')
}
ast.MatchExpr {
g.match_expr(node)
@ -988,8 +951,8 @@ fn (mut g JsGen) expr(node ast.Expr) {
ast.TypeNode {
typ := g.unwrap_generic(node.typ)
sym := g.table.get_type_symbol(typ)
name := sym.name.replace_once('${g.ns.name}.', '')
g.write('$name')
g.write('${g.js_name(sym.name)}')
}
ast.Likely {
g.write('(')
@ -1023,9 +986,9 @@ fn (mut g JsGen) gen_assert_metainfo(node ast.AssertStmt) string {
src := node.expr.str()
metaname := 'v_assert_meta_info_$g.new_tmp_var()'
g.writeln('let $metaname = {}')
g.writeln('${metaname}.fpath = new builtin.string("$mod_path");')
g.writeln('${metaname}.line_nr = new builtin.int("$line_nr")')
g.writeln('${metaname}.fn_name = new builtin.string("$fn_name")')
g.writeln('${metaname}.fpath = new string("$mod_path");')
g.writeln('${metaname}.line_nr = new int("$line_nr")')
g.writeln('${metaname}.fn_name = new string("$fn_name")')
metasrc := src
g.writeln('${metaname}.src = "$metasrc"')
@ -1034,18 +997,18 @@ fn (mut g JsGen) gen_assert_metainfo(node ast.AssertStmt) string {
expr_op_str := node.expr.op.str()
expr_left_str := node.expr.left.str()
expr_right_str := node.expr.right.str()
g.writeln('\t${metaname}.op = new builtin.string("$expr_op_str");')
g.writeln('\t${metaname}.llabel = new builtin.string("$expr_left_str");')
g.writeln('\t${metaname}.rlabel = new builtin.string("$expr_right_str");')
g.write('\t${metaname}.lvalue = new builtin.string("')
g.writeln('\t${metaname}.op = new string("$expr_op_str");')
g.writeln('\t${metaname}.llabel = new string("$expr_left_str");')
g.writeln('\t${metaname}.rlabel = new string("$expr_right_str");')
g.write('\t${metaname}.lvalue = new string("')
g.gen_assert_single_expr(node.expr.left, node.expr.left_type)
g.writeln('");')
g.write('\t${metaname}.rvalue = new builtin.string("')
g.write('\t${metaname}.rvalue = new string("')
g.gen_assert_single_expr(node.expr.right, node.expr.right_type)
g.writeln('");')
}
ast.CallExpr {
g.writeln('\t${metaname}.op = new builtin.string("call");')
g.writeln('\t${metaname}.op = new string("call");')
}
else {}
}
@ -1081,25 +1044,25 @@ fn (mut g JsGen) gen_assert_stmt(a ast.AssertStmt) {
g.writeln('// assert')
g.write('if( ')
g.expr(a.expr)
g.write(' ) {')
g.write('.valueOf() ) {')
s_assertion := a.expr.str().replace('"', "'")
mut mod_path := g.file.path.replace('\\', '\\\\')
if g.is_test {
metaname_ok := g.gen_assert_metainfo(a)
g.writeln(' g_test_oks++;')
g.writeln(' cb_assertion_ok($metaname_ok);')
g.writeln(' main__cb_assertion_ok($metaname_ok);')
g.writeln('} else {')
metaname_fail := g.gen_assert_metainfo(a)
g.writeln(' g_test_fails++;')
g.writeln(' cb_assertion_failed($metaname_fail);')
g.writeln(' builtin.exit(1);')
g.writeln(' main__cb_assertion_failed($metaname_fail);')
g.writeln(' exit(1);')
g.writeln('}')
return
}
g.writeln('} else {')
g.inc_indent()
g.writeln('builtin.eprintln("$mod_path:${a.pos.line_nr + 1}: FAIL: fn ${g.fn_decl.name}(): assert $s_assertion");')
g.writeln('builtin.exit(1);')
g.writeln('eprintln(new string("$mod_path:${a.pos.line_nr + 1}: FAIL: fn ${g.fn_decl.name}(): assert $s_assertion"));')
g.writeln('exit(1);')
g.dec_indent()
g.writeln('}')
}
@ -1228,9 +1191,6 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) {
if should_cast {
g.cast_stack << stmt.left_types.first()
g.write('new ')
if g.file.mod.name != 'builtin' {
g.write('builtin.')
}
g.write('${g.typ(stmt.left_types.first())}(')
}
g.expr(val)
@ -1345,10 +1305,14 @@ fn (g &JsGen) fn_gen_type(it &ast.FnDecl) FnGenType {
fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) {
res := g.fn_gen_type(it)
if it.language == .js {
return
}
/*
if res == .struct_method {
// Struct methods are handled by class generation code.
return
}
}*/
if g.inside_builtin {
g.builtin_fns << it.name
}
@ -1370,15 +1334,131 @@ fn fn_has_go(node ast.FnDecl) bool {
return has_go
}
// cc_type whether to prefix 'struct' or not (C__Foo -> struct Foo)
fn (mut g JsGen) cc_type(typ ast.Type, is_prefix_struct bool) string {
sym := g.table.get_type_symbol(g.unwrap_generic(typ))
mut styp := sym.cname
match mut sym.info {
ast.Struct, ast.Interface, ast.SumType {
if sym.info.is_generic {
mut sgtyps := '_T'
for gt in sym.info.generic_types {
gts := g.table.get_type_symbol(g.unwrap_generic(gt))
sgtyps += '_$gts.cname'
}
styp += sgtyps
}
}
else {}
}
return styp
}
fn (mut g JsGen) generic_fn_name(types []ast.Type, before string, is_decl bool) string {
if types.len == 0 {
return before
}
mut name := before + '_T'
for typ in types {
name += '_' + strings.repeat_string('__ptr__', typ.nr_muls()) + g.typ(typ.set_nr_muls(0))
}
return name
}
fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) {
unsafe {
g.fn_decl = &it
}
cur_fn_save := g.table.cur_fn
defer {
g.table.cur_fn = cur_fn_save
}
unsafe {
g.table.cur_fn = &it
}
node := it
mut name := it.name
if name in ['+', '-', '*', '/', '%', '<', '=='] {
name = util.replace_op(name)
}
if node.is_method {
unwrapped_rec_sym := g.table.get_type_symbol(g.unwrap_generic(node.receiver.typ))
if unwrapped_rec_sym.kind == .placeholder {
return
}
name = g.cc_type(node.receiver.typ, false) + '_' + name
}
name = g.js_name(name)
name = g.generic_fn_name(g.table.cur_concrete_types, name, true)
has_go := fn_has_go(it)
if it.is_pub && !it.is_method {
g.push_pub_var(name)
}
is_main := it.name == 'main.main'
g.gen_attrs(it.attrs)
if is_main {
// there is no concept of main in JS but we do have iife
g.writeln('/* program entry point */')
g.write('(')
if has_go {
g.write('async ')
}
g.write('function(')
} else if it.is_anon {
g.write('function (')
} else {
c := name[0]
if c in [`+`, `-`, `*`, `/`] {
name = util.replace_op(name)
}
// type_name := g.typ(it.return_type)
// generate jsdoc for the function
g.doc.gen_fn(it)
if has_go {
g.write('async ')
}
g.write('function ')
g.write('${name}(')
if it.is_pub && !it.is_method {
g.push_pub_var(name)
}
}
mut args := it.params
g.fn_args(args, it.is_variadic)
g.write(') {')
for i, arg in args {
is_varg := i == args.len - 1 && it.is_variadic
arg_name := g.js_name(arg.name)
if is_varg {
g.writeln('$arg_name = new array($arg_name);')
} else {
if arg.typ.is_ptr() || arg.is_mut {
g.writeln('$arg_name = new \$ref($arg_name)')
}
}
}
g.stmts(it.stmts)
g.writeln('}')
if is_main {
g.write(')();')
}
g.writeln('')
/*
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 {
g.writeln('builtin.')
g.writeln('')
}
g.writeln('${name}.prototype.$it.name = function ')
}
@ -1468,7 +1548,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) {
if typ == .struct_method || typ == .alias_method || typ == .iface_method {
g.writeln('\n')
}
*/
g.fn_decl = voidptr(0)
}
@ -1522,7 +1602,7 @@ fn (mut g JsGen) gen_for_in_stmt(it ast.ForInStmt) {
g.expr(it.cond)
g.write('; $i < ')
g.expr(it.high)
g.writeln('; $i = new builtin.int($i + 1)) {')
g.writeln('; $i = new int($i + 1)) {')
g.inside_loop = false
g.stmts(it.stmts)
g.writeln('}')
@ -1539,9 +1619,9 @@ fn (mut g JsGen) gen_for_in_stmt(it ast.ForInStmt) {
g.write('.valueOf()')
}
g.write('.str.split(\'\').entries(), ([$it.key_var, $val]) => [$it.key_var, ')
if g.ns.name == 'builtin' {
g.write('new ')
}
g.write('new ')
g.write('byte($val)])')
} else {
g.expr(it.cond)
@ -1562,9 +1642,9 @@ fn (mut g JsGen) gen_for_in_stmt(it ast.ForInStmt) {
// cast characters to bytes
if val !in ['', '_'] && it.kind == .string {
g.write('.map(c => ')
if g.ns.name == 'builtin' {
g.write('new ')
}
g.write('new ')
g.write('byte(c))')
}
}
@ -1604,15 +1684,12 @@ fn (mut g JsGen) gen_for_stmt(it ast.ForStmt) {
fn (mut g JsGen) gen_go_expr(node ast.GoExpr) {
// TODO Handle joinable expressions
// node.is_expr
mut name := node.call_expr.name
mut name := g.js_name(node.call_expr.name)
if node.call_expr.is_method {
receiver_sym := g.table.get_type_symbol(node.call_expr.receiver_type)
name = receiver_sym.name + '.' + name
}
// todo: please add a name feild without the mod name for ast.CallExpr
if name.starts_with('${node.call_expr.mod}.') {
name = name[node.call_expr.mod.len + 1..]
}
g.writeln('await new Promise(function(resolve){')
g.inc_indent()
g.write('${name}(')
@ -1644,7 +1721,7 @@ fn (mut g JsGen) gen_interface_decl(it ast.InterfaceDecl) {
}
fn (mut g JsGen) gen_optional_error(expr ast.Expr) {
g.write('new builtin.Option({ state: new builtin.byte(2),err: ')
g.write('new Option({ state: new byte(2),err: ')
g.expr(expr)
g.write('})')
}
@ -1682,9 +1759,7 @@ fn (mut g JsGen) gen_return_stmt(it ast.Return) {
if fn_return_is_optional {
tmp := g.new_tmp_var()
g.write('const $tmp = new ')
if g.ns.name != 'builtin' {
g.write('builtin.')
}
g.writeln('Option({});')
g.write('${tmp}.data = ')
if it.exprs.len == 1 {
@ -1746,17 +1821,6 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
g.writeln('...${etyp}.prototype,')
}
fns := g.method_fn_decls[name]
for field in node.fields {
typ := g.typ(field.typ)
g.doc.gen_typ(typ)
g.write('$field.name: ${g.to_js_typ_val(field.typ)}')
g.writeln(',')
}
for cfn in fns {
g.gen_method_decl(cfn, .struct_method)
g.writeln(',')
}
// gen toString method
fn_names := fns.map(it.name)
if 'toString' !in fn_names {
@ -1778,9 +1842,22 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
g.dec_indent()
g.writeln('},')
}
for field in node.fields {
typ := g.typ(field.typ)
g.doc.gen_typ(typ)
g.write('$field.name: ${g.to_js_typ_val(field.typ)}')
g.writeln(',')
}
g.writeln('\$toJS() { return this; }')
g.dec_indent()
g.writeln('};\n')
g.dec_indent()
/*
for cfn in fns {
g.gen_method_decl(cfn, .struct_method)
}*/
if node.is_pub {
g.push_pub_var(name)
}
@ -2104,7 +2181,7 @@ fn (mut g JsGen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) {
g.expr(stmt.expr)
g.writeln(';')
} else {
g.write('builtin.opt_ok(')
g.write('opt_ok(')
g.stmt(stmt)
g.writeln(', $tmp_var);')
}
@ -2167,9 +2244,7 @@ fn (mut g JsGen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var M
g.expr(branch.exprs[sumtype_index])
} else {
g.write(' instanceof ')
if g.ns.name != 'builtin' {
g.write('builtin.')
}
g.write('None__')
}
}
@ -2352,11 +2427,16 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) {
left_typ := g.table.get_type_symbol(expr.left_type)
// TODO: Handle splice setting if it's implemented
if expr.index is ast.RangeExpr {
if left_typ.kind == .array {
g.write('array_slice(')
} else {
g.write('string_slice(')
}
g.expr(expr.left)
if expr.left_type.is_ptr() {
g.write('.valueOf()')
}
g.write('.slice(')
g.write(',')
if expr.index.has_low {
g.expr(expr.index.low)
} else {
@ -2370,7 +2450,7 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) {
if expr.left_type.is_ptr() {
g.write('.valueOf()')
}
g.write('.length')
g.write('.len')
}
g.write(')')
} else if left_typ.kind == .map {
@ -2444,9 +2524,7 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
it.right_type
} // g.greater_typ(it.left_type, it.right_type)
g.write('new ')
if g.ns.name != 'builtin' {
g.write('builtin.')
}
g.write('${g.typ(greater_typ)}(')
g.cast_stack << greater_typ
g.write('BigInt((')
@ -2466,10 +2544,7 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
return
}
if it.op == .logical_or || it.op == .and {
if g.ns.name == 'builtin' {
g.write('new ')
}
g.write('bool(')
g.write('new bool(')
g.expr(it.left)
g.write('.valueOf()')
g.write(it.op.str())
@ -2477,6 +2552,7 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
g.write('.valueOf()')
g.write(')')
} else if it.op == .eq || it.op == .ne {
/*
has_operator_overloading := g.table.type_has_method(l_sym, '==')
if has_operator_overloading {
g.expr(it.left)
@ -2506,6 +2582,32 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
g.expr(it.right)
g.gen_deref_ptr(it.right_type)
g.write(')')
}*/
node := it
left := g.unwrap(node.left_type)
right := g.unwrap(node.right_type)
has_operator_overloading := g.table.type_has_method(left.sym, '==')
if has_operator_overloading
|| (l_sym.kind in js.shallow_equatables && r_sym.kind in js.shallow_equatables) {
if node.op == .ne {
g.write('!')
}
g.write(g.typ(left.unaliased.set_nr_muls(0)))
g.write('__eq(')
g.expr(node.left)
g.gen_deref_ptr(left.typ)
g.write(',')
g.expr(node.right)
g.gen_deref_ptr(right.typ)
g.write(')')
} else {
g.write('vEq(')
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write(', ')
g.expr(it.right)
g.gen_deref_ptr(it.right_type)
g.write(')')
}
} else if l_sym.kind == .array && it.op == .left_shift { // arr << 1
g.write('Array.prototype.push.call(')
@ -2609,14 +2711,14 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
}
if is_arithmetic {
if g.ns.name == 'builtin' {
g.write('new ')
}
g.write('new ')
g.write('${g.typ(greater_typ)}(')
g.cast_stack << greater_typ
}
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
// g.write('.val')
g.write(' $it.op ')
@ -2732,7 +2834,7 @@ fn (mut g JsGen) type_name(raw_type ast.Type) {
} else {
s = g.table.type_to_str(g.unwrap_generic(typ))
}
g.write('new builtin.string("$s")')
g.write('new string("$s")')
}
fn (mut g JsGen) gen_selector_expr(it ast.SelectorExpr) {
@ -2744,7 +2846,7 @@ fn (mut g JsGen) gen_selector_expr(it ast.SelectorExpr) {
return
}
.typ {
g.write('new builtin.int(')
g.write('new int(')
g.write('${int(g.unwrap_generic(it.name_type))}')
g.write(')')
@ -2756,7 +2858,7 @@ fn (mut g JsGen) gen_selector_expr(it ast.SelectorExpr) {
g.type_name(it.name_type)
return
} else if node.field_name == 'idx' {
g.write('new builtin.int(')
g.write('new int(')
g.write('${int(g.unwrap_generic(it.name_type))}')
g.write(')')
return
@ -2777,9 +2879,8 @@ fn (mut g JsGen) gen_selector_expr(it ast.SelectorExpr) {
fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) {
should_cast := !(g.cast_stack.len > 0 && g.cast_stack.last() == ast.string_type_idx)
if should_cast {
if g.file.mod.name == 'builtin' {
g.write('new ')
}
g.write('new ')
g.write('string(')
}
g.write('`')
@ -2817,9 +2918,8 @@ fn (mut g JsGen) gen_string_literal(it ast.StringLiteral) {
text = text.replace('"', '\\"')
should_cast := !(g.cast_stack.len > 0 && g.cast_stack.last() == ast.string_type_idx)
if true || should_cast {
if g.file.mod.name == 'builtin' {
g.write('new ')
}
g.write('new ')
g.write('string(')
}
if it.is_raw {
@ -2892,10 +2992,8 @@ fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) {
tsym := g.table.get_final_type_symbol(it.typ)
if it.expr is ast.IntegerLiteral && (tsym.kind == .i64 || tsym.kind == .u64) {
g.write('new ')
if g.ns.name != 'builtin' {
g.write('builtin.')
}
g.write(tsym.kind.str())
g.write('$tsym.kind.str()')
g.write('(BigInt(')
g.write(it.expr.val)
g.write('n))')
@ -2913,9 +3011,9 @@ fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) {
if it.typ.is_ptr() {
g.write('new \$ref(')
}
if typ !in js.v_types || g.ns.name == 'builtin' {
g.write('new ')
}
g.write('new ')
g.write('${typ}(')
}
g.expr(it.expr)
@ -2954,17 +3052,12 @@ fn (mut g JsGen) gen_integer_literal_expr(it ast.IntegerLiteral) {
if g.cast_stack.len > 0 {
if g.cast_stack[g.cast_stack.len - 1] in ast.integer_type_idxs {
g.write('new ')
if g.ns.name != 'builtin' {
g.write('builtin.')
}
g.write('int($it.val)')
return
}
}
g.write('new ')
if g.ns.name != 'builtin' {
g.write('builtin.')
}
g.write('${g.typ(typ)}($it.val)')
}
@ -3003,9 +3096,6 @@ fn (mut g JsGen) gen_float_literal_expr(it ast.FloatLiteral) {
}
}
g.write('new ')
if g.ns.name != 'builtin' {
g.write('builtin.')
}
g.write('${g.typ(typ)}($it.val)')
}
@ -3020,3 +3110,17 @@ fn (mut g JsGen) unwrap_generic(typ ast.Type) ast.Type {
}
return typ
}
fn replace_op(s string) string {
return match s {
'+' { '_plus' }
'-' { '_minus' }
'*' { '_mult' }
'/' { '_div' }
'%' { '_mod' }
'<' { '_lt' }
'>' { '_gt' }
'==' { '_eq' }
else { '' }
}
}

View File

@ -3,13 +3,14 @@ module js
import v.ast
fn (mut g JsGen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
is_shared := etype.has_flag(.shared_f)
mut typ := etype
if etype.has_flag(.shared_f) {
if is_shared {
typ = typ.clear_flag(.shared_f).set_nr_muls(0)
}
mut sym := g.table.get_type_symbol(typ)
// when type is alias, print the aliased value
if mut sym.info is ast.Alias {
parent_sym := g.table.get_type_symbol(sym.info.parent_type)
if parent_sym.has_method('str') {
@ -17,69 +18,58 @@ fn (mut g JsGen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
sym = parent_sym
}
}
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
is_var_mut := expr.is_auto_deref_var()
if typ.has_flag(.variadic) {
// todo(playX): generate str method just like in the C backend
g.write('new string(')
str_fn_name := g.gen_str_for_type(typ)
g.write('${str_fn_name}(')
g.expr(expr)
g.write('.valueOf()')
g.write('.toString())')
g.write(')')
} else if typ == ast.string_type {
g.expr(expr)
} else if typ == ast.bool_type {
g.write('new string(')
g.write('new string((')
g.expr(expr)
g.write('.valueOf() ? "true" : "false")')
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 {
str_fn_name := g.gen_str_for_type(typ)
g.write('${str_fn_name}(')
g.expr(expr)
g.write('.valueOf()')
g.write('.toString()')
g.write(')')
} else {
g.write('"')
g.write('new string("')
g.expr(expr)
g.write('"')
g.write('")')
}
g.write(')')
} else if sym.kind == .interface_ && sym_has_str_method {
} else if sym_has_str_method
|| sym.kind in [.array, .array_fixed, .map, .struct_, .multi_return, .sum_type, .interface_] {
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)
str_fn_name := g.gen_str_for_type(typ)
g.write('${str_fn_name}(')
if str_method_expects_ptr && !is_ptr {
g.write('new \$ref(')
g.expr(expr)
g.write(')')
} else if (!str_method_expects_ptr && is_ptr && !is_shared) || is_var_mut {
g.expr(expr)
g.gen_deref_ptr(etype)
}
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())')
g.write(')')
} else {
g.write('new string(')
g.expr(expr)
g.gen_deref_ptr(typ)
g.write('.valueOf().toString())')
str_fn_name := g.gen_str_for_type(typ)
g.write('${str_fn_name}(')
if sym.kind != .function {
g.expr(expr)
if expr.is_auto_deref_var() {
g.write('.val')
}
}
g.write(')')
}
}

View File

@ -60,16 +60,16 @@ true
0
1
1.1
[[1, 2], 3, 4]
[[1, 2], [5, 6], 3, 4]
[1, 2, 3, 4]
[1, 5, 6, 2, 3, 4]
0
1
1
0
1
1.1
[[1, 2], 3, 4]
[[5, 6], [1, 2], 3, 4]
[1, 2, 3, 4]
[5, 6, 1, 2, 3, 4]
5
true
1.1
@ -135,8 +135,8 @@ true
0
2
-1
-1
-1
1
2
-1
2
3
@ -226,7 +226,6 @@ true
true
[1, 3, 5, hi]
[-3, 7, 42, 67, 108]
[97, 98, 99, 100, 101, 102]
0
1
79
@ -278,15 +277,15 @@ a
123
123
[[1, 2, 3]]
[[[1, 2, 3]]]
[[1, 2, 3]]
[[[1, 2, 3]]]
[[1, 2, 3]]
[[1, 2, 3]]
true
true
true
true
true
true
false
true
true
true
@ -297,7 +296,13 @@ true
[[], [], [], []]
[[], [], [123], []]
[{}, {}, {}, {}]
[{}, {}, {'123': 123}, {}]
Numbers { odds: [1, 3, 5] , evens: [2, 4] }
Numbers { odds: [3, 5, 7] , evens: [2, 6, 10] }
[{}, {}, {123: 123}, {}]
Numbers{
odds: [1, 3, 5]
evens: [2, 4]
}
Numbers{
odds: [3, 5, 7]
evens: [2, 6, 10]
}
[[10, 10, 10], [10, 10, 10], [10, 10, 10]]

View File

@ -798,10 +798,11 @@ fn main() {
*/
}
{
/*
// test rune sort
mut bs := [`f`, `e`, `d`, `b`, `c`, `a`]
bs.sort()
println(bs)
println(bs)*/
/*
bs.sort(a > b)

View File

@ -1,3 +1,6 @@
Foo { x: 5 , y: 5 }
Foo{
x: 5
y: 5
}
true
true

View File

@ -0,0 +1,35 @@
module js
import v.ast
struct Type {
// typ is the original type
typ ast.Type [required]
sym &ast.TypeSymbol [required]
// unaliased is `typ` once aliased have been resolved
// it may not contain informations such as flags and nr_muls
unaliased ast.Type [required]
unaliased_sym &ast.TypeSymbol [required]
}
// unwrap returns the following variants of a type:
// * generics unwrapped
// * alias unwrapped
fn (mut g JsGen) unwrap(typ ast.Type) Type {
no_generic := g.unwrap_generic(typ)
no_generic_sym := g.table.get_type_symbol(no_generic)
if no_generic_sym.kind != .alias {
return Type{
typ: no_generic
sym: no_generic_sym
unaliased: no_generic
unaliased_sym: no_generic_sym
}
}
return Type{
typ: no_generic
sym: no_generic_sym
unaliased: no_generic_sym.parent_idx
unaliased_sym: g.table.get_type_symbol(no_generic_sym.parent_idx)
}
}

View File

@ -0,0 +1 @@
module stats_import