Serenity Operating System
at master 640 lines 29 kB view raw
1/* 2 * Copyright (c) 2021, the SerenityOS developers. 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "Formatter.h" 8#include "Shell.h" 9#include <LibRegex/Regex.h> 10 11namespace Shell { 12 13ErrorOr<RefPtr<AST::Node>> Shell::immediate_length_impl(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments, bool across) 14{ 15 auto name = across ? "length_across" : "length"; 16 if (arguments.size() < 1 || arguments.size() > 2) { 17 raise_error(ShellError::EvaluatedSyntaxError, DeprecatedString::formatted("Expected one or two arguments to `{}'", name), invoking_node.position()); 18 return nullptr; 19 } 20 21 enum { 22 Infer, 23 String, 24 List, 25 } mode { Infer }; 26 27 bool is_inferred = false; 28 29 const AST::Node* expr_node; 30 if (arguments.size() == 2) { 31 // length string <expr> 32 // length list <expr> 33 34 auto& mode_arg = arguments.first(); 35 if (!mode_arg->is_bareword()) { 36 raise_error(ShellError::EvaluatedSyntaxError, DeprecatedString::formatted("Expected a bareword (either 'string' or 'list') in the two-argument form of the `{}' immediate", name), mode_arg->position()); 37 return nullptr; 38 } 39 40 auto const& mode_name = static_cast<const AST::BarewordLiteral&>(*mode_arg).text(); 41 if (mode_name == "list") { 42 mode = List; 43 } else if (mode_name == "string") { 44 mode = String; 45 } else if (mode_name == "infer") { 46 mode = Infer; 47 } else { 48 raise_error(ShellError::EvaluatedSyntaxError, DeprecatedString::formatted("Expected either 'string' or 'list' (and not {}) in the two-argument form of the `{}' immediate", mode_name, name), mode_arg->position()); 49 return nullptr; 50 } 51 52 expr_node = arguments[1]; 53 } else { 54 expr_node = arguments[0]; 55 } 56 57 if (mode == Infer) { 58 is_inferred = true; 59 if (expr_node->is_list()) 60 mode = List; 61 else if (expr_node->is_simple_variable()) // "Look inside" variables 62 mode = TRY(TRY(const_cast<AST::Node*>(expr_node)->run(this))->resolve_without_cast(this))->is_list_without_resolution() ? List : String; 63 else if (is<AST::ImmediateExpression>(expr_node)) 64 mode = List; 65 else 66 mode = String; 67 } 68 69 auto value_with_number = [&](auto number) -> ErrorOr<NonnullRefPtr<AST::Node>> { 70 return AST::make_ref_counted<AST::BarewordLiteral>(invoking_node.position(), TRY(String::number(number))); 71 }; 72 73 auto do_across = [&](StringView mode_name, auto& values) -> ErrorOr<RefPtr<AST::Node>> { 74 if (is_inferred) 75 mode_name = "infer"sv; 76 // Translate to a list of applications of `length <mode_name>` 77 Vector<NonnullRefPtr<AST::Node>> resulting_nodes; 78 resulting_nodes.ensure_capacity(values.size()); 79 for (auto& entry : values) { 80 // ImmediateExpression(length <mode_name> <entry>) 81 resulting_nodes.unchecked_append(AST::make_ref_counted<AST::ImmediateExpression>( 82 expr_node->position(), 83 AST::NameWithPosition { TRY("length"_string), invoking_node.function_position() }, 84 Vector<NonnullRefPtr<AST::Node>> { Vector<NonnullRefPtr<AST::Node>> { 85 static_cast<NonnullRefPtr<AST::Node>>(AST::make_ref_counted<AST::BarewordLiteral>(expr_node->position(), TRY(String::from_utf8(mode_name)))), 86 AST::make_ref_counted<AST::SyntheticNode>(expr_node->position(), NonnullRefPtr<AST::Value>(entry)), 87 } }, 88 expr_node->position())); 89 } 90 91 return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(resulting_nodes)); 92 }; 93 94 switch (mode) { 95 default: 96 case Infer: 97 VERIFY_NOT_REACHED(); 98 case List: { 99 auto value = TRY(const_cast<AST::Node*>(expr_node)->run(this)); 100 if (!value) 101 return value_with_number(0); 102 103 value = TRY(value->resolve_without_cast(this)); 104 105 if (auto list = dynamic_cast<AST::ListValue*>(value.ptr())) { 106 if (across) 107 return do_across("list"sv, list->values()); 108 109 return value_with_number(list->values().size()); 110 } 111 112 auto list = TRY(value->resolve_as_list(this)); 113 if (!across) 114 return value_with_number(list.size()); 115 116 dbgln("List has {} entries", list.size()); 117 auto values = AST::make_ref_counted<AST::ListValue>(move(list)); 118 return do_across("list"sv, values->values()); 119 } 120 case String: { 121 // 'across' will only accept lists, and '!across' will only accept non-lists here. 122 if (expr_node->is_list()) { 123 if (!across) { 124 raise_no_list_allowed:; 125 Formatter formatter { *expr_node }; 126 127 if (is_inferred) { 128 raise_error(ShellError::EvaluatedSyntaxError, 129 DeprecatedString::formatted("Could not infer expression type, please explicitly use `{0} string' or `{0} list'", name), 130 invoking_node.position()); 131 return nullptr; 132 } 133 134 auto source = formatter.format(); 135 raise_error(ShellError::EvaluatedSyntaxError, 136 source.is_empty() 137 ? "Invalid application of `length' to a list" 138 : DeprecatedString::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"), 139 expr_node->position()); 140 return nullptr; 141 } 142 } 143 144 auto value = TRY(const_cast<AST::Node*>(expr_node)->run(this)); 145 if (!value) 146 return value_with_number(0); 147 148 value = TRY(value->resolve_without_cast(*this)); 149 150 if (auto list = dynamic_cast<AST::ListValue*>(value.ptr())) { 151 if (!across) 152 goto raise_no_list_allowed; 153 154 return do_across("string"sv, list->values()); 155 } 156 157 if (across && !value->is_list()) { 158 Formatter formatter { *expr_node }; 159 160 auto source = formatter.format(); 161 raise_error(ShellError::EvaluatedSyntaxError, 162 DeprecatedString::formatted("Invalid application of `length_across' to a non-list\nperhaps you meant `{1}length {0}{2}'?", source, "\x1b[32m", "\x1b[0m"), 163 expr_node->position()); 164 return nullptr; 165 } 166 167 // Evaluate the nodes and substitute with the lengths. 168 auto list = TRY(value->resolve_as_list(this)); 169 170 if (!expr_node->is_list()) { 171 if (list.size() == 1) { 172 if (across) 173 goto raise_no_list_allowed; 174 175 // This is the normal case, the expression is a normal non-list expression. 176 return value_with_number(list.first().bytes_as_string_view().length()); 177 } 178 179 // This can be hit by asking for the length of a command list (e.g. `(>/dev/null)`) 180 // raise an error about misuse of command lists for now. 181 // FIXME: What's the length of `(>/dev/null)` supposed to be? 182 raise_error(ShellError::EvaluatedSyntaxError, "Length of meta value (or command list) requested, this is currently not supported.", expr_node->position()); 183 return nullptr; 184 } 185 186 auto values = AST::make_ref_counted<AST::ListValue>(move(list)); 187 return do_across("string"sv, values->values()); 188 } 189 } 190} 191 192ErrorOr<RefPtr<AST::Node>> Shell::immediate_length(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 193{ 194 return immediate_length_impl(invoking_node, arguments, false); 195} 196 197ErrorOr<RefPtr<AST::Node>> Shell::immediate_length_across(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 198{ 199 return immediate_length_impl(invoking_node, arguments, true); 200} 201 202ErrorOr<RefPtr<AST::Node>> Shell::immediate_regex_replace(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 203{ 204 if (arguments.size() != 3) { 205 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 3 arguments to regex_replace", invoking_node.position()); 206 return nullptr; 207 } 208 209 auto pattern = TRY(const_cast<AST::Node&>(*arguments[0]).run(this)); 210 auto replacement = TRY(const_cast<AST::Node&>(*arguments[1]).run(this)); 211 auto value = TRY(TRY(const_cast<AST::Node&>(*arguments[2]).run(this))->resolve_without_cast(this)); 212 213 if (!pattern->is_string()) { 214 raise_error(ShellError::EvaluatedSyntaxError, "Expected the regex_replace pattern to be a string", arguments[0]->position()); 215 return nullptr; 216 } 217 218 if (!replacement->is_string()) { 219 raise_error(ShellError::EvaluatedSyntaxError, "Expected the regex_replace replacement string to be a string", arguments[1]->position()); 220 return nullptr; 221 } 222 223 if (!value->is_string()) { 224 raise_error(ShellError::EvaluatedSyntaxError, "Expected the regex_replace target value to be a string", arguments[2]->position()); 225 return nullptr; 226 } 227 228 Regex<PosixExtendedParser> re { TRY(pattern->resolve_as_list(this)).first().to_deprecated_string() }; 229 auto result = re.replace( 230 TRY(value->resolve_as_list(this))[0], 231 TRY(replacement->resolve_as_list(this))[0], 232 PosixFlags::Global | PosixFlags::Multiline | PosixFlags::Unicode); 233 234 return AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), TRY(String::from_utf8(result)), AST::StringLiteral::EnclosureType::None); 235} 236 237ErrorOr<RefPtr<AST::Node>> Shell::immediate_remove_suffix(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 238{ 239 if (arguments.size() != 2) { 240 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to remove_suffix", invoking_node.position()); 241 return nullptr; 242 } 243 244 auto suffix = TRY(const_cast<AST::Node&>(*arguments[0]).run(this)); 245 auto value = TRY(TRY(const_cast<AST::Node&>(*arguments[1]).run(this))->resolve_without_cast(this)); 246 247 if (!suffix->is_string()) { 248 raise_error(ShellError::EvaluatedSyntaxError, "Expected the remove_suffix suffix string to be a string", arguments[0]->position()); 249 return nullptr; 250 } 251 252 auto suffix_str = TRY(suffix->resolve_as_list(this))[0]; 253 auto values = TRY(value->resolve_as_list(this)); 254 255 Vector<NonnullRefPtr<AST::Node>> nodes; 256 257 for (auto& value_str : values) { 258 String removed = TRY(String::from_utf8(value_str)); 259 260 if (value_str.bytes_as_string_view().ends_with(suffix_str)) 261 removed = TRY(removed.substring_from_byte_offset(0, value_str.bytes_as_string_view().length() - suffix_str.bytes_as_string_view().length())); 262 263 nodes.append(AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), move(removed), AST::StringLiteral::EnclosureType::None)); 264 } 265 266 return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(nodes)); 267} 268 269ErrorOr<RefPtr<AST::Node>> Shell::immediate_remove_prefix(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 270{ 271 if (arguments.size() != 2) { 272 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to remove_prefix", invoking_node.position()); 273 return nullptr; 274 } 275 276 auto prefix = TRY(const_cast<AST::Node&>(*arguments[0]).run(this)); 277 auto value = TRY(TRY(const_cast<AST::Node&>(*arguments[1]).run(this))->resolve_without_cast(this)); 278 279 if (!prefix->is_string()) { 280 raise_error(ShellError::EvaluatedSyntaxError, "Expected the remove_prefix prefix string to be a string", arguments[0]->position()); 281 return nullptr; 282 } 283 284 auto prefix_str = TRY(prefix->resolve_as_list(this))[0]; 285 auto values = TRY(value->resolve_as_list(this)); 286 287 Vector<NonnullRefPtr<AST::Node>> nodes; 288 289 for (auto& value_str : values) { 290 String removed = TRY(String::from_utf8(value_str)); 291 292 if (value_str.bytes_as_string_view().starts_with(prefix_str)) 293 removed = TRY(removed.substring_from_byte_offset(prefix_str.bytes_as_string_view().length())); 294 nodes.append(AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), move(removed), AST::StringLiteral::EnclosureType::None)); 295 } 296 297 return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(nodes)); 298} 299 300ErrorOr<RefPtr<AST::Node>> Shell::immediate_split(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 301{ 302 if (arguments.size() != 2) { 303 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to split", invoking_node.position()); 304 return nullptr; 305 } 306 307 auto delimiter = TRY(const_cast<AST::Node&>(*arguments[0]).run(this)); 308 auto value = TRY(TRY(const_cast<AST::Node&>(*arguments[1]).run(this))->resolve_without_cast(this)); 309 310 if (!delimiter->is_string()) { 311 raise_error(ShellError::EvaluatedSyntaxError, "Expected the split delimiter string to be a string", arguments[0]->position()); 312 return nullptr; 313 } 314 315 auto delimiter_str = TRY(delimiter->resolve_as_list(this))[0]; 316 317 auto transform = [&](auto const& values) { 318 // Translate to a list of applications of `split <delimiter>` 319 Vector<NonnullRefPtr<AST::Node>> resulting_nodes; 320 resulting_nodes.ensure_capacity(values.size()); 321 for (auto& entry : values) { 322 // ImmediateExpression(split <delimiter> <entry>) 323 resulting_nodes.unchecked_append(AST::make_ref_counted<AST::ImmediateExpression>( 324 arguments[1]->position(), 325 invoking_node.function(), 326 Vector<NonnullRefPtr<AST::Node>> { Vector<NonnullRefPtr<AST::Node>> { 327 arguments[0], 328 AST::make_ref_counted<AST::SyntheticNode>(arguments[1]->position(), NonnullRefPtr<AST::Value>(entry)), 329 } }, 330 arguments[1]->position())); 331 } 332 333 return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(resulting_nodes)); 334 }; 335 336 if (auto list = dynamic_cast<AST::ListValue*>(value.ptr())) { 337 return transform(list->values()); 338 } 339 340 // Otherwise, just resolve to a list and transform that. 341 auto list = TRY(value->resolve_as_list(this)); 342 if (!value->is_list()) { 343 if (list.is_empty()) 344 return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), Vector<NonnullRefPtr<AST::Node>> {}); 345 346 auto& value = list.first(); 347 Vector<String> split_strings; 348 if (delimiter_str.is_empty()) { 349 StringBuilder builder; 350 for (auto code_point : Utf8View { value }) { 351 builder.append_code_point(code_point); 352 split_strings.append(TRY(builder.to_string())); 353 builder.clear(); 354 } 355 } else { 356 auto split = StringView { value }.split_view(delimiter_str, options.inline_exec_keep_empty_segments ? SplitBehavior::KeepEmpty : SplitBehavior::Nothing); 357 split_strings.ensure_capacity(split.size()); 358 for (auto& entry : split) 359 split_strings.append(TRY(String::from_utf8(entry))); 360 } 361 return AST::make_ref_counted<AST::SyntheticNode>(invoking_node.position(), AST::make_ref_counted<AST::ListValue>(move(split_strings))); 362 } 363 364 return transform(AST::make_ref_counted<AST::ListValue>(list)->values()); 365} 366 367ErrorOr<RefPtr<AST::Node>> Shell::immediate_concat_lists(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 368{ 369 Vector<NonnullRefPtr<AST::Node>> result; 370 371 for (auto& argument : arguments) { 372 if (auto* list = dynamic_cast<AST::ListConcatenate const*>(argument.ptr())) { 373 result.extend(list->list()); 374 } else { 375 auto list_of_values = TRY(TRY(const_cast<AST::Node&>(*argument).run(this))->resolve_without_cast(this)); 376 if (auto* list = dynamic_cast<AST::ListValue*>(list_of_values.ptr())) { 377 for (auto& entry : static_cast<Vector<NonnullRefPtr<AST::Value>>&>(list->values())) 378 result.append(AST::make_ref_counted<AST::SyntheticNode>(argument->position(), entry)); 379 } else { 380 auto values = TRY(list_of_values->resolve_as_list(this)); 381 for (auto& entry : values) 382 result.append(AST::make_ref_counted<AST::StringLiteral>(argument->position(), entry, AST::StringLiteral::EnclosureType::None)); 383 } 384 } 385 } 386 387 return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(result)); 388} 389 390ErrorOr<RefPtr<AST::Node>> Shell::immediate_filter_glob(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 391{ 392 // filter_glob string list 393 if (arguments.size() != 2) { 394 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly two arguments to filter_glob (<glob> <list>)", invoking_node.position()); 395 return nullptr; 396 } 397 398 auto glob_list = TRY(TRY(const_cast<AST::Node&>(*arguments[0]).run(*this))->resolve_as_list(*this)); 399 if (glob_list.size() != 1) { 400 raise_error(ShellError::EvaluatedSyntaxError, "Expected the <glob> argument to filter_glob to be a single string", arguments[0]->position()); 401 return nullptr; 402 } 403 auto& glob = glob_list.first(); 404 auto& list_node = arguments[1]; 405 406 Vector<NonnullRefPtr<AST::Node>> result; 407 408 TRY(const_cast<AST::Node&>(*list_node).for_each_entry(*this, [&](NonnullRefPtr<AST::Value> entry) -> ErrorOr<IterationDecision> { 409 auto value = TRY(entry->resolve_as_list(*this)); 410 if (value.size() == 0) 411 return IterationDecision::Continue; 412 if (value.size() == 1) { 413 if (!value.first().bytes_as_string_view().matches(glob)) 414 return IterationDecision::Continue; 415 result.append(AST::make_ref_counted<AST::StringLiteral>(arguments[1]->position(), value.first(), AST::StringLiteral::EnclosureType::None)); 416 return IterationDecision::Continue; 417 } 418 419 for (auto& entry : value) { 420 if (entry.bytes_as_string_view().matches(glob)) { 421 Vector<NonnullRefPtr<AST::Node>> nodes; 422 for (auto& string : value) 423 nodes.append(AST::make_ref_counted<AST::StringLiteral>(arguments[1]->position(), string, AST::StringLiteral::EnclosureType::None)); 424 result.append(AST::make_ref_counted<AST::ListConcatenate>(arguments[1]->position(), move(nodes))); 425 return IterationDecision::Continue; 426 } 427 } 428 return IterationDecision::Continue; 429 })); 430 431 return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(result)); 432} 433 434ErrorOr<RefPtr<AST::Node>> Shell::immediate_join(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 435{ 436 if (arguments.size() != 2) { 437 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to join", invoking_node.position()); 438 return nullptr; 439 } 440 441 auto delimiter = TRY(const_cast<AST::Node&>(*arguments[0]).run(this)); 442 if (!delimiter->is_string()) { 443 raise_error(ShellError::EvaluatedSyntaxError, "Expected the join delimiter string to be a string", arguments[0]->position()); 444 return nullptr; 445 } 446 447 auto value = TRY(TRY(const_cast<AST::Node&>(*arguments[1]).run(this))->resolve_without_cast(this)); 448 if (!value->is_list()) { 449 raise_error(ShellError::EvaluatedSyntaxError, "Expected the joined list to be a list", arguments[1]->position()); 450 return nullptr; 451 } 452 453 auto delimiter_str = TRY(delimiter->resolve_as_list(this))[0]; 454 StringBuilder builder; 455 builder.join(delimiter_str, TRY(value->resolve_as_list(*this))); 456 457 return AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), TRY(builder.to_string()), AST::StringLiteral::EnclosureType::None); 458} 459 460ErrorOr<RefPtr<AST::Node>> Shell::immediate_value_or_default(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 461{ 462 if (arguments.size() != 2) { 463 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to value_or_default", invoking_node.position()); 464 return nullptr; 465 } 466 467 auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); 468 if (!TRY(local_variable_or(name, ""sv)).is_empty()) 469 return make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); 470 471 return arguments.last(); 472} 473 474ErrorOr<RefPtr<AST::Node>> Shell::immediate_assign_default(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 475{ 476 if (arguments.size() != 2) { 477 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to assign_default", invoking_node.position()); 478 return nullptr; 479 } 480 481 auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); 482 if (!TRY(local_variable_or(name, ""sv)).is_empty()) 483 return make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); 484 485 auto value = TRY(TRY(const_cast<AST::Node&>(*arguments.last()).run(*this))->resolve_without_cast(*this)); 486 set_local_variable(name.to_deprecated_string(), value); 487 488 return make_ref_counted<AST::SyntheticNode>(invoking_node.position(), value); 489} 490 491ErrorOr<RefPtr<AST::Node>> Shell::immediate_error_if_empty(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 492{ 493 if (arguments.size() != 2) { 494 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to error_if_empty", invoking_node.position()); 495 return nullptr; 496 } 497 498 auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); 499 if (!TRY(local_variable_or(name, ""sv)).is_empty()) 500 return make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); 501 502 auto error_value = TRY(TRY(const_cast<AST::Node&>(*arguments.last()).run(*this))->resolve_as_string(*this)); 503 if (error_value.is_empty()) 504 error_value = TRY(String::formatted("Expected {} to be non-empty", name)); 505 506 raise_error(ShellError::EvaluatedSyntaxError, error_value.bytes_as_string_view(), invoking_node.position()); 507 return nullptr; 508} 509 510ErrorOr<RefPtr<AST::Node>> Shell::immediate_null_or_alternative(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 511{ 512 if (arguments.size() != 2) { 513 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to null_or_alternative", invoking_node.position()); 514 return nullptr; 515 } 516 517 auto value = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_without_cast(*this)); 518 if ((value->is_string() && TRY(value->resolve_as_string(*this)).is_empty()) || (value->is_list() && TRY(value->resolve_as_list(*this)).is_empty())) 519 return make_ref_counted<AST::SyntheticNode>(invoking_node.position(), value); 520 521 return arguments.last(); 522} 523 524ErrorOr<RefPtr<AST::Node>> Shell::immediate_defined_value_or_default(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 525{ 526 if (arguments.size() != 2) { 527 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to defined_value_or_default", invoking_node.position()); 528 return nullptr; 529 } 530 531 auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); 532 if (!find_frame_containing_local_variable(name)) 533 return arguments.last(); 534 535 return make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); 536} 537 538ErrorOr<RefPtr<AST::Node>> Shell::immediate_assign_defined_default(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 539{ 540 if (arguments.size() != 2) { 541 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to assign_defined_default", invoking_node.position()); 542 return nullptr; 543 } 544 545 auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); 546 if (find_frame_containing_local_variable(name)) 547 return make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); 548 549 auto value = TRY(TRY(const_cast<AST::Node&>(*arguments.last()).run(*this))->resolve_without_cast(*this)); 550 set_local_variable(name.to_deprecated_string(), value); 551 552 return make_ref_counted<AST::SyntheticNode>(invoking_node.position(), value); 553} 554 555ErrorOr<RefPtr<AST::Node>> Shell::immediate_error_if_unset(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 556{ 557 if (arguments.size() != 2) { 558 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to error_if_unset", invoking_node.position()); 559 return nullptr; 560 } 561 562 auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); 563 if (find_frame_containing_local_variable(name)) 564 return make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); 565 566 auto error_value = TRY(TRY(const_cast<AST::Node&>(*arguments.last()).run(*this))->resolve_as_string(*this)); 567 if (error_value.is_empty()) 568 error_value = TRY(String::formatted("Expected {} to be set", name)); 569 570 raise_error(ShellError::EvaluatedSyntaxError, error_value.bytes_as_string_view(), invoking_node.position()); 571 return nullptr; 572} 573 574ErrorOr<RefPtr<AST::Node>> Shell::immediate_null_if_unset_or_alternative(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 575{ 576 if (arguments.size() != 2) { 577 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to null_if_unset_or_alternative", invoking_node.position()); 578 return nullptr; 579 } 580 581 auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); 582 if (!find_frame_containing_local_variable(name)) 583 return arguments.last(); 584 585 return make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); 586} 587 588ErrorOr<RefPtr<AST::Node>> Shell::immediate_reexpand(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 589{ 590 if (arguments.size() != 1) { 591 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 1 argument to reexpand", invoking_node.position()); 592 return nullptr; 593 } 594 595 auto value = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); 596 return parse(value, m_is_interactive, false); 597} 598 599ErrorOr<RefPtr<AST::Node>> Shell::immediate_length_of_variable(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 600{ 601 if (arguments.size() != 1) { 602 raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 1 argument to length_of_variable", invoking_node.position()); 603 return nullptr; 604 } 605 606 auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); 607 auto variable = make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); 608 609 return immediate_length_impl( 610 invoking_node, 611 { move(variable) }, 612 false); 613} 614 615ErrorOr<RefPtr<AST::Node>> Shell::run_immediate_function(StringView str, AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) 616{ 617#define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(name) \ 618 if (str == #name) \ 619 return immediate_##name(invoking_node, arguments); 620 621 ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS() 622 623#undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION 624 raise_error(ShellError::EvaluatedSyntaxError, DeprecatedString::formatted("Unknown immediate function {}", str), invoking_node.position()); 625 return nullptr; 626} 627 628bool Shell::has_immediate_function(StringView str) 629{ 630#define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(name) \ 631 if (str == #name) \ 632 return true; 633 634 ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS() 635 636#undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION 637 638 return false; 639} 640}