mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
fmt: further fixes for string interpolation and builtin macros
This commit is contained in:
parent
f526754535
commit
3b6e66db0d
@ -185,7 +185,7 @@ fn html_highlight(code string, tb &table.Table) string {
|
|||||||
} else { tok.lit }
|
} else { tok.lit }
|
||||||
return if typ in [.unone, .name] { lit } else { '<span class="token $typ">$lit</span>' }
|
return if typ in [.unone, .name] { lit } else { '<span class="token $typ">$lit</span>' }
|
||||||
}
|
}
|
||||||
s := scanner.new_scanner(code, .parse_comments)
|
s := scanner.new_scanner(code, .parse_comments, false)
|
||||||
mut tok := s.scan()
|
mut tok := s.scan()
|
||||||
mut next_tok := s.scan()
|
mut next_tok := s.scan()
|
||||||
mut buf := strings.new_builder(200)
|
mut buf := strings.new_builder(200)
|
||||||
|
@ -140,7 +140,7 @@ fn main() {
|
|||||||
|
|
||||||
fn (foptions &FormatOptions) format_file(file string) {
|
fn (foptions &FormatOptions) format_file(file string) {
|
||||||
mut prefs := pref.new_preferences()
|
mut prefs := pref.new_preferences()
|
||||||
prefs.is_fmt = util.is_fmt()
|
prefs.is_fmt = true
|
||||||
if foptions.is_verbose {
|
if foptions.is_verbose {
|
||||||
eprintln('vfmt2 running fmt.fmt over file: $file')
|
eprintln('vfmt2 running fmt.fmt over file: $file')
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,8 @@ pub fn (x &InfixExpr) str() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Expressions in string interpolations may have to be put in braces if they
|
// 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
|
// are non-trivial, if they would interfere with the next character or if a
|
||||||
|
// format specification is given. In the latter case
|
||||||
// the format specifier must be appended, separated by a colon:
|
// 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}'
|
// '$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
|
// This method creates the format specifier (including the colon) or an empty
|
||||||
@ -121,7 +122,7 @@ pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) {
|
|||||||
if !needs_braces {
|
if !needs_braces {
|
||||||
if i+1 < lit.vals.len && lit.vals[i+1].len > 0 {
|
if i+1 < lit.vals.len && lit.vals[i+1].len > 0 {
|
||||||
next_char := lit.vals[i+1][0]
|
next_char := lit.vals[i+1][0]
|
||||||
if util.is_func_char(next_char) || next_char == `.` {
|
if util.is_func_char(next_char) || next_char == `.` || next_char == `(` {
|
||||||
needs_braces = true
|
needs_braces = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,6 +132,15 @@ pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) {
|
|||||||
for {
|
for {
|
||||||
match sub_expr as sx {
|
match sub_expr as sx {
|
||||||
Ident {
|
Ident {
|
||||||
|
if sx.name[0] == `@` {
|
||||||
|
needs_braces = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
CallExpr {
|
||||||
|
if sx.args.len != 0 {
|
||||||
|
needs_braces = true
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
SelectorExpr {
|
SelectorExpr {
|
||||||
|
@ -5,7 +5,6 @@ module fmt
|
|||||||
|
|
||||||
import v.ast
|
import v.ast
|
||||||
import v.table
|
import v.table
|
||||||
import v.util
|
|
||||||
import strings
|
import strings
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -29,6 +28,7 @@ pub mut:
|
|||||||
file ast.File
|
file ast.File
|
||||||
did_imports bool
|
did_imports bool
|
||||||
is_assign bool
|
is_assign bool
|
||||||
|
is_inside_interp bool
|
||||||
auto_imports []string // automatically inserted imports that the user forgot to specify
|
auto_imports []string // automatically inserted imports that the user forgot to specify
|
||||||
import_pos int // position of the imports in the resulting string for later autoimports insertion
|
import_pos int // position of the imports in the resulting string for later autoimports insertion
|
||||||
used_imports []string // to remove unused imports
|
used_imports []string // to remove unused imports
|
||||||
@ -619,8 +619,12 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
|||||||
}
|
}
|
||||||
ast.InfixExpr {
|
ast.InfixExpr {
|
||||||
f.expr(it.left)
|
f.expr(it.left)
|
||||||
|
if f.is_inside_interp {
|
||||||
|
f.write('$it.op.str()')
|
||||||
|
} else {
|
||||||
f.write(' $it.op.str() ')
|
f.write(' $it.op.str() ')
|
||||||
f.wrap_long_line()
|
f.wrap_long_line()
|
||||||
|
}
|
||||||
f.expr(it.right)
|
f.expr(it.right)
|
||||||
}
|
}
|
||||||
ast.IndexExpr {
|
ast.IndexExpr {
|
||||||
@ -717,7 +721,19 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
|||||||
}
|
}
|
||||||
ast.StringInterLiteral {
|
ast.StringInterLiteral {
|
||||||
// TODO: this code is very similar to ast.Expr.str()
|
// TODO: this code is very similar to ast.Expr.str()
|
||||||
|
mut contains_single_quote := false
|
||||||
|
for val in it.vals {
|
||||||
|
if val.contains("'") {
|
||||||
|
contains_single_quote = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if contains_single_quote {
|
||||||
|
f.write('"')
|
||||||
|
} else {
|
||||||
f.write("'")
|
f.write("'")
|
||||||
|
}
|
||||||
|
f.is_inside_interp = true
|
||||||
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 {
|
||||||
@ -734,8 +750,13 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
|||||||
f.expr(it.exprs[i])
|
f.expr(it.exprs[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
f.is_inside_interp = false
|
||||||
|
if contains_single_quote {
|
||||||
|
f.write('"')
|
||||||
|
} else {
|
||||||
f.write("'")
|
f.write("'")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ast.StructInit {
|
ast.StructInit {
|
||||||
f.struct_init(it)
|
f.struct_init(it)
|
||||||
}
|
}
|
||||||
@ -782,7 +803,11 @@ pub fn (mut f Fmt) call_args(args []ast.CallArg) {
|
|||||||
}
|
}
|
||||||
f.expr(arg.expr)
|
f.expr(arg.expr)
|
||||||
if i < args.len - 1 {
|
if i < args.len - 1 {
|
||||||
|
if f.is_inside_interp {
|
||||||
f.write(',')
|
f.write(',')
|
||||||
|
} else {
|
||||||
|
f.write(', ')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,9 @@ fn test_fmt() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
table := table.new_table()
|
table := table.new_table()
|
||||||
file_ast := parser.parse_file(ipath, table, .parse_comments, &pref.Preferences{}, &ast.Scope{
|
file_ast := parser.parse_file(ipath, table, .parse_comments, &pref.Preferences{
|
||||||
|
is_fmt: true
|
||||||
|
}, &ast.Scope{
|
||||||
parent: 0
|
parent: 0
|
||||||
})
|
})
|
||||||
result_ocontent := fmt.fmt(file_ast, table, false)
|
result_ocontent := fmt.fmt(file_ast, table, false)
|
||||||
|
@ -44,7 +44,9 @@ fn test_fmt() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
table := table.new_table()
|
table := table.new_table()
|
||||||
file_ast := parser.parse_file(ipath, table, .parse_comments, &pref.Preferences{}, &ast.Scope{
|
file_ast := parser.parse_file(ipath, table, .parse_comments, &pref.Preferences{
|
||||||
|
is_fmt: true
|
||||||
|
}, &ast.Scope{
|
||||||
parent: 0
|
parent: 0
|
||||||
})
|
})
|
||||||
result_ocontent := fmt.fmt(file_ast, table, false)
|
result_ocontent := fmt.fmt(file_ast, table, false)
|
||||||
|
@ -43,7 +43,9 @@ fn test_vlib_fmt() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
table := table.new_table()
|
table := table.new_table()
|
||||||
file_ast := parser.parse_file(ipath, table, .parse_comments, &pref.Preferences{}, &ast.Scope{
|
file_ast := parser.parse_file(ipath, table, .parse_comments, &pref.Preferences{
|
||||||
|
is_fmt: true
|
||||||
|
}, &ast.Scope{
|
||||||
parent: 0
|
parent: 0
|
||||||
})
|
})
|
||||||
result_ocontent := fmt.fmt(file_ast, table, false)
|
result_ocontent := fmt.fmt(file_ast, table, false)
|
||||||
|
@ -10,6 +10,14 @@ struct Cc {
|
|||||||
a []Aa
|
a []Aa
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (c &Cc) f() int {
|
||||||
|
return c.a[0].xy
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c &Cc) g(k, l int) int {
|
||||||
|
return c.a[k].xy + l
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
st := Bb{Aa{5}}
|
st := Bb{Aa{5}}
|
||||||
ar := Cc{[Aa{3}, Aa{-4}, Aa{12}]}
|
ar := Cc{[Aa{3}, Aa{-4}, Aa{12}]}
|
||||||
@ -19,4 +27,6 @@ fn main() {
|
|||||||
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('${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}')
|
println('${z:-5} ${z:+5.3} ${z:+09.3f} ${z:-7.2} ${z:+09} ${z:08.3f}')
|
||||||
|
println('$ar.f() ${ar.g(1,2)} ${ar.a}() ${z}(')
|
||||||
|
println('${z>12.3*z-3} ${@VEXE} ${4*5}')
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,14 @@ struct Cc {
|
|||||||
a []Aa
|
a []Aa
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (c &Cc) f() int {
|
||||||
|
return c.a[0].xy
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c &Cc) g(k, l int) int {
|
||||||
|
return c.a[k].xy+l
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
st := Bb{Aa{5}}
|
st := Bb{Aa{5}}
|
||||||
ar := Cc{[Aa{3}, Aa{-4}, Aa{12}]}
|
ar := Cc{[Aa{3}, Aa{-4}, Aa{12}]}
|
||||||
@ -19,4 +27,6 @@ fn main() {
|
|||||||
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('${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}')
|
println('${z:-5} ${z:+5.3} ${z:+09.3f} ${z:-07.2} ${z:+009} ${z:008.3f}')
|
||||||
|
println('${ar.f()} ${ar.g(1, 2)} ${ar.a}() ${z}(')
|
||||||
|
println('${z > 12.3 * z - 3} ${@VEXE} ${4 * 5}')
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ mut:
|
|||||||
|
|
||||||
// for tests
|
// for tests
|
||||||
pub fn parse_stmt(text string, table &table.Table, scope &ast.Scope) ast.Stmt {
|
pub fn parse_stmt(text string, table &table.Table, scope &ast.Scope) ast.Stmt {
|
||||||
s := scanner.new_scanner(text, .skip_comments)
|
s := scanner.new_scanner(text, .skip_comments, false)
|
||||||
mut p := Parser{
|
mut p := Parser{
|
||||||
scanner: s
|
scanner: s
|
||||||
table: table
|
table: table
|
||||||
@ -77,7 +77,7 @@ pub fn parse_stmt(text string, table &table.Table, scope &ast.Scope) ast.Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_text(text string, b_table &table.Table, pref &pref.Preferences, scope, global_scope &ast.Scope) ast.File {
|
pub fn parse_text(text string, b_table &table.Table, pref &pref.Preferences, scope, global_scope &ast.Scope) ast.File {
|
||||||
s := scanner.new_scanner(text, .skip_comments)
|
s := scanner.new_scanner(text, .skip_comments, pref.is_fmt)
|
||||||
mut p := Parser{
|
mut p := Parser{
|
||||||
scanner: s
|
scanner: s
|
||||||
table: b_table
|
table: b_table
|
||||||
@ -100,7 +100,7 @@ pub fn parse_file(path string, b_table &table.Table, comments_mode scanner.Comme
|
|||||||
// panic(err)
|
// panic(err)
|
||||||
// }
|
// }
|
||||||
mut p := Parser{
|
mut p := Parser{
|
||||||
scanner: scanner.new_scanner_file(path, comments_mode)
|
scanner: scanner.new_scanner_file(path, comments_mode, pref.is_fmt)
|
||||||
comments_mode: comments_mode
|
comments_mode: comments_mode
|
||||||
table: b_table
|
table: b_table
|
||||||
file_name: path
|
file_name: path
|
||||||
|
@ -94,7 +94,7 @@ pub enum CommentsMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// new scanner from file.
|
// new scanner from file.
|
||||||
pub fn new_scanner_file(file_path string, comments_mode CommentsMode) &Scanner {
|
pub fn new_scanner_file(file_path string, comments_mode CommentsMode, is_fmt bool) &Scanner {
|
||||||
if !os.exists(file_path) {
|
if !os.exists(file_path) {
|
||||||
verror("$file_path doesn't exist")
|
verror("$file_path doesn't exist")
|
||||||
}
|
}
|
||||||
@ -102,20 +102,20 @@ pub fn new_scanner_file(file_path string, comments_mode CommentsMode) &Scanner {
|
|||||||
verror(err)
|
verror(err)
|
||||||
return voidptr(0)
|
return voidptr(0)
|
||||||
}
|
}
|
||||||
mut s := new_scanner(raw_text, comments_mode) // .skip_comments)
|
mut s := new_scanner(raw_text, comments_mode, is_fmt) // .skip_comments)
|
||||||
// s.init_fmt()
|
// s.init_fmt()
|
||||||
s.file_path = file_path
|
s.file_path = file_path
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// new scanner from string.
|
// new scanner from string.
|
||||||
pub fn new_scanner(text string, comments_mode CommentsMode) &Scanner {
|
pub fn new_scanner(text string, comments_mode CommentsMode, is_fmt bool) &Scanner {
|
||||||
s := &Scanner{
|
s := &Scanner{
|
||||||
text: text
|
text: text
|
||||||
is_print_line_on_error: true
|
is_print_line_on_error: true
|
||||||
is_print_colored_error: true
|
is_print_colored_error: true
|
||||||
is_print_rel_paths_on_error: true
|
is_print_rel_paths_on_error: true
|
||||||
is_fmt: util.is_fmt()
|
is_fmt: is_fmt
|
||||||
comments_mode: comments_mode
|
comments_mode: comments_mode
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
@ -883,6 +883,9 @@ fn (mut s Scanner) text_scan() token.Token {
|
|||||||
`@` {
|
`@` {
|
||||||
s.pos++
|
s.pos++
|
||||||
name := s.ident_name()
|
name := s.ident_name()
|
||||||
|
if s.is_fmt {
|
||||||
|
return s.new_token(.name, '@' + name, name.len+1)
|
||||||
|
}
|
||||||
// @FN => will be substituted with the name of the current V function
|
// @FN => will be substituted with the name of the current V function
|
||||||
// @MOD => will be substituted with the name of the current V module
|
// @MOD => will be substituted with the name of the current V module
|
||||||
// @STRUCT => will be substituted with the name of the current V struct
|
// @STRUCT => will be substituted with the name of the current V struct
|
||||||
@ -1204,14 +1207,14 @@ fn (mut s Scanner) ident_string() string {
|
|||||||
s.error('0 character in a string literal')
|
s.error('0 character in a string literal')
|
||||||
}
|
}
|
||||||
// ${var} (ignore in vfmt mode)
|
// ${var} (ignore in vfmt mode)
|
||||||
if c == `{` && prevc == `$` && !is_raw && !s.is_fmt && s.count_symbol_before(s.pos - 2, slash) % 2 == 0 {
|
if c == `{` && prevc == `$` && !is_raw && s.count_symbol_before(s.pos - 2, slash) % 2 == 0 {
|
||||||
s.is_inside_string = true
|
s.is_inside_string = true
|
||||||
// so that s.pos points to $ at the next step
|
// so that s.pos points to $ at the next step
|
||||||
s.pos -= 2
|
s.pos -= 2
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// $var
|
// $var
|
||||||
if util.is_name_char(c) && prevc == `$` && !s.is_fmt && !is_raw && s.count_symbol_before(s.pos - 2, slash) % 2 == 0 {
|
if util.is_name_char(c) && prevc == `$` && !is_raw && s.count_symbol_before(s.pos - 2, slash) % 2 == 0 {
|
||||||
s.is_inside_string = true
|
s.is_inside_string = true
|
||||||
s.is_inter_start = true
|
s.is_inter_start = true
|
||||||
s.pos -= 2
|
s.pos -= 2
|
||||||
|
@ -45,7 +45,7 @@ fn fn_name_mod_level_high_order(cb fn(int)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn scan_kinds(text string) []token.Kind {
|
fn scan_kinds(text string) []token.Kind {
|
||||||
mut scanner := new_scanner(text, .skip_comments)
|
mut scanner := new_scanner(text, .skip_comments, false)
|
||||||
mut token_kinds := []token.Kind{}
|
mut token_kinds := []token.Kind{}
|
||||||
for {
|
for {
|
||||||
tok := scanner.scan()
|
tok := scanner.scan()
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
module util
|
module util
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
pub fn is_name_char(c byte) bool {
|
pub fn is_name_char(c byte) bool {
|
||||||
return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) || c == `_`
|
return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) || c == `_`
|
||||||
@ -43,7 +41,3 @@ pub fn good_type_name(s string) bool {
|
|||||||
pub fn cescaped_path(s string) string {
|
pub fn cescaped_path(s string) string {
|
||||||
return s.replace('\\', '\\\\')
|
return s.replace('\\', '\\\\')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_fmt() bool {
|
|
||||||
return os.executable().contains('vfmt')
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user