fmt: more sophisticated analysis for string interpolat
parent
325a7c7ec5
commit
9c9f6415da
|
@ -4,6 +4,7 @@
|
||||||
module ast
|
module ast
|
||||||
|
|
||||||
import v.table
|
import v.table
|
||||||
|
import v.util
|
||||||
import strings
|
import strings
|
||||||
|
|
||||||
pub fn (node &FnDecl) modname() string {
|
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()}'
|
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
|
// string representaiton of expr
|
||||||
pub fn (x Expr) str() string {
|
pub fn (x Expr) str() string {
|
||||||
match x {
|
match x {
|
||||||
|
@ -157,31 +217,14 @@ pub fn (x Expr) str() string {
|
||||||
for i, val in it.vals {
|
for i, val in it.vals {
|
||||||
res << val
|
res << val
|
||||||
if i >= it.exprs.len {
|
if i >= it.exprs.len {
|
||||||
continue
|
break
|
||||||
}
|
}
|
||||||
res << '$'
|
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
|
fspec_str, needs_braces := it.get_fspec_braces(i)
|
||||||
if needs_fspec || (it.exprs[i] !is Ident && it.exprs[i] !is SelectorExpr) {
|
if needs_braces {
|
||||||
res << '{'
|
res << '{'
|
||||||
res << it.exprs[i].str()
|
res << it.exprs[i].str()
|
||||||
if needs_fspec {
|
res << fspec_str
|
||||||
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 << '}'
|
res << '}'
|
||||||
} else {
|
} else {
|
||||||
res << it.exprs[i].str()
|
res << it.exprs[i].str()
|
||||||
|
|
|
@ -5,6 +5,7 @@ module fmt
|
||||||
|
|
||||||
import v.ast
|
import v.ast
|
||||||
import v.table
|
import v.table
|
||||||
|
import v.util
|
||||||
import strings
|
import strings
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -715,36 +716,19 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast.StringInterLiteral {
|
ast.StringInterLiteral {
|
||||||
|
// TODO: this code is very similar to ast.Expr.str()
|
||||||
f.write("'")
|
f.write("'")
|
||||||
for i, val in it.vals {
|
for i, val in it.vals {
|
||||||
f.write(val)
|
f.write(val)
|
||||||
if i >= it.exprs.len {
|
if i >= it.exprs.len {
|
||||||
continue
|
break
|
||||||
}
|
}
|
||||||
f.write('$')
|
f.write('$')
|
||||||
needs_fspec := it.need_fmts[i] || it.pluss[i] || (it.fills[i] && it.fwidths[i] >=
|
fspec_str, needs_braces := it.get_fspec_braces(i)
|
||||||
0) || it.fwidths[i] != 0 || it.precisions[i] != 0
|
if needs_braces {
|
||||||
if needs_fspec || (it.exprs[i] !is ast.Ident && it.exprs[i] !is ast.SelectorExpr) {
|
|
||||||
f.write('{')
|
f.write('{')
|
||||||
f.expr(it.exprs[i])
|
f.expr(it.exprs[i])
|
||||||
if needs_fspec {
|
f.write(fspec_str)
|
||||||
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('}')
|
f.write('}')
|
||||||
} else {
|
} else {
|
||||||
f.expr(it.exprs[i])
|
f.expr(it.exprs[i])
|
||||||
|
|
|
@ -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}')
|
||||||
|
}
|
|
@ -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 fill := false
|
||||||
mut fmt := `_` // placeholder
|
mut fmt := `_` // placeholder
|
||||||
if p.tok.kind == .colon {
|
if p.tok.kind == .colon {
|
||||||
has_fmt = true
|
|
||||||
p.next()
|
p.next()
|
||||||
// ${num:-2d}
|
// ${num:-2d}
|
||||||
if p.tok.kind == .minus {
|
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.kind == .name {
|
||||||
if p.tok.lit.len == 1 {
|
if p.tok.lit.len == 1 {
|
||||||
fmt = p.tok.lit[0]
|
fmt = p.tok.lit[0]
|
||||||
|
has_fmt = true
|
||||||
p.next()
|
p.next()
|
||||||
} else {
|
} else {
|
||||||
p.error('format specifier may only be one letter')
|
p.error('format specifier may only be one letter')
|
||||||
|
|
Loading…
Reference in New Issue