2021-03-05 13:03:23 +00:00
/*
2021-04-28 20:46:44 +00:00
* Copyright ( c ) 2021 , the SerenityOS developers .
2021-03-05 13:03:23 +00:00
*
2021-04-22 08:24:48 +00:00
* SPDX - License - Identifier : BSD - 2 - Clause
2021-03-05 13:03:23 +00:00
*/
# include "Formatter.h"
# include "Shell.h"
# include <LibRegex/Regex.h>
namespace Shell {
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_length_impl ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments , bool across )
2021-03-05 13:03:23 +00:00
{
auto name = across ? " length_across " : " length " ;
if ( arguments . size ( ) < 1 | | arguments . size ( ) > 2 ) {
2022-12-04 18:02:33 +00:00
raise_error ( ShellError : : EvaluatedSyntaxError , DeprecatedString : : formatted ( " Expected one or two arguments to `{}' " , name ) , invoking_node . position ( ) ) ;
2021-03-05 13:03:23 +00:00
return nullptr ;
}
enum {
Infer ,
String ,
List ,
} mode { Infer } ;
bool is_inferred = false ;
const AST : : Node * expr_node ;
if ( arguments . size ( ) = = 2 ) {
// length string <expr>
// length list <expr>
auto & mode_arg = arguments . first ( ) ;
if ( ! mode_arg . is_bareword ( ) ) {
2022-12-04 18:02:33 +00:00
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 ( ) ) ;
2021-03-05 13:03:23 +00:00
return nullptr ;
}
2022-04-01 17:58:27 +00:00
auto const & mode_name = static_cast < const AST : : BarewordLiteral & > ( mode_arg ) . text ( ) ;
2021-03-05 13:03:23 +00:00
if ( mode_name = = " list " ) {
mode = List ;
} else if ( mode_name = = " string " ) {
mode = String ;
} else if ( mode_name = = " infer " ) {
mode = Infer ;
} else {
2022-12-04 18:02:33 +00:00
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 ( ) ) ;
2021-03-05 13:03:23 +00:00
return nullptr ;
}
expr_node = & arguments [ 1 ] ;
} else {
expr_node = & arguments [ 0 ] ;
}
if ( mode = = Infer ) {
is_inferred = true ;
if ( expr_node - > is_list ( ) )
mode = List ;
else if ( expr_node - > is_simple_variable ( ) ) // "Look inside" variables
2023-02-18 13:44:19 +00:00
mode = TRY ( const_cast < AST : : Node * > ( expr_node ) - > run ( this ) - > resolve_without_cast ( this ) ) - > is_list_without_resolution ( ) ? List : String ;
2021-03-05 13:03:23 +00:00
else if ( is < AST : : ImmediateExpression > ( expr_node ) )
mode = List ;
else
mode = String ;
}
2023-02-18 13:44:19 +00:00
auto value_with_number = [ & ] ( auto number ) - > ErrorOr < NonnullRefPtr < AST : : Node > > {
return AST : : make_ref_counted < AST : : BarewordLiteral > ( invoking_node . position ( ) , TRY ( String : : number ( number ) ) ) ;
2021-03-05 13:03:23 +00:00
} ;
2023-02-18 13:44:19 +00:00
auto do_across = [ & ] ( StringView mode_name , auto & values ) - > ErrorOr < RefPtr < AST : : Node > > {
2021-03-05 13:03:23 +00:00
if ( is_inferred )
2022-07-11 17:32:29 +00:00
mode_name = " infer " sv ;
2021-03-05 13:03:23 +00:00
// Translate to a list of applications of `length <mode_name>`
Vector < NonnullRefPtr < AST : : Node > > resulting_nodes ;
resulting_nodes . ensure_capacity ( values . size ( ) ) ;
for ( auto & entry : values ) {
// ImmediateExpression(length <mode_name> <entry>)
2021-09-02 22:07:29 +00:00
resulting_nodes . unchecked_append ( AST : : make_ref_counted < AST : : ImmediateExpression > (
2021-03-05 13:03:23 +00:00
expr_node - > position ( ) ,
2023-02-18 13:44:19 +00:00
AST : : NameWithPosition { TRY ( String : : from_utf8 ( " length " sv ) ) , invoking_node . function_position ( ) } ,
2021-03-05 13:03:23 +00:00
NonnullRefPtrVector < AST : : Node > { Vector < NonnullRefPtr < AST : : Node > > {
2023-02-18 13:44:19 +00:00
static_cast < NonnullRefPtr < AST : : Node > > ( AST : : make_ref_counted < AST : : BarewordLiteral > ( expr_node - > position ( ) , TRY ( String : : from_utf8 ( mode_name ) ) ) ) ,
2021-09-02 22:07:29 +00:00
AST : : make_ref_counted < AST : : SyntheticNode > ( expr_node - > position ( ) , NonnullRefPtr < AST : : Value > ( entry ) ) ,
2021-03-05 13:03:23 +00:00
} } ,
expr_node - > position ( ) ) ) ;
}
2021-09-02 22:07:29 +00:00
return AST : : make_ref_counted < AST : : ListConcatenate > ( invoking_node . position ( ) , move ( resulting_nodes ) ) ;
2021-03-05 13:03:23 +00:00
} ;
switch ( mode ) {
default :
case Infer :
VERIFY_NOT_REACHED ( ) ;
case List : {
auto value = ( const_cast < AST : : Node * > ( expr_node ) ) - > run ( this ) ;
if ( ! value )
return value_with_number ( 0 ) ;
2023-02-18 13:44:19 +00:00
value = TRY ( value - > resolve_without_cast ( this ) ) ;
2021-03-05 13:03:23 +00:00
if ( auto list = dynamic_cast < AST : : ListValue * > ( value . ptr ( ) ) ) {
if ( across )
2022-07-11 17:32:29 +00:00
return do_across ( " list " sv , list - > values ( ) ) ;
2021-03-05 13:03:23 +00:00
return value_with_number ( list - > values ( ) . size ( ) ) ;
}
2023-02-18 13:44:19 +00:00
auto list = TRY ( value - > resolve_as_list ( this ) ) ;
2021-03-05 13:03:23 +00:00
if ( ! across )
return value_with_number ( list . size ( ) ) ;
dbgln ( " List has {} entries " , list . size ( ) ) ;
2021-09-02 22:07:29 +00:00
auto values = AST : : make_ref_counted < AST : : ListValue > ( move ( list ) ) ;
2022-07-11 17:32:29 +00:00
return do_across ( " list " sv , values - > values ( ) ) ;
2021-03-05 13:03:23 +00:00
}
case String : {
// 'across' will only accept lists, and '!across' will only accept non-lists here.
if ( expr_node - > is_list ( ) ) {
if ( ! across ) {
raise_no_list_allowed : ;
Formatter formatter { * expr_node } ;
if ( is_inferred ) {
raise_error ( ShellError : : EvaluatedSyntaxError ,
2022-12-04 18:02:33 +00:00
DeprecatedString : : formatted ( " Could not infer expression type, please explicitly use `{0} string' or `{0} list' " , name ) ,
2021-03-05 13:03:23 +00:00
invoking_node . position ( ) ) ;
return nullptr ;
}
auto source = formatter . format ( ) ;
raise_error ( ShellError : : EvaluatedSyntaxError ,
source . is_empty ( )
? " Invalid application of `length' to a list "
2022-12-04 18:02:33 +00:00
: DeprecatedString : : formatted ( " Invalid application of `length' to a list \n perhaps you meant `{1}length \" {0} \" {2}' or `{1}length_across {0}{2}'? " , source , " \x1b [32m " , " \x1b [0m " ) ,
2021-03-05 13:03:23 +00:00
expr_node - > position ( ) ) ;
return nullptr ;
}
}
auto value = ( const_cast < AST : : Node * > ( expr_node ) ) - > run ( this ) ;
if ( ! value )
return value_with_number ( 0 ) ;
2023-02-18 13:44:19 +00:00
value = TRY ( value - > resolve_without_cast ( * this ) ) ;
2021-03-05 13:03:23 +00:00
if ( auto list = dynamic_cast < AST : : ListValue * > ( value . ptr ( ) ) ) {
if ( ! across )
goto raise_no_list_allowed ;
2022-07-11 17:32:29 +00:00
return do_across ( " string " sv , list - > values ( ) ) ;
2021-03-05 13:03:23 +00:00
}
if ( across & & ! value - > is_list ( ) ) {
Formatter formatter { * expr_node } ;
auto source = formatter . format ( ) ;
raise_error ( ShellError : : EvaluatedSyntaxError ,
2022-12-04 18:02:33 +00:00
DeprecatedString : : formatted ( " Invalid application of `length_across' to a non-list \n perhaps you meant `{1}length {0}{2}'? " , source , " \x1b [32m " , " \x1b [0m " ) ,
2021-03-05 13:03:23 +00:00
expr_node - > position ( ) ) ;
return nullptr ;
}
// Evaluate the nodes and substitute with the lengths.
2023-02-18 13:44:19 +00:00
auto list = TRY ( value - > resolve_as_list ( this ) ) ;
2021-03-05 13:03:23 +00:00
if ( ! expr_node - > is_list ( ) ) {
if ( list . size ( ) = = 1 ) {
if ( across )
goto raise_no_list_allowed ;
// This is the normal case, the expression is a normal non-list expression.
2023-02-18 06:45:08 +00:00
return value_with_number ( list . first ( ) . bytes_as_string_view ( ) . length ( ) ) ;
2021-03-05 13:03:23 +00:00
}
// This can be hit by asking for the length of a command list (e.g. `(>/dev/null)`)
// raise an error about misuse of command lists for now.
// FIXME: What's the length of `(>/dev/null)` supposed to be?
raise_error ( ShellError : : EvaluatedSyntaxError , " Length of meta value (or command list) requested, this is currently not supported. " , expr_node - > position ( ) ) ;
return nullptr ;
}
2021-09-02 22:07:29 +00:00
auto values = AST : : make_ref_counted < AST : : ListValue > ( move ( list ) ) ;
2022-07-11 17:32:29 +00:00
return do_across ( " string " sv , values - > values ( ) ) ;
2021-03-05 13:03:23 +00:00
}
}
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_length ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2021-03-05 13:03:23 +00:00
{
return immediate_length_impl ( invoking_node , arguments , false ) ;
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_length_across ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2021-03-05 13:03:23 +00:00
{
return immediate_length_impl ( invoking_node , arguments , true ) ;
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_regex_replace ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2021-03-05 13:03:23 +00:00
{
if ( arguments . size ( ) ! = 3 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 3 arguments to regex_replace " , invoking_node . position ( ) ) ;
return nullptr ;
}
auto pattern = const_cast < AST : : Node & > ( arguments [ 0 ] ) . run ( this ) ;
auto replacement = const_cast < AST : : Node & > ( arguments [ 1 ] ) . run ( this ) ;
2023-02-18 13:44:19 +00:00
auto value = TRY ( const_cast < AST : : Node & > ( arguments [ 2 ] ) . run ( this ) - > resolve_without_cast ( this ) ) ;
2021-03-05 13:03:23 +00:00
if ( ! pattern - > is_string ( ) ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected the regex_replace pattern to be a string " , arguments [ 0 ] . position ( ) ) ;
return nullptr ;
}
if ( ! replacement - > is_string ( ) ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected the regex_replace replacement string to be a string " , arguments [ 1 ] . position ( ) ) ;
return nullptr ;
}
if ( ! value - > is_string ( ) ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected the regex_replace target value to be a string " , arguments [ 2 ] . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
Regex < PosixExtendedParser > re { TRY ( pattern - > resolve_as_list ( this ) ) . first ( ) . to_deprecated_string ( ) } ;
2023-02-18 06:45:08 +00:00
auto result = re . replace (
2023-02-18 13:44:19 +00:00
TRY ( value - > resolve_as_list ( this ) ) [ 0 ] ,
TRY ( replacement - > resolve_as_list ( this ) ) [ 0 ] ,
2023-02-18 06:45:08 +00:00
PosixFlags : : Global | PosixFlags : : Multiline | PosixFlags : : Unicode ) ;
2021-03-05 13:03:23 +00:00
2023-02-18 13:44:19 +00:00
return AST : : make_ref_counted < AST : : StringLiteral > ( invoking_node . position ( ) , TRY ( String : : from_utf8 ( result ) ) , AST : : StringLiteral : : EnclosureType : : None ) ;
2021-03-05 13:03:23 +00:00
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_remove_suffix ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2021-03-05 13:03:23 +00:00
{
if ( arguments . size ( ) ! = 2 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 2 arguments to remove_suffix " , invoking_node . position ( ) ) ;
return nullptr ;
}
auto suffix = const_cast < AST : : Node & > ( arguments [ 0 ] ) . run ( this ) ;
2023-02-18 13:44:19 +00:00
auto value = TRY ( const_cast < AST : : Node & > ( arguments [ 1 ] ) . run ( this ) - > resolve_without_cast ( this ) ) ;
2021-03-05 13:03:23 +00:00
if ( ! suffix - > is_string ( ) ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected the remove_suffix suffix string to be a string " , arguments [ 0 ] . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto suffix_str = TRY ( suffix - > resolve_as_list ( this ) ) [ 0 ] ;
auto values = TRY ( value - > resolve_as_list ( this ) ) ;
2021-03-05 13:03:23 +00:00
Vector < NonnullRefPtr < AST : : Node > > nodes ;
for ( auto & value_str : values ) {
2023-02-18 13:44:19 +00:00
String removed = TRY ( String : : from_utf8 ( value_str ) ) ;
2021-03-05 13:03:23 +00:00
2023-02-18 06:45:08 +00:00
if ( value_str . bytes_as_string_view ( ) . ends_with ( suffix_str ) )
2023-02-18 13:44:19 +00:00
removed = TRY ( removed . substring_from_byte_offset ( 0 , value_str . bytes_as_string_view ( ) . length ( ) - suffix_str . bytes_as_string_view ( ) . length ( ) ) ) ;
2023-02-18 06:45:08 +00:00
nodes . append ( AST : : make_ref_counted < AST : : StringLiteral > ( invoking_node . position ( ) , move ( removed ) , AST : : StringLiteral : : EnclosureType : : None ) ) ;
2021-03-05 13:03:23 +00:00
}
2021-09-02 22:07:29 +00:00
return AST : : make_ref_counted < AST : : ListConcatenate > ( invoking_node . position ( ) , move ( nodes ) ) ;
2021-03-05 13:03:23 +00:00
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_remove_prefix ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2021-03-05 13:03:23 +00:00
{
if ( arguments . size ( ) ! = 2 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 2 arguments to remove_prefix " , invoking_node . position ( ) ) ;
return nullptr ;
}
auto prefix = const_cast < AST : : Node & > ( arguments [ 0 ] ) . run ( this ) ;
2023-02-18 13:44:19 +00:00
auto value = TRY ( const_cast < AST : : Node & > ( arguments [ 1 ] ) . run ( this ) - > resolve_without_cast ( this ) ) ;
2021-03-05 13:03:23 +00:00
if ( ! prefix - > is_string ( ) ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected the remove_prefix prefix string to be a string " , arguments [ 0 ] . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto prefix_str = TRY ( prefix - > resolve_as_list ( this ) ) [ 0 ] ;
auto values = TRY ( value - > resolve_as_list ( this ) ) ;
2021-03-05 13:03:23 +00:00
Vector < NonnullRefPtr < AST : : Node > > nodes ;
for ( auto & value_str : values ) {
2023-02-18 13:44:19 +00:00
String removed = TRY ( String : : from_utf8 ( value_str ) ) ;
2021-03-05 13:03:23 +00:00
2023-02-18 06:45:08 +00:00
if ( value_str . bytes_as_string_view ( ) . starts_with ( prefix_str ) )
2023-02-18 13:44:19 +00:00
removed = TRY ( removed . substring_from_byte_offset ( prefix_str . bytes_as_string_view ( ) . length ( ) ) ) ;
2023-02-18 06:45:08 +00:00
nodes . append ( AST : : make_ref_counted < AST : : StringLiteral > ( invoking_node . position ( ) , move ( removed ) , AST : : StringLiteral : : EnclosureType : : None ) ) ;
2021-03-05 13:03:23 +00:00
}
2021-09-02 22:07:29 +00:00
return AST : : make_ref_counted < AST : : ListConcatenate > ( invoking_node . position ( ) , move ( nodes ) ) ;
2021-03-05 13:03:23 +00:00
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_split ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2021-03-05 13:03:23 +00:00
{
if ( arguments . size ( ) ! = 2 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 2 arguments to split " , invoking_node . position ( ) ) ;
return nullptr ;
}
auto delimiter = const_cast < AST : : Node & > ( arguments [ 0 ] ) . run ( this ) ;
2023-02-18 13:44:19 +00:00
auto value = TRY ( const_cast < AST : : Node & > ( arguments [ 1 ] ) . run ( this ) - > resolve_without_cast ( this ) ) ;
2021-03-05 13:03:23 +00:00
if ( ! delimiter - > is_string ( ) ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected the split delimiter string to be a string " , arguments [ 0 ] . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto delimiter_str = TRY ( delimiter - > resolve_as_list ( this ) ) [ 0 ] ;
2021-03-05 13:03:23 +00:00
2022-04-01 17:58:27 +00:00
auto transform = [ & ] ( auto const & values ) {
2021-03-05 13:03:23 +00:00
// Translate to a list of applications of `split <delimiter>`
Vector < NonnullRefPtr < AST : : Node > > resulting_nodes ;
resulting_nodes . ensure_capacity ( values . size ( ) ) ;
for ( auto & entry : values ) {
// ImmediateExpression(split <delimiter> <entry>)
2021-09-02 22:07:29 +00:00
resulting_nodes . unchecked_append ( AST : : make_ref_counted < AST : : ImmediateExpression > (
2021-03-05 13:03:23 +00:00
arguments [ 1 ] . position ( ) ,
invoking_node . function ( ) ,
NonnullRefPtrVector < AST : : Node > { Vector < NonnullRefPtr < AST : : Node > > {
arguments [ 0 ] ,
2021-09-02 22:07:29 +00:00
AST : : make_ref_counted < AST : : SyntheticNode > ( arguments [ 1 ] . position ( ) , NonnullRefPtr < AST : : Value > ( entry ) ) ,
2021-03-05 13:03:23 +00:00
} } ,
arguments [ 1 ] . position ( ) ) ) ;
}
2021-09-02 22:07:29 +00:00
return AST : : make_ref_counted < AST : : ListConcatenate > ( invoking_node . position ( ) , move ( resulting_nodes ) ) ;
2021-03-05 13:03:23 +00:00
} ;
if ( auto list = dynamic_cast < AST : : ListValue * > ( value . ptr ( ) ) ) {
return transform ( list - > values ( ) ) ;
}
// Otherwise, just resolve to a list and transform that.
2023-02-18 13:44:19 +00:00
auto list = TRY ( value - > resolve_as_list ( this ) ) ;
2021-03-05 13:03:23 +00:00
if ( ! value - > is_list ( ) ) {
if ( list . is_empty ( ) )
2021-09-02 22:07:29 +00:00
return AST : : make_ref_counted < AST : : ListConcatenate > ( invoking_node . position ( ) , NonnullRefPtrVector < AST : : Node > { } ) ;
2021-03-05 13:03:23 +00:00
auto & value = list . first ( ) ;
2023-02-18 06:45:08 +00:00
Vector < String > split_strings ;
2021-03-05 13:03:23 +00:00
if ( delimiter_str . is_empty ( ) ) {
StringBuilder builder ;
for ( auto code_point : Utf8View { value } ) {
builder . append_code_point ( code_point ) ;
2023-02-18 13:44:19 +00:00
split_strings . append ( TRY ( builder . to_string ( ) ) ) ;
2021-03-05 13:03:23 +00:00
builder . clear ( ) ;
}
} else {
2022-10-22 13:38:21 +00:00
auto split = StringView { value } . split_view ( delimiter_str , options . inline_exec_keep_empty_segments ? SplitBehavior : : KeepEmpty : SplitBehavior : : Nothing ) ;
2021-03-05 13:03:23 +00:00
split_strings . ensure_capacity ( split . size ( ) ) ;
for ( auto & entry : split )
2023-02-18 13:44:19 +00:00
split_strings . append ( TRY ( String : : from_utf8 ( entry ) ) ) ;
2021-03-05 13:03:23 +00:00
}
2021-09-02 22:07:29 +00:00
return AST : : make_ref_counted < AST : : SyntheticNode > ( invoking_node . position ( ) , AST : : make_ref_counted < AST : : ListValue > ( move ( split_strings ) ) ) ;
2021-03-05 13:03:23 +00:00
}
2021-09-02 22:07:29 +00:00
return transform ( AST : : make_ref_counted < AST : : ListValue > ( list ) - > values ( ) ) ;
2021-03-05 13:03:23 +00:00
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_concat_lists ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2021-03-05 13:03:23 +00:00
{
NonnullRefPtrVector < AST : : Node > result ;
for ( auto & argument : arguments ) {
if ( auto * list = dynamic_cast < const AST : : ListConcatenate * > ( & argument ) ) {
2021-06-12 11:24:45 +00:00
result . extend ( list - > list ( ) ) ;
2021-03-05 13:03:23 +00:00
} else {
2023-02-18 13:44:19 +00:00
auto list_of_values = TRY ( const_cast < AST : : Node & > ( argument ) . run ( this ) - > resolve_without_cast ( this ) ) ;
2021-03-05 13:03:23 +00:00
if ( auto * list = dynamic_cast < AST : : ListValue * > ( list_of_values . ptr ( ) ) ) {
for ( auto & entry : static_cast < Vector < NonnullRefPtr < AST : : Value > > & > ( list - > values ( ) ) )
2021-09-02 22:07:29 +00:00
result . append ( AST : : make_ref_counted < AST : : SyntheticNode > ( argument . position ( ) , entry ) ) ;
2021-03-05 13:03:23 +00:00
} else {
2023-02-18 13:44:19 +00:00
auto values = TRY ( list_of_values - > resolve_as_list ( this ) ) ;
2021-03-05 13:03:23 +00:00
for ( auto & entry : values )
2022-03-06 08:28:49 +00:00
result . append ( AST : : make_ref_counted < AST : : StringLiteral > ( argument . position ( ) , entry , AST : : StringLiteral : : EnclosureType : : None ) ) ;
2021-03-05 13:03:23 +00:00
}
}
}
2021-09-02 22:07:29 +00:00
return AST : : make_ref_counted < AST : : ListConcatenate > ( invoking_node . position ( ) , move ( result ) ) ;
2021-03-05 13:03:23 +00:00
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_filter_glob ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2022-02-23 05:46:31 +00:00
{
// filter_glob string list
if ( arguments . size ( ) ! = 2 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly two arguments to filter_glob (<glob> <list>) " , invoking_node . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto glob_list = TRY ( const_cast < AST : : Node & > ( arguments [ 0 ] ) . run ( * this ) - > resolve_as_list ( * this ) ) ;
2022-02-23 05:46:31 +00:00
if ( glob_list . size ( ) ! = 1 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected the <glob> argument to filter_glob to be a single string " , arguments [ 0 ] . position ( ) ) ;
return nullptr ;
}
auto & glob = glob_list . first ( ) ;
auto & list_node = arguments [ 1 ] ;
NonnullRefPtrVector < AST : : Node > result ;
const_cast < AST : : Node & > ( list_node ) . for_each_entry ( * this , [ & ] ( NonnullRefPtr < AST : : Value > entry ) {
2023-02-18 06:45:08 +00:00
auto value = entry - > resolve_as_list ( * this ) . release_value_but_fixme_should_propagate_errors ( ) ;
2022-02-23 05:46:31 +00:00
if ( value . size ( ) = = 0 )
return IterationDecision : : Continue ;
if ( value . size ( ) = = 1 ) {
2023-02-18 06:45:08 +00:00
if ( ! value . first ( ) . bytes_as_string_view ( ) . matches ( glob ) )
2022-02-23 05:46:31 +00:00
return IterationDecision : : Continue ;
2022-03-22 20:44:48 +00:00
result . append ( AST : : make_ref_counted < AST : : StringLiteral > ( arguments [ 1 ] . position ( ) , value . first ( ) , AST : : StringLiteral : : EnclosureType : : None ) ) ;
2022-02-23 05:46:31 +00:00
return IterationDecision : : Continue ;
}
for ( auto & entry : value ) {
2023-02-18 06:45:08 +00:00
if ( entry . bytes_as_string_view ( ) . matches ( glob ) ) {
2022-02-23 05:46:31 +00:00
NonnullRefPtrVector < AST : : Node > nodes ;
for ( auto & string : value )
2022-03-22 20:44:48 +00:00
nodes . append ( AST : : make_ref_counted < AST : : StringLiteral > ( arguments [ 1 ] . position ( ) , string , AST : : StringLiteral : : EnclosureType : : None ) ) ;
2022-02-23 05:46:31 +00:00
result . append ( AST : : make_ref_counted < AST : : ListConcatenate > ( arguments [ 1 ] . position ( ) , move ( nodes ) ) ) ;
return IterationDecision : : Continue ;
}
}
return IterationDecision : : Continue ;
} ) ;
return AST : : make_ref_counted < AST : : ListConcatenate > ( invoking_node . position ( ) , move ( result ) ) ;
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_join ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2022-02-23 05:46:31 +00:00
{
if ( arguments . size ( ) ! = 2 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 2 arguments to join " , invoking_node . position ( ) ) ;
return nullptr ;
}
auto delimiter = const_cast < AST : : Node & > ( arguments [ 0 ] ) . run ( this ) ;
if ( ! delimiter - > is_string ( ) ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected the join delimiter string to be a string " , arguments [ 0 ] . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto value = TRY ( const_cast < AST : : Node & > ( arguments [ 1 ] ) . run ( this ) - > resolve_without_cast ( this ) ) ;
2022-02-23 05:46:31 +00:00
if ( ! value - > is_list ( ) ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected the joined list to be a list " , arguments [ 1 ] . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto delimiter_str = TRY ( delimiter - > resolve_as_list ( this ) ) [ 0 ] ;
2022-02-23 05:46:31 +00:00
StringBuilder builder ;
2023-02-18 13:44:19 +00:00
builder . join ( delimiter_str , TRY ( value - > resolve_as_list ( * this ) ) ) ;
2022-02-23 05:46:31 +00:00
2023-02-18 13:44:19 +00:00
return AST : : make_ref_counted < AST : : StringLiteral > ( invoking_node . position ( ) , TRY ( builder . to_string ( ) ) , AST : : StringLiteral : : EnclosureType : : None ) ;
2022-02-23 05:46:31 +00:00
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_value_or_default ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2023-02-11 14:29:15 +00:00
{
if ( arguments . size ( ) ! = 2 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 2 arguments to value_or_default " , invoking_node . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto name = TRY ( const_cast < AST : : Node & > ( arguments . first ( ) ) . run ( * this ) - > resolve_as_string ( * this ) ) ;
2023-02-11 14:29:15 +00:00
if ( ! local_variable_or ( name , " " sv ) . is_empty ( ) )
return make_ref_counted < AST : : SimpleVariable > ( invoking_node . position ( ) , name ) ;
return arguments . last ( ) ;
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_assign_default ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2023-02-11 14:29:15 +00:00
{
if ( arguments . size ( ) ! = 2 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 2 arguments to assign_default " , invoking_node . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto name = TRY ( const_cast < AST : : Node & > ( arguments . first ( ) ) . run ( * this ) - > resolve_as_string ( * this ) ) ;
2023-02-11 14:29:15 +00:00
if ( ! local_variable_or ( name , " " sv ) . is_empty ( ) )
return make_ref_counted < AST : : SimpleVariable > ( invoking_node . position ( ) , name ) ;
2023-02-18 13:44:19 +00:00
auto value = TRY ( const_cast < AST : : Node & > ( arguments . last ( ) ) . run ( * this ) - > resolve_without_cast ( * this ) ) ;
2023-02-18 06:45:08 +00:00
set_local_variable ( name . to_deprecated_string ( ) , value ) ;
2023-02-11 14:29:15 +00:00
return make_ref_counted < AST : : SyntheticNode > ( invoking_node . position ( ) , value ) ;
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_error_if_empty ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2023-02-11 14:29:15 +00:00
{
if ( arguments . size ( ) ! = 2 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 2 arguments to error_if_empty " , invoking_node . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto name = TRY ( const_cast < AST : : Node & > ( arguments . first ( ) ) . run ( * this ) - > resolve_as_string ( * this ) ) ;
2023-02-11 14:29:15 +00:00
if ( ! local_variable_or ( name , " " sv ) . is_empty ( ) )
return make_ref_counted < AST : : SimpleVariable > ( invoking_node . position ( ) , name ) ;
2023-02-18 13:44:19 +00:00
auto error_value = TRY ( const_cast < AST : : Node & > ( arguments . last ( ) ) . run ( * this ) - > resolve_as_string ( * this ) ) ;
2023-02-11 14:29:15 +00:00
if ( error_value . is_empty ( ) )
2023-02-18 13:44:19 +00:00
error_value = TRY ( String : : formatted ( " Expected {} to be non-empty " , name ) ) ;
2023-02-11 14:29:15 +00:00
2023-02-18 06:45:08 +00:00
raise_error ( ShellError : : EvaluatedSyntaxError , error_value . bytes_as_string_view ( ) , invoking_node . position ( ) ) ;
2023-02-11 14:29:15 +00:00
return nullptr ;
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_null_or_alternative ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2023-02-11 14:29:15 +00:00
{
if ( arguments . size ( ) ! = 2 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 2 arguments to null_or_alternative " , invoking_node . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto value = TRY ( const_cast < AST : : Node & > ( arguments . first ( ) ) . run ( * this ) - > resolve_without_cast ( * this ) ) ;
if ( ( value - > is_string ( ) & & TRY ( value - > resolve_as_string ( * this ) ) . is_empty ( ) ) | | ( value - > is_list ( ) & & TRY ( value - > resolve_as_list ( * this ) ) . is_empty ( ) ) )
2023-02-11 14:29:15 +00:00
return make_ref_counted < AST : : SyntheticNode > ( invoking_node . position ( ) , value ) ;
return arguments . last ( ) ;
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_defined_value_or_default ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2023-02-11 14:29:15 +00:00
{
if ( arguments . size ( ) ! = 2 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 2 arguments to defined_value_or_default " , invoking_node . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto name = TRY ( const_cast < AST : : Node & > ( arguments . first ( ) ) . run ( * this ) - > resolve_as_string ( * this ) ) ;
2023-02-11 14:29:15 +00:00
if ( ! find_frame_containing_local_variable ( name ) )
return arguments . last ( ) ;
return make_ref_counted < AST : : SimpleVariable > ( invoking_node . position ( ) , name ) ;
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_assign_defined_default ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2023-02-11 14:29:15 +00:00
{
if ( arguments . size ( ) ! = 2 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 2 arguments to assign_defined_default " , invoking_node . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto name = TRY ( const_cast < AST : : Node & > ( arguments . first ( ) ) . run ( * this ) - > resolve_as_string ( * this ) ) ;
2023-02-11 14:29:15 +00:00
if ( find_frame_containing_local_variable ( name ) )
return make_ref_counted < AST : : SimpleVariable > ( invoking_node . position ( ) , name ) ;
2023-02-18 13:44:19 +00:00
auto value = TRY ( const_cast < AST : : Node & > ( arguments . last ( ) ) . run ( * this ) - > resolve_without_cast ( * this ) ) ;
2023-02-18 06:45:08 +00:00
set_local_variable ( name . to_deprecated_string ( ) , value ) ;
2023-02-11 14:29:15 +00:00
return make_ref_counted < AST : : SyntheticNode > ( invoking_node . position ( ) , value ) ;
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_error_if_unset ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2023-02-11 14:29:15 +00:00
{
if ( arguments . size ( ) ! = 2 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 2 arguments to error_if_unset " , invoking_node . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto name = TRY ( const_cast < AST : : Node & > ( arguments . first ( ) ) . run ( * this ) - > resolve_as_string ( * this ) ) ;
2023-02-11 14:29:15 +00:00
if ( find_frame_containing_local_variable ( name ) )
return make_ref_counted < AST : : SimpleVariable > ( invoking_node . position ( ) , name ) ;
2023-02-18 13:44:19 +00:00
auto error_value = TRY ( const_cast < AST : : Node & > ( arguments . last ( ) ) . run ( * this ) - > resolve_as_string ( * this ) ) ;
2023-02-11 14:29:15 +00:00
if ( error_value . is_empty ( ) )
2023-02-18 13:44:19 +00:00
error_value = TRY ( String : : formatted ( " Expected {} to be set " , name ) ) ;
2023-02-11 14:29:15 +00:00
2023-02-18 06:45:08 +00:00
raise_error ( ShellError : : EvaluatedSyntaxError , error_value . bytes_as_string_view ( ) , invoking_node . position ( ) ) ;
2023-02-11 14:29:15 +00:00
return nullptr ;
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_null_if_unset_or_alternative ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2023-02-11 14:29:15 +00:00
{
if ( arguments . size ( ) ! = 2 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 2 arguments to null_if_unset_or_alternative " , invoking_node . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto name = TRY ( const_cast < AST : : Node & > ( arguments . first ( ) ) . run ( * this ) - > resolve_as_string ( * this ) ) ;
2023-02-11 14:29:15 +00:00
if ( ! find_frame_containing_local_variable ( name ) )
return arguments . last ( ) ;
return make_ref_counted < AST : : SimpleVariable > ( invoking_node . position ( ) , name ) ;
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_reexpand ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2023-02-11 14:29:15 +00:00
{
if ( arguments . size ( ) ! = 1 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 1 argument to reexpand " , invoking_node . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto value = TRY ( const_cast < AST : : Node & > ( arguments . first ( ) ) . run ( * this ) - > resolve_as_string ( * this ) ) ;
2023-02-11 14:29:15 +00:00
return parse ( value , m_is_interactive , false ) ;
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : immediate_length_of_variable ( AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2023-02-11 14:29:15 +00:00
{
if ( arguments . size ( ) ! = 1 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected exactly 1 argument to length_of_variable " , invoking_node . position ( ) ) ;
return nullptr ;
}
2023-02-18 13:44:19 +00:00
auto name = TRY ( const_cast < AST : : Node & > ( arguments . first ( ) ) . run ( * this ) - > resolve_as_string ( * this ) ) ;
2023-02-11 14:29:15 +00:00
auto variable = make_ref_counted < AST : : SimpleVariable > ( invoking_node . position ( ) , name ) ;
return immediate_length_impl (
invoking_node ,
{ move ( variable ) } ,
false ) ;
}
2023-02-18 13:44:19 +00:00
ErrorOr < RefPtr < AST : : Node > > Shell : : run_immediate_function ( StringView str , AST : : ImmediateExpression & invoking_node , NonnullRefPtrVector < AST : : Node > const & arguments )
2021-03-05 13:03:23 +00:00
{
# define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(name) \
if ( str = = # name ) \
return immediate_ # # name ( invoking_node , arguments ) ;
ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS ( )
# undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION
2022-12-04 18:02:33 +00:00
raise_error ( ShellError : : EvaluatedSyntaxError , DeprecatedString : : formatted ( " Unknown immediate function {} " , str ) , invoking_node . position ( ) ) ;
2021-03-05 13:03:23 +00:00
return nullptr ;
}
2021-11-10 23:55:02 +00:00
bool Shell : : has_immediate_function ( StringView str )
2021-03-05 13:03:23 +00:00
{
# define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(name) \
if ( str = = # name ) \
return true ;
ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS ( )
# undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION
return false ;
}
}