diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 3454deb6ad..b4a381fa27 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -5,6 +5,7 @@ module fmt import v.ast import v.table +import v.token import strings const ( @@ -25,9 +26,11 @@ pub mut: indent int empty_line bool line_len int - expr_recursion int - expr_bufs []string - penalties []int + buffering bool // expressions will be analyzed later by adjust_complete_line() before finally written + expr_bufs []string // and stored here in the meantime (expr_bufs.len-1 = penalties.len = precedences.len) + 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 cur_mod string file ast.File @@ -85,7 +88,7 @@ fn (mut f Fmt) find_comment(line_nr int) { } */ 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 < tabs.len { 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) { - empty_fifo := f.expr_recursion > 0 + empty_fifo := f.buffering if empty_fifo { f.write(s) f.expr_bufs << f.out.str() f.out = f.out_save f.adjust_complete_line() - f.expr_recursion = 0 + f.buffering = false for i, p in f.penalties { f.write(f.expr_bufs[i]) 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.expr_bufs = []string{} f.penalties = []int{} - f.expr_recursion = 0 + f.precedences = []int{} } if f.indent > 0 && f.empty_line { // 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 // penalties to maximum fn (mut f Fmt) adjust_complete_line() { - mut l := 0 - for _, s in f.expr_bufs { - l += s.len - } - if f.line_len + l <= max_len[max_len.len - 1] { - for i, _ in f.penalties { - f.penalties[i] = max_len.len - 1 + for i, buf in f.expr_bufs { + // search for low penalties + if i == 0 || f.penalties[i-1] <= 1 { + precedence := if i == 0 { 0 } else { f.precedences[i-1] } + mut len_sub_expr := if i == 0 { buf.len + f.line_len } else { buf.len } + mut sub_expr_end_idx := f.penalties.len + // 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.expr(node.right) } else { - rec_save := f.expr_recursion - if f.expr_recursion == 0 { + buffering_save := f.buffering + if !f.buffering { f.out_save = f.out f.out = strings.new_builder(60) - f.expr_recursion++ + f.buffering = true } f.expr(node.left) f.write(' $node.op.str() ') @@ -733,13 +756,15 @@ pub fn (mut f Fmt) expr(node ast.Expr) { 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.expr_recursion++ + f.buffering = true 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.out = f.out_save - f.expr_recursion = 0 + f.buffering = false f.adjust_complete_line() for i, p in f.penalties { 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.expr_bufs = []string{} f.penalties = []int{} + f.precedences = []int{} } } } @@ -802,7 +828,9 @@ pub fn (mut f Fmt) expr(node ast.Expr) { } ast.ParExpr { f.write('(') + f.par_level++ f.expr(node.expr) + f.par_level-- f.write(')') } ast.PostfixExpr { diff --git a/vlib/v/fmt/tests/expressions_expected.vv b/vlib/v/fmt/tests/expressions_expected.vv index 4500718956..12012374ab 100644 --- a/vlib/v/fmt/tests/expressions_expected.vv +++ b/vlib/v/fmt/tests/expressions_expected.vv @@ -2,6 +2,7 @@ import v.checker import v.ast import v.table import v.gen +import v.token fn string_inter_lit(mut c checker.Checker, mut node ast.StringInterLiteral) table.Type { 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_pointer() && fmt !in [`p`, `x`, `X`]) || (typ.is_string() && fmt != `s`) || - (typ.idx() in [table.i64_type_idx, table.f64_type_idx] && - fmt == `c`) { + (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]) } @@ -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));') } } + +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) + } +} diff --git a/vlib/v/fmt/tests/expressions_input.vv b/vlib/v/fmt/tests/expressions_input.vv index b9bd78d516..8ea6f5ab95 100644 --- a/vlib/v/fmt/tests/expressions_input.vv +++ b/vlib/v/fmt/tests/expressions_input.vv @@ -2,6 +2,7 @@ import v.checker import v.ast import v.table import v.gen +import v.token fn string_inter_lit(mut c checker.Checker, mut node ast.StringInterLiteral) table.Type { 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));') } } + +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) + } +}