fmt: further improvements by taking precedence into account for subexpressions

pull/5483/head
Uwe Krüger 2020-06-24 15:19:30 +02:00 committed by GitHub
parent 9d0cc7912a
commit 0018e44102
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 78 additions and 22 deletions

View File

@ -5,6 +5,7 @@ module fmt
import v.ast import v.ast
import v.table import v.table
import v.token
import strings import strings
const ( const (
@ -25,9 +26,11 @@ pub mut:
indent int indent int
empty_line bool empty_line bool
line_len int line_len int
expr_recursion int buffering bool // expressions will be analyzed later by adjust_complete_line() before finally written
expr_bufs []string expr_bufs []string // and stored here in the meantime (expr_bufs.len-1 = penalties.len = precedences.len)
penalties []int penalties []int // how hard should it be to break line after each expression
precedences []int // operator/parenthese precedences for operator at end of each expression
par_level int // how many parentheses are put around the current expression
single_line_if bool single_line_if bool
cur_mod string cur_mod string
file ast.File file ast.File
@ -85,7 +88,7 @@ fn (mut f Fmt) find_comment(line_nr int) {
} }
*/ */
pub fn (mut f Fmt) write(s string) { pub fn (mut f Fmt) write(s string) {
if f.expr_recursion == 0 || f.is_inside_interp { if !f.buffering || f.is_inside_interp {
if f.indent > 0 && f.empty_line { if f.indent > 0 && f.empty_line {
if f.indent < tabs.len { if f.indent < tabs.len {
f.out.write(tabs[f.indent]) f.out.write(tabs[f.indent])
@ -106,13 +109,13 @@ pub fn (mut f Fmt) write(s string) {
} }
pub fn (mut f Fmt) writeln(s string) { pub fn (mut f Fmt) writeln(s string) {
empty_fifo := f.expr_recursion > 0 empty_fifo := f.buffering
if empty_fifo { if empty_fifo {
f.write(s) f.write(s)
f.expr_bufs << f.out.str() f.expr_bufs << f.out.str()
f.out = f.out_save f.out = f.out_save
f.adjust_complete_line() f.adjust_complete_line()
f.expr_recursion = 0 f.buffering = false
for i, p in f.penalties { for i, p in f.penalties {
f.write(f.expr_bufs[i]) f.write(f.expr_bufs[i])
f.wrap_long_line(p, true) f.wrap_long_line(p, true)
@ -120,7 +123,7 @@ pub fn (mut f Fmt) writeln(s string) {
f.write(f.expr_bufs[f.expr_bufs.len - 1]) f.write(f.expr_bufs[f.expr_bufs.len - 1])
f.expr_bufs = []string{} f.expr_bufs = []string{}
f.penalties = []int{} f.penalties = []int{}
f.expr_recursion = 0 f.precedences = []int{}
} }
if f.indent > 0 && f.empty_line { if f.indent > 0 && f.empty_line {
// println(f.indent.str() + s) // println(f.indent.str() + s)
@ -139,13 +142,33 @@ pub fn (mut f Fmt) writeln(s string) {
// only prevents line breaks if everything fits in max_len[last] by increasing // only prevents line breaks if everything fits in max_len[last] by increasing
// penalties to maximum // penalties to maximum
fn (mut f Fmt) adjust_complete_line() { fn (mut f Fmt) adjust_complete_line() {
mut l := 0 for i, buf in f.expr_bufs {
for _, s in f.expr_bufs { // search for low penalties
l += s.len if i == 0 || f.penalties[i-1] <= 1 {
} precedence := if i == 0 { 0 } else { f.precedences[i-1] }
if f.line_len + l <= max_len[max_len.len - 1] { mut len_sub_expr := if i == 0 { buf.len + f.line_len } else { buf.len }
for i, _ in f.penalties { mut sub_expr_end_idx := f.penalties.len
f.penalties[i] = max_len.len - 1 // search for next position with low penalty and same precedence to form subexpression
for j in i..f.penalties.len {
if f.penalties[j] <= 1 && f.precedences[j] == precedence {
sub_expr_end_idx = j
break
} else {
len_sub_expr += f.expr_bufs[j+1].len
}
}
// if subexpression would fit in single line adjust penalties to actually do so
if len_sub_expr <= max_len[max_len.len-1] {
for j in i..sub_expr_end_idx {
f.penalties[j] = max_len.len-1
}
if i > 0 {
f.penalties[i-1] = 0
}
if sub_expr_end_idx < f.penalties.len {
f.penalties[sub_expr_end_idx] = 0
}
}
} }
} }
} }
@ -716,11 +739,11 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
f.write('$node.op.str()') f.write('$node.op.str()')
f.expr(node.right) f.expr(node.right)
} else { } else {
rec_save := f.expr_recursion buffering_save := f.buffering
if f.expr_recursion == 0 { if !f.buffering {
f.out_save = f.out f.out_save = f.out
f.out = strings.new_builder(60) f.out = strings.new_builder(60)
f.expr_recursion++ f.buffering = true
} }
f.expr(node.left) f.expr(node.left)
f.write(' $node.op.str() ') f.write(' $node.op.str() ')
@ -733,13 +756,15 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
penalty-- penalty--
} }
f.penalties << penalty f.penalties << penalty
// combine parentheses level with operator precedence to form effective precedence
f.precedences << int(token.precedences[node.op]) | (f.par_level << 16)
f.out = strings.new_builder(60) f.out = strings.new_builder(60)
f.expr_recursion++ f.buffering = true
f.expr(node.right) f.expr(node.right)
if rec_save == 0 && f.expr_recursion > 0 { // now decide if and where to break if !buffering_save && f.buffering { // now decide if and where to break
f.expr_bufs << f.out.str() f.expr_bufs << f.out.str()
f.out = f.out_save f.out = f.out_save
f.expr_recursion = 0 f.buffering = false
f.adjust_complete_line() f.adjust_complete_line()
for i, p in f.penalties { for i, p in f.penalties {
f.write(f.expr_bufs[i]) f.write(f.expr_bufs[i])
@ -748,6 +773,7 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
f.write(f.expr_bufs[f.expr_bufs.len - 1]) f.write(f.expr_bufs[f.expr_bufs.len - 1])
f.expr_bufs = []string{} f.expr_bufs = []string{}
f.penalties = []int{} f.penalties = []int{}
f.precedences = []int{}
} }
} }
} }
@ -802,7 +828,9 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
} }
ast.ParExpr { ast.ParExpr {
f.write('(') f.write('(')
f.par_level++
f.expr(node.expr) f.expr(node.expr)
f.par_level--
f.write(')') f.write(')')
} }
ast.PostfixExpr { ast.PostfixExpr {

View File

@ -2,6 +2,7 @@ import v.checker
import v.ast import v.ast
import v.table import v.table
import v.gen import v.gen
import v.token
fn string_inter_lit(mut c checker.Checker, mut node ast.StringInterLiteral) table.Type { fn string_inter_lit(mut c checker.Checker, mut node ast.StringInterLiteral) table.Type {
for i, expr in node.exprs { for i, expr in node.exprs {
@ -25,8 +26,7 @@ fn string_inter_lit(mut c checker.Checker, mut node ast.StringInterLiteral) tabl
(typ.is_float() && fmt !in [`E`, `F`, `G`, `e`, `f`, `g`]) || (typ.is_float() && fmt !in [`E`, `F`, `G`, `e`, `f`, `g`]) ||
(typ.is_pointer() && fmt !in [`p`, `x`, `X`]) || (typ.is_pointer() && fmt !in [`p`, `x`, `X`]) ||
(typ.is_string() && fmt != `s`) || (typ.is_string() && fmt != `s`) ||
(typ.idx() in [table.i64_type_idx, table.f64_type_idx] && (typ.idx() in [table.i64_type_idx, table.f64_type_idx] && fmt == `c`) {
fmt == `c`) {
c.error('illegal format specifier `${fmt:c}` for type `${c.table.get_type_name(ftyp)}`', c.error('illegal format specifier `${fmt:c}` for type `${c.table.get_type_name(ftyp)}`',
node.fmt_poss[i]) node.fmt_poss[i])
} }
@ -53,3 +53,16 @@ fn gen_str_for_multi_return(mut g gen.Gen, info table.MultiReturn, styp, str_fn_
println('\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, a.arg$i));') println('\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, a.arg$i));')
} }
} }
struct Parser {
peek_tok token.Token
peek_tok2 token.Token
peek_tok3 token.Token
}
fn (mut p Parser) name_expr() {
if p.peek_tok.kind == .lpar ||
(p.peek_tok.kind == .lt && p.peek_tok2.kind == .name && p.peek_tok3.kind == .gt) {
println(p.peek_tok.lit)
}
}

View File

@ -2,6 +2,7 @@ import v.checker
import v.ast import v.ast
import v.table import v.table
import v.gen import v.gen
import v.token
fn string_inter_lit(mut c checker.Checker, mut node ast.StringInterLiteral) table.Type { fn string_inter_lit(mut c checker.Checker, mut node ast.StringInterLiteral) table.Type {
for i, expr in node.exprs { for i, expr in node.exprs {
@ -63,3 +64,17 @@ for i, _ in info.types {
println('\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, a.arg$i));') println('\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, a.arg$i));')
} }
} }
struct Parser {
peek_tok token.Token
peek_tok2 token.Token
peek_tok3 token.Token
}
fn (mut p Parser) name_expr() {
if p.peek_tok.kind ==
.lpar || (p.peek_tok.kind == .lt && p.peek_tok2.kind == .name &&
p.peek_tok3.kind == .gt) {
println(p.peek_tok.lit)
}
}