From 8c3475b902cd7b03a559311e9818d4c616ad2b5a Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sat, 10 Aug 2019 23:02:48 +0200 Subject: [PATCH] generate `.str()` for all arrays --- compiler/comptime.v | 27 ++++++++++++++++++++++++ compiler/fn.v | 18 ++++++++++++++-- compiler/parser.v | 11 +++++++--- compiler/query.v | 6 +++++- compiler/table.v | 3 ++- compiler/tests/mut_test.v | 8 +++---- compiler/tests/str_gen_test.v | 16 ++++++++++++++ examples/vweb/test_app.v | 6 ++++++ vlib/builtin/array.v | 39 +++++++---------------------------- 9 files changed, 92 insertions(+), 42 deletions(-) create mode 100644 compiler/tests/str_gen_test.v diff --git a/compiler/comptime.v b/compiler/comptime.v index 28d10ae618..6b7bb7ee3c 100644 --- a/compiler/comptime.v +++ b/compiler/comptime.v @@ -236,3 +236,30 @@ fn (p mut Parser) comptime_method_call(typ Type) { } } +fn (p mut Parser) gen_array_str(typ mut Type) { + typ.add_method(Fn{ + name: 'str', + typ: 'string' + args: [Var{typ: typ.name, is_arg:true}] + is_method: true + receiver_typ: typ.name + }) + t := typ.name + elm_type := t.right(6) + p.cgen.fns << ' +string ${t}_str($t a) { + strings__Builder sb = strings__new_builder(a.len * 3); + strings__Builder_write(&sb, tos2("[")) ; + for (int i = 0; i < a.len; i++) { + strings__Builder_write(&sb, ${elm_type}_str( (($elm_type *) a.data)[i])); + + if (i < a.len - 1) { + strings__Builder_write(&sb, tos2(", ")) ; + + } +} +strings__Builder_write(&sb, tos2("]")) ; +return strings__Builder_str(sb); +} ' +} + diff --git a/compiler/fn.v b/compiler/fn.v index 0930b16920..8374050b7a 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -794,8 +794,15 @@ fn (p mut Parser) fn_call_args(f mut Fn) *Fn { // If `arg` is mutable, the caller needs to provide `mut`: // `mut numbers := [1,2,3]; reverse(mut numbers);` if arg.is_mut { - if p.tok != .key_mut { - p.error('`$arg.name` is a mutable argument, you need to provide `mut`: `$f.name(...mut a...)`') + if p.tok != .key_mut && p.tok == .name { + mut dots_example := 'mut $p.lit' + if i > 0 { + dots_example = '.., ' + dots_example + } + if i < f.args.len - 1 { + dots_example = dots_example + ',..' + } + p.error('`$arg.name` is a mutable argument, you need to provide `mut`: `$f.name($dots_example)`') } if p.peek() != .name { p.error('`$arg.name` is a mutable argument, you need to provide a variable to modify: `$f.name(... mut a...)`') @@ -831,6 +838,13 @@ fn (p mut Parser) fn_call_args(f mut Fn) *Fn { } // Make sure this type has a `str()` method if !T.has_method('str') { + // Arrays have automatic `str()` methods + if T.name.starts_with('array_') { + p.gen_array_str(mut T) + p.cgen.set_placeholder(ph, '${typ}_str(') + p.gen(')') + continue + } error_msg := ('`$typ` needs to have method `str() string` to be printable') if T.fields.len > 0 { mut index := p.cgen.cur_line.len - 1 diff --git a/compiler/parser.v b/compiler/parser.v index 2005fe9a6b..99696e0958 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -793,7 +793,7 @@ fn (p mut Parser) error(s string) { p.cgen.save() // V git pull hint cur_path := os.getwd() - if !p.pref.is_repl && ( p.file_path.contains('v/compiler') || cur_path.contains('v/compiler') ){ + if !p.pref.is_repl && !p.pref.is_test && ( p.file_path.contains('v/compiler') || cur_path.contains('v/compiler') ){ println('\n=========================') println('It looks like you are building V. It is being frequently updated every day.') println('If you didn\'t modify the compiler\'s code, most likely there was a change that ') @@ -1102,7 +1102,7 @@ fn (p mut Parser) vh_genln(s string) { } fn (p mut Parser) statement(add_semi bool) string { - if(p.returns) { + if p.returns { //&& !p.is_vweb { p.error('unreachable code') } p.cgen.is_tmp = false @@ -1780,7 +1780,12 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string { //println('dot() field_name=$field_name typ=$str_typ prev_tok=${prev_tok.str()}') //} has_field := p.table.type_has_field(typ, field_name) - has_method := p.table.type_has_method(typ, field_name) + mut has_method := p.table.type_has_method(typ, field_name) + // generate `.str()` + if !has_method && field_name == 'str' && typ.name.starts_with('array_') { + p.gen_array_str(mut typ) + has_method = true + } if !typ.is_c && !has_field && !has_method && !p.first_pass() { if typ.name.starts_with('Option_') { opt_type := typ.name.right(7) diff --git a/compiler/query.v b/compiler/query.v index 3189329d7d..402adfd77a 100644 --- a/compiler/query.v +++ b/compiler/query.v @@ -1,3 +1,7 @@ +// Copyright (c) 2019 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 main import strings @@ -29,7 +33,7 @@ fn (p mut Parser) select_query(fn_ph int) string { } for field in typ.fields { //println('registering sql field var $field.name') - p.cur_fn.register_var({ field | is_used:true}) + p.cur_fn.register_var({ field | is_used:true }) } q += table_name // `where` statement diff --git a/compiler/table.v b/compiler/table.v index 1873d27850..5294ddd89a 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -60,6 +60,7 @@ mut: // It allows having things like `fn (f Foo) bar()` before `Foo` is defined. // This information is needed in the first pass. is_placeholder bool + gen_str bool // needs `.str()` method generation } // For debugging types @@ -434,7 +435,7 @@ fn (t mut Type) add_gen_type(type_name string) { } */ -fn (p &Parser) find_type(name string) *Type { +fn (p &Parser) find_type(name string) &Type { typ := p.table.find_type(name) if typ.name.len == 0 { return p.table.find_type(p.prepend_mod(name)) diff --git a/compiler/tests/mut_test.v b/compiler/tests/mut_test.v index 509de43e68..a8d7cf002d 100644 --- a/compiler/tests/mut_test.v +++ b/compiler/tests/mut_test.v @@ -1,14 +1,14 @@ -fn foo(a mut []int) { +fn foo(b int, a mut []int) { a[0] = 7 a << 4 } // TODO fn test_mut() { - mut a := [1,2,3] - foo(mut a) + mut numbers := [1,2,3] + foo(7, numbers) //assert a.len == 4 - assert a[0] == 7 + assert numbers[0] == 7 //assert a[3] == 4 n := 1 diff --git a/compiler/tests/str_gen_test.v b/compiler/tests/str_gen_test.v new file mode 100644 index 0000000000..97230724d5 --- /dev/null +++ b/compiler/tests/str_gen_test.v @@ -0,0 +1,16 @@ +struct Foo { + a int +} + +fn test_array_str() { + f := Foo{34} + println(f) + //s := f.str() + //println(s) + n := [i64(1), 2, 3] + assert n.str() == '[1, 2, 3]' + println(n) // make sure the array is printable + n2 := [4,5,6] + assert n2.str() == '[4, 5, 6]' + println(n2) +} diff --git a/examples/vweb/test_app.v b/examples/vweb/test_app.v index e0e6ae73bd..65599620e7 100644 --- a/examples/vweb/test_app.v +++ b/examples/vweb/test_app.v @@ -23,7 +23,13 @@ pub fn (app mut App) json_endpoint() { app.vweb.json('{"a": 3}') } +/* pub fn (app mut App) index() { + $vweb.html() +} +*/ + +pub fn (app mut App) text() { app.vweb.text('hello world') } diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index 4d4449ec35..5c7d38ff54 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -4,6 +4,8 @@ module builtin +import strings + struct array { pub: // Using a void pointer allows to implement arrays without generics and without generating @@ -206,31 +208,6 @@ pub fn (a array) reverse() array { return arr } -pub fn (a []int) str() string { - mut res := '[' - for i := 0; i < a.len; i++ { - val := a[i] - res += '$val' - if i < a.len - 1 { - res += ', ' - } - } - res += ']' - return res -} - -pub fn (a []u64) str() string { - mut res := '[' - for i := 0; i < a.len; i++ { - val := a[i] - res += '$val' - if i < a.len - 1 { - res += ', ' - } - } - res += ']' - return res -} //pub fn (a []int) free() { pub fn (a array) free() { //if a.is_slice { @@ -239,19 +216,19 @@ pub fn (a array) free() { C.free(a.data) } -// TODO generic // "[ 'a', 'b', 'c' ]" pub fn (a []string) str() string { - mut res := '[' + mut sb := strings.new_builder(a.len * 3) + sb.write('[') for i := 0; i < a.len; i++ { val := a[i] - res += '"$val"' + sb.write('"$val"') if i < a.len - 1 { - res += ', ' + sb.write(', ') } } - res += ']' - return res + sb.write(']') + return sb.str() } pub fn (b []byte) hex() string {