mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 14:48:14 +00:00
Shell: Add support for 'immediate' expressions as variable substitutions
This commit adds a few basic variable substitution operations: - length Find the length of a string or a list - length_across Find the lengths of things inside a list - remove_{suffix,prefix} Remove a suffix or a prefix from all the passed values - regex_replace Replace all matches of a given regex with a given template - split Split the given string with the given delimiter (or to its code points if the delimiter is empty) - concat_lists concatenates any given lists into one Closes #4316 (the ancient version of this same feature)
This commit is contained in:
parent
a303b69caa
commit
a45b2ea6fb
16 changed files with 911 additions and 37 deletions
|
@ -88,8 +88,8 @@ bool Parser::expect(const StringView& expected)
|
|||
if (expected.length() + m_offset > m_input.length())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < expected.length(); ++i) {
|
||||
if (peek() != expected[i]) {
|
||||
for (auto& c : expected) {
|
||||
if (peek() != c) {
|
||||
restore_to(offset_at_start, line_at_start);
|
||||
return false;
|
||||
}
|
||||
|
@ -353,7 +353,7 @@ RefPtr<AST::Node> Parser::parse_function_decl()
|
|||
if (!expect('('))
|
||||
return restore();
|
||||
|
||||
Vector<AST::FunctionDeclaration::NameWithPosition> arguments;
|
||||
Vector<AST::NameWithPosition> arguments;
|
||||
for (;;) {
|
||||
consume_while(is_whitespace);
|
||||
|
||||
|
@ -380,7 +380,7 @@ RefPtr<AST::Node> Parser::parse_function_decl()
|
|||
}
|
||||
if (!expect('{')) {
|
||||
return create<AST::FunctionDeclaration>(
|
||||
AST::FunctionDeclaration::NameWithPosition {
|
||||
AST::NameWithPosition {
|
||||
move(function_name),
|
||||
{ pos_before_name.offset, pos_after_name.offset, pos_before_name.line, pos_after_name.line } },
|
||||
move(arguments),
|
||||
|
@ -404,7 +404,7 @@ RefPtr<AST::Node> Parser::parse_function_decl()
|
|||
body = move(syntax_error);
|
||||
|
||||
return create<AST::FunctionDeclaration>(
|
||||
AST::FunctionDeclaration::NameWithPosition {
|
||||
AST::NameWithPosition {
|
||||
move(function_name),
|
||||
{ pos_before_name.offset, pos_after_name.offset, pos_before_name.line, pos_after_name.line } },
|
||||
move(arguments),
|
||||
|
@ -413,7 +413,7 @@ RefPtr<AST::Node> Parser::parse_function_decl()
|
|||
}
|
||||
|
||||
return create<AST::FunctionDeclaration>(
|
||||
AST::FunctionDeclaration::NameWithPosition {
|
||||
AST::NameWithPosition {
|
||||
move(function_name),
|
||||
{ pos_before_name.offset, pos_after_name.offset, pos_before_name.line, pos_after_name.line } },
|
||||
move(arguments),
|
||||
|
@ -1101,6 +1101,9 @@ RefPtr<AST::Node> Parser::parse_expression()
|
|||
if (auto variable = parse_variable())
|
||||
return read_concat(variable.release_nonnull());
|
||||
|
||||
if (auto immediate = parse_immediate_expression())
|
||||
return read_concat(immediate.release_nonnull());
|
||||
|
||||
if (auto inline_exec = parse_evaluate())
|
||||
return read_concat(inline_exec.release_nonnull());
|
||||
}
|
||||
|
@ -1266,29 +1269,26 @@ RefPtr<AST::Node> Parser::parse_doublequoted_string_inner()
|
|||
}
|
||||
if (peek() == '$') {
|
||||
auto string_literal = create<AST::StringLiteral>(builder.to_string()); // String Literal
|
||||
if (auto variable = parse_variable()) {
|
||||
auto read_concat = [&](auto&& node) {
|
||||
auto inner = create<AST::StringPartCompose>(
|
||||
move(string_literal),
|
||||
variable.release_nonnull()); // Compose String Variable
|
||||
move(node)); // Compose String Node
|
||||
|
||||
if (auto string = parse_doublequoted_string_inner()) {
|
||||
return create<AST::StringPartCompose>(move(inner), string.release_nonnull()); // Compose Composition Composition
|
||||
}
|
||||
|
||||
return inner;
|
||||
}
|
||||
};
|
||||
|
||||
if (auto evaluate = parse_evaluate()) {
|
||||
auto composition = create<AST::StringPartCompose>(
|
||||
move(string_literal),
|
||||
evaluate.release_nonnull()); // Compose String Sequence
|
||||
if (auto variable = parse_variable())
|
||||
return read_concat(variable.release_nonnull());
|
||||
|
||||
if (auto string = parse_doublequoted_string_inner()) {
|
||||
return create<AST::StringPartCompose>(move(composition), string.release_nonnull()); // Compose Composition Composition
|
||||
}
|
||||
if (auto immediate = parse_immediate_expression())
|
||||
return read_concat(immediate.release_nonnull());
|
||||
|
||||
return composition;
|
||||
}
|
||||
if (auto evaluate = parse_evaluate())
|
||||
return read_concat(evaluate.release_nonnull());
|
||||
}
|
||||
|
||||
builder.append(consume());
|
||||
|
@ -1364,6 +1364,75 @@ RefPtr<AST::Node> Parser::parse_evaluate()
|
|||
return inner;
|
||||
}
|
||||
|
||||
RefPtr<AST::Node> Parser::parse_immediate_expression()
|
||||
{
|
||||
auto rule_start = push_start();
|
||||
if (at_end())
|
||||
return nullptr;
|
||||
|
||||
if (peek() != '$')
|
||||
return nullptr;
|
||||
|
||||
consume();
|
||||
|
||||
if (peek() != '{') {
|
||||
restore_to(*rule_start);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
consume();
|
||||
consume_while(is_whitespace);
|
||||
|
||||
auto function_name_start_offset = current_position();
|
||||
auto function_name = consume_while(is_word_character);
|
||||
auto function_name_end_offset = current_position();
|
||||
AST::Position function_position {
|
||||
function_name_start_offset.offset,
|
||||
function_name_end_offset.offset,
|
||||
function_name_start_offset.line,
|
||||
function_name_end_offset.line,
|
||||
};
|
||||
|
||||
consume_while(is_whitespace);
|
||||
|
||||
NonnullRefPtrVector<AST::Node> arguments;
|
||||
do {
|
||||
auto expr = parse_expression();
|
||||
if (!expr)
|
||||
break;
|
||||
arguments.append(expr.release_nonnull());
|
||||
} while (!consume_while(is_whitespace).is_empty());
|
||||
|
||||
auto ending_brace_start_offset = current_position();
|
||||
if (peek() == '}')
|
||||
consume();
|
||||
|
||||
auto ending_brace_end_offset = current_position();
|
||||
|
||||
auto ending_brace_position = ending_brace_start_offset.offset == ending_brace_end_offset.offset
|
||||
? Optional<AST::Position> {}
|
||||
: Optional<AST::Position> {
|
||||
AST::Position {
|
||||
ending_brace_start_offset.offset,
|
||||
ending_brace_end_offset.offset,
|
||||
ending_brace_start_offset.line,
|
||||
ending_brace_end_offset.line,
|
||||
}
|
||||
};
|
||||
|
||||
auto node = create<AST::ImmediateExpression>(
|
||||
AST::NameWithPosition { function_name, move(function_position) },
|
||||
move(arguments),
|
||||
ending_brace_position);
|
||||
|
||||
if (!ending_brace_position.has_value())
|
||||
node->set_is_syntax_error(create<AST::SyntaxError>("Expected a closing brace '}' to end an immediate expression", true));
|
||||
else if (node->function_name().is_empty())
|
||||
node->set_is_syntax_error(create<AST::SyntaxError>("Expected an immediate function name"));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
RefPtr<AST::Node> Parser::parse_history_designator()
|
||||
{
|
||||
auto rule_start = push_start();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue