Formatter.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  1. /*
  2. * Copyright (c) 2020, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "Formatter.h"
  7. #include "AST.h"
  8. #include "Parser.h"
  9. #include <AK/Hex.h>
  10. #include <AK/ScopedValueRollback.h>
  11. #include <AK/TemporaryChange.h>
  12. namespace Shell {
  13. String Formatter::format()
  14. {
  15. auto node = m_root_node ? m_root_node : Parser(m_source).parse();
  16. if (m_cursor >= 0)
  17. m_output_cursor = m_cursor;
  18. if (!node)
  19. return String();
  20. if (node->is_syntax_error())
  21. return m_source;
  22. if (m_cursor >= 0) {
  23. auto hit_test = node->hit_test_position(m_cursor);
  24. if (hit_test.matching_node)
  25. m_hit_node = hit_test.matching_node.ptr();
  26. else
  27. m_hit_node = nullptr;
  28. }
  29. m_parent_node = nullptr;
  30. node->visit(*this);
  31. VERIFY(m_builders.size() == 1);
  32. auto string = current_builder().string_view();
  33. if (!string.ends_with(' '))
  34. current_builder().append(m_trivia);
  35. return current_builder().to_string();
  36. }
  37. void Formatter::with_added_indent(int indent, Function<void()> callback)
  38. {
  39. TemporaryChange indent_change { m_current_indent, m_current_indent + indent };
  40. callback();
  41. }
  42. void Formatter::in_new_block(Function<void()> callback)
  43. {
  44. current_builder().append('{');
  45. with_added_indent(1, [&] {
  46. insert_separator();
  47. callback();
  48. });
  49. insert_separator();
  50. current_builder().append('}');
  51. }
  52. String Formatter::in_new_builder(Function<void()> callback, StringBuilder new_builder)
  53. {
  54. m_builders.append(move(new_builder));
  55. callback();
  56. return m_builders.take_last().to_string();
  57. }
  58. void Formatter::test_and_update_output_cursor(const AST::Node* node)
  59. {
  60. if (!node)
  61. return;
  62. if (node != m_hit_node)
  63. return;
  64. m_output_cursor = current_builder().length() + m_cursor - node->position().start_offset;
  65. }
  66. void Formatter::visited(const AST::Node* node)
  67. {
  68. m_last_visited_node = node;
  69. }
  70. void Formatter::will_visit(const AST::Node* node)
  71. {
  72. if (!m_last_visited_node)
  73. return;
  74. if (!node)
  75. return;
  76. auto direct_sequence_child = !m_parent_node || m_parent_node->kind() == AST::Node::Kind::Sequence;
  77. if (direct_sequence_child && node->kind() != AST::Node::Kind::Sequence && node->kind() != AST::Node::Kind::Execute) {
  78. // Collapse more than one empty line to a single one.
  79. if (node->position().start_line.line_number - m_last_visited_node->position().end_line.line_number > 1)
  80. insert_separator();
  81. }
  82. }
  83. void Formatter::insert_separator(bool escaped)
  84. {
  85. if (escaped)
  86. current_builder().append('\\');
  87. current_builder().append('\n');
  88. if (!escaped && !m_heredocs_to_append_after_sequence.is_empty()) {
  89. for (auto& entry : m_heredocs_to_append_after_sequence) {
  90. current_builder().append(entry);
  91. }
  92. m_heredocs_to_append_after_sequence.clear();
  93. }
  94. insert_indent();
  95. }
  96. void Formatter::insert_indent()
  97. {
  98. for (size_t i = 0; i < m_current_indent; ++i)
  99. current_builder().append(" "sv);
  100. }
  101. void Formatter::visit(const AST::PathRedirectionNode* node)
  102. {
  103. will_visit(node);
  104. test_and_update_output_cursor(node);
  105. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  106. NodeVisitor::visit(node);
  107. visited(node);
  108. }
  109. void Formatter::visit(const AST::And* node)
  110. {
  111. will_visit(node);
  112. test_and_update_output_cursor(node);
  113. auto should_indent = m_parent_node && m_parent_node->kind() != AST::Node::Kind::And;
  114. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  115. with_added_indent(should_indent ? 1 : 0, [&] {
  116. node->left()->visit(*this);
  117. current_builder().append(' ');
  118. insert_separator(true);
  119. current_builder().append("&& "sv);
  120. node->right()->visit(*this);
  121. });
  122. visited(node);
  123. }
  124. void Formatter::visit(const AST::ListConcatenate* node)
  125. {
  126. will_visit(node);
  127. test_and_update_output_cursor(node);
  128. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  129. auto first = true;
  130. for (auto& subnode : node->list()) {
  131. if (!first)
  132. current_builder().append(' ');
  133. first = false;
  134. subnode->visit(*this);
  135. }
  136. visited(node);
  137. }
  138. void Formatter::visit(const AST::Background* node)
  139. {
  140. will_visit(node);
  141. test_and_update_output_cursor(node);
  142. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  143. NodeVisitor::visit(node);
  144. current_builder().append(" &"sv);
  145. visited(node);
  146. }
  147. void Formatter::visit(const AST::BarewordLiteral* node)
  148. {
  149. will_visit(node);
  150. test_and_update_output_cursor(node);
  151. current_builder().append(node->text());
  152. visited(node);
  153. }
  154. void Formatter::visit(const AST::BraceExpansion* node)
  155. {
  156. will_visit(node);
  157. test_and_update_output_cursor(node);
  158. if (!m_parent_node || m_parent_node->kind() != AST::Node::Kind::Slice)
  159. current_builder().append('{');
  160. {
  161. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  162. bool first = true;
  163. for (auto& entry : node->entries()) {
  164. if (!first)
  165. current_builder().append(',');
  166. first = false;
  167. entry.visit(*this);
  168. }
  169. }
  170. if (!m_parent_node || m_parent_node->kind() != AST::Node::Kind::Slice)
  171. current_builder().append('}');
  172. visited(node);
  173. }
  174. void Formatter::visit(const AST::CastToCommand* node)
  175. {
  176. will_visit(node);
  177. test_and_update_output_cursor(node);
  178. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  179. NodeVisitor::visit(node);
  180. visited(node);
  181. }
  182. void Formatter::visit(const AST::CastToList* node)
  183. {
  184. will_visit(node);
  185. test_and_update_output_cursor(node);
  186. current_builder().append('(');
  187. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  188. NodeVisitor::visit(node);
  189. current_builder().append(')');
  190. visited(node);
  191. }
  192. void Formatter::visit(const AST::CloseFdRedirection* node)
  193. {
  194. will_visit(node);
  195. test_and_update_output_cursor(node);
  196. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  197. current_builder().appendff("{}>&-", node->fd());
  198. visited(node);
  199. }
  200. void Formatter::visit(const AST::CommandLiteral*)
  201. {
  202. VERIFY_NOT_REACHED();
  203. }
  204. void Formatter::visit(const AST::Comment* node)
  205. {
  206. will_visit(node);
  207. test_and_update_output_cursor(node);
  208. current_builder().append("#"sv);
  209. current_builder().append(node->text());
  210. visited(node);
  211. }
  212. void Formatter::visit(const AST::ContinuationControl* node)
  213. {
  214. will_visit(node);
  215. test_and_update_output_cursor(node);
  216. if (node->continuation_kind() == AST::ContinuationControl::Break)
  217. current_builder().append("break"sv);
  218. else if (node->continuation_kind() == AST::ContinuationControl::Continue)
  219. current_builder().append("continue"sv);
  220. else
  221. VERIFY_NOT_REACHED();
  222. visited(node);
  223. }
  224. void Formatter::visit(const AST::DynamicEvaluate* node)
  225. {
  226. will_visit(node);
  227. test_and_update_output_cursor(node);
  228. current_builder().append('$');
  229. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  230. NodeVisitor::visit(node);
  231. visited(node);
  232. }
  233. void Formatter::visit(const AST::DoubleQuotedString* node)
  234. {
  235. will_visit(node);
  236. test_and_update_output_cursor(node);
  237. auto not_in_heredoc = m_parent_node->kind() != AST::Node::Kind::Heredoc;
  238. if (not_in_heredoc)
  239. current_builder().append("\""sv);
  240. TemporaryChange quotes { m_options.in_double_quotes, true };
  241. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  242. NodeVisitor::visit(node);
  243. if (not_in_heredoc)
  244. current_builder().append("\""sv);
  245. visited(node);
  246. }
  247. void Formatter::visit(const AST::Fd2FdRedirection* node)
  248. {
  249. will_visit(node);
  250. test_and_update_output_cursor(node);
  251. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  252. current_builder().appendff("{}>&{}", node->source_fd(), node->dest_fd());
  253. if (m_hit_node == node)
  254. ++m_output_cursor;
  255. visited(node);
  256. }
  257. void Formatter::visit(const AST::FunctionDeclaration* node)
  258. {
  259. will_visit(node);
  260. test_and_update_output_cursor(node);
  261. current_builder().append(node->name().name);
  262. current_builder().append('(');
  263. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  264. auto first = true;
  265. for (auto& arg : node->arguments()) {
  266. if (!first)
  267. current_builder().append(' ');
  268. first = false;
  269. current_builder().append(arg.name);
  270. }
  271. current_builder().append(") "sv);
  272. in_new_block([&] {
  273. if (node->block())
  274. node->block()->visit(*this);
  275. });
  276. visited(node);
  277. }
  278. void Formatter::visit(const AST::ForLoop* node)
  279. {
  280. will_visit(node);
  281. test_and_update_output_cursor(node);
  282. auto is_loop = node->iterated_expression().is_null();
  283. current_builder().append(is_loop ? "loop"sv : "for "sv);
  284. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  285. if (!is_loop) {
  286. if (node->index_variable().has_value()) {
  287. current_builder().append("index "sv);
  288. current_builder().append(node->index_variable()->name);
  289. current_builder().append(" "sv);
  290. }
  291. if (node->variable().has_value() && node->variable()->name != "it") {
  292. current_builder().append(node->variable()->name);
  293. current_builder().append(" in "sv);
  294. }
  295. node->iterated_expression()->visit(*this);
  296. }
  297. current_builder().append(' ');
  298. in_new_block([&] {
  299. if (node->block())
  300. node->block()->visit(*this);
  301. });
  302. visited(node);
  303. }
  304. void Formatter::visit(const AST::Glob* node)
  305. {
  306. will_visit(node);
  307. test_and_update_output_cursor(node);
  308. current_builder().append(node->text());
  309. visited(node);
  310. }
  311. void Formatter::visit(const AST::Heredoc* node)
  312. {
  313. will_visit(node);
  314. test_and_update_output_cursor(node);
  315. current_builder().append("<<"sv);
  316. if (node->deindent())
  317. current_builder().append('~');
  318. else
  319. current_builder().append('-');
  320. if (node->allow_interpolation())
  321. current_builder().appendff("{}", node->end());
  322. else
  323. current_builder().appendff("'{}'", node->end());
  324. auto content = in_new_builder([&] {
  325. if (!node->contents())
  326. return;
  327. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  328. TemporaryChange heredoc { m_options.in_heredoc, true };
  329. auto& contents = *node->contents();
  330. contents.visit(*this);
  331. current_builder().appendff("\n{}\n", node->end());
  332. });
  333. m_heredocs_to_append_after_sequence.append(move(content));
  334. visited(node);
  335. }
  336. void Formatter::visit(const AST::HistoryEvent* node)
  337. {
  338. will_visit(node);
  339. test_and_update_output_cursor(node);
  340. current_builder().append('!');
  341. switch (node->selector().event.kind) {
  342. case AST::HistorySelector::EventKind::ContainingStringLookup:
  343. current_builder().append('?');
  344. current_builder().append(node->selector().event.text);
  345. break;
  346. case AST::HistorySelector::EventKind::StartingStringLookup:
  347. current_builder().append(node->selector().event.text);
  348. break;
  349. case AST::HistorySelector::EventKind::IndexFromStart:
  350. current_builder().append(node->selector().event.text);
  351. break;
  352. case AST::HistorySelector::EventKind::IndexFromEnd:
  353. if (node->selector().event.index == 0)
  354. current_builder().append('!');
  355. else
  356. current_builder().append(node->selector().event.text);
  357. break;
  358. }
  359. auto& range = node->selector().word_selector_range;
  360. if (!range.end.has_value()
  361. || range.end.value().kind != AST::HistorySelector::WordSelectorKind::Last
  362. || range.start.kind != AST::HistorySelector::WordSelectorKind::Index || range.start.selector != 0) {
  363. auto append_word = [this](auto& selector) {
  364. switch (selector.kind) {
  365. case AST::HistorySelector::WordSelectorKind::Index:
  366. if (selector.selector == 0)
  367. current_builder().append('^');
  368. else
  369. current_builder().appendff("{}", selector.selector);
  370. break;
  371. case AST::HistorySelector::WordSelectorKind::Last:
  372. current_builder().append('$');
  373. break;
  374. }
  375. };
  376. current_builder().append(':');
  377. append_word(range.start);
  378. if (range.end.has_value()) {
  379. current_builder().append('-');
  380. append_word(range.end.value());
  381. }
  382. }
  383. visited(node);
  384. }
  385. void Formatter::visit(const AST::Execute* node)
  386. {
  387. will_visit(node);
  388. test_and_update_output_cursor(node);
  389. auto& builder = current_builder();
  390. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  391. ScopedValueRollback options_rollback { m_options };
  392. if (node->does_capture_stdout())
  393. builder.append("$("sv);
  394. NodeVisitor::visit(node);
  395. if (node->does_capture_stdout())
  396. builder.append(")"sv);
  397. visited(node);
  398. }
  399. void Formatter::visit(const AST::IfCond* node)
  400. {
  401. will_visit(node);
  402. test_and_update_output_cursor(node);
  403. current_builder().append("if "sv);
  404. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  405. node->condition()->visit(*this);
  406. current_builder().append(' ');
  407. in_new_block([&] {
  408. if (node->true_branch())
  409. node->true_branch()->visit(*this);
  410. });
  411. if (node->false_branch()) {
  412. current_builder().append(" else "sv);
  413. if (node->false_branch()->kind() != AST::Node::Kind::IfCond) {
  414. in_new_block([&] {
  415. node->false_branch()->visit(*this);
  416. });
  417. } else {
  418. node->false_branch()->visit(*this);
  419. }
  420. } else if (node->else_position().has_value()) {
  421. current_builder().append(" else "sv);
  422. }
  423. visited(node);
  424. }
  425. void Formatter::visit(const AST::ImmediateExpression* node)
  426. {
  427. will_visit(node);
  428. test_and_update_output_cursor(node);
  429. current_builder().append("${"sv);
  430. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  431. current_builder().append(node->function_name());
  432. for (auto& node : node->arguments()) {
  433. current_builder().append(' ');
  434. node.visit(*this);
  435. }
  436. if (node->has_closing_brace())
  437. current_builder().append('}');
  438. visited(node);
  439. }
  440. void Formatter::visit(const AST::Join* node)
  441. {
  442. will_visit(node);
  443. test_and_update_output_cursor(node);
  444. auto should_parenthesise = m_options.explicit_parentheses;
  445. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  446. TemporaryChange parens { m_options.explicit_parentheses, false };
  447. if (should_parenthesise)
  448. current_builder().append('(');
  449. node->left()->visit(*this);
  450. current_builder().append(' ');
  451. node->right()->visit(*this);
  452. if (should_parenthesise)
  453. current_builder().append(')');
  454. visited(node);
  455. }
  456. void Formatter::visit(const AST::MatchExpr* node)
  457. {
  458. will_visit(node);
  459. test_and_update_output_cursor(node);
  460. current_builder().append("match "sv);
  461. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  462. node->matched_expr()->visit(*this);
  463. if (!node->expr_name().is_empty()) {
  464. current_builder().append(" as "sv);
  465. current_builder().append(node->expr_name());
  466. }
  467. current_builder().append(' ');
  468. in_new_block([&] {
  469. auto first_entry = true;
  470. for (auto& entry : node->entries()) {
  471. if (!first_entry)
  472. insert_separator();
  473. first_entry = false;
  474. auto first = true;
  475. entry.options.visit(
  476. [&](NonnullRefPtrVector<AST::Node> const& patterns) {
  477. for (auto& option : patterns) {
  478. if (!first)
  479. current_builder().append(" | "sv);
  480. first = false;
  481. option.visit(*this);
  482. }
  483. },
  484. [&](Vector<Regex<ECMA262>> const& patterns) {
  485. for (auto& option : patterns) {
  486. if (!first)
  487. current_builder().append(" | "sv);
  488. first = false;
  489. auto node = make_ref_counted<AST::BarewordLiteral>(AST::Position {}, option.pattern_value);
  490. node->visit(*this);
  491. }
  492. });
  493. current_builder().append(' ');
  494. if (entry.match_names.has_value() && !entry.match_names.value().is_empty()) {
  495. current_builder().append("as ("sv);
  496. auto first = true;
  497. for (auto& name : entry.match_names.value()) {
  498. if (!first)
  499. current_builder().append(' ');
  500. first = false;
  501. current_builder().append(name);
  502. }
  503. current_builder().append(") "sv);
  504. }
  505. in_new_block([&] {
  506. if (entry.body)
  507. entry.body->visit(*this);
  508. });
  509. }
  510. });
  511. visited(node);
  512. }
  513. void Formatter::visit(const AST::Or* node)
  514. {
  515. will_visit(node);
  516. test_and_update_output_cursor(node);
  517. auto should_indent = m_parent_node && m_parent_node->kind() != AST::Node::Kind::Or;
  518. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  519. with_added_indent(should_indent ? 1 : 0, [&] {
  520. node->left()->visit(*this);
  521. current_builder().append(" "sv);
  522. insert_separator(true);
  523. current_builder().append("|| "sv);
  524. node->right()->visit(*this);
  525. });
  526. visited(node);
  527. }
  528. void Formatter::visit(const AST::Pipe* node)
  529. {
  530. will_visit(node);
  531. test_and_update_output_cursor(node);
  532. auto should_indent = m_parent_node && m_parent_node->kind() != AST::Node::Kind::Pipe;
  533. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  534. node->left()->visit(*this);
  535. current_builder().append(" "sv);
  536. with_added_indent(should_indent ? 1 : 0, [&] {
  537. insert_separator(true);
  538. current_builder().append("| "sv);
  539. node->right()->visit(*this);
  540. });
  541. visited(node);
  542. }
  543. void Formatter::visit(const AST::Range* node)
  544. {
  545. will_visit(node);
  546. test_and_update_output_cursor(node);
  547. if (!m_parent_node || m_parent_node->kind() != AST::Node::Kind::Slice)
  548. current_builder().append('{');
  549. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  550. node->start()->visit(*this);
  551. current_builder().append(".."sv);
  552. node->end()->visit(*this);
  553. if (!m_parent_node || m_parent_node->kind() != AST::Node::Kind::Slice)
  554. current_builder().append('}');
  555. visited(node);
  556. }
  557. void Formatter::visit(const AST::ReadRedirection* node)
  558. {
  559. will_visit(node);
  560. test_and_update_output_cursor(node);
  561. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  562. if (node->fd() != 0)
  563. current_builder().appendff(" {}<", node->fd());
  564. else
  565. current_builder().append(" <"sv);
  566. NodeVisitor::visit(node);
  567. visited(node);
  568. }
  569. void Formatter::visit(const AST::ReadWriteRedirection* node)
  570. {
  571. will_visit(node);
  572. test_and_update_output_cursor(node);
  573. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  574. if (node->fd() != 0)
  575. current_builder().appendff(" {}<>", node->fd());
  576. else
  577. current_builder().append(" <>"sv);
  578. NodeVisitor::visit(node);
  579. visited(node);
  580. }
  581. void Formatter::visit(const AST::Sequence* node)
  582. {
  583. will_visit(node);
  584. test_and_update_output_cursor(node);
  585. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  586. bool first = true;
  587. for (auto& entry : node->entries()) {
  588. if (first)
  589. first = false;
  590. else
  591. insert_separator();
  592. entry.visit(*this);
  593. }
  594. visited(node);
  595. }
  596. void Formatter::visit(const AST::Subshell* node)
  597. {
  598. will_visit(node);
  599. test_and_update_output_cursor(node);
  600. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  601. in_new_block([&] {
  602. NodeVisitor::visit(node);
  603. });
  604. visited(node);
  605. }
  606. void Formatter::visit(const AST::Slice* node)
  607. {
  608. will_visit(node);
  609. test_and_update_output_cursor(node);
  610. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  611. current_builder().append('[');
  612. node->selector()->visit(*this);
  613. current_builder().append(']');
  614. visited(node);
  615. }
  616. void Formatter::visit(const AST::SimpleVariable* node)
  617. {
  618. will_visit(node);
  619. test_and_update_output_cursor(node);
  620. current_builder().append('$');
  621. current_builder().append(node->name());
  622. if (const AST::Node* slice = node->slice())
  623. slice->visit(*this);
  624. visited(node);
  625. }
  626. void Formatter::visit(const AST::SpecialVariable* node)
  627. {
  628. will_visit(node);
  629. test_and_update_output_cursor(node);
  630. current_builder().append('$');
  631. current_builder().append(node->name());
  632. if (const AST::Node* slice = node->slice())
  633. slice->visit(*this);
  634. visited(node);
  635. }
  636. void Formatter::visit(const AST::Juxtaposition* node)
  637. {
  638. will_visit(node);
  639. test_and_update_output_cursor(node);
  640. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  641. NodeVisitor::visit(node);
  642. visited(node);
  643. }
  644. void Formatter::visit(const AST::StringLiteral* node)
  645. {
  646. will_visit(node);
  647. test_and_update_output_cursor(node);
  648. if (!m_options.in_double_quotes && !m_options.in_heredoc)
  649. current_builder().append("'"sv);
  650. if (m_options.in_double_quotes && !m_options.in_heredoc) {
  651. for (auto ch : node->text()) {
  652. switch (ch) {
  653. case '"':
  654. case '\\':
  655. case '$':
  656. current_builder().append('\\');
  657. break;
  658. case '\n':
  659. current_builder().append("\\n"sv);
  660. continue;
  661. case '\r':
  662. current_builder().append("\\r"sv);
  663. continue;
  664. case '\t':
  665. current_builder().append("\\t"sv);
  666. continue;
  667. case '\v':
  668. current_builder().append("\\v"sv);
  669. continue;
  670. case '\f':
  671. current_builder().append("\\f"sv);
  672. continue;
  673. case '\a':
  674. current_builder().append("\\a"sv);
  675. continue;
  676. case '\e':
  677. current_builder().append("\\e"sv);
  678. continue;
  679. default:
  680. break;
  681. }
  682. current_builder().append(ch);
  683. }
  684. } else {
  685. current_builder().append(node->text());
  686. }
  687. if (!m_options.in_double_quotes && !m_options.in_heredoc)
  688. current_builder().append("'"sv);
  689. visited(node);
  690. }
  691. void Formatter::visit(const AST::StringPartCompose* node)
  692. {
  693. will_visit(node);
  694. test_and_update_output_cursor(node);
  695. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  696. NodeVisitor::visit(node);
  697. visited(node);
  698. }
  699. void Formatter::visit(const AST::SyntaxError* node)
  700. {
  701. will_visit(node);
  702. test_and_update_output_cursor(node);
  703. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  704. NodeVisitor::visit(node);
  705. visited(node);
  706. }
  707. void Formatter::visit(const AST::Tilde* node)
  708. {
  709. will_visit(node);
  710. test_and_update_output_cursor(node);
  711. current_builder().append(node->text());
  712. visited(node);
  713. }
  714. void Formatter::visit(const AST::VariableDeclarations* node)
  715. {
  716. will_visit(node);
  717. test_and_update_output_cursor(node);
  718. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  719. auto first = true;
  720. for (auto& entry : node->variables()) {
  721. if (!first)
  722. current_builder().append(' ');
  723. first = false;
  724. entry.name->visit(*this);
  725. current_builder().append('=');
  726. if (entry.value->is_command())
  727. current_builder().append('(');
  728. entry.value->visit(*this);
  729. if (entry.value->is_command())
  730. current_builder().append(')');
  731. }
  732. visited(node);
  733. }
  734. void Formatter::visit(const AST::WriteAppendRedirection* node)
  735. {
  736. will_visit(node);
  737. test_and_update_output_cursor(node);
  738. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  739. if (node->fd() != 1)
  740. current_builder().appendff(" {}>>", node->fd());
  741. else
  742. current_builder().append(" >>"sv);
  743. NodeVisitor::visit(node);
  744. visited(node);
  745. }
  746. void Formatter::visit(const AST::WriteRedirection* node)
  747. {
  748. will_visit(node);
  749. test_and_update_output_cursor(node);
  750. TemporaryChange<const AST::Node*> parent { m_parent_node, node };
  751. if (node->fd() != 1)
  752. current_builder().appendff(" {}>", node->fd());
  753. else
  754. current_builder().append(" >"sv);
  755. NodeVisitor::visit(node);
  756. visited(node);
  757. }
  758. }