1
0
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:
Uwe Krüger 2020-06-17 19:49:13 +02:00 committed by GitHub
parent 325a7c7ec5
commit 9c9f6415da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 115 additions and 44 deletions

View File

@ -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()

View File

@ -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])

View 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}')
}

View 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}')
}

View File

@ -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')