github.com/madlambda/nash@v0.2.2-0.20230113003044-f2284521680b/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 }