v/vlib/v/fmt/fmt.v

583 lines
11 KiB
V
Raw Normal View History

2020-02-03 05:00:36 +01:00
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
2019-12-22 02:34:37 +01:00
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module fmt
2020-02-17 22:50:04 +01:00
import (
v.ast
v.table
strings
)
const (
2020-02-18 03:28:39 +01:00
tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t']
2020-02-17 22:50:04 +01:00
// tabs = ['', ' ', ' ', ' ', ' ']
2020-02-21 15:32:48 +01:00
max_len = 80
2020-02-17 22:50:04 +01:00
)
2019-12-22 02:34:37 +01:00
struct Fmt {
2020-02-21 16:48:37 +01:00
out strings.Builder
table &table.Table
2020-02-17 22:50:04 +01:00
mut:
2020-02-21 16:48:37 +01:00
indent int
empty_line bool
line_len int
single_line_if bool
2020-02-21 19:56:37 +01:00
cur_mod string
2020-02-17 22:50:04 +01:00
}
pub fn fmt(file ast.File, table &table.Table) string {
2020-02-17 22:50:04 +01:00
mut f := Fmt{
out: strings.new_builder(1000)
table: table
2020-02-18 22:35:14 +01:00
indent: 0
2020-02-17 22:50:04 +01:00
}
2020-02-19 16:12:39 +01:00
f.mod(file.mod)
f.imports(file.imports)
for stmt in file.stmts {
f.stmt(stmt)
}
return f.out.str().trim_space() + '\n'
2020-02-17 22:50:04 +01:00
}
pub fn (f mut Fmt) write(s string) {
if f.indent > 0 && f.empty_line {
f.out.write(tabs[f.indent])
2020-02-22 16:59:50 +01:00
f.line_len += f.indent * 4
2020-02-17 22:50:04 +01:00
}
f.out.write(s)
2020-02-21 15:32:48 +01:00
f.line_len += s.len
2020-02-18 03:28:39 +01:00
f.empty_line = false
2020-02-17 22:50:04 +01:00
}
pub fn (f mut Fmt) writeln(s string) {
2020-02-18 03:28:39 +01:00
if f.indent > 0 && f.empty_line {
// println(f.indent.str() + s)
2020-02-17 22:50:04 +01:00
f.out.write(tabs[f.indent])
}
f.out.writeln(s)
f.empty_line = true
2020-02-21 15:32:48 +01:00
f.line_len = 0
2020-02-17 22:50:04 +01:00
}
2020-02-18 22:35:14 +01:00
fn (f mut Fmt) mod(mod ast.Module) {
if mod.name != 'main' {
f.writeln('module ${mod.name}\n')
}
2020-02-21 19:56:37 +01:00
f.cur_mod = mod.name
2020-02-18 22:35:14 +01:00
}
fn (f mut Fmt) imports(imports []ast.Import) {
if imports.len == 1 {
imp_stmt_str := f.imp_stmt_str(imports[0])
f.writeln('import ${imp_stmt_str}\n')
2020-02-19 16:12:39 +01:00
}
else if imports.len > 1 {
2020-02-18 22:35:14 +01:00
f.writeln('import (')
f.indent++
for imp in imports {
f.writeln(f.imp_stmt_str(imp))
}
f.indent--
f.writeln(')\n')
}
}
fn (f Fmt) imp_stmt_str(imp ast.Import) string {
2020-02-21 20:12:55 +01:00
is_diff := imp.alias != imp.mod && !imp.mod.ends_with('.' + imp.alias)
imp_alias_suffix := if is_diff { ' as ${imp.alias}' } else { '' }
2020-02-18 22:35:14 +01:00
return '${imp.mod}${imp_alias_suffix}'
}
2020-02-17 22:50:04 +01:00
fn (f mut Fmt) stmts(stmts []ast.Stmt) {
f.indent++
for stmt in stmts {
f.stmt(stmt)
}
f.indent--
}
fn (f mut Fmt) stmt(node ast.Stmt) {
match node {
ast.AssignStmt {
for i,ident in it.left {
var_info := ident.var_info()
if var_info.is_mut {
2020-02-28 14:41:19 +01:00
f.write('mut ')
}
f.expr(ident)
if i < it.left.len-1 {
2020-02-17 22:50:04 +01:00
f.write(', ')
}
}
f.write(' $it.op.str() ')
for i,val in it.right {
f.expr(val)
if i < it.right.len-1 {
f.write(', ')
}
2020-02-17 22:50:04 +01:00
}
f.writeln('')
2020-02-17 22:50:04 +01:00
}
2020-02-24 17:18:14 +01:00
ast.Attr {
f.writeln('[$it.name]')
}
2020-02-18 03:28:39 +01:00
ast.BranchStmt {
match it.tok.kind {
.key_break {
f.writeln('break')
}
.key_continue {
f.writeln('continue')
}
else {}
}
2020-02-18 03:28:39 +01:00
}
2020-02-17 22:50:04 +01:00
ast.ConstDecl {
if it.is_pub {
f.write('pub ')
}
2020-02-17 22:50:04 +01:00
f.writeln('const (')
f.indent++
for i, field in it.fields {
2020-02-22 16:59:50 +01:00
name := field.name.after('.')
f.write('$name = ')
2020-02-17 22:50:04 +01:00
f.expr(it.exprs[i])
2020-02-21 20:12:55 +01:00
f.writeln('')
2020-02-17 22:50:04 +01:00
}
f.indent--
2020-02-21 22:56:44 +01:00
f.writeln(')\n')
2020-02-17 22:50:04 +01:00
}
2020-02-26 20:44:42 +01:00
ast.DeferStmt {
f.writeln('defer {')
f.stmts(it.stmts)
f.writeln('}')
}
2020-02-28 17:21:20 +01:00
ast.EnumDecl {
if it.is_pub {
f.write('pub ')
}
2020-02-28 17:21:20 +01:00
f.writeln('enum $it.name {')
for val in it.vals {
f.writeln('\t' + val)
}
f.writeln('}\n')
}
2020-02-17 22:50:04 +01:00
ast.ExprStmt {
f.expr(it.expr)
2020-02-21 16:48:37 +01:00
if !f.single_line_if {
f.writeln('')
}
2020-02-17 22:50:04 +01:00
}
ast.FnDecl {
2020-02-28 17:21:20 +01:00
s := it.str(f.table)
// f.write(it.str(f.table))
f.write(s.replace(f.cur_mod + '.', '')) // `Expr` instead of `ast.Expr` in mod ast
2020-02-18 20:20:15 +01:00
f.writeln(' {')
2020-02-17 22:50:04 +01:00
f.stmts(it.stmts)
f.writeln('}\n')
}
2020-02-22 14:39:25 +01:00
ast.ForInStmt {
f.write('for $it.key_var')
if it.val_var != '' {
f.write(', $it.val_var')
}
f.write(' in ')
2020-02-22 16:59:50 +01:00
f.expr(it.cond)
if it.is_range {
f.write(' .. ')
f.expr(it.high)
}
2020-02-22 16:59:50 +01:00
f.writeln(' {')
2020-02-22 14:39:25 +01:00
f.stmts(it.stmts)
f.writeln('}')
}
2020-02-18 03:28:39 +01:00
ast.ForStmt {
f.write('for ')
f.expr(it.cond)
f.writeln(' {')
f.stmts(it.stmts)
f.writeln('}')
}
2020-03-02 17:41:32 +01:00
ast.GotoLabel {
f.writeln('$it.name:')
}
ast.GotoStmt {
f.writeln('goto $it.name')
}
2020-02-29 17:51:35 +01:00
ast.LineComment {
f.writeln('// $it.text')
}
2020-02-17 22:50:04 +01:00
ast.Return {
f.write('return')
// multiple returns
if it.exprs.len > 1 {
2020-02-18 03:28:39 +01:00
f.write(' ')
2020-02-17 22:50:04 +01:00
for i, expr in it.exprs {
f.expr(expr)
if i < it.exprs.len - 1 {
f.write(', ')
}
}
}
// normal return
else if it.exprs.len == 1 {
f.write(' ')
f.expr(it.exprs[0])
}
f.writeln('')
}
ast.StructDecl {
f.struct_decl(it)
}
2020-02-26 20:45:03 +01:00
ast.UnsafeStmt {
f.writeln('unsafe {')
f.stmts(it.stmts)
f.writeln('}')
}
ast.Import {
// already handled in f.imports
}
ast.TypeDecl {
f.type_decl( it )
}
2020-02-17 22:50:04 +01:00
else {
eprintln('fmt stmt: unknown node: ' + typeof(node))
2020-02-17 22:50:04 +01:00
// exit(1)
}
}
}
fn (f mut Fmt) type_decl(node ast.TypeDecl) {
match node {
ast.AliasTypeDecl {
if it.is_pub {
f.write('pub ')
}
ptype := f.table.type_to_str( it.parent_type )
f.write('type $it.name $ptype')
}
ast.SumTypeDecl {
if it.is_pub {
f.write('pub ')
}
f.write('type $it.name = ')
mut sum_type_names := []string
for t in it.sub_types {
sum_type_names << f.table.type_to_str(t)
}
f.write( sum_type_names.join(' | ') )
}
else {
eprintln('fmt type_decl: unknown ' + typeof(node))
}
}
f.writeln('\n')
}
2020-02-17 22:50:04 +01:00
fn (f mut Fmt) struct_decl(node ast.StructDecl) {
2020-02-29 17:51:35 +01:00
if node.is_pub {
f.write('pub ')
}
2020-02-17 22:50:04 +01:00
f.writeln('struct $node.name {')
mut max := 0
for field in node.fields {
if field.name.len > max {
max = field.name.len
}
}
2020-02-22 14:13:19 +01:00
for i, field in node.fields {
if i == node.mut_pos {
f.writeln('mut:')
}
else if i == node.pub_pos {
f.writeln('pub:')
}
else if i == node.pub_mut_pos {
f.writeln('pub mut:')
}
2020-02-17 22:50:04 +01:00
f.write('\t$field.name ')
f.write(strings.repeat(` `, max - field.name.len))
2020-02-21 19:56:37 +01:00
f.writeln(f.type_to_str(field.typ))
2020-02-17 22:50:04 +01:00
}
f.writeln('}\n')
}
2020-02-21 19:56:37 +01:00
fn (f &Fmt) type_to_str(t table.Type) string {
res := f.table.type_to_str(t)
return res.replace(f.cur_mod + '.', '')
}
2020-02-17 22:50:04 +01:00
fn (f mut Fmt) expr(node ast.Expr) {
match node {
ast.ArrayInit {
2020-02-27 21:00:33 +01:00
// `x := []string`
if it.exprs.len == 0 && it.typ != 0 {
f.write(f.table.type_to_str(it.typ))
}
// `[1,2,3]`
else {
// type_sym := f.table.get_type_symbol(it.typ)
f.write('[')
for i, expr in it.exprs {
if i > 0 && it.exprs.len > 1 {
f.wrap_long_line()
}
f.expr(expr)
if i < it.exprs.len - 1 {
f.write(', ')
}
2020-02-17 22:50:04 +01:00
}
2020-02-27 21:00:33 +01:00
f.write(']')
2020-02-17 22:50:04 +01:00
}
}
2020-03-06 22:12:15 +01:00
ast.AsCast {
type_str := f.table.type_to_str(it.typ)
f.expr(it.expr)
f.write(' as $type_str')
}
2020-02-17 22:50:04 +01:00
ast.AssignExpr {
f.expr(it.left)
f.write(' $it.op.str() ')
f.expr(it.val)
}
2020-02-22 14:39:25 +01:00
ast.Assoc {
f.writeln('{')
// f.indent++
2020-02-29 18:07:29 +01:00
f.writeln('\t$it.var_name |')
2020-02-22 14:39:25 +01:00
// TODO StructInit copy pasta
for i, field in it.fields {
f.write('\t$field: ')
f.expr(it.exprs[i])
f.writeln('')
}
// f.indent--
f.write('}')
}
2020-02-17 22:50:04 +01:00
ast.BoolLiteral {
f.write(it.val.str())
}
ast.CastExpr {
f.write(f.table.type_to_str(it.typ) + '(')
f.expr(it.expr)
f.write(')')
}
2020-02-17 22:50:04 +01:00
ast.CallExpr {
f.write('${it.name}(')
f.call_args(it.args)
2020-02-17 22:50:04 +01:00
f.write(')')
2020-03-02 17:09:45 +01:00
f.or_expr(it.or_block)
2020-02-17 22:50:04 +01:00
}
2020-02-27 21:51:40 +01:00
ast.CharLiteral {
f.write('`$it.val`')
}
2020-02-18 03:28:39 +01:00
ast.EnumVal {
2020-02-25 19:59:47 +01:00
f.write(it.enum_name + '.' + it.val)
2020-02-18 03:28:39 +01:00
}
2020-02-17 22:50:04 +01:00
ast.FloatLiteral {
f.write(it.val)
}
ast.IfExpr {
single_line := it.branches.len == 2 && it.has_else //
&& it.branches[0].stmts.len == 1 && it.branches[1].stmts.len == 1
2020-02-21 16:48:37 +01:00
f.single_line_if = single_line
for i, branch in it.branches {
if i == 0 {
f.write('if ')
f.expr(branch.cond)
f.write(' {')
}
else if i < it.branches.len-1 || !it.has_else {
f.write('} else if ')
f.expr(branch.cond)
f.write(' {')
}
else if i == it.branches.len-1 && it.has_else {
f.write('} else {')
}
2020-02-21 16:48:37 +01:00
if single_line {
f.write(' ')
}
else {
f.writeln('')
}
f.stmts(branch.stmts)
2020-02-21 16:48:37 +01:00
if single_line {
f.write(' ')
}
2020-02-17 22:50:04 +01:00
}
f.write('}')
2020-02-21 16:48:37 +01:00
f.single_line_if = false
2020-02-17 22:50:04 +01:00
}
ast.Ident {
2020-02-27 21:51:40 +01:00
if it.kind == .blank_ident {
f.write('_')
}
else {
f.write('$it.name')
}
2020-02-17 22:50:04 +01:00
}
ast.InfixExpr {
f.expr(it.left)
f.write(' $it.op.str() ')
2020-02-21 16:14:15 +01:00
f.wrap_long_line()
2020-02-17 22:50:04 +01:00
f.expr(it.right)
}
ast.IndexExpr {
2020-02-29 17:45:08 +01:00
f.expr(it.left)
f.write('[')
f.expr(it.index)
f.write(']')
2020-02-17 22:50:04 +01:00
}
ast.IntegerLiteral {
f.write(it.val)
2020-02-17 22:50:04 +01:00
}
2020-02-22 14:13:19 +01:00
ast.MapInit {
f.writeln('{')
f.indent++
/*
mut max := 0
for i, key in it.keys {
if key.len > max {
max = key.len
}
}
*/
for i, key in it.keys {
f.expr(key)
// f.write(strings.repeat(` `, max - field.name.len))
f.write(': ')
f.expr(it.vals[i])
f.writeln('')
}
f.indent--
f.write('}')
}
2020-02-29 20:43:15 +01:00
ast.MatchExpr {
f.write('match ')
f.expr(it.cond)
f.writeln(' {')
f.indent++
2020-03-04 12:14:37 +01:00
for i, branch in it.branches {
// normal branch
if i < it.branches.len - 1 {
for j,expr in branch.exprs {
f.expr(expr)
if j < branch.exprs.len - 1 {
f.write(', ')
}
}
}
// else branch
else {
2020-03-04 12:39:18 +01:00
f.write('else')
}
if (branch.stmts.len == 0) {
f.writeln(' {}')
} else {
f.writeln(' {')
f.stmts(branch.stmts)
f.writeln('}')
2020-03-04 12:14:37 +01:00
}
2020-02-29 20:43:15 +01:00
}
f.indent--
f.write('}')
}
2020-02-18 03:28:39 +01:00
ast.MethodCallExpr {
f.expr(it.expr)
f.write('.' + it.name + '(')
f.call_args(it.args)
2020-02-18 03:28:39 +01:00
f.write(')')
2020-03-02 17:09:45 +01:00
f.or_expr(it.or_block)
2020-02-18 03:28:39 +01:00
}
2020-02-21 17:52:20 +01:00
ast.None {
f.write('none')
}
ast.IfGuardExpr {
f.write(it.var_name + ' := ')
f.expr(it.expr)
}
ast.ParExpr {
2020-02-28 14:41:19 +01:00
f.write('(')
f.expr(it.expr)
f.write(')')
}
2020-02-17 22:50:04 +01:00
ast.PostfixExpr {
f.expr(it.expr)
f.write(it.op.str())
}
ast.PrefixExpr {
f.write(it.op.str())
f.expr(it.right)
}
2020-02-29 17:45:08 +01:00
ast.RangeExpr {
f.expr(it.low)
f.write('..')
f.expr(it.high)
}
2020-02-17 22:50:04 +01:00
ast.SelectorExpr {
f.expr(it.expr)
f.write('.')
f.write(it.field)
}
ast.StringLiteral {
2020-02-18 03:28:39 +01:00
if it.val.contains("'") {
f.write('"$it.val"')
}
else {
f.write("'$it.val'")
}
2020-02-17 22:50:04 +01:00
}
ast.StructInit {
type_sym := f.table.get_type_symbol(it.typ)
2020-02-22 16:59:50 +01:00
// `Foo{}` on one line if there are no fields
if it.fields.len == 0 {
f.write('$type_sym.name{}')
}
else {
f.writeln('$type_sym.name{')
for i, field in it.fields {
f.write('\t$field: ')
f.expr(it.exprs[i])
f.writeln('')
}
f.write('}')
2020-02-17 22:50:04 +01:00
}
}
else {
eprintln('fmt expr: unhandled node ' + typeof(node))
}
2020-02-17 22:50:04 +01:00
}
}
2019-12-22 02:34:37 +01:00
2020-02-21 16:14:15 +01:00
fn (f mut Fmt) wrap_long_line() {
if f.line_len > max_len {
f.write('\n' + tabs[f.indent + 1])
f.line_len = 0
}
}
2020-03-02 17:09:45 +01:00
fn (f mut Fmt) call_args(args []ast.CallArg) {
2020-03-02 17:09:45 +01:00
for i, arg in args {
if arg.is_mut {
2020-03-02 17:09:45 +01:00
f.write('mut ')
}
if i > 0 {
f.wrap_long_line()
}
f.expr(arg.expr)
2020-03-02 17:09:45 +01:00
if i < args.len - 1 {
f.write(', ')
}
}
}
fn (f mut Fmt) or_expr(or_block ast.OrExpr) {
if or_block.stmts.len > 0 {
f.writeln(' or {')
f.stmts(or_block.stmts)
f.write('}')
}
}