ImmediateFunctions.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /*
  2. * Copyright (c) 2021, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "Formatter.h"
  7. #include "Shell.h"
  8. #include <LibRegex/Regex.h>
  9. namespace Shell {
  10. RefPtr<AST::Node> Shell::immediate_length_impl(AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const& arguments, bool across)
  11. {
  12. auto name = across ? "length_across" : "length";
  13. if (arguments.size() < 1 || arguments.size() > 2) {
  14. raise_error(ShellError::EvaluatedSyntaxError, String::formatted("Expected one or two arguments to `{}'", name), invoking_node.position());
  15. return nullptr;
  16. }
  17. enum {
  18. Infer,
  19. String,
  20. List,
  21. } mode { Infer };
  22. bool is_inferred = false;
  23. const AST::Node* expr_node;
  24. if (arguments.size() == 2) {
  25. // length string <expr>
  26. // length list <expr>
  27. auto& mode_arg = arguments.first();
  28. if (!mode_arg.is_bareword()) {
  29. raise_error(ShellError::EvaluatedSyntaxError, String::formatted("Expected a bareword (either 'string' or 'list') in the two-argument form of the `{}' immediate", name), mode_arg.position());
  30. return nullptr;
  31. }
  32. auto const& mode_name = static_cast<const AST::BarewordLiteral&>(mode_arg).text();
  33. if (mode_name == "list") {
  34. mode = List;
  35. } else if (mode_name == "string") {
  36. mode = String;
  37. } else if (mode_name == "infer") {
  38. mode = Infer;
  39. } else {
  40. raise_error(ShellError::EvaluatedSyntaxError, String::formatted("Expected either 'string' or 'list' (and not {}) in the two-argument form of the `{}' immediate", mode_name, name), mode_arg.position());
  41. return nullptr;
  42. }
  43. expr_node = &arguments[1];
  44. } else {
  45. expr_node = &arguments[0];
  46. }
  47. if (mode == Infer) {
  48. is_inferred = true;
  49. if (expr_node->is_list())
  50. mode = List;
  51. else if (expr_node->is_simple_variable()) // "Look inside" variables
  52. mode = const_cast<AST::Node*>(expr_node)->run(this)->resolve_without_cast(this)->is_list_without_resolution() ? List : String;
  53. else if (is<AST::ImmediateExpression>(expr_node))
  54. mode = List;
  55. else
  56. mode = String;
  57. }
  58. auto value_with_number = [&](auto number) -> NonnullRefPtr<AST::Node> {
  59. return AST::make_ref_counted<AST::BarewordLiteral>(invoking_node.position(), String::number(number));
  60. };
  61. auto do_across = [&](StringView mode_name, auto& values) {
  62. if (is_inferred)
  63. mode_name = "infer"sv;
  64. // Translate to a list of applications of `length <mode_name>`
  65. Vector<NonnullRefPtr<AST::Node>> resulting_nodes;
  66. resulting_nodes.ensure_capacity(values.size());
  67. for (auto& entry : values) {
  68. // ImmediateExpression(length <mode_name> <entry>)
  69. resulting_nodes.unchecked_append(AST::make_ref_counted<AST::ImmediateExpression>(
  70. expr_node->position(),
  71. AST::NameWithPosition { "length", invoking_node.function_position() },
  72. NonnullRefPtrVector<AST::Node> { Vector<NonnullRefPtr<AST::Node>> {
  73. static_cast<NonnullRefPtr<AST::Node>>(AST::make_ref_counted<AST::BarewordLiteral>(expr_node->position(), mode_name)),
  74. AST::make_ref_counted<AST::SyntheticNode>(expr_node->position(), NonnullRefPtr<AST::Value>(entry)),
  75. } },
  76. expr_node->position()));
  77. }
  78. return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(resulting_nodes));
  79. };
  80. switch (mode) {
  81. default:
  82. case Infer:
  83. VERIFY_NOT_REACHED();
  84. case List: {
  85. auto value = (const_cast<AST::Node*>(expr_node))->run(this);
  86. if (!value)
  87. return value_with_number(0);
  88. value = value->resolve_without_cast(this);
  89. if (auto list = dynamic_cast<AST::ListValue*>(value.ptr())) {
  90. if (across)
  91. return do_across("list"sv, list->values());
  92. return value_with_number(list->values().size());
  93. }
  94. auto list = value->resolve_as_list(this);
  95. if (!across)
  96. return value_with_number(list.size());
  97. dbgln("List has {} entries", list.size());
  98. auto values = AST::make_ref_counted<AST::ListValue>(move(list));
  99. return do_across("list"sv, values->values());
  100. }
  101. case String: {
  102. // 'across' will only accept lists, and '!across' will only accept non-lists here.
  103. if (expr_node->is_list()) {
  104. if (!across) {
  105. raise_no_list_allowed:;
  106. Formatter formatter { *expr_node };
  107. if (is_inferred) {
  108. raise_error(ShellError::EvaluatedSyntaxError,
  109. String::formatted("Could not infer expression type, please explicitly use `{0} string' or `{0} list'", name),
  110. invoking_node.position());
  111. return nullptr;
  112. }
  113. auto source = formatter.format();
  114. raise_error(ShellError::EvaluatedSyntaxError,
  115. source.is_empty()
  116. ? "Invalid application of `length' to a list"
  117. : String::formatted("Invalid application of `length' to a list\nperhaps you meant `{1}length \"{0}\"{2}' or `{1}length_across {0}{2}'?", source, "\x1b[32m", "\x1b[0m"),
  118. expr_node->position());
  119. return nullptr;
  120. }
  121. }
  122. auto value = (const_cast<AST::Node*>(expr_node))->run(this);
  123. if (!value)
  124. return value_with_number(0);
  125. value = value->resolve_without_cast(*this);
  126. if (auto list = dynamic_cast<AST::ListValue*>(value.ptr())) {
  127. if (!across)
  128. goto raise_no_list_allowed;
  129. return do_across("string"sv, list->values());
  130. }
  131. if (across && !value->is_list()) {
  132. Formatter formatter { *expr_node };
  133. auto source = formatter.format();
  134. raise_error(ShellError::EvaluatedSyntaxError,
  135. String::formatted("Invalid application of `length_across' to a non-list\nperhaps you meant `{1}length {0}{2}'?", source, "\x1b[32m", "\x1b[0m"),
  136. expr_node->position());
  137. return nullptr;
  138. }
  139. // Evaluate the nodes and substitute with the lengths.
  140. auto list = value->resolve_as_list(this);
  141. if (!expr_node->is_list()) {
  142. if (list.size() == 1) {
  143. if (across)
  144. goto raise_no_list_allowed;
  145. // This is the normal case, the expression is a normal non-list expression.
  146. return value_with_number(list.first().length());
  147. }
  148. // This can be hit by asking for the length of a command list (e.g. `(>/dev/null)`)
  149. // raise an error about misuse of command lists for now.
  150. // FIXME: What's the length of `(>/dev/null)` supposed to be?
  151. raise_error(ShellError::EvaluatedSyntaxError, "Length of meta value (or command list) requested, this is currently not supported.", expr_node->position());
  152. return nullptr;
  153. }
  154. auto values = AST::make_ref_counted<AST::ListValue>(move(list));
  155. return do_across("string"sv, values->values());
  156. }
  157. }
  158. }
  159. RefPtr<AST::Node> Shell::immediate_length(AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const& arguments)
  160. {
  161. return immediate_length_impl(invoking_node, arguments, false);
  162. }
  163. RefPtr<AST::Node> Shell::immediate_length_across(AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const& arguments)
  164. {
  165. return immediate_length_impl(invoking_node, arguments, true);
  166. }
  167. RefPtr<AST::Node> Shell::immediate_regex_replace(AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const& arguments)
  168. {
  169. if (arguments.size() != 3) {
  170. raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 3 arguments to regex_replace", invoking_node.position());
  171. return nullptr;
  172. }
  173. auto pattern = const_cast<AST::Node&>(arguments[0]).run(this);
  174. auto replacement = const_cast<AST::Node&>(arguments[1]).run(this);
  175. auto value = const_cast<AST::Node&>(arguments[2]).run(this)->resolve_without_cast(this);
  176. if (!pattern->is_string()) {
  177. raise_error(ShellError::EvaluatedSyntaxError, "Expected the regex_replace pattern to be a string", arguments[0].position());
  178. return nullptr;
  179. }
  180. if (!replacement->is_string()) {
  181. raise_error(ShellError::EvaluatedSyntaxError, "Expected the regex_replace replacement string to be a string", arguments[1].position());
  182. return nullptr;
  183. }
  184. if (!value->is_string()) {
  185. raise_error(ShellError::EvaluatedSyntaxError, "Expected the regex_replace target value to be a string", arguments[2].position());
  186. return nullptr;
  187. }
  188. Regex<PosixExtendedParser> re { pattern->resolve_as_list(this).first() };
  189. auto result = re.replace(value->resolve_as_list(this)[0], replacement->resolve_as_list(this)[0], PosixFlags::Global | PosixFlags::Multiline | PosixFlags::Unicode);
  190. return AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), move(result), AST::StringLiteral::EnclosureType::None);
  191. }
  192. RefPtr<AST::Node> Shell::immediate_remove_suffix(AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const& arguments)
  193. {
  194. if (arguments.size() != 2) {
  195. raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to remove_suffix", invoking_node.position());
  196. return nullptr;
  197. }
  198. auto suffix = const_cast<AST::Node&>(arguments[0]).run(this);
  199. auto value = const_cast<AST::Node&>(arguments[1]).run(this)->resolve_without_cast(this);
  200. if (!suffix->is_string()) {
  201. raise_error(ShellError::EvaluatedSyntaxError, "Expected the remove_suffix suffix string to be a string", arguments[0].position());
  202. return nullptr;
  203. }
  204. auto suffix_str = suffix->resolve_as_list(this)[0];
  205. auto values = value->resolve_as_list(this);
  206. Vector<NonnullRefPtr<AST::Node>> nodes;
  207. for (auto& value_str : values) {
  208. StringView removed { value_str };
  209. if (value_str.ends_with(suffix_str))
  210. removed = removed.substring_view(0, value_str.length() - suffix_str.length());
  211. nodes.append(AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), removed, AST::StringLiteral::EnclosureType::None));
  212. }
  213. return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(nodes));
  214. }
  215. RefPtr<AST::Node> Shell::immediate_remove_prefix(AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const& arguments)
  216. {
  217. if (arguments.size() != 2) {
  218. raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to remove_prefix", invoking_node.position());
  219. return nullptr;
  220. }
  221. auto prefix = const_cast<AST::Node&>(arguments[0]).run(this);
  222. auto value = const_cast<AST::Node&>(arguments[1]).run(this)->resolve_without_cast(this);
  223. if (!prefix->is_string()) {
  224. raise_error(ShellError::EvaluatedSyntaxError, "Expected the remove_prefix prefix string to be a string", arguments[0].position());
  225. return nullptr;
  226. }
  227. auto prefix_str = prefix->resolve_as_list(this)[0];
  228. auto values = value->resolve_as_list(this);
  229. Vector<NonnullRefPtr<AST::Node>> nodes;
  230. for (auto& value_str : values) {
  231. StringView removed { value_str };
  232. if (value_str.starts_with(prefix_str))
  233. removed = removed.substring_view(prefix_str.length());
  234. nodes.append(AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), removed, AST::StringLiteral::EnclosureType::None));
  235. }
  236. return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(nodes));
  237. }
  238. RefPtr<AST::Node> Shell::immediate_split(AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const& arguments)
  239. {
  240. if (arguments.size() != 2) {
  241. raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to split", invoking_node.position());
  242. return nullptr;
  243. }
  244. auto delimiter = const_cast<AST::Node&>(arguments[0]).run(this);
  245. auto value = const_cast<AST::Node&>(arguments[1]).run(this)->resolve_without_cast(this);
  246. if (!delimiter->is_string()) {
  247. raise_error(ShellError::EvaluatedSyntaxError, "Expected the split delimiter string to be a string", arguments[0].position());
  248. return nullptr;
  249. }
  250. auto delimiter_str = delimiter->resolve_as_list(this)[0];
  251. auto transform = [&](auto const& values) {
  252. // Translate to a list of applications of `split <delimiter>`
  253. Vector<NonnullRefPtr<AST::Node>> resulting_nodes;
  254. resulting_nodes.ensure_capacity(values.size());
  255. for (auto& entry : values) {
  256. // ImmediateExpression(split <delimiter> <entry>)
  257. resulting_nodes.unchecked_append(AST::make_ref_counted<AST::ImmediateExpression>(
  258. arguments[1].position(),
  259. invoking_node.function(),
  260. NonnullRefPtrVector<AST::Node> { Vector<NonnullRefPtr<AST::Node>> {
  261. arguments[0],
  262. AST::make_ref_counted<AST::SyntheticNode>(arguments[1].position(), NonnullRefPtr<AST::Value>(entry)),
  263. } },
  264. arguments[1].position()));
  265. }
  266. return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(resulting_nodes));
  267. };
  268. if (auto list = dynamic_cast<AST::ListValue*>(value.ptr())) {
  269. return transform(list->values());
  270. }
  271. // Otherwise, just resolve to a list and transform that.
  272. auto list = value->resolve_as_list(this);
  273. if (!value->is_list()) {
  274. if (list.is_empty())
  275. return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), NonnullRefPtrVector<AST::Node> {});
  276. auto& value = list.first();
  277. Vector<String> split_strings;
  278. if (delimiter_str.is_empty()) {
  279. StringBuilder builder;
  280. for (auto code_point : Utf8View { value }) {
  281. builder.append_code_point(code_point);
  282. split_strings.append(builder.build());
  283. builder.clear();
  284. }
  285. } else {
  286. auto split = StringView { value }.split_view(delimiter_str, options.inline_exec_keep_empty_segments ? SplitBehavior::KeepEmpty : SplitBehavior::Nothing);
  287. split_strings.ensure_capacity(split.size());
  288. for (auto& entry : split)
  289. split_strings.append(entry);
  290. }
  291. return AST::make_ref_counted<AST::SyntheticNode>(invoking_node.position(), AST::make_ref_counted<AST::ListValue>(move(split_strings)));
  292. }
  293. return transform(AST::make_ref_counted<AST::ListValue>(list)->values());
  294. }
  295. RefPtr<AST::Node> Shell::immediate_concat_lists(AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const& arguments)
  296. {
  297. NonnullRefPtrVector<AST::Node> result;
  298. for (auto& argument : arguments) {
  299. if (auto* list = dynamic_cast<const AST::ListConcatenate*>(&argument)) {
  300. result.extend(list->list());
  301. } else {
  302. auto list_of_values = const_cast<AST::Node&>(argument).run(this)->resolve_without_cast(this);
  303. if (auto* list = dynamic_cast<AST::ListValue*>(list_of_values.ptr())) {
  304. for (auto& entry : static_cast<Vector<NonnullRefPtr<AST::Value>>&>(list->values()))
  305. result.append(AST::make_ref_counted<AST::SyntheticNode>(argument.position(), entry));
  306. } else {
  307. auto values = list_of_values->resolve_as_list(this);
  308. for (auto& entry : values)
  309. result.append(AST::make_ref_counted<AST::StringLiteral>(argument.position(), entry, AST::StringLiteral::EnclosureType::None));
  310. }
  311. }
  312. }
  313. return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(result));
  314. }
  315. RefPtr<AST::Node> Shell::immediate_filter_glob(AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const& arguments)
  316. {
  317. // filter_glob string list
  318. if (arguments.size() != 2) {
  319. raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly two arguments to filter_glob (<glob> <list>)", invoking_node.position());
  320. return nullptr;
  321. }
  322. auto glob_list = const_cast<AST::Node&>(arguments[0]).run(*this)->resolve_as_list(*this);
  323. if (glob_list.size() != 1) {
  324. raise_error(ShellError::EvaluatedSyntaxError, "Expected the <glob> argument to filter_glob to be a single string", arguments[0].position());
  325. return nullptr;
  326. }
  327. auto& glob = glob_list.first();
  328. auto& list_node = arguments[1];
  329. NonnullRefPtrVector<AST::Node> result;
  330. const_cast<AST::Node&>(list_node).for_each_entry(*this, [&](NonnullRefPtr<AST::Value> entry) {
  331. auto value = entry->resolve_as_list(*this);
  332. if (value.size() == 0)
  333. return IterationDecision::Continue;
  334. if (value.size() == 1) {
  335. if (!value.first().matches(glob))
  336. return IterationDecision::Continue;
  337. result.append(AST::make_ref_counted<AST::StringLiteral>(arguments[1].position(), value.first(), AST::StringLiteral::EnclosureType::None));
  338. return IterationDecision::Continue;
  339. }
  340. for (auto& entry : value) {
  341. if (entry.matches(glob)) {
  342. NonnullRefPtrVector<AST::Node> nodes;
  343. for (auto& string : value)
  344. nodes.append(AST::make_ref_counted<AST::StringLiteral>(arguments[1].position(), string, AST::StringLiteral::EnclosureType::None));
  345. result.append(AST::make_ref_counted<AST::ListConcatenate>(arguments[1].position(), move(nodes)));
  346. return IterationDecision::Continue;
  347. }
  348. }
  349. return IterationDecision::Continue;
  350. });
  351. return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(result));
  352. }
  353. RefPtr<AST::Node> Shell::immediate_join(AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const& arguments)
  354. {
  355. if (arguments.size() != 2) {
  356. raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to join", invoking_node.position());
  357. return nullptr;
  358. }
  359. auto delimiter = const_cast<AST::Node&>(arguments[0]).run(this);
  360. if (!delimiter->is_string()) {
  361. raise_error(ShellError::EvaluatedSyntaxError, "Expected the join delimiter string to be a string", arguments[0].position());
  362. return nullptr;
  363. }
  364. auto value = const_cast<AST::Node&>(arguments[1]).run(this)->resolve_without_cast(this);
  365. if (!value->is_list()) {
  366. raise_error(ShellError::EvaluatedSyntaxError, "Expected the joined list to be a list", arguments[1].position());
  367. return nullptr;
  368. }
  369. auto delimiter_str = delimiter->resolve_as_list(this)[0];
  370. StringBuilder builder;
  371. builder.join(delimiter_str, value->resolve_as_list(*this));
  372. return AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), builder.to_string(), AST::StringLiteral::EnclosureType::None);
  373. }
  374. RefPtr<AST::Node> Shell::run_immediate_function(StringView str, AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const& arguments)
  375. {
  376. #define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(name) \
  377. if (str == #name) \
  378. return immediate_##name(invoking_node, arguments);
  379. ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS()
  380. #undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION
  381. raise_error(ShellError::EvaluatedSyntaxError, String::formatted("Unknown immediate function {}", str), invoking_node.position());
  382. return nullptr;
  383. }
  384. bool Shell::has_immediate_function(StringView str)
  385. {
  386. #define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(name) \
  387. if (str == #name) \
  388. return true;
  389. ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS()
  390. #undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION
  391. return false;
  392. }
  393. }