mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
fmt: more sophisticated analysis for string interpolat
This commit is contained in:
parent
325a7c7ec5
commit
9c9f6415da
@ -4,6 +4,7 @@
|
||||
module ast
|
||||
|
||||
import v.table
|
||||
import v.util
|
||||
import strings
|
||||
|
||||
pub fn (node &FnDecl) modname() string {
|
||||
@ -105,6 +106,65 @@ pub fn (x &InfixExpr) str() string {
|
||||
return '${x.left.str()} $x.op.str() ${x.right.str()}'
|
||||
}
|
||||
|
||||
// Expressions in string interpolations may have to be put in braces if they
|
||||
// are non-trivial or if a format specification is given. In the latter case
|
||||
// the format specifier must be appended, separated by a colon:
|
||||
// '$z $z.b $z.c.x ${x[4]} ${z:8.3f} ${a:-20} ${a>b+2}'
|
||||
// This method creates the format specifier (including the colon) or an empty
|
||||
// string if none is needed and also returns (as bool) if the expression
|
||||
// must be enclosed in braces.
|
||||
|
||||
pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) {
|
||||
mut res := []string{}
|
||||
needs_fspec := lit.need_fmts[i] || lit.pluss[i] || (lit.fills[i] && lit.fwidths[i] >= 0) || lit.fwidths[i] != 0 || lit.precisions[i] != 0
|
||||
mut needs_braces := needs_fspec
|
||||
if !needs_braces {
|
||||
if i+1 < lit.vals.len && lit.vals[i+1].len > 0 {
|
||||
next_char := lit.vals[i+1][0]
|
||||
if util.is_func_char(next_char) || next_char == `.` {
|
||||
needs_braces = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if !needs_braces {
|
||||
mut sub_expr := lit.exprs[i]
|
||||
for {
|
||||
match sub_expr {
|
||||
Ident {
|
||||
break
|
||||
}
|
||||
SelectorExpr {
|
||||
sub_expr = it.expr
|
||||
continue
|
||||
}
|
||||
else {
|
||||
needs_braces = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if needs_fspec {
|
||||
res << ':'
|
||||
if lit.pluss[i] {
|
||||
res << '+'
|
||||
}
|
||||
if lit.fills[i] && lit.fwidths[i] >= 0 {
|
||||
res << '0'
|
||||
}
|
||||
if lit.fwidths[i] != 0 {
|
||||
res << '${lit.fwidths[i]}'
|
||||
}
|
||||
if lit.precisions[i] != 0 {
|
||||
res << '.${lit.precisions[i]}'
|
||||
}
|
||||
if lit.need_fmts[i] {
|
||||
res << '${lit.fmts[i]:c}'
|
||||
}
|
||||
}
|
||||
return res.join(''), needs_braces
|
||||
}
|
||||
|
||||
// string representaiton of expr
|
||||
pub fn (x Expr) str() string {
|
||||
match x {
|
||||
@ -157,31 +217,14 @@ pub fn (x Expr) str() string {
|
||||
for i, val in it.vals {
|
||||
res << val
|
||||
if i >= it.exprs.len {
|
||||
continue
|
||||
break
|
||||
}
|
||||
res << '$'
|
||||
needs_fspec := it.need_fmts[i] || it.pluss[i] || (it.fills[i] && it.fwidths[i] >= 0) || it.fwidths[i] != 0 || it.precisions[i] != 0
|
||||
if needs_fspec || (it.exprs[i] !is Ident && it.exprs[i] !is SelectorExpr) {
|
||||
fspec_str, needs_braces := it.get_fspec_braces(i)
|
||||
if needs_braces {
|
||||
res << '{'
|
||||
res << it.exprs[i].str()
|
||||
if needs_fspec {
|
||||
res << ':'
|
||||
if it.pluss[i] {
|
||||
res << '+'
|
||||
}
|
||||
if it.fills[i] && it.fwidths[i] >= 0 {
|
||||
res << '0'
|
||||
}
|
||||
if it.fwidths[i] != 0 {
|
||||
res << '${it.fwidths[i]}'
|
||||
}
|
||||
if it.precisions[i] != 0 {
|
||||
res << '.${it.precisions[i]}'
|
||||
}
|
||||
if it.need_fmts[i] {
|
||||
res << '${it.fmts[i]:c}'
|
||||
}
|
||||
}
|
||||
res << fspec_str
|
||||
res << '}'
|
||||
} else {
|
||||
res << it.exprs[i].str()
|
||||
|
@ -5,6 +5,7 @@ module fmt
|
||||
|
||||
import v.ast
|
||||
import v.table
|
||||
import v.util
|
||||
import strings
|
||||
|
||||
const (
|
||||
@ -715,36 +716,19 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
||||
}
|
||||
}
|
||||
ast.StringInterLiteral {
|
||||
// TODO: this code is very similar to ast.Expr.str()
|
||||
f.write("'")
|
||||
for i, val in it.vals {
|
||||
f.write(val)
|
||||
if i >= it.exprs.len {
|
||||
continue
|
||||
break
|
||||
}
|
||||
f.write('$')
|
||||
needs_fspec := it.need_fmts[i] || it.pluss[i] || (it.fills[i] && it.fwidths[i] >=
|
||||
0) || it.fwidths[i] != 0 || it.precisions[i] != 0
|
||||
if needs_fspec || (it.exprs[i] !is ast.Ident && it.exprs[i] !is ast.SelectorExpr) {
|
||||
fspec_str, needs_braces := it.get_fspec_braces(i)
|
||||
if needs_braces {
|
||||
f.write('{')
|
||||
f.expr(it.exprs[i])
|
||||
if needs_fspec {
|
||||
f.write(':')
|
||||
if it.pluss[i] {
|
||||
f.write('+')
|
||||
}
|
||||
if it.fills[i] && it.fwidths[i] >= 0 {
|
||||
f.write('0')
|
||||
}
|
||||
if it.fwidths[i] != 0 {
|
||||
f.write('${it.fwidths[i]}')
|
||||
}
|
||||
if it.precisions[i] != 0 {
|
||||
f.write('.${it.precisions[i]}')
|
||||
}
|
||||
if it.need_fmts[i] {
|
||||
f.write('${it.fmts[i]:c}')
|
||||
}
|
||||
}
|
||||
f.write(fspec_str)
|
||||
f.write('}')
|
||||
} else {
|
||||
f.expr(it.exprs[i])
|
||||
|
22
vlib/v/fmt/tests/string_interpolation_expected.vv
Normal file
22
vlib/v/fmt/tests/string_interpolation_expected.vv
Normal file
@ -0,0 +1,22 @@
|
||||
struct Aa {
|
||||
xy int
|
||||
}
|
||||
|
||||
struct Bb {
|
||||
a Aa
|
||||
}
|
||||
|
||||
struct Cc {
|
||||
a []Aa
|
||||
}
|
||||
|
||||
fn main() {
|
||||
st := Bb{Aa{5}}
|
||||
ar := Cc{[Aa{3}, Aa{-4}, Aa{12}]}
|
||||
aa := Aa{-13}
|
||||
z := -14.75
|
||||
println('$st.a.xy ${ar.a[2].xy} $aa.xy $z')
|
||||
println('$st.a.xy${ar.a[2].xy}$aa.xy$z')
|
||||
println('${st.a.xy}ya ${ar.a[2].xy}X2 ${aa.xy}.b ${z}3')
|
||||
println('${z:-5} ${z:+5.3} ${z:+09.3f} ${z:-7.2} ${z:+09} ${z:08.3f}')
|
||||
}
|
22
vlib/v/fmt/tests/string_interpolation_input.vv
Normal file
22
vlib/v/fmt/tests/string_interpolation_input.vv
Normal file
@ -0,0 +1,22 @@
|
||||
struct Aa {
|
||||
xy int
|
||||
}
|
||||
|
||||
struct Bb {
|
||||
a Aa
|
||||
}
|
||||
|
||||
struct Cc {
|
||||
a []Aa
|
||||
}
|
||||
|
||||
fn main() {
|
||||
st := Bb{Aa{5}}
|
||||
ar := Cc{[Aa{3}, Aa{-4}, Aa{12}]}
|
||||
aa := Aa{-13}
|
||||
z := -14.75
|
||||
println('${st.a.xy} ${ar.a[2].xy} ${aa.xy} ${z}')
|
||||
println('${st.a.xy}${ar.a[2].xy}${aa.xy}${z}')
|
||||
println('${st.a.xy}ya ${ar.a[2].xy}X2 ${aa.xy}.b ${z}3')
|
||||
println('${z:-5} ${z:+5.3} ${z:+09.3f} ${z:-07.2} ${z:+009} ${z:008.3f}')
|
||||
}
|
@ -1177,7 +1177,6 @@ fn (mut p Parser) string_expr() ast.Expr {
|
||||
mut fill := false
|
||||
mut fmt := `_` // placeholder
|
||||
if p.tok.kind == .colon {
|
||||
has_fmt = true
|
||||
p.next()
|
||||
// ${num:-2d}
|
||||
if p.tok.kind == .minus {
|
||||
@ -1205,6 +1204,7 @@ fn (mut p Parser) string_expr() ast.Expr {
|
||||
if p.tok.kind == .name {
|
||||
if p.tok.lit.len == 1 {
|
||||
fmt = p.tok.lit[0]
|
||||
has_fmt = true
|
||||
p.next()
|
||||
} else {
|
||||
p.error('format specifier may only be one letter')
|
||||
|
Loading…
Reference in New Issue
Block a user