cgen: restructure string_inter_literal()
parent
015d0c5e33
commit
f2d9fa3815
|
@ -73,10 +73,16 @@ pub struct StringInterLiteral {
|
||||||
pub:
|
pub:
|
||||||
vals []string
|
vals []string
|
||||||
exprs []Expr
|
exprs []Expr
|
||||||
expr_fmts []string
|
fwidths []int
|
||||||
|
precisions []int
|
||||||
|
pluss []bool
|
||||||
|
fills []bool
|
||||||
|
fmt_poss []token.Position
|
||||||
pos token.Position
|
pos token.Position
|
||||||
pub mut:
|
pub mut:
|
||||||
expr_types []table.Type
|
expr_types []table.Type
|
||||||
|
fmts []byte
|
||||||
|
need_fmts []bool // an explicit non-default fmt required, e.g. `x`
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CharLiteral {
|
pub struct CharLiteral {
|
||||||
|
|
|
@ -160,10 +160,28 @@ pub fn (x Expr) str() string {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
res << '$'
|
res << '$'
|
||||||
if it.expr_fmts[i].len > 0 {
|
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) {
|
||||||
res << '{'
|
res << '{'
|
||||||
res << it.exprs[i].str()
|
res << it.exprs[i].str()
|
||||||
res << it.expr_fmts[i]
|
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 << '}'
|
res << '}'
|
||||||
} else {
|
} else {
|
||||||
res << it.exprs[i].str()
|
res << it.exprs[i].str()
|
||||||
|
|
|
@ -5,6 +5,7 @@ module checker
|
||||||
|
|
||||||
import v.table
|
import v.table
|
||||||
import v.token
|
import v.token
|
||||||
|
import v.ast
|
||||||
|
|
||||||
pub fn (c &Checker) check_basic(got, expected table.Type) bool {
|
pub fn (c &Checker) check_basic(got, expected table.Type) bool {
|
||||||
t := c.table
|
t := c.table
|
||||||
|
@ -254,3 +255,66 @@ pub fn (c &Checker) symmetric_check(left, right table.Type) bool {
|
||||||
}
|
}
|
||||||
return c.check_basic(left, right)
|
return c.check_basic(left, right)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (c &Checker) get_default_fmt(ftyp, typ table.Type) byte {
|
||||||
|
if typ.is_float() {
|
||||||
|
return `g`
|
||||||
|
} else if typ.is_signed() || typ.is_any_int() {
|
||||||
|
return `d`
|
||||||
|
} else if typ.is_unsigned() {
|
||||||
|
return `u`
|
||||||
|
} else if typ.is_pointer() {
|
||||||
|
return `p`
|
||||||
|
} else {
|
||||||
|
sym := c.table.get_type_symbol(ftyp)
|
||||||
|
if ftyp in [table.string_type, table.bool_type] || sym.kind in
|
||||||
|
[.enum_, .array, .array_fixed, .struct_, .map] || ftyp.has_flag(.optional) ||
|
||||||
|
sym.has_method('str') {
|
||||||
|
return `s`
|
||||||
|
} else {
|
||||||
|
return `_`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (c &Checker) string_inter_lit(mut node ast.StringInterLiteral) table.Type {
|
||||||
|
for i, expr in node.exprs {
|
||||||
|
ftyp := c.expr(expr)
|
||||||
|
node.expr_types << ftyp
|
||||||
|
typ := c.table.unalias_num_type(ftyp)
|
||||||
|
mut fmt := node.fmts[i]
|
||||||
|
// analyze and validate format specifier
|
||||||
|
if fmt !in [`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `p`, `_`] {
|
||||||
|
c.error('unknown format specifier `${fmt:c}`', node.fmt_poss[i])
|
||||||
|
}
|
||||||
|
if fmt == `_` { // set default representation for type if none has been given
|
||||||
|
fmt = c.get_default_fmt(ftyp, typ)
|
||||||
|
if fmt == `_` {
|
||||||
|
c.error('no known default format for type `${c.table.get_type_name(ftyp)}`',
|
||||||
|
node.fmt_poss[i])
|
||||||
|
} else {
|
||||||
|
node.fmts[i] = fmt
|
||||||
|
node.need_fmts[i] = false
|
||||||
|
}
|
||||||
|
} else { // check if given format specifier is valid for type
|
||||||
|
if node.precisions[i] != 0 && !typ.is_float() {
|
||||||
|
c.error('precision specification only valid for float types', node.fmt_poss[i])
|
||||||
|
}
|
||||||
|
if node.pluss[i] && !typ.is_number() {
|
||||||
|
c.error('plus prefix only allowd for numbers', node.fmt_poss[i])
|
||||||
|
}
|
||||||
|
if (typ.is_unsigned() && fmt !in [`u`, `x`, `X`, `o`, `c`]) ||
|
||||||
|
(typ.is_signed() && fmt !in [`d`, `x`, `X`, `o`, `c`]) ||
|
||||||
|
(typ.is_any_int() && fmt !in [`d`, `c`, `x`, `X`, `o`, `u`, `x`, `X`, `o`]) ||
|
||||||
|
(typ.is_float() && fmt !in [`E`, `F`, `G`, `e`, `f`, `g`]) ||
|
||||||
|
(typ.is_pointer() && fmt !in [`p`, `x`, `X`]) ||
|
||||||
|
(typ.is_string() && fmt != `s`) ||
|
||||||
|
(typ.idx() in [table.i64_type_idx, table.f64_type_idx] && fmt == `c`) {
|
||||||
|
c.error('illegal format specifier `${fmt:c}` for type `${c.table.get_type_name(ftyp)}`',
|
||||||
|
node.fmt_poss[i])
|
||||||
|
}
|
||||||
|
node.need_fmts[i] = fmt != c.get_default_fmt(ftyp, typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return table.string_type
|
||||||
|
}
|
||||||
|
|
|
@ -1985,10 +1985,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
||||||
return table.string_type
|
return table.string_type
|
||||||
}
|
}
|
||||||
ast.StringInterLiteral {
|
ast.StringInterLiteral {
|
||||||
for expr in it.exprs {
|
return c.string_inter_lit(mut it)
|
||||||
it.expr_types << c.expr(expr)
|
|
||||||
}
|
|
||||||
return table.string_type
|
|
||||||
}
|
}
|
||||||
ast.StructInit {
|
ast.StructInit {
|
||||||
return c.struct_init(mut it)
|
return c.struct_init(mut it)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
vlib/v/checker/tests/string_interpolation_invalid_fmt.v:3:12: error: format specifier may only be one letter
|
||||||
|
1 | fn interpolate_wrong() string {
|
||||||
|
2 | a := 5
|
||||||
|
3 | x := '${a:xy}'
|
||||||
|
| ~~
|
||||||
|
4 | return x
|
||||||
|
5 | }
|
|
@ -0,0 +1,5 @@
|
||||||
|
fn interpolate_wrong() string {
|
||||||
|
a := 5
|
||||||
|
x := '${a:xy}'
|
||||||
|
return x
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
vlib/v/checker/tests/string_interpolation_wrong_fmt.v:3:16: error: precision specification only valid for float types
|
||||||
|
1 | fn interpolate_str() string {
|
||||||
|
2 | a := 'hallo'
|
||||||
|
3 | x := '>${a:8.3s}<'
|
||||||
|
| ^
|
||||||
|
4 | y := '${a:G}'
|
||||||
|
5 | z := '${a:d}'
|
||||||
|
vlib/v/checker/tests/string_interpolation_wrong_fmt.v:4:12: error: illegal format specifier `G` for type `string`
|
||||||
|
2 | a := 'hallo'
|
||||||
|
3 | x := '>${a:8.3s}<'
|
||||||
|
4 | y := '${a:G}'
|
||||||
|
| ^
|
||||||
|
5 | z := '${a:d}'
|
||||||
|
6 | return x + y + z
|
||||||
|
vlib/v/checker/tests/string_interpolation_wrong_fmt.v:5:12: error: illegal format specifier `d` for type `string`
|
||||||
|
3 | x := '>${a:8.3s}<'
|
||||||
|
4 | y := '${a:G}'
|
||||||
|
5 | z := '${a:d}'
|
||||||
|
| ^
|
||||||
|
6 | return x + y + z
|
||||||
|
7 | }
|
||||||
|
vlib/v/checker/tests/string_interpolation_wrong_fmt.v:11:15: error: illegal format specifier `s` for type `f64`
|
||||||
|
9 | fn interpolate_f64() string {
|
||||||
|
10 | b := 1367.57
|
||||||
|
11 | x := '>${b:20s}<'
|
||||||
|
| ^
|
||||||
|
12 | y := '${b:d}'
|
||||||
|
13 | return x + y
|
||||||
|
vlib/v/checker/tests/string_interpolation_wrong_fmt.v:12:12: error: illegal format specifier `d` for type `f64`
|
||||||
|
10 | b := 1367.57
|
||||||
|
11 | x := '>${b:20s}<'
|
||||||
|
12 | y := '${b:d}'
|
||||||
|
| ^
|
||||||
|
13 | return x + y
|
||||||
|
14 | }
|
||||||
|
vlib/v/checker/tests/string_interpolation_wrong_fmt.v:19:14: error: illegal format specifier `d` for type `u32`
|
||||||
|
17 | u := u32(15)
|
||||||
|
18 | s := -12
|
||||||
|
19 | x := '${u:13d}'
|
||||||
|
| ^
|
||||||
|
20 | y := '${s:04u}'
|
||||||
|
21 | z := '${s:f}'
|
||||||
|
vlib/v/checker/tests/string_interpolation_wrong_fmt.v:20:14: error: illegal format specifier `u` for type `int`
|
||||||
|
18 | s := -12
|
||||||
|
19 | x := '${u:13d}'
|
||||||
|
20 | y := '${s:04u}'
|
||||||
|
| ^
|
||||||
|
21 | z := '${s:f}'
|
||||||
|
22 | q := '${u:v}'
|
||||||
|
vlib/v/checker/tests/string_interpolation_wrong_fmt.v:21:12: error: illegal format specifier `f` for type `int`
|
||||||
|
19 | x := '${u:13d}'
|
||||||
|
20 | y := '${s:04u}'
|
||||||
|
21 | z := '${s:f}'
|
||||||
|
| ^
|
||||||
|
22 | q := '${u:v}'
|
||||||
|
23 | return x + y + z + q
|
||||||
|
vlib/v/checker/tests/string_interpolation_wrong_fmt.v:22:12: error: unknown format specifier `v`
|
||||||
|
20 | y := '${s:04u}'
|
||||||
|
21 | z := '${s:f}'
|
||||||
|
22 | q := '${u:v}'
|
||||||
|
| ^
|
||||||
|
23 | return x + y + z + q
|
||||||
|
24 | }
|
|
@ -0,0 +1,24 @@
|
||||||
|
fn interpolate_str() string {
|
||||||
|
a := 'hallo'
|
||||||
|
x := '>${a:8.3s}<'
|
||||||
|
y := '${a:G}'
|
||||||
|
z := '${a:d}'
|
||||||
|
return x + y + z
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interpolate_f64() string {
|
||||||
|
b := 1367.57
|
||||||
|
x := '>${b:20s}<'
|
||||||
|
y := '${b:d}'
|
||||||
|
return x + y
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interpolate_int() string {
|
||||||
|
u := u32(15)
|
||||||
|
s := -12
|
||||||
|
x := '${u:13d}'
|
||||||
|
y := '${s:04u}'
|
||||||
|
z := '${s:f}'
|
||||||
|
q := '${u:v}'
|
||||||
|
return x + y + z + q
|
||||||
|
}
|
|
@ -721,10 +721,28 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f.write('$')
|
f.write('$')
|
||||||
if it.expr_fmts[i].len > 0 {
|
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) {
|
||||||
f.write('{')
|
f.write('{')
|
||||||
f.expr(it.exprs[i])
|
f.expr(it.exprs[i])
|
||||||
f.write(it.expr_fmts[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('}')
|
f.write('}')
|
||||||
} else {
|
} else {
|
||||||
f.expr(it.exprs[i])
|
f.expr(it.exprs[i])
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
module gen
|
module gen
|
||||||
|
|
||||||
import strings
|
import strings
|
||||||
import strconv
|
|
||||||
import v.ast
|
import v.ast
|
||||||
import v.table
|
import v.table
|
||||||
import v.pref
|
import v.pref
|
||||||
|
@ -2918,101 +2917,40 @@ fn (g Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
|
fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
|
||||||
//if g.pref.autofree {
|
|
||||||
//g.write('_STR_TMP("')
|
|
||||||
//} else {
|
|
||||||
g.write('_STR("')
|
g.write('_STR("')
|
||||||
//}
|
|
||||||
// Build the string with %
|
// Build the string with %
|
||||||
mut fieldwidths := []int{}
|
|
||||||
mut specs := []byte{}
|
|
||||||
mut end_string := false
|
mut end_string := false
|
||||||
for i, val in node.vals {
|
for i, val in node.vals {
|
||||||
escaped_val := val.replace_each(['"', '\\"', '\r\n', '\\n', '\n', '\\n', '%', '%%'])
|
escaped_val := val.replace_each(['"', '\\"', '\r\n', '\\n', '\n', '\\n', '%', '%%'])
|
||||||
if i >= node.exprs.len {
|
if i >= node.exprs.len {
|
||||||
if escaped_val.len > 0 {
|
if escaped_val.len > 0 {
|
||||||
end_string = true
|
end_string = true
|
||||||
//if !g.pref.autofree {
|
g.write('\\000')
|
||||||
g.write('\\000')
|
|
||||||
//}
|
|
||||||
g.write(escaped_val)
|
g.write(escaped_val)
|
||||||
}
|
}
|
||||||
continue
|
break
|
||||||
}
|
}
|
||||||
g.write(escaped_val)
|
g.write(escaped_val)
|
||||||
sym := g.table.get_type_symbol(node.expr_types[i])
|
|
||||||
sfmt := node.expr_fmts[i]
|
|
||||||
mut fspec := `_` // placeholder
|
|
||||||
mut fmt := '' // field width and precision
|
|
||||||
if sfmt.len > 0 {
|
|
||||||
// analyze and validate format specifier
|
|
||||||
if sfmt[sfmt.len - 1] in [`E`, `F`, `G`, `e`, `f`, `g`,
|
|
||||||
`d`, `u`, `x`, `X`, `o`, `c`, `s`, `p`] {
|
|
||||||
fspec = sfmt[sfmt.len - 1]
|
|
||||||
}
|
|
||||||
fmt = if fspec == `_` {
|
|
||||||
sfmt[1..sfmt.len]
|
|
||||||
} else {
|
|
||||||
sfmt[1..sfmt.len - 1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if fspec == `_` { // set default representation for type if still missing
|
|
||||||
if node.expr_types[i].is_float() {
|
|
||||||
fspec = `g`
|
|
||||||
} else if node.expr_types[i].is_signed() || node.expr_types[i].is_any_int() {
|
|
||||||
fspec = `d`
|
|
||||||
} else if node.expr_types[i].is_unsigned() {
|
|
||||||
fspec = `u`
|
|
||||||
} else if node.expr_types[i].is_pointer() {
|
|
||||||
fspec = `p`
|
|
||||||
} else if node.expr_types[i] in [table.string_type, table.bool_type] || sym.kind in
|
|
||||||
[.enum_, .array, .array_fixed, .struct_, .map] || g.typ(node.expr_types[i]).starts_with('Option') ||
|
|
||||||
sym.has_method('str') {
|
|
||||||
fspec = `s`
|
|
||||||
} else {
|
|
||||||
// default to int - TODO: should better be checked
|
|
||||||
fspec = `d`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fields := fmt.split('.')
|
|
||||||
// validate format
|
|
||||||
// only floats should have precision specifier
|
|
||||||
/*
|
|
||||||
if fields.len > 2 || fields.len == 2 && !(node.expr_types[i].is_float()) || node.expr_types[i].is_signed() &&
|
|
||||||
fspec !in [`d`, `c`, `x`, `X`, `o`] || node.expr_types[i].is_unsigned() && fspec !in [`u`, `x`,
|
|
||||||
`X`, `o`, `c`] || node.expr_types[i].is_any_int() && fspec !in [`d`, `c`, `x`, `X`,
|
|
||||||
`o`, `u`,
|
|
||||||
`x`, `X`, `o`] || node.expr_types[i].is_float() && fspec !in [`E`, `F`, `G`, `e`,
|
|
||||||
`f`, `g`] || node.expr_types[i].is_pointer() && fspec !in [`p`, `x`, `X`] {
|
|
||||||
verror('illegal format specifier ${fspec:c} for type ${g.table.get_type_name(node.expr_types[i])}')
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// make sure that format paramters are valid numbers
|
|
||||||
/*
|
|
||||||
for j, f in fields {
|
|
||||||
for k, c in f {
|
|
||||||
if (c < `0` || c > `9`) && !(j == 0 && k == 0 && (node.expr_types[i].is_number() &&
|
|
||||||
c == `+` || c == `-`)) {
|
|
||||||
verror('illegal character ${c:c} in format specifier ${fmt}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
specs << fspec
|
|
||||||
fieldwidths << if fields.len == 0 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
strconv.atoi(fields[0])
|
|
||||||
}
|
|
||||||
// write correct format specifier to intermediate string
|
// write correct format specifier to intermediate string
|
||||||
g.write('%')
|
g.write('%')
|
||||||
|
fspec := node.fmts[i]
|
||||||
|
mut fmt := if node.pluss[i] { '+' } else { '' }
|
||||||
|
if node.fills[i] && node.fwidths[i] >= 0 {
|
||||||
|
fmt = '${fmt}0'
|
||||||
|
}
|
||||||
|
if node.fwidths[i] != 0 {
|
||||||
|
fmt = '$fmt${node.fwidths[i]}'
|
||||||
|
}
|
||||||
|
if node.precisions[i] != 0 {
|
||||||
|
fmt = '${fmt}.${node.precisions[i]}'
|
||||||
|
}
|
||||||
if fspec == `s` {
|
if fspec == `s` {
|
||||||
if fields.len == 0 || strconv.atoi(fields[0]) == 0 {
|
if node.fwidths[i] == 0 {
|
||||||
g.write('.*s')
|
g.write('.*s')
|
||||||
} else {
|
} else {
|
||||||
g.write('*.*s')
|
g.write('*.*s')
|
||||||
}
|
}
|
||||||
} else if node.expr_types[i].is_float() || node.expr_types[i].is_pointer() {
|
} else if node.expr_types[i].is_float() {
|
||||||
g.write('$fmt${fspec:c}')
|
g.write('$fmt${fspec:c}')
|
||||||
} else if node.expr_types[i].is_pointer() {
|
} else if node.expr_types[i].is_pointer() {
|
||||||
if fspec == `p` {
|
if fspec == `p` {
|
||||||
|
@ -3022,11 +2960,7 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
|
||||||
}
|
}
|
||||||
} else if node.expr_types[i].is_int() {
|
} else if node.expr_types[i].is_int() {
|
||||||
if fspec == `c` {
|
if fspec == `c` {
|
||||||
if node.expr_types[i].idx() in [table.i64_type_idx, table.f64_type_idx] {
|
g.write('${fmt}c')
|
||||||
verror('64 bit integer types cannot be interpolated as character')
|
|
||||||
} else {
|
|
||||||
g.write('${fmt}c')
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
g.write('${fmt}"PRI${fspec:c}')
|
g.write('${fmt}"PRI${fspec:c}')
|
||||||
if node.expr_types[i] in [table.i8_type, table.byte_type] {
|
if node.expr_types[i] in [table.i8_type, table.byte_type] {
|
||||||
|
@ -3057,9 +2991,9 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
|
||||||
} else if node.expr_types[i] == table.bool_type {
|
} else if node.expr_types[i] == table.bool_type {
|
||||||
g.expr(expr)
|
g.expr(expr)
|
||||||
g.write(' ? _SLIT("true") : _SLIT("false")')
|
g.write(' ? _SLIT("true") : _SLIT("false")')
|
||||||
} else if node.expr_types[i].is_number() || node.expr_types[i].is_pointer() || specs[i] ==
|
} else if node.expr_types[i].is_number() || node.expr_types[i].is_pointer() || node.fmts[i] ==
|
||||||
`d` {
|
`d` {
|
||||||
if node.expr_types[i].is_signed() && specs[i] in [`x`, `X`, `o`] {
|
if node.expr_types[i].is_signed() && node.fmts[i] in [`x`, `X`, `o`] {
|
||||||
// convert to unsigned first befors C's integer propagation strikes
|
// convert to unsigned first befors C's integer propagation strikes
|
||||||
if node.expr_types[i] == table.i8_type {
|
if node.expr_types[i] == table.i8_type {
|
||||||
g.write('(byte)(')
|
g.write('(byte)(')
|
||||||
|
@ -3075,13 +3009,13 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
|
||||||
} else {
|
} else {
|
||||||
g.expr(expr)
|
g.expr(expr)
|
||||||
}
|
}
|
||||||
} else if specs[i] == `s` {
|
} else if node.fmts[i] == `s` {
|
||||||
g.gen_expr_to_string(expr, node.expr_types[i])
|
g.gen_expr_to_string(expr, node.expr_types[i])
|
||||||
} else {
|
} else {
|
||||||
g.expr(expr)
|
g.expr(expr)
|
||||||
}
|
}
|
||||||
if specs[i] == `s` && fieldwidths[i] != 0 {
|
if node.fmts[i] == `s` && node.fwidths[i] != 0 {
|
||||||
g.write(', ${fieldwidths[i]}')
|
g.write(', ${node.fwidths[i]}')
|
||||||
}
|
}
|
||||||
if i < node.exprs.len - 1 {
|
if i < node.exprs.len - 1 {
|
||||||
g.write(', ')
|
g.write(', ')
|
||||||
|
|
|
@ -1367,9 +1367,11 @@ fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
expr := it.exprs[i]
|
expr := it.exprs[i]
|
||||||
sfmt := it.expr_fmts[i]
|
fmt := it.fmts[i]
|
||||||
|
fwidth := it.fwidths[i]
|
||||||
|
precision := it.precisions[i]
|
||||||
g.write('\${')
|
g.write('\${')
|
||||||
if sfmt.len > 0 {
|
if fmt != `_` || fwidth !=0 || precision != 0 {
|
||||||
// TODO: Handle formatting
|
// TODO: Handle formatting
|
||||||
g.expr(expr)
|
g.expr(expr)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import v.errors
|
||||||
import os
|
import os
|
||||||
import runtime
|
import runtime
|
||||||
import time
|
import time
|
||||||
|
import strconv
|
||||||
|
|
||||||
pub struct Parser {
|
pub struct Parser {
|
||||||
file_name string // "/home/user/hello.v"
|
file_name string // "/home/user/hello.v"
|
||||||
|
@ -1166,7 +1167,13 @@ fn (mut p Parser) string_expr() ast.Expr {
|
||||||
}
|
}
|
||||||
mut exprs := []ast.Expr{}
|
mut exprs := []ast.Expr{}
|
||||||
mut vals := []string{}
|
mut vals := []string{}
|
||||||
mut efmts := []string{}
|
mut has_fmts := []bool{}
|
||||||
|
mut fwidths := []int{}
|
||||||
|
mut precisions := []int{}
|
||||||
|
mut visible_pluss := []bool{}
|
||||||
|
mut fills := []bool{}
|
||||||
|
mut fmts := []byte{}
|
||||||
|
mut fposs := []token.Position{}
|
||||||
// Handle $ interpolation
|
// Handle $ interpolation
|
||||||
p.inside_str_interp = true
|
p.inside_str_interp = true
|
||||||
for p.tok.kind == .string {
|
for p.tok.kind == .string {
|
||||||
|
@ -1177,31 +1184,66 @@ fn (mut p Parser) string_expr() ast.Expr {
|
||||||
}
|
}
|
||||||
p.next()
|
p.next()
|
||||||
exprs << p.expr(0)
|
exprs << p.expr(0)
|
||||||
mut efmt := []string{}
|
mut has_fmt := false
|
||||||
|
mut fwidth := 0
|
||||||
|
mut fwidthneg := false
|
||||||
|
mut precision := 0
|
||||||
|
mut visible_plus := false
|
||||||
|
mut fill := false
|
||||||
|
mut fmt := `_` // placeholder
|
||||||
if p.tok.kind == .colon {
|
if p.tok.kind == .colon {
|
||||||
efmt << ':'
|
has_fmt = true
|
||||||
p.next()
|
p.next()
|
||||||
// ${num:-2d}
|
// ${num:-2d}
|
||||||
if p.tok.kind == .minus {
|
if p.tok.kind == .minus {
|
||||||
efmt << '-'
|
fwidthneg = true
|
||||||
|
p.next()
|
||||||
|
} else if p.tok.kind == .plus {
|
||||||
|
visible_plus = true
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
// ${num:2d}
|
// ${num:2d}
|
||||||
if p.tok.kind == .number {
|
if p.tok.kind == .number {
|
||||||
efmt << p.tok.lit
|
fields := p.tok.lit.split('.')
|
||||||
|
if fields[0].len > 0 && fields[0][0] == `0` {
|
||||||
|
fill = true
|
||||||
|
}
|
||||||
|
fwidth = strconv.atoi(fields[0])
|
||||||
|
if fwidthneg {
|
||||||
|
fwidth = -fwidth
|
||||||
|
}
|
||||||
|
if fields.len > 1 {
|
||||||
|
precision = strconv.atoi(fields[1])
|
||||||
|
}
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
if p.tok.kind == .name && p.tok.lit.len == 1 {
|
if p.tok.kind == .name {
|
||||||
efmt << p.tok.lit
|
if p.tok.lit.len == 1 {
|
||||||
p.next()
|
fmt = p.tok.lit[0]
|
||||||
|
p.next()
|
||||||
|
} else {
|
||||||
|
p.error('format specifier may only be one letter')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
efmts << efmt.join('')
|
fwidths << fwidth
|
||||||
|
has_fmts << has_fmt
|
||||||
|
precisions << precision
|
||||||
|
visible_pluss << visible_plus
|
||||||
|
fmts << fmt
|
||||||
|
fills << fill
|
||||||
|
fposs << p.prev_tok.position()
|
||||||
}
|
}
|
||||||
node = ast.StringInterLiteral{
|
node = ast.StringInterLiteral{
|
||||||
vals: vals
|
vals: vals
|
||||||
exprs: exprs
|
exprs: exprs
|
||||||
expr_fmts: efmts
|
need_fmts: has_fmts // prelimery - until checker finds out if really needed
|
||||||
|
fwidths: fwidths
|
||||||
|
precisions: precisions
|
||||||
|
pluss: visible_pluss
|
||||||
|
fills: fills
|
||||||
|
fmts: fmts
|
||||||
|
fmt_poss: fposs
|
||||||
pos: pos
|
pos: pos
|
||||||
}
|
}
|
||||||
p.inside_str_interp = false
|
p.inside_str_interp = false
|
||||||
|
|
|
@ -187,6 +187,11 @@ pub fn (typ Type) is_number() bool {
|
||||||
return typ.idx() in number_type_idxs
|
return typ.idx() in number_type_idxs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[inline]
|
||||||
|
pub fn (typ Type) is_string() bool {
|
||||||
|
return typ.idx() in string_type_idxs
|
||||||
|
}
|
||||||
|
|
||||||
pub const (
|
pub const (
|
||||||
void_type_idx = 1
|
void_type_idx = 1
|
||||||
voidptr_type_idx = 2
|
voidptr_type_idx = 2
|
||||||
|
|
Loading…
Reference in New Issue