From 8f9b6ac2489823a54641faaf4a7883e2c0b02c4e Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sun, 8 Dec 2019 22:22:47 +0300 Subject: [PATCH] '$foo()' string interpolation --- vlib/compiler/compile_errors.v | 2 +- vlib/compiler/comptime.v | 2 +- vlib/compiler/fn.v | 16 +++++++++------- vlib/compiler/gen_c.v | 8 ++++---- vlib/compiler/json_gen.v | 22 +++++++++++----------- vlib/compiler/main.v | 8 +++++--- vlib/compiler/parser.v | 18 +++++++++--------- vlib/compiler/query.v | 3 ++- vlib/compiler/scanner.v | 16 +++++++++++++--- vlib/compiler/table.v | 2 +- vlib/sdl/examples/tvintris/tvintris.v | 2 +- 11 files changed, 57 insertions(+), 42 deletions(-) diff --git a/vlib/compiler/compile_errors.v b/vlib/compiler/compile_errors.v index 7fa0c861e4..4d0a8f89c4 100644 --- a/vlib/compiler/compile_errors.v +++ b/vlib/compiler/compile_errors.v @@ -242,7 +242,7 @@ fn (p mut Parser) mutable_arg_error(i int, arg Var, f Fn) { dots_example = dots_example + ',..' } p.error('`$arg.name` is a mutable argument, you need to provide `mut`: ' + - '`$f.name($dots_example)`') + '`$f.name ($dots_example)`') } const ( diff --git a/vlib/compiler/comptime.v b/vlib/compiler/comptime.v index ed9cb7a903..14321be008 100644 --- a/vlib/compiler/comptime.v +++ b/vlib/compiler/comptime.v @@ -279,7 +279,7 @@ fn (p mut Parser) comptime_method_call(typ Type) { p.gen(' else ') } p.genln('if ( string_eq($var, _STR("$method.name")) ) ' + - '${typ.name}_$method.name($amp $p.expr_var.name);') + '${typ.name}_$method.name ($amp $p.expr_var.name);') j++ } p.check(.lpar) diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index a8c9319b25..173415dff7 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -455,7 +455,7 @@ fn (p mut Parser) fn_decl() { } if is_fn_header { - p.genln('$typ $fn_name_cgen($str_args);') + p.genln('$typ $fn_name_cgen ($str_args);') p.fgen_nl() } if is_c { @@ -495,7 +495,7 @@ fn (p mut Parser) fn_decl() { fn_name_cgen = '(* $fn_name_cgen )' } // Function definition that goes to the top of the C file. - mut fn_decl := '$dll_export_linkage$typ $fn_name_cgen($str_args)' + mut fn_decl := '$dll_export_linkage$typ $fn_name_cgen ($str_args)' if p.pref.obfuscate { fn_decl += '; // $f.name' } @@ -692,7 +692,7 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type if p.os == .windows { wrapper_type = 'DWORD WINAPI' } - wrapper_text := '$wrapper_type $wrapper_name($arg_struct_name * arg) {$fn_name( /*f*/$str_args ); return 0; }' + wrapper_text := '$wrapper_type $wrapper_name ($arg_struct_name * arg) {$fn_name ( /*f*/$str_args ); return 0; }' p.cgen.register_thread_fn(wrapper_name, wrapper_text, arg_struct) // Create thread object tmp_nr := p.get_tmp_counter() @@ -1024,13 +1024,15 @@ fn (p mut Parser) fn_call_args(f mut Fn) { p.mutable_arg_error(i, arg, f) } if p.peek() != .name { - p.error('`$arg.name` is a mutable argument, you need to provide a variable to modify: `$f.name(... mut a...)`') + p.error('`$arg.name` is a mutable argument, you need to ' + + 'provide a variable to modify: `${f.name}(... mut a...)`') } p.check(.key_mut) p.fspace() var_name := p.lit v := p.find_var(var_name) or { - p.error('`$arg.name` is a mutable argument, you need to provide a variable to modify: `$f.name(... mut a...)`') + p.error('`$arg.name` is a mutable argument, you need to ' + + 'provide a variable to modify: `${f.name}(... mut a...)`') exit(1) } if !v.is_changed { @@ -1156,7 +1158,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) { nr = 'third' } p.error('cannot use type `$typ` as type `$arg.typ` in $nr ' + - 'argument to `$f.name()`') + 'argument to `${f.name}()`') } else { saved_args << '' } @@ -1575,7 +1577,7 @@ fn (fns []Fn) contains(f Fn) bool { return false } fn (p &Parser) fn_signature(f &Fn) string { - return '$f.typ $f.name(${f.str_args(p.table)})' + return '$f.typ ${f.name}(${f.str_args(p.table)})' } pub fn (f &Fn) v_fn_module() string { diff --git a/vlib/compiler/gen_c.v b/vlib/compiler/gen_c.v index 5e8e2e92fd..ceb3276b3d 100644 --- a/vlib/compiler/gen_c.v +++ b/vlib/compiler/gen_c.v @@ -60,7 +60,7 @@ fn (p mut Parser) gen_fn_decl(f Fn, typ, str_args string) { } fn_name_cgen := p.table.fn_gen_name(f) //str_args := f.str_args(p.table) - p.genln('$dll_export_linkage$typ $fn_name_cgen($str_args) {') + p.genln('$dll_export_linkage$typ $fn_name_cgen ($str_args) {') } // blank identifer assignment `_ = 111` @@ -74,7 +74,7 @@ fn (p mut Parser) gen_blank_identifier_assign() { p.is_var_decl = true typ := p.bool_expression() if typ == 'void' { - p.error_with_token_index('$next_expr() $err_used_as_value', p.token_idx-2) + p.error_with_token_index('${next_expr}() $err_used_as_value', p.token_idx-2) } p.is_var_decl = false if !is_indexer && !is_fn_call { @@ -417,7 +417,7 @@ fn (p mut Parser) gen_array_init(typ string, no_alloc bool, new_arr_ph int, nr_e // Need to do this in the second pass, otherwise it goes to the very top of the out.c file if !p.first_pass() { p.cgen.set_placeholder(new_arr_ph, - '$new_arr($nr_elems, $nr_elems, sizeof($typ), EMPTY_ARRAY_OF_ELEMS( $typ, $nr_elems ) { ') + '${new_arr}($nr_elems, $nr_elems, sizeof($typ), EMPTY_ARRAY_OF_ELEMS( $typ, $nr_elems ) { ') } } @@ -494,7 +494,7 @@ fn (p mut Parser) gen_struct_init(typ string, t Type) bool { else { if p.tok == .not { // old &User{!} ==> 0 hack - p.error('use `$t.name(0)` instead of `&$t.name{!}`') + p.error('use `${t.name}(0)` instead of `&$t.name{!}`') /* p.next() p.gen('0') diff --git a/vlib/compiler/json_gen.v b/vlib/compiler/json_gen.v index e7c1a33d36..42b6057090 100644 --- a/vlib/compiler/json_gen.v +++ b/vlib/compiler/json_gen.v @@ -57,8 +57,8 @@ fn (p mut Parser) gen_json_for_type(typ Type) { p.table.register_fn(enc_fn) // Code gen decoder dec += ' -//$t $dec_fn.name(cJSON* root) { -Option $dec_fn.name(cJSON* root, $t* res) { +//$t $dec_fn.name (cJSON* root) { +Option ${dec_fn.name}(cJSON* root, $t* res) { // $t res; if (!root) { const char *error_ptr = cJSON_GetErrorPtr(); @@ -71,7 +71,7 @@ Option $dec_fn.name(cJSON* root, $t* res) { ' // Code gen encoder enc += ' -cJSON* $enc_fn.name($t val) { +cJSON* $enc_fn.name ($t val) { cJSON *o = cJSON_CreateObject(); string res = tos2(""); ' @@ -96,24 +96,24 @@ string res = tos2(""); if field.attr == 'raw' { dec += ' res->$field.name = tos2(cJSON_PrintUnformatted(' + 'js_get(root, "$name")));\n' - + } else { // Now generate decoders for all field types in this struct // need to do it here so that these functions are generated first p.gen_json_for_type(field_type) - + dec_name := js_dec_name(_typ) if is_js_prim(_typ) { - dec += ' res->$field.name = $dec_name(js_get(' + + dec += ' res->$field.name = $dec_name (js_get(' + 'root, "$name"))' } else { - dec += ' $dec_name(js_get(root, "$name"), & (res->$field.name))' + dec += ' $dec_name (js_get(root, "$name"), & (res->$field.name))' } dec += ';\n' } - enc += ' cJSON_AddItemToObject(o, "$name",$enc_name(val.$field.name)); \n' + enc += ' cJSON_AddItemToObject(o, "$name",$enc_name (val.$field.name)); \n' } // cJSON_delete //p.cgen.fns << '$dec return opt_ok(res); \n}' @@ -136,10 +136,10 @@ fn (p mut Parser) decode_array(array_type string) string { p.gen_json_for_type(t) mut s := '' if is_js_prim(typ) { - s = '$typ val= $fn_name(jsval); ' + s = '$typ val= $fn_name (jsval); ' } else { - s = ' $typ val; $fn_name(jsval, &val); ' + s = ' $typ val; $fn_name (jsval, &val); ' } return ' *res = new_array(0, 0, sizeof($typ)); @@ -168,7 +168,7 @@ fn (p &Parser) encode_array(array_type string) string { return ' o = cJSON_CreateArray(); for (int i = 0; i < val.len; i++){ - cJSON_AddItemToArray(o, $fn_name( (($typ*)val.data)[i] )); + cJSON_AddItemToArray(o, $fn_name ( (($typ*)val.data)[i] )); } ' } diff --git a/vlib/compiler/main.v b/vlib/compiler/main.v index 1a928c40c0..aa22165be2 100644 --- a/vlib/compiler/main.v +++ b/vlib/compiler/main.v @@ -502,13 +502,15 @@ pub fn (v mut V) generate_main() { v.gen_main_start(false) if v.pref.is_stats { - cgen.genln('BenchedTests bt = main__start_testing();') + // QQQ + //cgen.genln('BenchedTests bt = main__start_testing();') } for _, f in v.table.fns { if f.name.starts_with('main__test_') { - if v.pref.is_stats { cgen.genln('BenchedTests_testing_step_start(&bt, tos3("$f.name"));') } - cgen.genln('$f.name();') + if v.pref.is_stats { + cgen.genln('BenchedTests_testing_step_start(&bt, tos3("$f.name"));') } + cgen.genln('$f.name ();') if v.pref.is_stats { cgen.genln('BenchedTests_testing_step_end(&bt);') } } } diff --git a/vlib/compiler/parser.v b/vlib/compiler/parser.v index 1c5c74e866..7bfa27bbb9 100644 --- a/vlib/compiler/parser.v +++ b/vlib/compiler/parser.v @@ -1183,11 +1183,11 @@ fn (p mut Parser) free_var(v Var) { if !v.is_returned && v.typ != 'FILE*' { //!v.is_c { prev_line := p.cgen.lines[p.cgen.lines.len-2] p.cgen.lines[p.cgen.lines.len-2] = - '$free_fn($v.name); /* :) close_scope free $v.typ */' + prev_line + '$free_fn ($v.name); /* :) close_scope free $v.typ */' + prev_line } } else { //if p.fileis('mem.v') {println(v.name)} - p.genln('$free_fn($v.name); // close_scope free') + p.genln('$free_fn ($v.name); // close_scope free') } } @@ -1408,7 +1408,7 @@ fn ($v.name mut $v.typ) $p.cur_fn.name (...) { //} if expr_type == 'void' { _, fn_name := p.is_expr_fn_call(p.token_idx-3) - p.error_with_token_index('$fn_name() $err_used_as_value', p.token_idx-2) + p.error_with_token_index('${fn_name}() $err_used_as_value', p.token_idx-2) } // Allow `num = 4` where `num` is an `?int` if p.assigned_type.starts_with('Option_') && @@ -1517,7 +1517,7 @@ fn (p mut Parser) var_decl() { t := p.gen_var_decl(p.var_decl_name, is_static) if t == 'void' { _, fn_name := p.is_expr_fn_call(p.token_idx-3) - p.error_with_token_index('$fn_name() $err_used_as_value', p.token_idx-2) + p.error_with_token_index('${fn_name}() $err_used_as_value', p.token_idx-2) } mut var_types := [t] // multiple returns types @@ -1698,7 +1698,7 @@ fn (p mut Parser) get_c_func_type(name string) string { // Do not allow it. if !name.starts_with('gl') && !name.starts_with('glad') { p.error('undefined C function `$f.name`\n' + - 'define it with `fn C.$name([args]) [return_type]`') + 'define it with `fn C.${name}([args]) [return_type]`') } return 'void*' } @@ -2534,7 +2534,7 @@ if (!$tmp) { tos3("$filename"), $p.scanner.line_nr, tos3("$sourceline"), - tos3("$p.cur_fn.name()") + tos3("${p.cur_fn.name}()") ); exit(1); // TODO @@ -2545,7 +2545,7 @@ if (!$tmp) { tos3("$filename"), $p.scanner.line_nr, tos3("$sourceline"), - tos3("$p.cur_fn.name()") + tos3("${p.cur_fn.name}()") ); } @@ -2745,7 +2745,7 @@ fn (p mut Parser) js_decode() string { decl += 'cJSON* $cjson_tmp = json__json_parse($expr);' p.cgen.insert_before(decl) // p.gen('jsdecode_$typ(json_parse($expr), &$tmp);') - p.gen('json__jsdecode_$typ($cjson_tmp, &$tmp); cJSON_Delete($cjson_tmp);') + p.gen('json__jsdecode_${typ}($cjson_tmp, &$tmp); cJSON_Delete($cjson_tmp);') opt_type := 'Option_$typ' p.cgen.typedefs << 'typedef Option $opt_type;' p.table.register_builtin(opt_type) @@ -2757,7 +2757,7 @@ fn (p mut Parser) js_decode() string { T := p.table.find_type(typ) p.gen_json_for_type(T) p.check(.rpar) - p.gen('json__json_print(json__jsencode_$typ($expr))') + p.gen('json__json_print(json__jsencode_${typ}($expr))') return 'string' } else { diff --git a/vlib/compiler/query.v b/vlib/compiler/query.v index 862c0d0cc2..e9ba5387a8 100644 --- a/vlib/compiler/query.v +++ b/vlib/compiler/query.v @@ -123,7 +123,8 @@ fn (p mut Parser) select_query(fn_ph int) string { if field.typ == 'int' { cast = 'v_string_int' } - obj_gen.writeln('${qprefix}$tmp . $field.name = $cast( *(string*)array_get(${qprefix}row.vals, $i) );') + obj_gen.writeln('${qprefix}${tmp}.$field.name = ' + + '${cast}(*(string*)array_get(${qprefix}row.vals, $i));') } // One object if query_one { diff --git a/vlib/compiler/scanner.v b/vlib/compiler/scanner.v index c1cf0fc74d..846d9190d3 100644 --- a/vlib/compiler/scanner.v +++ b/vlib/compiler/scanner.v @@ -299,7 +299,8 @@ fn (s mut Scanner) scan() ScanRes { } } // end of `$expr` - if s.inter_start && next_char != `.` { + // allow `'$a.b'` and `'$a.c()'` + if s.inter_start && next_char != `.` && next_char != `(` { s.inter_end = true s.inter_start = false } @@ -315,6 +316,16 @@ fn (s mut Scanner) scan() ScanRes { num := s.ident_number() return scan_res(.number, num) } + // Handle `'$fn()'` + if c == `)` && s.inter_start { + s.inter_end = true + s.inter_start = false + next_char := if s.pos + 1 < s.text.len { s.text[s.pos + 1] } else { `\0` } + if next_char == s.quote { + s.inside_string = false + } + return scan_res(.rpar, '') + } // all other tokens match c { `+` { @@ -383,8 +394,7 @@ fn (s mut Scanner) scan() ScanRes { return scan_res(.rsbr, '') } `{` { - // Skip { in ${ in strings - // } + // Skip { in `${` in strings if s.inside_string { return s.scan() } diff --git a/vlib/compiler/table.v b/vlib/compiler/table.v index 21825e86d2..9459a39efe 100644 --- a/vlib/compiler/table.v +++ b/vlib/compiler/table.v @@ -188,7 +188,7 @@ const ( fn (f Fn) str() string { t := Table{} str_args := f.str_args(t) - return '$f.name($str_args) $f.typ' + return '${f.name}($str_args) $f.typ' } pub fn (t &Table) debug_fns() string { diff --git a/vlib/sdl/examples/tvintris/tvintris.v b/vlib/sdl/examples/tvintris/tvintris.v index 403235bb27..d6838ef566 100644 --- a/vlib/sdl/examples/tvintris/tvintris.v +++ b/vlib/sdl/examples/tvintris/tvintris.v @@ -676,7 +676,7 @@ fn (g mut Game) generate_tetro() { g.pos_x = FieldWidth / 2 - TetroSize / 2 g.tetro_idx = g.rand_tetro() // println('idx=${g.tetro_idx}') - g.tetro_stats[g.tetro_idx] += 1 + g.tetro_stats[g.tetro_idx]+= 2 -1 g.tetro_total++ g.rotation_idx = 0 g.get_tetro()