github.com/NeowayLabs/nash@v0.2.2-0.20200127205349-a227041ffd50/ast/node_fmt.go (about)

     1  package ast
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  )
     8  
     9  func (s *StringExpr) String() string {
    10  	if s.quoted {
    11  		return `"` + stringify(s.str) + `"`
    12  	}
    13  
    14  	return s.str
    15  }
    16  
    17  func (i *IntExpr) String() string {
    18  	return strconv.Itoa(i.val)
    19  }
    20  
    21  func (l *ListExpr) string() (string, bool) {
    22  	elems := make([]string, len(l.List))
    23  	columnCount := 0
    24  	forceMulti := false
    25  
    26  	for i := 0; i < len(l.List); i++ {
    27  		if l.List[i].Type() == NodeListExpr {
    28  			forceMulti = true
    29  		}
    30  
    31  		elems[i] = l.List[i].String()
    32  		columnCount += len(elems[i])
    33  	}
    34  
    35  	if columnCount+len(elems) > 50 || forceMulti {
    36  		forceMulti = true
    37  		return "(\n\t" + strings.Join(elems, "\n\t") + "\n)", forceMulti
    38  	}
    39  
    40  	return "(" + strings.Join(elems, " ") + ")", false
    41  }
    42  
    43  func (l *ListExpr) String() string {
    44  	str, _ := l.string()
    45  	return str
    46  }
    47  
    48  func (c *ConcatExpr) String() string {
    49  	ret := ""
    50  
    51  	for i := 0; i < len(c.concat); i++ {
    52  		ret += c.concat[i].String()
    53  
    54  		if i < (len(c.concat) - 1) {
    55  			ret += "+"
    56  		}
    57  	}
    58  
    59  	return ret
    60  }
    61  
    62  func (v *VarExpr) String() string {
    63  	if v.IsVariadic {
    64  		return v.Name + "..."
    65  	}
    66  	return v.Name
    67  }
    68  
    69  func (i *IndexExpr) String() string {
    70  	ret := fmt.Sprintf("%s[%s]", i.Var, i.Index)
    71  	if i.IsVariadic {
    72  		return ret + "..."
    73  	}
    74  	return ret
    75  }
    76  
    77  func (l *BlockNode) adjustGroupAssign(node assignable, nodes []Node) {
    78  	var (
    79  		eqSpace int = node.getEqSpace()
    80  		i       int
    81  	)
    82  
    83  	lhs := getlhs(node)
    84  
    85  	eqSpace = len(lhs) + 1
    86  
    87  	for i = 0; i < len(nodes); i++ {
    88  		assign, ok := nodes[i].(assignable)
    89  
    90  		if !ok {
    91  			break
    92  		}
    93  
    94  		if len(getlhs(assign))+1 > eqSpace {
    95  			eqSpace = len(getlhs(assign)) + 1
    96  		}
    97  	}
    98  
    99  	for j := 0; j < i; j++ {
   100  		knode := nodes[j].(assignable)
   101  		knode.setEqSpace(eqSpace)
   102  	}
   103  
   104  	node.setEqSpace(eqSpace)
   105  }
   106  
   107  func (l *BlockNode) String() string {
   108  	nodes := l.Nodes
   109  	content := make([]string, 0, 8192)
   110  
   111  	last := (len(nodes) - 1)
   112  
   113  	for i := 0; i < len(nodes); i++ {
   114  		addEOL := false
   115  		node := nodes[i]
   116  
   117  		nodebytes := node.String()
   118  
   119  		if i == 0 && node.Type() == NodeComment &&
   120  			strings.HasPrefix(node.String(), "#!") {
   121  			addEOL = true
   122  		} else if (node.Type() == NodeComment) && i < last {
   123  			nextNode := nodes[i+1]
   124  
   125  			if nextNode.Line() > node.Line()+1 {
   126  				addEOL = true
   127  			}
   128  		} else if i < last {
   129  			nextNode := nodes[i+1]
   130  
   131  			if node.Type() != nextNode.Type() {
   132  				addEOL = true
   133  			} else if node.Type() == NodeFnDecl {
   134  				addEOL = true
   135  			} else if node.Type() == NodeAssign || node.Type() == NodeExecAssign {
   136  				nodeAssign := node.(assignable)
   137  
   138  				if nodeAssign.getEqSpace() == -1 {
   139  					// lookahead to decide about best '=' distance
   140  					l.adjustGroupAssign(nodeAssign, nodes[i+1:])
   141  				}
   142  
   143  				nodebytes, addEOL = nodeAssign.string()
   144  			}
   145  		}
   146  
   147  		if addEOL {
   148  			nodebytes += "\n"
   149  		}
   150  
   151  		content = append(content, nodebytes)
   152  	}
   153  
   154  	return strings.Join(content, "\n")
   155  }
   156  
   157  // String returns the string representation of the import
   158  func (n *ImportNode) String() string {
   159  	return `import ` + n.Path.String()
   160  }
   161  
   162  // String returns the string representation of assignment
   163  func (n *SetenvNode) String() string {
   164  	if n.assign == nil {
   165  		return "setenv " + n.Name
   166  	}
   167  
   168  	return "setenv " + n.assign.String()
   169  }
   170  
   171  func (n *NameNode) String() string {
   172  	if n.Index != nil {
   173  		return n.Ident + "[" + n.Index.String() + "]"
   174  	}
   175  
   176  	return n.Ident
   177  }
   178  
   179  func (n *AssignNode) string() (string, bool) {
   180  	var (
   181  		multi bool
   182  	)
   183  
   184  	objs := n.Values
   185  	lhs := getlhs(n)
   186  
   187  	ret := ""
   188  
   189  	for i := 0; i < len(objs); i++ {
   190  		var (
   191  			objStr   string
   192  			objmulti bool
   193  		)
   194  
   195  		obj := objs[i]
   196  
   197  		if obj.Type().IsExpr() {
   198  			if obj.Type() == NodeListExpr {
   199  				lobj := obj.(*ListExpr)
   200  				objStr, objmulti = lobj.string()
   201  			} else {
   202  				objStr = obj.String()
   203  			}
   204  		}
   205  
   206  		if i == 0 {
   207  			if n.eqSpace > len(lhs) && !multi {
   208  				ret = lhs + strings.Repeat(" ", n.eqSpace-len(lhs)) + "= " + objStr
   209  			} else {
   210  				ret = lhs + " = " + objStr
   211  			}
   212  		} else if i < len(objs)-1 {
   213  			ret = ret + ", " + objStr + ", "
   214  		} else {
   215  			ret = ret + ", " + objStr
   216  		}
   217  
   218  		if objmulti && !multi {
   219  			multi = true
   220  		}
   221  	}
   222  
   223  	return ret, multi
   224  }
   225  
   226  // String returns the string representation of assignment statement
   227  func (n *AssignNode) String() string {
   228  	str, _ := n.string()
   229  	return str
   230  }
   231  
   232  func (n *ExecAssignNode) string() (string, bool) {
   233  	var (
   234  		cmdStr string
   235  		multi  bool
   236  	)
   237  
   238  	lhs := getlhs(n)
   239  
   240  	if n.cmd.Type() == NodeCommand {
   241  		cmd := n.cmd.(*CommandNode)
   242  		cmdStr, multi = cmd.string()
   243  	} else if n.cmd.Type() == NodePipe {
   244  		cmd := n.cmd.(*PipeNode)
   245  		cmdStr, multi = cmd.string()
   246  	} else {
   247  		cmd := n.cmd.(*FnInvNode)
   248  		cmdStr, multi = cmd.string()
   249  	}
   250  
   251  	if n.eqSpace > len(lhs) {
   252  		ret := lhs + strings.Repeat(" ", n.eqSpace-len(lhs)) + "<= " + cmdStr
   253  		return ret, multi
   254  	}
   255  
   256  	return lhs + " <= " + cmdStr, multi
   257  }
   258  
   259  // String returns the string representation of command assignment statement
   260  func (n *ExecAssignNode) String() string {
   261  	str, _ := n.string()
   262  	return str
   263  }
   264  
   265  func (n *CommandNode) toStringParts() ([]string, int) {
   266  	var (
   267  		content  []string
   268  		line     string
   269  		last     = len(n.args) - 1
   270  		totalLen = 0
   271  	)
   272  
   273  	for i := 0; i < len(n.args); i += 2 {
   274  		var next string
   275  
   276  		arg := n.args[i].String()
   277  
   278  		if i < last {
   279  			next = n.args[i+1].String()
   280  		}
   281  
   282  		if i == 0 {
   283  			arg = n.name + " " + arg
   284  		}
   285  
   286  		if arg[0] == '-' {
   287  			if line != "" {
   288  				content = append(content, line)
   289  				line = ""
   290  			}
   291  
   292  			if len(next) > 0 && next[0] != '-' {
   293  				if line == "" {
   294  					line += arg + " " + next
   295  				} else {
   296  					line += " " + arg + " " + next
   297  				}
   298  			} else {
   299  				content = append(content, arg, next)
   300  			}
   301  		} else if next != "" {
   302  			if line == "" {
   303  				line += arg + " " + next
   304  			} else {
   305  				line += " " + arg + " " + next
   306  			}
   307  		} else {
   308  			if line == "" {
   309  				line += arg
   310  			} else {
   311  				line += " " + arg
   312  			}
   313  		}
   314  
   315  		totalLen += len(arg) + len(next) + 1
   316  
   317  	}
   318  
   319  	if line != "" {
   320  		content = append(content, line)
   321  	}
   322  
   323  	if len(content) == 0 {
   324  		content = append(content, n.name)
   325  	}
   326  
   327  	for i := 0; i < len(n.redirs); i++ {
   328  		rstr := n.redirs[i].String()
   329  		totalLen += len(rstr) + 1
   330  		content = append(content, rstr)
   331  	}
   332  
   333  	return content, totalLen
   334  }
   335  
   336  func (n *CommandNode) multiString() string {
   337  	content, totalLen := n.toStringParts()
   338  
   339  	if totalLen < 50 {
   340  		return "(" + strings.Join(content, " ") + ")"
   341  	}
   342  
   343  	content[0] = "\t" + content[0]
   344  
   345  	gentab := func(n int) string { return strings.Repeat("\t", n) }
   346  	tabLen := (len(content[0]) + 7) / 8
   347  
   348  	for i := 1; i < len(content); i++ {
   349  		content[i] = gentab(tabLen) + content[i]
   350  	}
   351  
   352  	return "(\n" + strings.Join(content, "\n") + "\n)"
   353  }
   354  
   355  // String returns the string representation of command statement
   356  func (n *CommandNode) string() (string, bool) {
   357  	if n.multi {
   358  		return n.multiString(), true
   359  	}
   360  
   361  	var content []string
   362  
   363  	content = append(content, n.name)
   364  
   365  	for i := 0; i < len(n.args); i++ {
   366  		content = append(content, n.args[i].String())
   367  	}
   368  
   369  	for i := 0; i < len(n.redirs); i++ {
   370  		content = append(content, n.redirs[i].String())
   371  	}
   372  
   373  	return strings.Join(content, " "), false
   374  }
   375  
   376  func (n *CommandNode) String() string {
   377  	str, _ := n.string()
   378  	return str
   379  }
   380  
   381  func (n *PipeNode) multiString() string {
   382  	totalLen := 0
   383  
   384  	type cmdData struct {
   385  		content  []string
   386  		totalLen int
   387  	}
   388  
   389  	content := make([]cmdData, len(n.cmds))
   390  
   391  	for i := 0; i < len(n.cmds); i++ {
   392  		cmdContent, cmdLen := n.cmds[i].toStringParts()
   393  
   394  		content[i] = cmdData{
   395  			cmdContent,
   396  			cmdLen,
   397  		}
   398  
   399  		totalLen += cmdLen
   400  	}
   401  
   402  	if totalLen+3 < 50 {
   403  		result := "("
   404  
   405  		for i := 0; i < len(content); i++ {
   406  			result += strings.Join(content[i].content, " ")
   407  
   408  			if i < len(content)-1 {
   409  				result += " | "
   410  			}
   411  		}
   412  
   413  		return result + ")"
   414  	}
   415  
   416  	gentab := func(n int) string { return strings.Repeat("\t", n) }
   417  
   418  	result := "(\n"
   419  
   420  	for i := 0; i < len(content); i++ {
   421  		cmdContent := content[i].content
   422  
   423  		cmdContent[0] = "\t" + cmdContent[0]
   424  		tabLen := (len(cmdContent[0]) + 7) / 8
   425  
   426  		for j := 1; j < len(cmdContent); j++ {
   427  			cmdContent[j] = gentab(tabLen) + cmdContent[j]
   428  		}
   429  
   430  		result += strings.Join(cmdContent, "\n")
   431  
   432  		if i < len(content)-1 {
   433  			result += " |\n"
   434  		}
   435  	}
   436  
   437  	return result + "\n)"
   438  }
   439  
   440  // String returns the string representation of pipeline statement
   441  func (n *PipeNode) string() (string, bool) {
   442  	if n.multi {
   443  		return n.multiString(), true
   444  	}
   445  
   446  	ret := ""
   447  
   448  	for i := 0; i < len(n.cmds); i++ {
   449  		ret += n.cmds[i].String()
   450  
   451  		if i < (len(n.cmds) - 1) {
   452  			ret += " | "
   453  		}
   454  	}
   455  
   456  	return ret, false
   457  }
   458  
   459  func (n *PipeNode) String() string {
   460  	str, _ := n.string()
   461  	return str
   462  }
   463  
   464  // String returns the string representation of redirect
   465  func (r *RedirectNode) String() string {
   466  	var result string
   467  
   468  	if r.rmap.lfd == r.rmap.rfd {
   469  		if r.location != nil {
   470  			return "> " + r.location.String()
   471  		}
   472  
   473  		return ""
   474  	}
   475  
   476  	if r.rmap.rfd >= 0 {
   477  		result = ">[" + strconv.Itoa(r.rmap.lfd) + "=" + strconv.Itoa(r.rmap.rfd) + "]"
   478  	} else if r.rmap.rfd == RedirMapNoValue {
   479  		result = ">[" + strconv.Itoa(r.rmap.lfd) + "]"
   480  	} else if r.rmap.rfd == RedirMapSupress {
   481  		result = ">[" + strconv.Itoa(r.rmap.lfd) + "=]"
   482  	}
   483  
   484  	if r.location != nil {
   485  		result = result + " " + r.location.String()
   486  	}
   487  
   488  	return result
   489  }
   490  
   491  // String returns the string representation of rfork statement
   492  func (n *RforkNode) String() string {
   493  	rforkstr := "rfork " + n.arg.String()
   494  	tree := n.Tree()
   495  
   496  	if tree != nil {
   497  		rforkstr += " {\n"
   498  		block := tree.String()
   499  		stmts := strings.Split(block, "\n")
   500  
   501  		for i := 0; i < len(stmts); i++ {
   502  			stmts[i] = "\t" + stmts[i]
   503  		}
   504  
   505  		rforkstr += strings.Join(stmts, "\n") + "\n}"
   506  	}
   507  
   508  	return rforkstr
   509  }
   510  
   511  // String returns the string representation of comment
   512  func (n *CommentNode) String() string {
   513  	return n.val
   514  }
   515  
   516  // String returns the string representation of if statement
   517  func (n *IfNode) String() string {
   518  	var lstr, rstr string
   519  
   520  	lstr = n.lvalue.String()
   521  	rstr = n.rvalue.String()
   522  
   523  	ifStr := "if " + lstr + " " + n.op + " " + rstr + " {\n"
   524  
   525  	ifTree := n.IfTree()
   526  
   527  	block := ifTree.String()
   528  	stmts := strings.Split(block, "\n")
   529  
   530  	if strings.TrimSpace(block) != "" {
   531  		for i := 0; i < len(stmts); i++ {
   532  			stmts[i] = "\t" + stmts[i]
   533  		}
   534  	}
   535  
   536  	ifStr += strings.Join(stmts, "\n") + "\n}"
   537  
   538  	elseTree := n.ElseTree()
   539  
   540  	if elseTree != nil {
   541  		ifStr += " else "
   542  
   543  		elseBlock := elseTree.String()
   544  		elsestmts := strings.Split(elseBlock, "\n")
   545  
   546  		for i := 0; i < len(elsestmts); i++ {
   547  			if !n.IsElseIf() {
   548  				elsestmts[i] = "\t" + elsestmts[i]
   549  			}
   550  		}
   551  
   552  		if !n.IsElseIf() {
   553  			ifStr += "{\n"
   554  		}
   555  
   556  		ifStr += strings.Join(elsestmts, "\n")
   557  
   558  		if !n.IsElseIf() {
   559  			ifStr += "\n}"
   560  		}
   561  	}
   562  
   563  	return ifStr
   564  }
   565  
   566  func (n *VarAssignDeclNode) String() string     { return "var " + n.Assign.String() }
   567  func (n *VarExecAssignDeclNode) String() string { return "var " + n.ExecAssign.String() }
   568  
   569  // String returns the string representation of function declaration
   570  func (n *FnDeclNode) String() string {
   571  	fnStr := "fn"
   572  
   573  	if n.name != "" {
   574  		fnStr += " " + n.name + "("
   575  	}
   576  
   577  	for i := 0; i < len(n.args); i++ {
   578  		fnStr += n.args[i].String()
   579  		if i < (len(n.args) - 1) {
   580  			fnStr += ", "
   581  		}
   582  	}
   583  
   584  	fnStr += ") {\n"
   585  
   586  	tree := n.Tree()
   587  
   588  	stmts := strings.Split(tree.String(), "\n")
   589  
   590  	for i := 0; i < len(stmts); i++ {
   591  		if len(stmts[i]) > 0 {
   592  			fnStr += "\t" + stmts[i] + "\n"
   593  		} else {
   594  			fnStr += "\n"
   595  		}
   596  	}
   597  
   598  	fnStr += "}"
   599  
   600  	return fnStr
   601  }
   602  
   603  func (arg *FnArgNode) String() string {
   604  	ret := arg.Name
   605  	if arg.IsVariadic {
   606  		ret += "..."
   607  	}
   608  	return ret
   609  }
   610  
   611  // String returns the string representation of function invocation
   612  func (n *FnInvNode) string() (string, bool) {
   613  	fnInvStr := n.name + "("
   614  
   615  	for i := 0; i < len(n.args); i++ {
   616  		fnInvStr += n.args[i].String()
   617  
   618  		if i < (len(n.args) - 1) {
   619  			fnInvStr += ", "
   620  		}
   621  	}
   622  
   623  	fnInvStr += ")"
   624  
   625  	return fnInvStr, false
   626  }
   627  
   628  func (n *FnInvNode) String() string {
   629  	str, _ := n.string()
   630  	return str
   631  }
   632  
   633  // String returns the string representation of bindfn
   634  func (n *BindFnNode) String() string {
   635  	return "bindfn " + n.name + " " + n.cmdname
   636  }
   637  
   638  // String returns the string representation of return statement
   639  func (n *ReturnNode) String() string {
   640  	var returns []string
   641  
   642  	ret := "return"
   643  
   644  	returnExprs := n.Returns
   645  
   646  	for i := 0; i < len(returnExprs); i++ {
   647  		returns = append(returns, returnExprs[i].String())
   648  	}
   649  
   650  	if len(returns) > 0 {
   651  		return ret + " " + strings.Join(returns, ", ")
   652  	}
   653  
   654  	return ret
   655  }
   656  
   657  // String returns the string representation of for statement
   658  func (n *ForNode) String() string {
   659  	ret := "for"
   660  
   661  	if n.identifier != "" {
   662  		ret += " " + n.identifier + " in " + n.inExpr.String()
   663  	}
   664  
   665  	ret += " {\n"
   666  
   667  	tree := n.Tree()
   668  
   669  	stmts := strings.Split(tree.String(), "\n")
   670  
   671  	for i := 0; i < len(stmts); i++ {
   672  		if len(stmts[i]) > 0 {
   673  			ret += "\t" + stmts[i] + "\n"
   674  		} else {
   675  			ret += "\n"
   676  		}
   677  	}
   678  
   679  	ret += "}"
   680  
   681  	return ret
   682  }
   683  
   684  func stringify(s string) string {
   685  	buf := make([]byte, 0, len(s))
   686  
   687  	for i := 0; i < len(s); i++ {
   688  		switch s[i] {
   689  		case '"':
   690  			buf = append(buf, '\\', '"')
   691  		case '\t':
   692  			buf = append(buf, '\\', 't')
   693  		case '\n':
   694  			buf = append(buf, '\\', 'n')
   695  		case '\r':
   696  			buf = append(buf, '\\', 'r')
   697  		case '\\':
   698  			buf = append(buf, '\\', '\\')
   699  		default:
   700  			buf = append(buf, s[i])
   701  		}
   702  	}
   703  
   704  	return string(buf)
   705  }
   706  
   707  func getlhs(node assignable) string {
   708  	var nameStrs []string
   709  
   710  	nodeNames := node.names()
   711  
   712  	for i := 0; i < len(nodeNames); i++ {
   713  		nameStrs = append(nameStrs, nodeNames[i].String())
   714  	}
   715  
   716  	return strings.Join(nameStrs, ", ")
   717  }