ImmediateFunctions.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. /*
  2. * Copyright (c) 2021, The SerenityOS developers.
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include "Formatter.h"
  27. #include "Shell.h"
  28. #include <LibRegex/Regex.h>
  29. namespace Shell {
  30. RefPtr<AST::Node> Shell::immediate_length_impl(AST::ImmediateExpression& invoking_node, const NonnullRefPtrVector<AST::Node>& arguments, bool across)
  31. {
  32. auto name = across ? "length_across" : "length";
  33. if (arguments.size() < 1 || arguments.size() > 2) {
  34. raise_error(ShellError::EvaluatedSyntaxError, String::formatted("Expected one or two arguments to `{}'", name), invoking_node.position());
  35. return nullptr;
  36. }
  37. enum {
  38. Infer,
  39. String,
  40. List,
  41. } mode { Infer };
  42. bool is_inferred = false;
  43. const AST::Node* expr_node;
  44. if (arguments.size() == 2) {
  45. // length string <expr>
  46. // length list <expr>
  47. auto& mode_arg = arguments.first();
  48. if (!mode_arg.is_bareword()) {
  49. 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());
  50. return nullptr;
  51. }
  52. const auto& mode_name = static_cast<const AST::BarewordLiteral&>(mode_arg).text();
  53. if (mode_name == "list") {
  54. mode = List;
  55. } else if (mode_name == "string") {
  56. mode = String;
  57. } else if (mode_name == "infer") {
  58. mode = Infer;
  59. } else {
  60. 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());
  61. return nullptr;
  62. }
  63. expr_node = &arguments[1];
  64. } else {
  65. expr_node = &arguments[0];
  66. }
  67. if (mode == Infer) {
  68. is_inferred = true;
  69. if (expr_node->is_list())
  70. mode = List;
  71. else if (expr_node->is_simple_variable()) // "Look inside" variables
  72. mode = const_cast<AST::Node*>(expr_node)->run(this)->resolve_without_cast(this)->is_list_without_resolution() ? List : String;
  73. else if (is<AST::ImmediateExpression>(expr_node))
  74. mode = List;
  75. else
  76. mode = String;
  77. }
  78. auto value_with_number = [&](auto number) -> NonnullRefPtr<AST::Node> {
  79. return AST::create<AST::BarewordLiteral>(invoking_node.position(), String::number(number));
  80. };
  81. auto do_across = [&](StringView mode_name, auto& values) {
  82. if (is_inferred)
  83. mode_name = "infer";
  84. // Translate to a list of applications of `length <mode_name>`
  85. Vector<NonnullRefPtr<AST::Node>> resulting_nodes;
  86. resulting_nodes.ensure_capacity(values.size());
  87. for (auto& entry : values) {
  88. // ImmediateExpression(length <mode_name> <entry>)
  89. resulting_nodes.unchecked_append(AST::create<AST::ImmediateExpression>(
  90. expr_node->position(),
  91. AST::NameWithPosition { "length", invoking_node.function_position() },
  92. NonnullRefPtrVector<AST::Node> { Vector<NonnullRefPtr<AST::Node>> {
  93. static_cast<NonnullRefPtr<AST::Node>>(AST::create<AST::BarewordLiteral>(expr_node->position(), mode_name)),
  94. AST::create<AST::SyntheticNode>(expr_node->position(), NonnullRefPtr<AST::Value>(entry)),
  95. } },
  96. expr_node->position()));
  97. }
  98. return AST::create<AST::ListConcatenate>(invoking_node.position(), move(resulting_nodes));
  99. };
  100. switch (mode) {
  101. default:
  102. case Infer:
  103. VERIFY_NOT_REACHED();
  104. case List: {
  105. auto value = (const_cast<AST::Node*>(expr_node))->run(this);
  106. if (!value)
  107. return value_with_number(0);
  108. value = value->resolve_without_cast(this);
  109. if (auto list = dynamic_cast<AST::ListValue*>(value.ptr())) {
  110. if (across)
  111. return do_across("list", list->values());
  112. return value_with_number(list->values().size());
  113. }
  114. auto list = value->resolve_as_list(this);
  115. if (!across)
  116. return value_with_number(list.size());
  117. dbgln("List has {} entries", list.size());
  118. auto values = AST::create<AST::ListValue>(move(list));
  119. return do_across("list", values->values());
  120. }
  121. case String: {
  122. // 'across' will only accept lists, and '!across' will only accept non-lists here.
  123. if (expr_node->is_list()) {
  124. if (!across) {
  125. raise_no_list_allowed:;
  126. Formatter formatter { *expr_node };
  127. if (is_inferred) {
  128. raise_error(ShellError::EvaluatedSyntaxError,
  129. String::formatted("Could not infer expression type, please explicitly use `{0} string' or `{0} list'", name),
  130. invoking_node.position());
  131. return nullptr;
  132. }
  133. auto source = formatter.format();
  134. raise_error(ShellError::EvaluatedSyntaxError,
  135. source.is_empty()
  136. ? "Invalid application of `length' to a list"
  137. : 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"),
  138. expr_node->position());
  139. return nullptr;
  140. }
  141. }
  142. auto value = (const_cast<AST::Node*>(expr_node))->run(this);
  143. if (!value)
  144. return value_with_number(0);
  145. value = value->resolve_without_cast(*this);
  146. if (auto list = dynamic_cast<AST::ListValue*>(value.ptr())) {
  147. if (!across)
  148. goto raise_no_list_allowed;
  149. return do_across("string", list->values());
  150. }
  151. if (across && !value->is_list()) {
  152. Formatter formatter { *expr_node };
  153. auto source = formatter.format();
  154. raise_error(ShellError::EvaluatedSyntaxError,
  155. String::formatted("Invalid application of `length_across' to a non-list\nperhaps you meant `{1}length {0}{2}'?", source, "\x1b[32m", "\x1b[0m"),
  156. expr_node->position());
  157. return nullptr;
  158. }
  159. // Evaluate the nodes and substitute with the lengths.
  160. auto list = value->resolve_as_list(this);
  161. if (!expr_node->is_list()) {
  162. if (list.size() == 1) {
  163. if (across)
  164. goto raise_no_list_allowed;
  165. // This is the normal case, the expression is a normal non-list expression.
  166. return value_with_number(list.first().length());
  167. }
  168. // This can be hit by asking for the length of a command list (e.g. `(>/dev/null)`)
  169. // raise an error about misuse of command lists for now.
  170. // FIXME: What's the length of `(>/dev/null)` supposed to be?
  171. raise_error(ShellError::EvaluatedSyntaxError, "Length of meta value (or command list) requested, this is currently not supported.", expr_node->position());
  172. return nullptr;
  173. }
  174. auto values = AST::create<AST::ListValue>(move(list));
  175. return do_across("string", values->values());
  176. }
  177. }
  178. }
  179. RefPtr<AST::Node> Shell::immediate_length(AST::ImmediateExpression& invoking_node, const NonnullRefPtrVector<AST::Node>& arguments)
  180. {
  181. return immediate_length_impl(invoking_node, arguments, false);
  182. }
  183. RefPtr<AST::Node> Shell::immediate_length_across(AST::ImmediateExpression& invoking_node, const NonnullRefPtrVector<AST::Node>& arguments)
  184. {
  185. return immediate_length_impl(invoking_node, arguments, true);
  186. }
  187. RefPtr<AST::Node> Shell::immediate_regex_replace(AST::ImmediateExpression& invoking_node, const NonnullRefPtrVector<AST::Node>& arguments)
  188. {
  189. if (arguments.size() != 3) {
  190. raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 3 arguments to regex_replace", invoking_node.position());
  191. return nullptr;
  192. }
  193. auto pattern = const_cast<AST::Node&>(arguments[0]).run(this);
  194. auto replacement = const_cast<AST::Node&>(arguments[1]).run(this);
  195. auto value = const_cast<AST::Node&>(arguments[2]).run(this)->resolve_without_cast(this);
  196. if (!pattern->is_string()) {
  197. raise_error(ShellError::EvaluatedSyntaxError, "Expected the regex_replace pattern to be a string", arguments[0].position());
  198. return nullptr;
  199. }
  200. if (!replacement->is_string()) {
  201. raise_error(ShellError::EvaluatedSyntaxError, "Expected the regex_replace replacement string to be a string", arguments[1].position());
  202. return nullptr;
  203. }
  204. if (!value->is_string()) {
  205. raise_error(ShellError::EvaluatedSyntaxError, "Expected the regex_replace target value to be a string", arguments[2].position());
  206. return nullptr;
  207. }
  208. Regex<PosixExtendedParser> re { pattern->resolve_as_list(this).first() };
  209. auto result = re.replace(value->resolve_as_list(this)[0], replacement->resolve_as_list(this)[0], PosixFlags::Global | PosixFlags::Multiline | PosixFlags::Unicode);
  210. return AST::create<AST::StringLiteral>(invoking_node.position(), move(result));
  211. }
  212. RefPtr<AST::Node> Shell::immediate_remove_suffix(AST::ImmediateExpression& invoking_node, const NonnullRefPtrVector<AST::Node>& arguments)
  213. {
  214. if (arguments.size() != 2) {
  215. raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to remove_suffix", invoking_node.position());
  216. return nullptr;
  217. }
  218. auto suffix = const_cast<AST::Node&>(arguments[0]).run(this);
  219. auto value = const_cast<AST::Node&>(arguments[1]).run(this)->resolve_without_cast(this);
  220. if (!suffix->is_string()) {
  221. raise_error(ShellError::EvaluatedSyntaxError, "Expected the remove_suffix suffix string to be a string", arguments[0].position());
  222. return nullptr;
  223. }
  224. auto suffix_str = suffix->resolve_as_list(this)[0];
  225. auto values = value->resolve_as_list(this);
  226. Vector<NonnullRefPtr<AST::Node>> nodes;
  227. for (auto& value_str : values) {
  228. StringView removed { value_str };
  229. if (value_str.ends_with(suffix_str))
  230. removed = removed.substring_view(0, value_str.length() - suffix_str.length());
  231. nodes.append(AST::create<AST::StringLiteral>(invoking_node.position(), removed));
  232. }
  233. return AST::create<AST::ListConcatenate>(invoking_node.position(), move(nodes));
  234. }
  235. RefPtr<AST::Node> Shell::immediate_remove_prefix(AST::ImmediateExpression& invoking_node, const NonnullRefPtrVector<AST::Node>& arguments)
  236. {
  237. if (arguments.size() != 2) {
  238. raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to remove_prefix", invoking_node.position());
  239. return nullptr;
  240. }
  241. auto prefix = const_cast<AST::Node&>(arguments[0]).run(this);
  242. auto value = const_cast<AST::Node&>(arguments[1]).run(this)->resolve_without_cast(this);
  243. if (!prefix->is_string()) {
  244. raise_error(ShellError::EvaluatedSyntaxError, "Expected the remove_prefix prefix string to be a string", arguments[0].position());
  245. return nullptr;
  246. }
  247. auto prefix_str = prefix->resolve_as_list(this)[0];
  248. auto values = value->resolve_as_list(this);
  249. Vector<NonnullRefPtr<AST::Node>> nodes;
  250. for (auto& value_str : values) {
  251. StringView removed { value_str };
  252. if (value_str.starts_with(prefix_str))
  253. removed = removed.substring_view(prefix_str.length());
  254. nodes.append(AST::create<AST::StringLiteral>(invoking_node.position(), removed));
  255. }
  256. return AST::create<AST::ListConcatenate>(invoking_node.position(), move(nodes));
  257. }
  258. RefPtr<AST::Node> Shell::immediate_split(AST::ImmediateExpression& invoking_node, const NonnullRefPtrVector<AST::Node>& arguments)
  259. {
  260. if (arguments.size() != 2) {
  261. raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to split", invoking_node.position());
  262. return nullptr;
  263. }
  264. auto delimiter = const_cast<AST::Node&>(arguments[0]).run(this);
  265. auto value = const_cast<AST::Node&>(arguments[1]).run(this)->resolve_without_cast(this);
  266. if (!delimiter->is_string()) {
  267. raise_error(ShellError::EvaluatedSyntaxError, "Expected the split delimiter string to be a string", arguments[0].position());
  268. return nullptr;
  269. }
  270. auto delimiter_str = delimiter->resolve_as_list(this)[0];
  271. auto transform = [&](const auto& values) {
  272. // Translate to a list of applications of `split <delimiter>`
  273. Vector<NonnullRefPtr<AST::Node>> resulting_nodes;
  274. resulting_nodes.ensure_capacity(values.size());
  275. for (auto& entry : values) {
  276. // ImmediateExpression(split <delimiter> <entry>)
  277. resulting_nodes.unchecked_append(AST::create<AST::ImmediateExpression>(
  278. arguments[1].position(),
  279. invoking_node.function(),
  280. NonnullRefPtrVector<AST::Node> { Vector<NonnullRefPtr<AST::Node>> {
  281. arguments[0],
  282. AST::create<AST::SyntheticNode>(arguments[1].position(), NonnullRefPtr<AST::Value>(entry)),
  283. } },
  284. arguments[1].position()));
  285. }
  286. return AST::create<AST::ListConcatenate>(invoking_node.position(), move(resulting_nodes));
  287. };
  288. if (auto list = dynamic_cast<AST::ListValue*>(value.ptr())) {
  289. return transform(list->values());
  290. }
  291. // Otherwise, just resolve to a list and transform that.
  292. auto list = value->resolve_as_list(this);
  293. if (!value->is_list()) {
  294. if (list.is_empty())
  295. return AST::create<AST::ListConcatenate>(invoking_node.position(), NonnullRefPtrVector<AST::Node> {});
  296. auto& value = list.first();
  297. Vector<String> split_strings;
  298. if (delimiter_str.is_empty()) {
  299. StringBuilder builder;
  300. for (auto code_point : Utf8View { value }) {
  301. builder.append_code_point(code_point);
  302. split_strings.append(builder.build());
  303. builder.clear();
  304. }
  305. } else {
  306. auto split = StringView { value }.split_view(delimiter_str, options.inline_exec_keep_empty_segments);
  307. split_strings.ensure_capacity(split.size());
  308. for (auto& entry : split)
  309. split_strings.append(entry);
  310. }
  311. return AST::create<AST::SyntheticNode>(invoking_node.position(), AST::create<AST::ListValue>(move(split_strings)));
  312. }
  313. return transform(AST::create<AST::ListValue>(list)->values());
  314. }
  315. RefPtr<AST::Node> Shell::immediate_concat_lists(AST::ImmediateExpression& invoking_node, const NonnullRefPtrVector<AST::Node>& arguments)
  316. {
  317. NonnullRefPtrVector<AST::Node> result;
  318. for (auto& argument : arguments) {
  319. if (auto* list = dynamic_cast<const AST::ListConcatenate*>(&argument)) {
  320. result.append(list->list());
  321. } else {
  322. auto list_of_values = const_cast<AST::Node&>(argument).run(this)->resolve_without_cast(this);
  323. if (auto* list = dynamic_cast<AST::ListValue*>(list_of_values.ptr())) {
  324. for (auto& entry : static_cast<Vector<NonnullRefPtr<AST::Value>>&>(list->values()))
  325. result.append(AST::create<AST::SyntheticNode>(argument.position(), entry));
  326. } else {
  327. auto values = list_of_values->resolve_as_list(this);
  328. for (auto& entry : values)
  329. result.append(AST::create<AST::StringLiteral>(argument.position(), entry));
  330. }
  331. }
  332. }
  333. return AST::create<AST::ListConcatenate>(invoking_node.position(), move(result));
  334. }
  335. RefPtr<AST::Node> Shell::run_immediate_function(StringView str, AST::ImmediateExpression& invoking_node, const NonnullRefPtrVector<AST::Node>& arguments)
  336. {
  337. #define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(name) \
  338. if (str == #name) \
  339. return immediate_##name(invoking_node, arguments);
  340. ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS()
  341. #undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION
  342. raise_error(ShellError::EvaluatedSyntaxError, String::formatted("Unknown immediate function {}", str), invoking_node.position());
  343. return nullptr;
  344. }
  345. bool Shell::has_immediate_function(const StringView& str)
  346. {
  347. #define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(name) \
  348. if (str == #name) \
  349. return true;
  350. ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS()
  351. #undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION
  352. return false;
  353. }
  354. }