fmt: further fixes for string interpolation and builtin macros
parent
f526754535
commit
3b6e66db0d
|
@ -185,7 +185,7 @@ fn html_highlight(code string, tb &table.Table) string {
|
|||
} else { tok.lit }
|
||||
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 next_tok := s.scan()
|
||||
mut buf := strings.new_builder(200)
|
||||
|
|
|
@ -140,7 +140,7 @@ fn main() {
|
|||
|
||||
fn (foptions &FormatOptions) format_file(file string) {
|
||||
mut prefs := pref.new_preferences()
|
||||
prefs.is_fmt = util.is_fmt()
|
||||
prefs.is_fmt = true
|
||||
if foptions.is_verbose {
|
||||
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
|
||||
// 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:
|
||||
// '$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
|
||||
|
@ -121,7 +122,7 @@ pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) {
|
|||
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 == `.` {
|
||||
if util.is_func_char(next_char) || next_char == `.` || next_char == `(` {
|
||||
needs_braces = true
|
||||
}
|
||||
}
|
||||
|
@ -131,6 +132,15 @@ pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) {
|
|||
for {
|
||||
match sub_expr as sx {
|
||||
Ident {
|
||||
if sx.name[0] == `@` {
|
||||
needs_braces = true
|
||||
}
|
||||
break
|
||||
}
|
||||
CallExpr {
|
||||
if sx.args.len != 0 {
|
||||
needs_braces = true
|
||||
}
|
||||
break
|
||||
}
|
||||
SelectorExpr {
|
||||
|
|
|
@ -5,7 +5,6 @@ module fmt
|
|||
|
||||
import v.ast
|
||||
import v.table
|
||||
import v.util
|
||||
import strings
|
||||
|
||||
const (
|
||||
|
@ -29,6 +28,7 @@ pub mut:
|
|||
file ast.File
|
||||
did_imports bool
|
||||
is_assign bool
|
||||
is_inside_interp bool
|
||||
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
|
||||
used_imports []string // to remove unused imports
|
||||
|
@ -619,8 +619,12 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
|||
}
|
||||
ast.InfixExpr {
|
||||
f.expr(it.left)
|
||||
f.write(' $it.op.str() ')
|
||||
f.wrap_long_line()
|
||||
if f.is_inside_interp {
|
||||
f.write('$it.op.str()')
|
||||
} else {
|
||||
f.write(' $it.op.str() ')
|
||||
f.wrap_long_line()
|
||||
}
|
||||
f.expr(it.right)
|
||||
}
|
||||
ast.IndexExpr {
|
||||
|
@ -717,7 +721,19 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
|||
}
|
||||
ast.StringInterLiteral {
|
||||
// TODO: this code is very similar to ast.Expr.str()
|
||||
f.write("'")
|
||||
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.is_inside_interp = true
|
||||
for i, val in it.vals {
|
||||
f.write(val)
|
||||
if i >= it.exprs.len {
|
||||
|
@ -734,7 +750,12 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
|||
f.expr(it.exprs[i])
|
||||
}
|
||||
}
|
||||
f.write("'")
|
||||
f.is_inside_interp = false
|
||||
if contains_single_quote {
|
||||
f.write('"')
|
||||
} else {
|
||||
f.write("'")
|
||||
}
|
||||
}
|
||||
ast.StructInit {
|
||||
f.struct_init(it)
|
||||
|
@ -782,7 +803,11 @@ pub fn (mut f Fmt) call_args(args []ast.CallArg) {
|
|||
}
|
||||
f.expr(arg.expr)
|
||||
if i < args.len - 1 {
|
||||
f.write(', ')
|
||||
if f.is_inside_interp {
|
||||
f.write(',')
|
||||
} else {
|
||||
f.write(', ')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,8 +44,10 @@ fn test_fmt() {
|
|||
continue
|
||||
}
|
||||
table := table.new_table()
|
||||
file_ast := parser.parse_file(ipath, table, .parse_comments, &pref.Preferences{}, &ast.Scope{
|
||||
parent: 0
|
||||
file_ast := parser.parse_file(ipath, table, .parse_comments, &pref.Preferences{
|
||||
is_fmt: true
|
||||
}, &ast.Scope{
|
||||
parent: 0
|
||||
})
|
||||
result_ocontent := fmt.fmt(file_ast, table, false)
|
||||
if expected_ocontent != result_ocontent {
|
||||
|
|
|
@ -44,8 +44,10 @@ fn test_fmt() {
|
|||
continue
|
||||
}
|
||||
table := table.new_table()
|
||||
file_ast := parser.parse_file(ipath, table, .parse_comments, &pref.Preferences{}, &ast.Scope{
|
||||
parent: 0
|
||||
file_ast := parser.parse_file(ipath, table, .parse_comments, &pref.Preferences{
|
||||
is_fmt: true
|
||||
}, &ast.Scope{
|
||||
parent: 0
|
||||
})
|
||||
result_ocontent := fmt.fmt(file_ast, table, false)
|
||||
if expected_ocontent != result_ocontent {
|
||||
|
|
|
@ -43,8 +43,10 @@ fn test_vlib_fmt() {
|
|||
continue
|
||||
}
|
||||
table := table.new_table()
|
||||
file_ast := parser.parse_file(ipath, table, .parse_comments, &pref.Preferences{}, &ast.Scope{
|
||||
parent: 0
|
||||
file_ast := parser.parse_file(ipath, table, .parse_comments, &pref.Preferences{
|
||||
is_fmt: true
|
||||
}, &ast.Scope{
|
||||
parent: 0
|
||||
})
|
||||
result_ocontent := fmt.fmt(file_ast, table, false)
|
||||
if expected_ocontent != result_ocontent {
|
||||
|
|
|
@ -10,6 +10,14 @@ struct Cc {
|
|||
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() {
|
||||
st := Bb{Aa{5}}
|
||||
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}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('$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
|
||||
}
|
||||
|
||||
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() {
|
||||
st := Bb{Aa{5}}
|
||||
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}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('${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
|
||||
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{
|
||||
scanner: s
|
||||
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 {
|
||||
s := scanner.new_scanner(text, .skip_comments)
|
||||
s := scanner.new_scanner(text, .skip_comments, pref.is_fmt)
|
||||
mut p := Parser{
|
||||
scanner: s
|
||||
table: b_table
|
||||
|
@ -100,7 +100,7 @@ pub fn parse_file(path string, b_table &table.Table, comments_mode scanner.Comme
|
|||
// panic(err)
|
||||
// }
|
||||
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
|
||||
table: b_table
|
||||
file_name: path
|
||||
|
|
|
@ -94,7 +94,7 @@ pub enum CommentsMode {
|
|||
}
|
||||
|
||||
// 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) {
|
||||
verror("$file_path doesn't exist")
|
||||
}
|
||||
|
@ -102,20 +102,20 @@ pub fn new_scanner_file(file_path string, comments_mode CommentsMode) &Scanner {
|
|||
verror(err)
|
||||
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.file_path = file_path
|
||||
return s
|
||||
}
|
||||
|
||||
// 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{
|
||||
text: text
|
||||
is_print_line_on_error: true
|
||||
is_print_colored_error: true
|
||||
is_print_rel_paths_on_error: true
|
||||
is_fmt: util.is_fmt()
|
||||
is_fmt: is_fmt
|
||||
comments_mode: comments_mode
|
||||
}
|
||||
return s
|
||||
|
@ -883,6 +883,9 @@ fn (mut s Scanner) text_scan() token.Token {
|
|||
`@` {
|
||||
s.pos++
|
||||
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
|
||||
// @MOD => will be substituted with the name of the current V module
|
||||
// @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')
|
||||
}
|
||||
// ${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
|
||||
// so that s.pos points to $ at the next step
|
||||
s.pos -= 2
|
||||
break
|
||||
}
|
||||
// $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_inter_start = true
|
||||
s.pos -= 2
|
||||
|
|
|
@ -45,7 +45,7 @@ fn fn_name_mod_level_high_order(cb fn(int)) {
|
|||
}
|
||||
|
||||
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{}
|
||||
for {
|
||||
tok := scanner.scan()
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
module util
|
||||
|
||||
import os
|
||||
|
||||
[inline]
|
||||
pub fn is_name_char(c byte) bool {
|
||||
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 {
|
||||
return s.replace('\\', '\\\\')
|
||||
}
|
||||
|
||||
pub fn is_fmt() bool {
|
||||
return os.executable().contains('vfmt')
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue