mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
scanner: fix new string interpolation println('{a}{b}{c}{d}') (#16258)
This commit is contained in:
parent
914f03a1a2
commit
64cbadc6f1
@ -47,6 +47,7 @@ const verify_known_failing_exceptions = [
|
|||||||
'vlib/builtin/int_test.v' /* special number formatting that should be tested */,
|
'vlib/builtin/int_test.v' /* special number formatting that should be tested */,
|
||||||
// TODOs and unfixed vfmt bugs
|
// TODOs and unfixed vfmt bugs
|
||||||
'vlib/v/gen/js/tests/js.v', /* local `hello` fn, gets replaced with module `hello` aliased as `hl` */
|
'vlib/v/gen/js/tests/js.v', /* local `hello` fn, gets replaced with module `hello` aliased as `hl` */
|
||||||
|
'vlib/v/tests/string_new_interpolation_test.v', /* new string interpolation */
|
||||||
]
|
]
|
||||||
|
|
||||||
const vfmt_verify_list = [
|
const vfmt_verify_list = [
|
||||||
|
@ -9,7 +9,7 @@ const (
|
|||||||
module eval
|
module eval
|
||||||
import v.token
|
import v.token
|
||||||
import v.ast
|
import v.ast
|
||||||
fn(e Eval)infix_expr(left Object,right Object,op token.Kind,expecting ast.Type)Object{match op{'
|
fn(e Eval)infix_expr(left Object,right Object,op token.Kind,expecting ast.Type)Object{ match op{'
|
||||||
footer = "else{ e.error('unknown infix expression: \$op')}}return empty // should e.error before this anyway
|
footer = "else{ e.error('unknown infix expression: \$op')}}return empty // should e.error before this anyway
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@ -37,26 +37,26 @@ fn main() {
|
|||||||
b.write_string(header)
|
b.write_string(header)
|
||||||
|
|
||||||
for enm, op in comparison {
|
for enm, op in comparison {
|
||||||
b.write_string('.$enm{match left{')
|
b.write_string('.$enm{ match left{')
|
||||||
for ct in compound_types {
|
for ct in compound_types {
|
||||||
b.write_string('$ct {match right{')
|
b.write_string('$ct { match right{')
|
||||||
for ct2 in compound_types {
|
for ct2 in compound_types {
|
||||||
b.write_string('$ct2{return left.val${op}right.val}')
|
b.write_string('$ct2{ return left.val${op}right.val}')
|
||||||
}
|
}
|
||||||
for lt2 in literal_types {
|
for lt2 in literal_types {
|
||||||
b.write_string('$lt2{return left.val${op}right}')
|
b.write_string('$lt2{ return left.val${op}right}')
|
||||||
}
|
}
|
||||||
b.write_string("else{ e.error('invalid operands to $op: $ct and \$right.type_name()')}}}")
|
b.write_string("else{ e.error('invalid operands to $op: $ct and \$right.type_name()')}}}")
|
||||||
}
|
}
|
||||||
for lt in literal_types {
|
for lt in literal_types {
|
||||||
b.write_string('$lt {match right{')
|
b.write_string('$lt { match right{')
|
||||||
for ct2 in compound_types {
|
for ct2 in compound_types {
|
||||||
b.write_string('$ct2{return left${op}right.val}')
|
b.write_string('$ct2{ return left${op}right.val}')
|
||||||
}
|
}
|
||||||
for lt2 in literal_types {
|
for lt2 in literal_types {
|
||||||
b.write_string('$lt2{return left${op}right}')
|
b.write_string('$lt2{ return left${op}right}')
|
||||||
}
|
}
|
||||||
b.write_string("else {e.error('invalid operands to $op: ")
|
b.write_string("else { e.error('invalid operands to $op: ")
|
||||||
b.write_string(if lt == 'i64' { 'int' } else { 'float' })
|
b.write_string(if lt == 'i64' { 'int' } else { 'float' })
|
||||||
b.write_string(" literal and \$right.type_name()')}}}")
|
b.write_string(" literal and \$right.type_name()')}}}")
|
||||||
}
|
}
|
||||||
@ -66,12 +66,12 @@ fn main() {
|
|||||||
b.write_string("else { e.error('invalid operands to $op: \$left.type_name() and \$right.type_name()')}}}")
|
b.write_string("else { e.error('invalid operands to $op: \$left.type_name() and \$right.type_name()')}}}")
|
||||||
}
|
}
|
||||||
for math, op in math_ops {
|
for math, op in math_ops {
|
||||||
b.write_string('.$math{match left{')
|
b.write_string('.$math{ match left{')
|
||||||
for ct in compound_types {
|
for ct in compound_types {
|
||||||
if op in ['<<', '>>'] && ct == 'Float' {
|
if op in ['<<', '>>'] && ct == 'Float' {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
b.write_string('$ct {match right{')
|
b.write_string('$ct { match right{')
|
||||||
for ct2 in compound_types {
|
for ct2 in compound_types {
|
||||||
if op in ['<<', '>>'] && ct2 == 'Float' {
|
if op in ['<<', '>>'] && ct2 == 'Float' {
|
||||||
continue
|
continue
|
||||||
@ -117,13 +117,13 @@ fn main() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
|
unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
|
||||||
b.write_string('$lt2{if expecting in ast.signed_integer_type_idxs{ return Int{ i64(left)${op}i64(right),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{ return Uint{ u64(left)${op}u64(right),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left)${op}i64(right))$unsafe_end}')
|
b.write_string('$lt2{ if expecting in ast.signed_integer_type_idxs{ return Int{ i64(left)${op}i64(right),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{ return Uint{ u64(left)${op}u64(right),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left)${op}i64(right))$unsafe_end}')
|
||||||
if op !in ['<<', '>>'] {
|
if op !in ['<<', '>>'] {
|
||||||
b.write_string('else if expecting in ast.float_type_idxs{ return Float{ f64(left)${op}f64(right), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{ return f64(f64(left)${op}f64(right))}')
|
b.write_string('else if expecting in ast.float_type_idxs{ return Float{ f64(left)${op}f64(right), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{ return f64(f64(left)${op}f64(right))}')
|
||||||
}
|
}
|
||||||
b.write_string(uk_expect_footer)
|
b.write_string(uk_expect_footer)
|
||||||
}
|
}
|
||||||
b.write_string("else {e.error('invalid operands to $op: ")
|
b.write_string("else { e.error('invalid operands to $op: ")
|
||||||
b.write_string(if lt == 'i64' { 'int' } else { 'float' })
|
b.write_string(if lt == 'i64' { 'int' } else { 'float' })
|
||||||
b.write_string(" literal and \$right.type_name()')}}}")
|
b.write_string(" literal and \$right.type_name()')}}}")
|
||||||
}
|
}
|
||||||
|
@ -1214,7 +1214,7 @@ fn (mut s Scanner) ident_string() string {
|
|||||||
u_escapes_pos << s.pos - 1
|
u_escapes_pos << s.pos - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ${var} (ignore in vfmt mode) (skip \$)
|
// '${var}' (ignore in vfmt mode) (skip \$)
|
||||||
if prevc == `$` && c == `{` && !is_raw
|
if prevc == `$` && c == `{` && !is_raw
|
||||||
&& s.count_symbol_before(s.pos - 2, scanner.backslash) % 2 == 0 {
|
&& s.count_symbol_before(s.pos - 2, scanner.backslash) % 2 == 0 {
|
||||||
s.is_inside_string = true
|
s.is_inside_string = true
|
||||||
@ -1223,7 +1223,7 @@ fn (mut s Scanner) ident_string() string {
|
|||||||
s.pos -= 2
|
s.pos -= 2
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// $var
|
// '$var'
|
||||||
if prevc == `$` && util.is_name_char(c) && !is_raw
|
if prevc == `$` && util.is_name_char(c) && !is_raw
|
||||||
&& s.count_symbol_before(s.pos - 2, scanner.backslash) % 2 == 0 {
|
&& s.count_symbol_before(s.pos - 2, scanner.backslash) % 2 == 0 {
|
||||||
s.is_inside_string = true
|
s.is_inside_string = true
|
||||||
@ -1231,7 +1231,7 @@ fn (mut s Scanner) ident_string() string {
|
|||||||
s.pos -= 2
|
s.pos -= 2
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// {var} (ignore in vfmt mode) (skip \{)
|
// '{var}' (ignore in vfmt mode) (skip \{)
|
||||||
if c == `{` && util.is_name_char(s.text[s.pos + 1]) && prevc != `$` && !is_raw
|
if c == `{` && util.is_name_char(s.text[s.pos + 1]) && prevc != `$` && !is_raw
|
||||||
&& s.count_symbol_before(s.pos - 1, scanner.backslash) % 2 == 0 {
|
&& s.count_symbol_before(s.pos - 1, scanner.backslash) % 2 == 0 {
|
||||||
// Detect certain strings with "{" that are not interpolation:
|
// Detect certain strings with "{" that are not interpolation:
|
||||||
@ -1258,6 +1258,33 @@ fn (mut s Scanner) ident_string() string {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// '{var1}{var2}'
|
||||||
|
if prevc == `{` && util.is_name_char(c) && s.text[s.pos - 2] !in [`$`, `{`] && !is_raw
|
||||||
|
&& s.count_symbol_before(s.pos - 2, scanner.backslash) % 2 == 0 {
|
||||||
|
// Detect certain strings with "{" that are not interpolation:
|
||||||
|
// e.g. "{init: " (no "}" at the end)
|
||||||
|
mut is_valid_inter := true
|
||||||
|
for i := s.pos; i < s.text.len; i++ {
|
||||||
|
if s.text[i] == `}` {
|
||||||
|
// No } in this string, so it's not a valid `{x}` interpolation
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if s.text[i] in [`=`, `:`, `\n`, s.inter_quote] {
|
||||||
|
// We reached the end of the line or string without reaching "}".
|
||||||
|
// Also if there's "=", there's no way it's a valid interpolation expression:
|
||||||
|
// e.g. `println("{a.b = 42}")` `println('{foo:bar}')`
|
||||||
|
is_valid_inter = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_valid_inter {
|
||||||
|
s.is_inside_string = true
|
||||||
|
s.is_enclosed_inter = true
|
||||||
|
// so that s.pos points to $ at the next step
|
||||||
|
s.pos -= 2
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
if c != scanner.backslash {
|
if c != scanner.backslash {
|
||||||
backslash_count = 0
|
backslash_count = 0
|
||||||
}
|
}
|
||||||
|
9
vlib/v/tests/string_new_interpolation_test.v
Normal file
9
vlib/v/tests/string_new_interpolation_test.v
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
fn test_string_new_interpolation() {
|
||||||
|
a, b, c, d := 1, 2, 3, 4
|
||||||
|
|
||||||
|
println('{a}{b}{c}{d}')
|
||||||
|
assert '{a}{b}{c}{d}' == '1234'
|
||||||
|
|
||||||
|
println('{a} {b} {c} {d}')
|
||||||
|
assert '{a} {b} {c} {d}' == '1 2 3 4'
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user