diff --git a/vlib/compiler/comptime.v b/vlib/compiler/comptime.v index 41afcc59da..d45151cebc 100644 --- a/vlib/compiler/comptime.v +++ b/vlib/compiler/comptime.v @@ -331,7 +331,32 @@ fn (p mut Parser) gen_struct_str(typ Type) { p.v.vgen_buf.writeln(sb.str()) // Need to manually add the definition to `fns` so that it stays // at the top of the file. - // This function will get parsee by V after the main pass. + // This function will get parsed by V after the main pass. + p.cgen.fns << 'string ${typ.name}_str();' +} + +fn (p mut Parser) gen_varg_str(typ Type) { + elm_type := typ.name[5..] + elm_type2 := p.table.find_type(elm_type) + is_array := elm_type.starts_with('array_') + if is_array { + p.gen_array_str(elm_type2) + } else if elm_type2.cat == .struct_ { + p.gen_struct_str(elm_type2) + } + p.v.vgen_buf.writeln(' +fn (a $typ.name) str() string { + mut sb := strings.new_builder(a.len * 3) + sb.write("[") + for i, elm in a { + sb.write(elm.str()) + if i < a.len - 1 { + sb.write(", ") + } + } + sb.write("]") + return sb.str() +}') p.cgen.fns << 'string ${typ.name}_str();' } diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index 515888225a..a6dff68281 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -676,7 +676,7 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s if f.is_deprecated { p.warn('$f.name is deprecated') } - if !f.is_public && !f.is_c && !p.pref.is_test && !f.is_interface && f.mod != p.mod { + if !f.is_public && !f.is_c && !p.pref.is_test && !f.is_interface && f.mod != p.mod { if f.name == 'contains' { println('use `value in numbers` instead of `numbers.contains(value)`') } @@ -713,7 +713,6 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s // p.cur_fn.called_fns << cgen_name // } - // If we have a method placeholder, // we need to preappend "method(receiver, ...)" if f.is_method { @@ -803,7 +802,6 @@ fn (p mut Parser) fn_args(f mut Fn) { if is_mut { p.check(.key_mut) } - mut typ := '' // variadic arg if p.tok == .ellipsis { p.check(.ellipsis) @@ -811,25 +809,28 @@ fn (p mut Parser) fn_args(f mut Fn) { p.error('you must provide a type for vargs: eg `...string`. multiple types `...` are not supported yet.') } f.is_variadic = true - t := p.get_type() - // register varg struct, incase function is never called - if p.first_pass() && !f.is_generic && !f.is_c{ - p.register_vargs_stuct(t, 0) - } - typ = '...$t' - } else { - typ = p.get_type() } - + mut typ := p.get_type() + if !p.first_pass() && !p.table.known_type(typ) { + p.error('fn_args: unknown type $typ') + } + if f.is_variadic { + if !f.is_c { + // register varg struct, incase function is never called + if p.first_pass() && !f.is_generic { + p.register_vargs_stuct(typ, 0) + } + typ = 'varg_$typ' + } else { + typ = '...$typ' // TODO: fix, this is invalid in C + } + } p.check_and_register_used_imported_type(typ) if is_mut && is_primitive_type(typ) { p.error('mutable arguments are only allowed for arrays, maps, and structs.' + '\nreturn values instead: `foo(n mut int)` => `foo(n int) int`') } for name in names { - if !p.first_pass() && !p.table.known_type(typ) { - p.error('fn_args: unknown type $typ') - } if is_mut { typ += '*' } @@ -908,7 +909,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) { continue } // Reached the final vararg? Quit - if i == f.args.len - 1 && arg.typ.starts_with('...') { + if i == f.args.len - 1 && arg.typ.starts_with('varg_') { break } ph := p.cgen.add_placeholder() @@ -982,13 +983,22 @@ fn (p mut Parser) fn_call_args(f mut Fn) { // Make sure this type has a `str()` method $if !js { if !T.has_method('str') { + // varg + if T.name.starts_with('varg_') { + p.gen_varg_str(T) + p.cgen.set_placeholder(ph, '${typ}_str(') + p.gen(')') + continue + } // Arrays have automatic `str()` methods - if T.name.starts_with('array_') { + else if T.name.starts_with('array_') { p.gen_array_str(T) p.cgen.set_placeholder(ph, '${typ}_str(') p.gen(')') continue - } else if T.cat == .struct_ { + } + // struct + else if T.cat == .struct_ { p.gen_struct_str(T) p.cgen.set_placeholder(ph, '${typ}_str(') p.gen(')') @@ -1190,11 +1200,12 @@ fn (p mut Parser) replace_type_params(f &Fn, ti TypeInst) []string { fi = fi[6..] fr += 'array_' } - is_varg := fi.starts_with('...') - if is_varg { fi = fi[3..] } + if fi.starts_with('varg_') { + fi = fi[5..] + fr += 'varg_' + } if fi in ti.inst.keys() { mut t := ti.inst[fi] - if is_varg { t = '...$t' } fr += t // println("replaced $a => $fr") } else { @@ -1206,7 +1217,7 @@ fn (p mut Parser) replace_type_params(f &Fn, ti TypeInst) []string { } fn (p mut Parser) register_vargs_stuct(typ string, len int) string { - vargs_struct := '_V_FnVargs_$typ' + vargs_struct := 'varg_$typ' varg_type := Type{ cat: TypeCategory.struct_, name: vargs_struct, @@ -1224,6 +1235,7 @@ fn (p mut Parser) register_vargs_stuct(typ string, len int) string { } p.table.add_field(vargs_struct, 'len', 'int', false, '', .public) p.table.add_field(vargs_struct, 'args[$varg_len]', typ, false, '', .public) + return vargs_struct } @@ -1242,7 +1254,7 @@ fn (p mut Parser) fn_call_vargs(f Fn) (string, []string) { p.cgen.start_tmp() mut varg_type := p.bool_expression() varg_value := p.cgen.end_tmp() - if varg_type.starts_with('...') && + if varg_type.starts_with('varg_') && (values.len > 0 || p.tok == .comma) { p.error('You cannot pass additional vargs when forwarding vargs to another function/method') } @@ -1274,7 +1286,7 @@ fn (p mut Parser) fn_call_vargs(f Fn) (string, []string) { } fn (p mut Parser) fn_gen_caller_vargs(f &Fn, varg_type string, values []string) { - is_varg := varg_type.starts_with('...') + is_varg := varg_type.starts_with('varg_') if is_varg { // forwarding varg p.cgen.gen('${values[0]}') } else { @@ -1304,7 +1316,7 @@ fn (p mut Parser) rename_generic_fn_instance(f mut Fn, ti TypeInst) { } f.name = f.name + '_T' for k in ti.inst.keys() { - f.name = f.name + '_' + type_to_safe_str(ti.inst[k].replace('...', '')) + f.name = f.name + '_' + type_to_safe_str(ti.inst[k]) } } @@ -1461,8 +1473,8 @@ fn (f &Fn) str_args(table &Table) string { s += ')' } } - else if arg.typ.starts_with('...') { - s += '_V_FnVargs_${arg.typ[3..]} *$arg.name' + else if arg.typ.starts_with('varg_') { + s += '$arg.typ *$arg.name' } else { // s += '$arg.typ $arg.name' diff --git a/vlib/compiler/main.v b/vlib/compiler/main.v index 6506f4999c..9bb8dbedf8 100644 --- a/vlib/compiler/main.v +++ b/vlib/compiler/main.v @@ -770,7 +770,7 @@ pub fn new_v(args[]string) &V { vroot := os.dir(vexe_path()) mut vgen_buf := strings.new_builder(1000) - vgen_buf.writeln('module main\nimport strings') + vgen_buf.writeln('module vgen\nimport strings') joined_args := args.join(' ') target_os := get_arg(joined_args, 'os', '') diff --git a/vlib/compiler/parser.v b/vlib/compiler/parser.v index cd97ba80eb..674f6f038d 100644 --- a/vlib/compiler/parser.v +++ b/vlib/compiler/parser.v @@ -900,7 +900,7 @@ fn (p mut Parser) get_type() string { p.error('unknown type `$typ`') } } - else if !t.is_public && t.mod != p.mod && t.name != '' && !p.first_pass() { + else if !t.is_public && t.mod != p.mod && !p.is_vgen && t.name != '' && !p.first_pass() { p.error('type `$t.name` is private') } } @@ -1949,8 +1949,7 @@ fn (p mut Parser) dot(str_typ_ string, method_ph int) string { //} mut str_typ := str_typ_ p.check(.dot) - is_variadic_arg := str_typ.starts_with('...') - if is_variadic_arg { str_typ = str_typ[3..] } + is_variadic_arg := str_typ.starts_with('varg_') mut typ := p.find_type(str_typ) if typ.name.len == 0 { p.error('dot(): cannot find type `$str_typ`') @@ -2102,7 +2101,7 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string { //println('index expr typ=$typ') //println(v.name) //} - is_variadic_arg := typ.starts_with('...') + is_variadic_arg := typ.starts_with('varg_') is_map := typ.starts_with('map_') is_str := typ == 'string' is_arr0 := typ.starts_with('array_') @@ -2132,6 +2131,7 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string { p.gen(',') } } + if is_variadic_arg { typ = typ[5..] } if is_fixed_arr { // `[10]int` => `int`, `[10][3]int` => `[3]int` if typ.contains('][') { @@ -3254,8 +3254,7 @@ fn (p mut Parser) for_st() { is_arr := typ.starts_with('array_') is_map := typ.starts_with('map_') is_str := typ == 'string' - is_variadic_arg := typ.starts_with('...') - if is_variadic_arg { typ = typ[3..] } + is_variadic_arg := typ.starts_with('varg_') if !is_arr && !is_str && !is_map && !is_variadic_arg { p.error('cannot range over type `$typ`') } @@ -3267,25 +3266,24 @@ fn (p mut Parser) for_st() { p.genln('$typ $tmp = $expr;') } } - pad := if is_arr { 6 } else { 4 } - var_typ := if is_str { 'byte' } - else if is_variadic_arg { typ } - else { typ[pad..] } // typ = strings.Replace(typ, "_ptr", "*", -1) mut i_var_type := 'int' if is_variadic_arg { + typ = typ[5..] p.gen_for_varg_header(i, expr, typ, val) } else if is_arr { - p.gen_for_header(i, tmp, var_typ, val) + typ = typ[6..] + p.gen_for_header(i, tmp, typ, val) } else if is_map { i_var_type = 'string' - p.gen_for_map_header(i, tmp, var_typ, val, typ) + typ = typ[4..] + p.gen_for_map_header(i, tmp, typ, val, typ) } else if is_str { - i_var_type = 'byte' - p.gen_for_str_header(i, tmp, var_typ, val) + typ = 'byte' + p.gen_for_str_header(i, tmp, typ, val) } // Register temp vars if i != '_' { @@ -3299,7 +3297,7 @@ fn (p mut Parser) for_st() { if val != '_' { p.register_var(Var { name: val - typ: var_typ + typ: typ ptr: typ.contains('*') }) } @@ -3315,8 +3313,7 @@ fn (p mut Parser) for_st() { mut typ := p.bool_expression() expr := p.cgen.end_tmp() is_range := p.tok == .dotdot - is_variadic_arg := typ.starts_with('...') - if is_variadic_arg { typ = typ[3..] } + is_variadic_arg := typ.starts_with('varg_') mut range_end := '' if is_range { p.check_types(typ, 'int') @@ -3339,28 +3336,28 @@ fn (p mut Parser) for_st() { } // TODO var_type := if... i := p.get_tmp() - mut var_type := typ if is_variadic_arg { + typ = typ[5..] p.gen_for_varg_header(i, expr, typ, val) } else if is_range { - var_type = 'int' - p.gen_for_range_header(i, range_end, tmp, var_type, val) + typ = 'int' + p.gen_for_range_header(i, range_end, tmp, typ, val) } else if is_arr { - var_type = typ[6..]// all after `array_` - p.gen_for_header(i, tmp, var_type, val) + typ = typ[6..]// all after `array_` + p.gen_for_header(i, tmp, typ, val) } else if is_str { - var_type = 'byte' - p.gen_for_str_header(i, tmp, var_type, val) + typ = 'byte' + p.gen_for_str_header(i, tmp, typ, val) } // println('for typ=$typ vartyp=$var_typ') // Register temp var if val != '_' { p.register_var(Var { name: val - typ: var_type + typ: typ ptr: typ.contains('*') is_changed: true is_mut: false diff --git a/vlib/compiler/table.v b/vlib/compiler/table.v index 14a868a5f2..a5762e61dc 100644 --- a/vlib/compiler/table.v +++ b/vlib/compiler/table.v @@ -328,10 +328,6 @@ fn (t mut Table) register_fn(new_fn Fn) { fn (table &Table) known_type(typ_ string) bool { mut typ := typ_ - // vararg - if typ.starts_with('...') && typ.len > 3 { - typ = typ[3..] - } // 'byte*' => look up 'byte', but don't mess up fns if typ.ends_with('*') && !typ.contains(' ') { typ = typ[..typ.len - 1] @@ -584,11 +580,11 @@ fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool { } // variadic - if expected.starts_with('...') { - expected = expected[3..] + if expected.starts_with('varg_') { + expected = expected[5..] } - if got.starts_with('...') { - got = got[3..] + if got.starts_with('varg_') { + got = got[5..] } // Allow ints to be used as floats if got == 'int' && expected == 'f32' {