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 {
2022-04-01 17:58:27 +00:00
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 ) {
raise_error ( ShellError : : EvaluatedSyntaxError , String : : formatted ( " Expected one or two arguments to `{}' " , name ) , invoking_node . position ( ) ) ;
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 ( ) ) {
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 ( ) ) ;
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 {
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 ( ) ) ;
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
mode = const_cast < AST : : Node * > ( expr_node ) - > run ( this ) - > resolve_without_cast ( this ) - > is_list_without_resolution ( ) ? List : String ;
else if ( is < AST : : ImmediateExpression > ( expr_node ) )
mode = List ;
else
mode = String ;
}
auto value_with_number = [ & ] ( auto number ) - > NonnullRefPtr < AST : : Node > {
2021-09-02 22:07:29 +00:00
return AST : : make_ref_counted < AST : : BarewordLiteral > ( invoking_node . position ( ) , String : : number ( number ) ) ;
2021-03-05 13:03:23 +00:00
} ;
auto do_across = [ & ] ( StringView mode_name , auto & values ) {
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 ( ) ,
AST : : NameWithPosition { " length " , invoking_node . function_position ( ) } ,
NonnullRefPtrVector < AST : : Node > { Vector < NonnullRefPtr < AST : : Node > > {
2021-09-02 22:07:29 +00:00
static_cast < NonnullRefPtr < AST : : Node > > ( AST : : make_ref_counted < AST : : BarewordLiteral > ( expr_node - > position ( ) , mode_name ) ) ,
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 ) ;
value = value - > resolve_without_cast ( this ) ;
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 ( ) ) ;
}
auto list = value - > resolve_as_list ( this ) ;
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 ,
String : : formatted ( " Could not infer expression type, please explicitly use `{0} string' or `{0} list' " , name ) ,
invoking_node . position ( ) ) ;
return nullptr ;
}
auto source = formatter . format ( ) ;
raise_error ( ShellError : : EvaluatedSyntaxError ,
source . is_empty ( )
? " Invalid application of `length' to a list "
: String : : 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 " ) ,
expr_node - > position ( ) ) ;
return nullptr ;
}
}
auto value = ( const_cast < AST : : Node * > ( expr_node ) ) - > run ( this ) ;
if ( ! value )
return value_with_number ( 0 ) ;
value = value - > resolve_without_cast ( * this ) ;
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 ,
String : : formatted ( " Invalid application of `length_across' to a non-list \n perhaps you meant `{1}length {0}{2}'? " , source , " \x1b [32m " , " \x1b [0m " ) ,
expr_node - > position ( ) ) ;
return nullptr ;
}
// Evaluate the nodes and substitute with the lengths.
auto list = value - > resolve_as_list ( this ) ;
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.
return value_with_number ( list . first ( ) . length ( ) ) ;
}
// 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
}
}
}
2022-04-01 17:58:27 +00:00
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 ) ;
}
2022-04-01 17:58:27 +00:00
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 ) ;
}
2022-04-01 17:58:27 +00:00
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 ) ;
auto value = const_cast < AST : : Node & > ( arguments [ 2 ] ) . run ( this ) - > resolve_without_cast ( this ) ;
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 ;
}
Regex < PosixExtendedParser > re { pattern - > resolve_as_list ( this ) . first ( ) } ;
auto result = re . replace ( value - > resolve_as_list ( this ) [ 0 ] , replacement - > resolve_as_list ( this ) [ 0 ] , PosixFlags : : Global | PosixFlags : : Multiline | PosixFlags : : Unicode ) ;
2022-03-06 08:28:49 +00:00
return AST : : make_ref_counted < AST : : StringLiteral > ( invoking_node . position ( ) , move ( result ) , AST : : StringLiteral : : EnclosureType : : None ) ;
2021-03-05 13:03:23 +00:00
}
2022-04-01 17:58:27 +00:00
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 ) ;
auto value = const_cast < AST : : Node & > ( arguments [ 1 ] ) . run ( this ) - > resolve_without_cast ( this ) ;
if ( ! suffix - > is_string ( ) ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected the remove_suffix suffix string to be a string " , arguments [ 0 ] . position ( ) ) ;
return nullptr ;
}
auto suffix_str = suffix - > resolve_as_list ( this ) [ 0 ] ;
auto values = value - > resolve_as_list ( this ) ;
Vector < NonnullRefPtr < AST : : Node > > nodes ;
for ( auto & value_str : values ) {
StringView removed { value_str } ;
if ( value_str . ends_with ( suffix_str ) )
removed = removed . substring_view ( 0 , value_str . length ( ) - suffix_str . length ( ) ) ;
2022-03-06 08:28:49 +00:00
nodes . append ( AST : : make_ref_counted < AST : : StringLiteral > ( invoking_node . position ( ) , 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
}
2022-04-01 17:58:27 +00:00
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 ) ;
auto value = const_cast < AST : : Node & > ( arguments [ 1 ] ) . run ( this ) - > resolve_without_cast ( this ) ;
if ( ! prefix - > is_string ( ) ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected the remove_prefix prefix string to be a string " , arguments [ 0 ] . position ( ) ) ;
return nullptr ;
}
auto prefix_str = prefix - > resolve_as_list ( this ) [ 0 ] ;
auto values = value - > resolve_as_list ( this ) ;
Vector < NonnullRefPtr < AST : : Node > > nodes ;
for ( auto & value_str : values ) {
StringView removed { value_str } ;
if ( value_str . starts_with ( prefix_str ) )
removed = removed . substring_view ( prefix_str . length ( ) ) ;
2022-03-06 08:28:49 +00:00
nodes . append ( AST : : make_ref_counted < AST : : StringLiteral > ( invoking_node . position ( ) , 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
}
2022-04-01 17:58:27 +00:00
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 ) ;
auto value = const_cast < AST : : Node & > ( arguments [ 1 ] ) . run ( this ) - > resolve_without_cast ( this ) ;
if ( ! delimiter - > is_string ( ) ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected the split delimiter string to be a string " , arguments [ 0 ] . position ( ) ) ;
return nullptr ;
}
auto delimiter_str = delimiter - > resolve_as_list ( this ) [ 0 ] ;
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.
auto list = value - > resolve_as_list ( this ) ;
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 ( ) ;
Vector < String > split_strings ;
if ( delimiter_str . is_empty ( ) ) {
StringBuilder builder ;
for ( auto code_point : Utf8View { value } ) {
builder . append_code_point ( code_point ) ;
split_strings . append ( builder . build ( ) ) ;
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 )
split_strings . append ( entry ) ;
}
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
}
2022-04-01 17:58:27 +00:00
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 {
auto list_of_values = const_cast < AST : : Node & > ( argument ) . run ( this ) - > resolve_without_cast ( this ) ;
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 {
auto values = list_of_values - > resolve_as_list ( this ) ;
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
}
2022-04-01 17:58:27 +00:00
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 ;
}
auto glob_list = const_cast < AST : : Node & > ( arguments [ 0 ] ) . run ( * this ) - > resolve_as_list ( * this ) ;
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 ) {
auto value = entry - > resolve_as_list ( * this ) ;
if ( value . size ( ) = = 0 )
return IterationDecision : : Continue ;
if ( value . size ( ) = = 1 ) {
if ( ! value . first ( ) . matches ( glob ) )
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 ) {
if ( entry . matches ( glob ) ) {
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 ) ) ;
}
2022-04-01 17:58:27 +00:00
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 ;
}
auto value = const_cast < AST : : Node & > ( arguments [ 1 ] ) . run ( this ) - > resolve_without_cast ( this ) ;
if ( ! value - > is_list ( ) ) {
raise_error ( ShellError : : EvaluatedSyntaxError , " Expected the joined list to be a list " , arguments [ 1 ] . position ( ) ) ;
return nullptr ;
}
auto delimiter_str = delimiter - > resolve_as_list ( this ) [ 0 ] ;
StringBuilder builder ;
builder . join ( delimiter_str , value - > resolve_as_list ( * this ) ) ;
2022-03-22 20:44:48 +00:00
return AST : : make_ref_counted < AST : : StringLiteral > ( invoking_node . position ( ) , builder . to_string ( ) , AST : : StringLiteral : : EnclosureType : : None ) ;
2022-02-23 05:46:31 +00:00
}
2022-04-01 17:58:27 +00:00
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
raise_error ( ShellError : : EvaluatedSyntaxError , String : : formatted ( " Unknown immediate function {} " , str ) , invoking_node . position ( ) ) ;
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 ;
}
}