2020-01-18 11:38:21 +03:00
/*
2020-06-17 16:35:06 +03:00
* Copyright ( c ) 2020 , the SerenityOS developers .
2020-01-18 11:38:21 +03:00
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2019-05-07 02:12:08 +03:00
# include "Parser.h"
2020-04-30 03:56:16 +03:00
# include <ctype.h>
2019-05-07 02:12:08 +03:00
# include <stdio.h>
# include <unistd.h>
2020-06-17 16:35:06 +03:00
char Parser : : peek ( )
2019-05-07 02:12:08 +03:00
{
2020-06-17 16:35:06 +03:00
if ( m_offset = = m_input . length ( ) )
return 0 ;
ASSERT ( m_offset < m_input . length ( ) ) ;
2020-09-16 03:41:09 +03:00
auto ch = m_input [ m_offset ] ;
if ( ch = = ' \\ ' & & m_input . length ( ) > m_offset + 1 & & m_input [ m_offset + 1 ] = = ' \n ' ) {
m_offset + = 2 ;
return peek ( ) ;
}
return ch ;
2020-06-17 16:35:06 +03:00
}
char Parser : : consume ( )
{
auto ch = peek ( ) ;
+ + m_offset ;
2020-09-16 03:41:09 +03:00
2020-06-17 16:35:06 +03:00
return ch ;
}
void Parser : : putback ( )
{
ASSERT ( m_offset > 0 ) ;
- - m_offset ;
}
bool Parser : : expect ( char ch )
{
return expect ( StringView { & ch , 1 } ) ;
}
bool Parser : : expect ( const StringView & expected )
{
2020-09-14 18:01:47 +03:00
auto offset_at_start = m_offset ;
2020-06-17 16:35:06 +03:00
if ( expected . length ( ) + m_offset > m_input . length ( ) )
return false ;
for ( size_t i = 0 ; i < expected . length ( ) ; + + i ) {
2020-09-14 18:01:47 +03:00
if ( peek ( ) ! = expected [ i ] ) {
m_offset = offset_at_start ;
2020-06-17 16:35:06 +03:00
return false ;
2020-09-14 18:01:47 +03:00
}
2020-06-17 16:35:06 +03:00
consume ( ) ;
2019-05-07 02:12:08 +03:00
}
2020-06-17 16:35:06 +03:00
return true ;
}
template < typename A , typename . . . Args >
2020-08-04 19:16:37 +03:00
NonnullRefPtr < A > Parser : : create ( Args . . . args )
2019-05-07 02:12:08 +03:00
{
2020-06-17 16:35:06 +03:00
return adopt ( * new A ( AST : : Position { m_rule_start_offsets . last ( ) , m_offset } , args . . . ) ) ;
2019-05-07 02:12:08 +03:00
}
2020-06-17 16:35:06 +03:00
[[nodiscard]] OwnPtr < Parser : : ScopedOffset > Parser : : push_start ( )
2019-08-30 07:54:05 +03:00
{
2020-06-17 16:35:06 +03:00
return make < ScopedOffset > ( m_rule_start_offsets , m_offset ) ;
2019-08-30 07:54:05 +03:00
}
2020-06-17 16:35:06 +03:00
static constexpr bool is_whitespace ( char c )
2019-05-07 02:12:08 +03:00
{
2020-06-17 16:35:06 +03:00
return c = = ' ' | | c = = ' \t ' ;
2019-05-07 02:12:08 +03:00
}
2020-06-17 16:35:06 +03:00
static constexpr bool is_word_character ( char c )
2019-05-07 02:12:08 +03:00
{
2020-06-17 16:35:06 +03:00
return ( c < = ' 9 ' & & c > = ' 0 ' ) | | ( c < = ' Z ' & & c > = ' A ' ) | | ( c < = ' z ' & & c > = ' a ' ) | | c = = ' _ ' ;
2019-05-07 02:12:08 +03:00
}
2020-06-17 16:35:06 +03:00
static constexpr bool is_digit ( char c )
2019-05-07 02:12:08 +03:00
{
2020-06-17 16:35:06 +03:00
return c < = ' 9 ' & & c > = ' 0 ' ;
2019-05-07 02:12:08 +03:00
}
2020-06-17 16:35:06 +03:00
static constexpr auto is_not ( char c )
2020-04-30 03:56:16 +03:00
{
2020-06-17 16:35:06 +03:00
return [ c ] ( char ch ) { return ch ! = c ; } ;
2020-04-30 03:56:16 +03:00
}
2020-06-28 17:13:37 +03:00
static constexpr auto is_any_of ( StringView s )
{
return [ s ] ( char ch ) { return s . contains ( ch ) ; } ;
}
2020-06-17 16:35:06 +03:00
static inline char to_byte ( char a , char b )
2019-05-07 02:12:08 +03:00
{
2020-06-17 16:35:06 +03:00
char buf [ 3 ] { a , b , 0 } ;
return strtol ( buf , nullptr , 16 ) ;
}
2020-05-24 21:30:46 +03:00
2020-06-17 16:35:06 +03:00
RefPtr < AST : : Node > Parser : : parse ( )
{
m_offset = 0 ;
2020-05-24 21:30:46 +03:00
2020-06-23 17:40:41 +03:00
auto toplevel = parse_toplevel ( ) ;
if ( m_offset < m_input . length ( ) ) {
// Parsing stopped midway, this is a syntax error.
auto error_start = push_start ( ) ;
m_offset = m_input . length ( ) ;
2020-07-07 15:46:01 +03:00
auto syntax_error_node = create < AST : : SyntaxError > ( " Unexpected tokens past the end " ) ;
2020-09-16 03:37:14 +03:00
if ( ! toplevel )
toplevel = move ( syntax_error_node ) ;
else
toplevel - > set_is_syntax_error ( * syntax_error_node ) ;
2020-06-23 17:40:41 +03:00
}
return toplevel ;
2020-06-17 16:35:06 +03:00
}
RefPtr < AST : : Node > Parser : : parse_toplevel ( )
{
auto rule_start = push_start ( ) ;
if ( auto sequence = parse_sequence ( ) )
2020-09-16 03:37:14 +03:00
return create < AST : : Execute > ( sequence . release_nonnull ( ) ) ;
2020-06-17 16:35:06 +03:00
return nullptr ;
}
RefPtr < AST : : Node > Parser : : parse_sequence ( )
{
2020-07-27 04:28:41 +03:00
consume_while ( is_any_of ( " \t \n ; " ) ) ; // ignore whitespaces or terminators without effect.
2020-07-12 00:12:46 +03:00
2020-06-17 16:35:06 +03:00
auto rule_start = push_start ( ) ;
auto var_decls = parse_variable_decls ( ) ;
2020-05-24 21:30:46 +03:00
2020-06-28 17:13:37 +03:00
switch ( peek ( ) ) {
2020-07-12 00:12:46 +03:00
case ' } ' :
return var_decls ;
2020-06-28 17:13:37 +03:00
case ' ; ' :
2020-07-12 00:12:46 +03:00
case ' \n ' : {
if ( ! var_decls )
break ;
2020-06-28 17:13:37 +03:00
consume_while ( is_any_of ( " \n ; " ) ) ;
2020-07-12 00:12:46 +03:00
auto rest = parse_sequence ( ) ;
if ( rest )
2020-09-16 03:37:14 +03:00
return create < AST : : Sequence > ( var_decls . release_nonnull ( ) , rest . release_nonnull ( ) ) ;
2020-07-12 00:12:46 +03:00
return var_decls ;
}
2020-06-28 17:13:37 +03:00
default :
break ;
}
2020-09-13 14:24:33 +03:00
auto first = parse_function_decl ( ) ;
if ( ! first )
first = parse_or_logical_sequence ( ) ;
2020-07-12 00:12:46 +03:00
if ( ! first )
2020-06-17 16:35:06 +03:00
return var_decls ;
if ( var_decls )
2020-09-16 03:37:14 +03:00
first = create < AST : : Sequence > ( var_decls . release_nonnull ( ) , first . release_nonnull ( ) ) ;
2020-06-17 16:35:06 +03:00
consume_while ( is_whitespace ) ;
switch ( peek ( ) ) {
case ' ; ' :
2020-06-28 17:13:37 +03:00
case ' \n ' :
consume_while ( is_any_of ( " \n ; " ) ) ;
2020-06-17 16:35:06 +03:00
if ( auto expr = parse_sequence ( ) ) {
2020-09-16 03:37:14 +03:00
return create < AST : : Sequence > ( first . release_nonnull ( ) , expr . release_nonnull ( ) ) ; // Sequence
2020-06-17 16:35:06 +03:00
}
2020-07-12 00:12:46 +03:00
return first ;
2020-06-17 16:35:06 +03:00
case ' & ' : {
2020-09-16 03:37:14 +03:00
auto execute_pipe_seq = first - > would_execute ( ) ? first . release_nonnull ( ) : static_cast < NonnullRefPtr < AST : : Node > > ( create < AST : : Execute > ( first . release_nonnull ( ) ) ) ;
2020-06-17 16:35:06 +03:00
consume ( ) ;
2020-09-16 03:37:14 +03:00
auto bg = create < AST : : Background > ( execute_pipe_seq ) ; // Execute Background
2020-06-17 16:35:06 +03:00
if ( auto rest = parse_sequence ( ) )
2020-09-16 03:37:14 +03:00
return create < AST : : Sequence > ( move ( bg ) , rest . release_nonnull ( ) ) ; // Sequence Background Sequence
2020-06-17 16:35:06 +03:00
return bg ;
}
default :
2020-07-12 00:12:46 +03:00
return first ;
2020-06-17 16:35:06 +03:00
}
}
RefPtr < AST : : Node > Parser : : parse_variable_decls ( )
{
auto rule_start = push_start ( ) ;
consume_while ( is_whitespace ) ;
auto offset_before_name = m_offset ;
auto var_name = consume_while ( is_word_character ) ;
if ( var_name . is_empty ( ) )
return nullptr ;
if ( ! expect ( ' = ' ) ) {
m_offset = offset_before_name ;
return nullptr ;
}
auto name_expr = create < AST : : BarewordLiteral > ( move ( var_name ) ) ;
2020-06-22 14:07:20 +03:00
auto start = push_start ( ) ;
2020-06-17 16:35:06 +03:00
auto expression = parse_expression ( ) ;
2020-06-22 14:07:20 +03:00
if ( ! expression | | expression - > is_syntax_error ( ) ) {
m_offset = start - > offset ;
if ( peek ( ) = = ' ( ' ) {
consume ( ) ;
auto command = parse_pipe_sequence ( ) ;
if ( ! command )
m_offset = start - > offset ;
2020-06-23 17:40:41 +03:00
else if ( ! expect ( ' ) ' ) )
2020-06-28 17:12:57 +03:00
command - > set_is_syntax_error ( * create < AST : : SyntaxError > ( " Expected a terminating close paren " ) ) ;
2020-06-22 14:07:20 +03:00
expression = command ;
}
}
2020-06-17 16:35:06 +03:00
if ( ! expression ) {
if ( is_whitespace ( peek ( ) ) ) {
auto string_start = push_start ( ) ;
expression = create < AST : : StringLiteral > ( " " ) ;
} else {
m_offset = offset_before_name ;
return nullptr ;
}
}
Vector < AST : : VariableDeclarations : : Variable > variables ;
2020-08-07 10:41:04 +03:00
variables . append ( { move ( name_expr ) , expression . release_nonnull ( ) } ) ;
2020-06-17 16:35:06 +03:00
if ( consume_while ( is_whitespace ) . is_empty ( ) )
return create < AST : : VariableDeclarations > ( move ( variables ) ) ;
auto rest = parse_variable_decls ( ) ;
if ( ! rest )
return create < AST : : VariableDeclarations > ( move ( variables ) ) ;
ASSERT ( rest - > is_variable_decls ( ) ) ;
auto * rest_decl = static_cast < AST : : VariableDeclarations * > ( rest . ptr ( ) ) ;
variables . append ( rest_decl - > variables ( ) ) ;
return create < AST : : VariableDeclarations > ( move ( variables ) ) ;
}
2020-09-13 14:24:33 +03:00
RefPtr < AST : : Node > Parser : : parse_function_decl ( )
{
auto rule_start = push_start ( ) ;
auto restore = [ & ] {
m_offset = rule_start - > offset ;
return nullptr ;
} ;
consume_while ( is_whitespace ) ;
auto offset_before_name = m_offset ;
auto function_name = consume_while ( is_word_character ) ;
auto offset_after_name = m_offset ;
if ( function_name . is_empty ( ) )
return restore ( ) ;
if ( ! expect ( ' ( ' ) )
return restore ( ) ;
Vector < AST : : FunctionDeclaration : : NameWithPosition > arguments ;
for ( ; ; ) {
consume_while ( is_whitespace ) ;
if ( expect ( ' ) ' ) )
break ;
auto name_offset = m_offset ;
auto arg_name = consume_while ( is_word_character ) ;
if ( arg_name . is_empty ( ) ) {
// FIXME: Should this be a syntax error, or just return?
return restore ( ) ;
}
arguments . append ( { arg_name , { name_offset , m_offset } } ) ;
}
consume_while ( is_whitespace ) ;
{
RefPtr < AST : : Node > syntax_error ;
{
auto obrace_error_start = push_start ( ) ;
syntax_error = create < AST : : SyntaxError > ( " Expected an open brace '{' to start a function body " ) ;
}
if ( ! expect ( ' { ' ) ) {
return create < AST : : FunctionDeclaration > (
AST : : FunctionDeclaration : : NameWithPosition {
move ( function_name ) ,
{ offset_before_name , offset_after_name } } ,
move ( arguments ) ,
move ( syntax_error ) ) ;
}
}
auto body = parse_toplevel ( ) ;
{
RefPtr < AST : : SyntaxError > syntax_error ;
{
auto cbrace_error_start = push_start ( ) ;
syntax_error = create < AST : : SyntaxError > ( " Expected a close brace '}' to end a function body " ) ;
}
if ( ! expect ( ' } ' ) ) {
if ( body )
body - > set_is_syntax_error ( * syntax_error ) ;
else
body = move ( syntax_error ) ;
return create < AST : : FunctionDeclaration > (
AST : : FunctionDeclaration : : NameWithPosition {
move ( function_name ) ,
{ offset_before_name , offset_after_name } } ,
move ( arguments ) ,
move ( body ) ) ;
}
}
return create < AST : : FunctionDeclaration > (
AST : : FunctionDeclaration : : NameWithPosition {
move ( function_name ) ,
{ offset_before_name , offset_after_name } } ,
move ( arguments ) ,
move ( body ) ) ;
}
2020-07-12 00:12:46 +03:00
RefPtr < AST : : Node > Parser : : parse_or_logical_sequence ( )
{
consume_while ( is_whitespace ) ;
auto rule_start = push_start ( ) ;
auto and_sequence = parse_and_logical_sequence ( ) ;
if ( ! and_sequence )
return nullptr ;
consume_while ( is_whitespace ) ;
auto saved_offset = m_offset ;
if ( ! expect ( " || " ) ) {
m_offset = saved_offset ;
return and_sequence ;
}
auto right_and_sequence = parse_and_logical_sequence ( ) ;
if ( ! right_and_sequence )
right_and_sequence = create < AST : : SyntaxError > ( " Expected an expression after '||' " ) ;
2020-09-16 03:37:14 +03:00
return create < AST : : Or > ( and_sequence . release_nonnull ( ) , right_and_sequence . release_nonnull ( ) ) ;
2020-07-12 00:12:46 +03:00
}
RefPtr < AST : : Node > Parser : : parse_and_logical_sequence ( )
{
consume_while ( is_whitespace ) ;
auto rule_start = push_start ( ) ;
auto pipe_sequence = parse_pipe_sequence ( ) ;
if ( ! pipe_sequence )
return nullptr ;
consume_while ( is_whitespace ) ;
auto saved_offset = m_offset ;
if ( ! expect ( " && " ) ) {
m_offset = saved_offset ;
return pipe_sequence ;
}
2020-07-16 19:12:12 +03:00
auto right_and_sequence = parse_and_logical_sequence ( ) ;
if ( ! right_and_sequence )
right_and_sequence = create < AST : : SyntaxError > ( " Expected an expression after '&&' " ) ;
2020-07-12 00:12:46 +03:00
2020-09-16 03:37:14 +03:00
return create < AST : : And > ( pipe_sequence . release_nonnull ( ) , right_and_sequence . release_nonnull ( ) ) ;
2020-07-12 00:12:46 +03:00
}
2020-06-17 16:35:06 +03:00
RefPtr < AST : : Node > Parser : : parse_pipe_sequence ( )
{
auto rule_start = push_start ( ) ;
2020-09-07 19:19:53 +03:00
auto left = parse_control_structure ( ) ;
if ( ! left ) {
if ( auto cmd = parse_command ( ) )
left = cmd ;
else
return nullptr ;
}
2020-06-17 16:35:06 +03:00
consume_while ( is_whitespace ) ;
if ( peek ( ) ! = ' | ' )
2020-09-07 19:19:53 +03:00
return left ;
2020-06-17 16:35:06 +03:00
consume ( ) ;
if ( auto pipe_seq = parse_pipe_sequence ( ) ) {
2020-09-16 03:37:14 +03:00
return create < AST : : Pipe > ( left . release_nonnull ( ) , pipe_seq . release_nonnull ( ) ) ; // Pipe
2020-06-17 16:35:06 +03:00
}
putback ( ) ;
2020-09-07 19:19:53 +03:00
return left ;
2020-06-17 16:35:06 +03:00
}
RefPtr < AST : : Node > Parser : : parse_command ( )
{
auto rule_start = push_start ( ) ;
consume_while ( is_whitespace ) ;
auto redir = parse_redirection ( ) ;
if ( ! redir ) {
auto list_expr = parse_list_expression ( ) ;
if ( ! list_expr )
return nullptr ;
2020-09-16 03:37:14 +03:00
auto cast = create < AST : : CastToCommand > ( list_expr . release_nonnull ( ) ) ; // Cast List Command
2020-06-20 16:30:45 +03:00
auto next_command = parse_command ( ) ;
2020-06-17 16:35:06 +03:00
if ( ! next_command )
return cast ;
2020-09-16 03:37:14 +03:00
return create < AST : : Join > ( move ( cast ) , next_command . release_nonnull ( ) ) ; // Join List Command
2020-06-17 16:35:06 +03:00
}
auto command = parse_command ( ) ;
if ( ! command )
return redir ;
2020-09-16 03:37:14 +03:00
return create < AST : : Join > ( redir . release_nonnull ( ) , command . release_nonnull ( ) ) ; // Join Command Command
2020-06-17 16:35:06 +03:00
}
2020-07-12 00:12:46 +03:00
RefPtr < AST : : Node > Parser : : parse_control_structure ( )
{
auto rule_start = push_start ( ) ;
consume_while ( is_whitespace ) ;
if ( auto for_loop = parse_for_loop ( ) )
return for_loop ;
2020-08-11 10:35:46 +03:00
if ( auto if_expr = parse_if_expr ( ) )
return if_expr ;
2020-09-08 14:29:07 +03:00
if ( auto subshell = parse_subshell ( ) )
return subshell ;
2020-09-14 18:02:21 +03:00
if ( auto match = parse_match_expr ( ) )
return match ;
2020-07-12 00:12:46 +03:00
return nullptr ;
}
RefPtr < AST : : Node > Parser : : parse_for_loop ( )
{
auto rule_start = push_start ( ) ;
if ( ! expect ( " for " ) ) {
m_offset = rule_start - > offset ;
return nullptr ;
}
if ( consume_while ( is_any_of ( " \t \n " ) ) . is_empty ( ) ) {
m_offset = rule_start - > offset ;
return nullptr ;
}
auto variable_name = consume_while ( is_word_character ) ;
Optional < size_t > in_start_position ;
if ( variable_name . is_empty ( ) ) {
variable_name = " it " ;
} else {
consume_while ( is_whitespace ) ;
auto in_error_start = push_start ( ) ;
in_start_position = in_error_start - > offset ;
if ( ! expect ( " in " ) ) {
auto syntax_error = create < AST : : SyntaxError > ( " Expected 'in' after a variable name in a 'for' loop " ) ;
return create < AST : : ForLoop > ( move ( variable_name ) , move ( syntax_error ) , nullptr ) ; // ForLoop Var Iterated Block
}
}
consume_while ( is_whitespace ) ;
RefPtr < AST : : Node > iterated_expression ;
{
auto iter_error_start = push_start ( ) ;
iterated_expression = parse_expression ( ) ;
if ( ! iterated_expression ) {
auto syntax_error = create < AST : : SyntaxError > ( " Expected an expression in 'for' loop " ) ;
return create < AST : : ForLoop > ( move ( variable_name ) , move ( syntax_error ) , nullptr , move ( in_start_position ) ) ; // ForLoop Var Iterated Block
}
}
consume_while ( is_any_of ( " \t \n " ) ) ;
{
auto obrace_error_start = push_start ( ) ;
if ( ! expect ( ' { ' ) ) {
auto syntax_error = create < AST : : SyntaxError > ( " Expected an open brace '{' to start a 'for' loop body " ) ;
2020-09-16 03:37:14 +03:00
return create < AST : : ForLoop > ( move ( variable_name ) , iterated_expression . release_nonnull ( ) , move ( syntax_error ) , move ( in_start_position ) ) ; // ForLoop Var Iterated Block
2020-07-12 00:12:46 +03:00
}
}
auto body = parse_toplevel ( ) ;
{
auto cbrace_error_start = push_start ( ) ;
if ( ! expect ( ' } ' ) ) {
auto error_start = push_start ( ) ;
2020-09-16 03:37:14 +03:00
auto syntax_error = create < AST : : SyntaxError > ( " Expected a close brace '}' to end a 'for' loop body " ) ;
2020-07-12 00:12:46 +03:00
if ( body )
body - > set_is_syntax_error ( * syntax_error ) ;
else
body = syntax_error ;
}
}
2020-09-16 03:37:14 +03:00
return create < AST : : ForLoop > ( move ( variable_name ) , iterated_expression . release_nonnull ( ) , move ( body ) , move ( in_start_position ) ) ; // ForLoop Var Iterated Block
2020-07-12 00:12:46 +03:00
}
2020-08-11 10:35:46 +03:00
RefPtr < AST : : Node > Parser : : parse_if_expr ( )
{
auto rule_start = push_start ( ) ;
if ( ! expect ( " if " ) ) {
m_offset = rule_start - > offset ;
return nullptr ;
}
if ( consume_while ( is_any_of ( " \t \n " ) ) . is_empty ( ) ) {
m_offset = rule_start - > offset ;
return nullptr ;
}
RefPtr < AST : : Node > condition ;
{
auto cond_error_start = push_start ( ) ;
condition = parse_or_logical_sequence ( ) ;
2020-09-16 03:37:14 +03:00
if ( ! condition )
condition = create < AST : : SyntaxError > ( " Expected a logical sequence after 'if' " ) ;
2020-08-11 10:35:46 +03:00
}
auto parse_braced_toplevel = [ & ] ( ) - > RefPtr < AST : : Node > {
2020-09-16 03:37:14 +03:00
RefPtr < AST : : Node > body ;
2020-08-11 10:35:46 +03:00
{
auto obrace_error_start = push_start ( ) ;
if ( ! expect ( ' { ' ) ) {
2020-09-16 03:37:14 +03:00
body = create < AST : : SyntaxError > ( " Expected an open brace '{' to start an 'if' true branch " ) ;
2020-08-11 10:35:46 +03:00
}
}
2020-09-16 03:37:14 +03:00
if ( ! body )
body = parse_toplevel ( ) ;
2020-08-11 10:35:46 +03:00
{
auto cbrace_error_start = push_start ( ) ;
if ( ! expect ( ' } ' ) ) {
auto error_start = push_start ( ) ;
RefPtr < AST : : SyntaxError > syntax_error = create < AST : : SyntaxError > ( " Expected a close brace '}' to end an 'if' true branch " ) ;
if ( body )
body - > set_is_syntax_error ( * syntax_error ) ;
else
body = syntax_error ;
}
}
return body ;
} ;
consume_while ( is_whitespace ) ;
auto true_branch = parse_braced_toplevel ( ) ;
consume_while ( is_whitespace ) ;
Optional < AST : : Position > else_position ;
{
auto else_start = push_start ( ) ;
if ( expect ( " else " ) )
else_position = AST : : Position { else_start - > offset , m_offset } ;
}
if ( else_position . has_value ( ) ) {
consume_while ( is_whitespace ) ;
if ( peek ( ) = = ' { ' ) {
auto false_branch = parse_braced_toplevel ( ) ;
2020-09-16 03:37:14 +03:00
return create < AST : : IfCond > ( else_position , condition . release_nonnull ( ) , move ( true_branch ) , move ( false_branch ) ) ; // If expr true_branch Else false_branch
2020-08-11 10:35:46 +03:00
}
auto else_if_branch = parse_if_expr ( ) ;
2020-09-16 03:37:14 +03:00
return create < AST : : IfCond > ( else_position , condition . release_nonnull ( ) , move ( true_branch ) , move ( else_if_branch ) ) ; // If expr true_branch Else If ...
2020-08-11 10:35:46 +03:00
}
2020-09-16 03:37:14 +03:00
return create < AST : : IfCond > ( else_position , condition . release_nonnull ( ) , move ( true_branch ) , nullptr ) ; // If expr true_branch
2020-08-11 10:35:46 +03:00
}
2020-09-08 14:29:07 +03:00
RefPtr < AST : : Node > Parser : : parse_subshell ( )
{
auto rule_start = push_start ( ) ;
if ( ! expect ( ' { ' ) )
return nullptr ;
auto body = parse_toplevel ( ) ;
{
auto cbrace_error_start = push_start ( ) ;
if ( ! expect ( ' } ' ) ) {
auto error_start = push_start ( ) ;
RefPtr < AST : : SyntaxError > syntax_error = create < AST : : SyntaxError > ( " Expected a close brace '}' to end a subshell " ) ;
if ( body )
body - > set_is_syntax_error ( * syntax_error ) ;
else
body = syntax_error ;
}
}
return create < AST : : Subshell > ( move ( body ) ) ;
}
2020-09-14 18:02:21 +03:00
RefPtr < AST : : Node > Parser : : parse_match_expr ( )
{
auto rule_start = push_start ( ) ;
if ( ! expect ( " match " ) )
return nullptr ;
if ( consume_while ( is_whitespace ) . is_empty ( ) ) {
m_offset = rule_start - > offset ;
return nullptr ;
}
auto match_expression = parse_expression ( ) ;
if ( ! match_expression ) {
return create < AST : : MatchExpr > (
create < AST : : SyntaxError > ( " Expected an expression after 'match' " ) ,
String { } , Optional < AST : : Position > { } , Vector < AST : : MatchEntry > { } ) ;
}
consume_while ( is_any_of ( " \t \n " ) ) ;
String match_name ;
Optional < AST : : Position > as_position ;
auto as_start = m_offset ;
if ( expect ( " as " ) ) {
as_position = AST : : Position { as_start , m_offset } ;
if ( consume_while ( is_any_of ( " \t \n " ) ) . is_empty ( ) ) {
auto node = create < AST : : MatchExpr > (
2020-09-16 03:37:14 +03:00
match_expression . release_nonnull ( ) ,
2020-09-14 18:02:21 +03:00
String { } , move ( as_position ) , Vector < AST : : MatchEntry > { } ) ;
node - > set_is_syntax_error ( create < AST : : SyntaxError > ( " Expected whitespace after 'as' in 'match' " ) ) ;
return node ;
}
match_name = consume_while ( is_word_character ) ;
if ( match_name . is_empty ( ) ) {
auto node = create < AST : : MatchExpr > (
2020-09-16 03:37:14 +03:00
match_expression . release_nonnull ( ) ,
2020-09-14 18:02:21 +03:00
String { } , move ( as_position ) , Vector < AST : : MatchEntry > { } ) ;
node - > set_is_syntax_error ( create < AST : : SyntaxError > ( " Expected an identifier after 'as' in 'match' " ) ) ;
return node ;
}
}
consume_while ( is_any_of ( " \t \n " ) ) ;
if ( ! expect ( ' { ' ) ) {
auto node = create < AST : : MatchExpr > (
2020-09-16 03:37:14 +03:00
match_expression . release_nonnull ( ) ,
2020-09-14 18:02:21 +03:00
move ( match_name ) , move ( as_position ) , Vector < AST : : MatchEntry > { } ) ;
node - > set_is_syntax_error ( create < AST : : SyntaxError > ( " Expected an open brace '{' to start a 'match' entry list " ) ) ;
return node ;
}
consume_while ( is_any_of ( " \t \n " ) ) ;
Vector < AST : : MatchEntry > entries ;
for ( ; ; ) {
auto entry = parse_match_entry ( ) ;
consume_while ( is_any_of ( " \t \n " ) ) ;
if ( entry . options . is_empty ( ) )
break ;
entries . append ( entry ) ;
}
consume_while ( is_any_of ( " \t \n " ) ) ;
if ( ! expect ( ' } ' ) ) {
auto node = create < AST : : MatchExpr > (
2020-09-16 03:37:14 +03:00
match_expression . release_nonnull ( ) ,
2020-09-14 18:02:21 +03:00
move ( match_name ) , move ( as_position ) , move ( entries ) ) ;
node - > set_is_syntax_error ( create < AST : : SyntaxError > ( " Expected a close brace '}' to end a 'match' entry list " ) ) ;
return node ;
}
2020-09-16 03:37:14 +03:00
return create < AST : : MatchExpr > ( match_expression . release_nonnull ( ) , move ( match_name ) , move ( as_position ) , move ( entries ) ) ;
2020-09-14 18:02:21 +03:00
}
AST : : MatchEntry Parser : : parse_match_entry ( )
{
auto rule_start = push_start ( ) ;
NonnullRefPtrVector < AST : : Node > patterns ;
Vector < AST : : Position > pipe_positions ;
auto pattern = parse_match_pattern ( ) ;
if ( ! pattern )
return { { } , { } , create < AST : : SyntaxError > ( " Expected a pattern in 'match' body " ) } ;
patterns . append ( pattern . release_nonnull ( ) ) ;
consume_while ( is_any_of ( " \t \n " ) ) ;
auto previous_pipe_start_position = m_offset ;
RefPtr < AST : : SyntaxError > error ;
while ( expect ( ' | ' ) ) {
pipe_positions . append ( { previous_pipe_start_position , m_offset } ) ;
consume_while ( is_any_of ( " \t \n " ) ) ;
auto pattern = parse_match_pattern ( ) ;
if ( ! pattern ) {
error = create < AST : : SyntaxError > ( " Expected a pattern to follow '|' in 'match' body " ) ;
break ;
}
consume_while ( is_any_of ( " \t \n " ) ) ;
patterns . append ( pattern . release_nonnull ( ) ) ;
}
consume_while ( is_any_of ( " \t \n " ) ) ;
if ( ! expect ( ' { ' ) ) {
if ( ! error )
error = create < AST : : SyntaxError > ( " Expected an open brace '{' to start a match entry body " ) ;
}
auto body = parse_toplevel ( ) ;
if ( ! expect ( ' } ' ) ) {
if ( ! error )
error = create < AST : : SyntaxError > ( " Expected a close brace '}' to end a match entry body " ) ;
}
if ( body & & error )
body - > set_is_syntax_error ( * error ) ;
else if ( error )
body = error ;
return { move ( patterns ) , move ( pipe_positions ) , move ( body ) } ;
}
RefPtr < AST : : Node > Parser : : parse_match_pattern ( )
{
return parse_expression ( ) ;
}
2020-06-17 16:35:06 +03:00
RefPtr < AST : : Node > Parser : : parse_redirection ( )
{
auto rule_start = push_start ( ) ;
auto pipe_fd = 0 ;
auto number = consume_while ( is_digit ) ;
if ( number . is_empty ( ) ) {
pipe_fd = - 1 ;
} else {
auto fd = number . to_int ( ) ;
ASSERT ( fd . has_value ( ) ) ;
pipe_fd = fd . value ( ) ;
}
switch ( peek ( ) ) {
case ' > ' : {
consume ( ) ;
if ( peek ( ) = = ' > ' ) {
consume ( ) ;
consume_while ( is_whitespace ) ;
pipe_fd = pipe_fd > = 0 ? pipe_fd : STDOUT_FILENO ;
auto path = parse_expression ( ) ;
if ( ! path ) {
if ( ! at_end ( ) ) {
// Eat a character and hope the problem goes away
consume ( ) ;
2019-05-07 02:12:08 +03:00
}
2020-09-16 03:37:14 +03:00
path = create < AST : : SyntaxError > ( " Expected a path after redirection " ) ;
2019-05-07 02:12:08 +03:00
}
2020-09-16 03:37:14 +03:00
return create < AST : : WriteAppendRedirection > ( pipe_fd , path . release_nonnull ( ) ) ; // Redirection WriteAppend
2020-06-17 16:35:06 +03:00
}
if ( peek ( ) = = ' & ' ) {
consume ( ) ;
// FIXME: 'fd>&-' Syntax not the best. needs discussion.
if ( peek ( ) = = ' - ' ) {
consume ( ) ;
pipe_fd = pipe_fd > = 0 ? pipe_fd : STDOUT_FILENO ;
return create < AST : : CloseFdRedirection > ( pipe_fd ) ; // Redirection CloseFd
2019-05-07 02:12:08 +03:00
}
2020-06-17 16:35:06 +03:00
int dest_pipe_fd = 0 ;
auto number = consume_while ( is_digit ) ;
pipe_fd = pipe_fd > = 0 ? pipe_fd : STDOUT_FILENO ;
if ( number . is_empty ( ) ) {
dest_pipe_fd = - 1 ;
} else {
auto fd = number . to_int ( ) ;
ASSERT ( fd . has_value ( ) ) ;
dest_pipe_fd = fd . value ( ) ;
2019-09-14 12:36:09 +03:00
}
2020-06-28 17:12:57 +03:00
auto redir = create < AST : : Fd2FdRedirection > ( pipe_fd , dest_pipe_fd ) ; // Redirection Fd2Fd
if ( dest_pipe_fd = = - 1 )
redir - > set_is_syntax_error ( * create < AST : : SyntaxError > ( " Expected a file descriptor " ) ) ;
return redir ;
2020-06-17 16:35:06 +03:00
}
consume_while ( is_whitespace ) ;
pipe_fd = pipe_fd > = 0 ? pipe_fd : STDOUT_FILENO ;
auto path = parse_expression ( ) ;
if ( ! path ) {
if ( ! at_end ( ) ) {
// Eat a character and hope the problem goes away
consume ( ) ;
2019-05-07 02:12:08 +03:00
}
2020-09-16 03:37:14 +03:00
path = create < AST : : SyntaxError > ( " Expected a path after redirection " ) ;
2020-06-17 16:35:06 +03:00
}
2020-09-16 03:37:14 +03:00
return create < AST : : WriteRedirection > ( pipe_fd , path . release_nonnull ( ) ) ; // Redirection Write
2020-06-17 16:35:06 +03:00
}
case ' < ' : {
consume ( ) ;
enum {
Read ,
ReadWrite ,
} mode { Read } ;
if ( peek ( ) = = ' > ' ) {
mode = ReadWrite ;
consume ( ) ;
}
consume_while ( is_whitespace ) ;
pipe_fd = pipe_fd > = 0 ? pipe_fd : STDIN_FILENO ;
auto path = parse_expression ( ) ;
if ( ! path ) {
if ( ! at_end ( ) ) {
// Eat a character and hope the problem goes away
consume ( ) ;
2019-05-07 02:12:08 +03:00
}
2020-09-16 03:37:14 +03:00
path = create < AST : : SyntaxError > ( " Expected a path after redirection " ) ;
2020-06-17 16:35:06 +03:00
}
if ( mode = = Read )
2020-09-16 03:37:14 +03:00
return create < AST : : ReadRedirection > ( pipe_fd , path . release_nonnull ( ) ) ; // Redirection Read
2020-04-30 03:56:16 +03:00
2020-09-16 03:37:14 +03:00
return create < AST : : ReadWriteRedirection > ( pipe_fd , path . release_nonnull ( ) ) ; // Redirection ReadWrite
2020-06-17 16:35:06 +03:00
}
default :
2020-08-14 14:56:45 +03:00
m_offset = rule_start - > offset ;
2020-06-17 16:35:06 +03:00
return nullptr ;
}
}
2019-12-04 15:31:53 +03:00
2020-06-17 16:35:06 +03:00
RefPtr < AST : : Node > Parser : : parse_list_expression ( )
{
consume_while ( is_whitespace ) ;
2019-12-04 15:31:53 +03:00
2020-06-17 16:35:06 +03:00
auto rule_start = push_start ( ) ;
2020-09-16 03:37:14 +03:00
Vector < NonnullRefPtr < AST : : Node > > nodes ;
2019-12-04 15:31:53 +03:00
2020-07-12 00:11:24 +03:00
do {
auto expr = parse_expression ( ) ;
if ( ! expr )
break ;
2020-09-16 03:37:14 +03:00
nodes . append ( expr . release_nonnull ( ) ) ;
2020-07-12 00:11:24 +03:00
} while ( ! consume_while ( is_whitespace ) . is_empty ( ) ) ;
2019-12-04 15:31:53 +03:00
2020-07-12 00:11:24 +03:00
if ( nodes . is_empty ( ) )
return nullptr ;
2019-05-26 01:38:11 +03:00
2020-07-12 00:11:24 +03:00
return create < AST : : ListConcatenate > ( move ( nodes ) ) ; // Concatenate List
2020-06-17 16:35:06 +03:00
}
RefPtr < AST : : Node > Parser : : parse_expression ( )
{
auto rule_start = push_start ( ) ;
auto starting_char = peek ( ) ;
2020-09-16 03:37:14 +03:00
auto read_concat = [ & ] ( auto & & expr ) - > NonnullRefPtr < AST : : Node > {
2020-06-20 16:30:45 +03:00
if ( is_whitespace ( peek ( ) ) )
2020-09-16 03:37:14 +03:00
return move ( expr ) ;
2020-06-20 16:30:45 +03:00
if ( auto next_expr = parse_expression ( ) )
2020-09-16 03:37:14 +03:00
return create < AST : : Juxtaposition > ( move ( expr ) , next_expr . release_nonnull ( ) ) ;
2020-06-20 16:30:45 +03:00
2020-09-16 03:37:14 +03:00
return move ( expr ) ;
2020-06-20 16:30:45 +03:00
} ;
2020-07-15 18:30:52 +03:00
if ( strchr ( " &|){} ;<> \n " , starting_char ) ! = nullptr )
2020-06-17 16:35:06 +03:00
return nullptr ;
if ( isdigit ( starting_char ) ) {
ScopedValueRollback offset_rollback { m_offset } ;
auto redir = parse_redirection ( ) ;
if ( redir )
return nullptr ;
}
if ( starting_char = = ' $ ' ) {
if ( auto variable = parse_variable ( ) )
2020-09-16 03:37:14 +03:00
return read_concat ( variable . release_nonnull ( ) ) ;
2020-06-17 16:35:06 +03:00
if ( auto inline_exec = parse_evaluate ( ) )
2020-09-16 03:37:14 +03:00
return read_concat ( inline_exec . release_nonnull ( ) ) ;
2020-06-17 16:35:06 +03:00
}
if ( starting_char = = ' # ' )
return parse_comment ( ) ;
if ( starting_char = = ' ( ' ) {
consume ( ) ;
auto list = parse_list_expression ( ) ;
2020-06-22 14:07:20 +03:00
if ( ! expect ( ' ) ' ) ) {
m_offset = rule_start - > offset ;
return nullptr ;
}
2020-06-20 16:30:45 +03:00
return read_concat ( create < AST : : CastToList > ( move ( list ) ) ) ; // Cast To List
2020-06-17 16:35:06 +03:00
}
2020-09-16 03:37:14 +03:00
if ( auto composite = parse_string_composite ( ) )
return read_concat ( composite . release_nonnull ( ) ) ;
return nullptr ;
2020-06-17 16:35:06 +03:00
}
RefPtr < AST : : Node > Parser : : parse_string_composite ( )
{
auto rule_start = push_start ( ) ;
if ( auto string = parse_string ( ) ) {
if ( auto next_part = parse_string_composite ( ) )
2020-09-16 03:37:14 +03:00
return create < AST : : Juxtaposition > ( string . release_nonnull ( ) , next_part . release_nonnull ( ) ) ; // Concatenate String StringComposite
2020-06-17 16:35:06 +03:00
return string ;
}
if ( auto variable = parse_variable ( ) ) {
if ( auto next_part = parse_string_composite ( ) )
2020-09-16 03:37:14 +03:00
return create < AST : : Juxtaposition > ( variable . release_nonnull ( ) , next_part . release_nonnull ( ) ) ; // Concatenate Variable StringComposite
2020-06-17 16:35:06 +03:00
return variable ;
}
if ( auto glob = parse_glob ( ) ) {
if ( auto next_part = parse_string_composite ( ) )
2020-09-16 03:37:14 +03:00
return create < AST : : Juxtaposition > ( glob . release_nonnull ( ) , next_part . release_nonnull ( ) ) ; // Concatenate Glob StringComposite
2020-06-17 16:35:06 +03:00
return glob ;
}
if ( auto bareword = parse_bareword ( ) ) {
if ( auto next_part = parse_string_composite ( ) )
2020-09-16 03:37:14 +03:00
return create < AST : : Juxtaposition > ( bareword . release_nonnull ( ) , next_part . release_nonnull ( ) ) ; // Concatenate Bareword StringComposite
2020-06-17 16:35:06 +03:00
return bareword ;
}
if ( auto inline_command = parse_evaluate ( ) ) {
if ( auto next_part = parse_string_composite ( ) )
2020-09-16 03:37:14 +03:00
return create < AST : : Juxtaposition > ( inline_command . release_nonnull ( ) , next_part . release_nonnull ( ) ) ; // Concatenate Execute StringComposite
2020-06-17 16:35:06 +03:00
return inline_command ;
}
return nullptr ;
}
RefPtr < AST : : Node > Parser : : parse_string ( )
{
auto rule_start = push_start ( ) ;
if ( at_end ( ) )
return nullptr ;
if ( peek ( ) = = ' " ' ) {
consume ( ) ;
auto inner = parse_doublequoted_string_inner ( ) ;
2020-06-23 17:40:41 +03:00
if ( ! inner )
2020-06-28 17:12:57 +03:00
inner = create < AST : : SyntaxError > ( " Unexpected EOF in string " ) ;
2020-06-23 17:40:41 +03:00
if ( ! expect ( ' " ' ) ) {
inner = create < AST : : DoubleQuotedString > ( move ( inner ) ) ;
2020-06-28 17:12:57 +03:00
inner - > set_is_syntax_error ( * create < AST : : SyntaxError > ( " Expected a terminating double quote " ) ) ;
2020-06-23 17:40:41 +03:00
return inner ;
}
2020-06-17 16:35:06 +03:00
return create < AST : : DoubleQuotedString > ( move ( inner ) ) ; // Double Quoted String
}
if ( peek ( ) = = ' \' ' ) {
consume ( ) ;
auto text = consume_while ( is_not ( ' \' ' ) ) ;
2020-06-23 17:40:41 +03:00
bool is_error = false ;
2020-06-17 16:35:06 +03:00
if ( ! expect ( ' \' ' ) )
2020-06-23 17:40:41 +03:00
is_error = true ;
auto result = create < AST : : StringLiteral > ( move ( text ) ) ; // String Literal
if ( is_error )
2020-06-28 17:12:57 +03:00
result - > set_is_syntax_error ( * create < AST : : SyntaxError > ( " Expected a terminating single quote " ) ) ;
2020-06-23 17:40:41 +03:00
return move ( result ) ;
2020-06-17 16:35:06 +03:00
}
return nullptr ;
}
RefPtr < AST : : Node > Parser : : parse_doublequoted_string_inner ( )
{
auto rule_start = push_start ( ) ;
if ( at_end ( ) )
return nullptr ;
StringBuilder builder ;
while ( ! at_end ( ) & & peek ( ) ! = ' " ' ) {
if ( peek ( ) = = ' \\ ' ) {
consume ( ) ;
if ( at_end ( ) ) {
2019-05-07 02:12:08 +03:00
break ;
}
2020-06-17 16:35:06 +03:00
auto ch = consume ( ) ;
switch ( ch ) {
case ' \\ ' :
default :
builder . append ( ch ) ;
2019-05-07 02:12:08 +03:00
break ;
2020-06-17 16:35:06 +03:00
case ' x ' : {
if ( m_input . length ( ) < = m_offset + 2 )
break ;
auto first_nibble = tolower ( consume ( ) ) ;
auto second_nibble = tolower ( consume ( ) ) ;
if ( ! isxdigit ( first_nibble ) | | ! isxdigit ( second_nibble ) ) {
builder . append ( first_nibble ) ;
builder . append ( second_nibble ) ;
break ;
2019-05-07 02:12:08 +03:00
}
2020-06-17 16:35:06 +03:00
builder . append ( to_byte ( first_nibble , second_nibble ) ) ;
2020-04-30 03:56:16 +03:00
break ;
}
2020-06-17 16:35:06 +03:00
case ' a ' :
builder . append ( ' \a ' ) ;
2020-04-30 03:56:16 +03:00
break ;
2020-06-17 16:35:06 +03:00
case ' b ' :
builder . append ( ' \b ' ) ;
2019-05-07 02:12:08 +03:00
break ;
2020-06-17 16:35:06 +03:00
case ' e ' :
builder . append ( ' \x1b ' ) ;
2019-05-07 02:12:08 +03:00
break ;
2020-06-17 16:35:06 +03:00
case ' f ' :
builder . append ( ' \f ' ) ;
2019-05-07 02:12:08 +03:00
break ;
2020-06-17 16:35:06 +03:00
case ' r ' :
builder . append ( ' \r ' ) ;
break ;
case ' n ' :
builder . append ( ' \n ' ) ;
2019-05-07 02:12:08 +03:00
break ;
}
2020-06-17 16:35:06 +03:00
continue ;
}
if ( peek ( ) = = ' $ ' ) {
auto string_literal = create < AST : : StringLiteral > ( builder . to_string ( ) ) ; // String Literal
if ( auto variable = parse_variable ( ) ) {
auto inner = create < AST : : StringPartCompose > (
move ( string_literal ) ,
2020-09-16 03:37:14 +03:00
variable . release_nonnull ( ) ) ; // Compose String Variable
2020-06-17 16:35:06 +03:00
if ( auto string = parse_doublequoted_string_inner ( ) ) {
2020-09-16 03:37:14 +03:00
return create < AST : : StringPartCompose > ( move ( inner ) , string . release_nonnull ( ) ) ; // Compose Composition Composition
2019-09-14 12:36:09 +03:00
}
2020-06-17 16:35:06 +03:00
return inner ;
}
if ( auto evaluate = parse_evaluate ( ) ) {
auto composition = create < AST : : StringPartCompose > (
move ( string_literal ) ,
2020-09-16 03:37:14 +03:00
evaluate . release_nonnull ( ) ) ; // Compose String Sequence
2020-06-17 16:35:06 +03:00
if ( auto string = parse_doublequoted_string_inner ( ) ) {
2020-09-16 03:37:14 +03:00
return create < AST : : StringPartCompose > ( move ( composition ) , string . release_nonnull ( ) ) ; // Compose Composition Composition
2019-09-14 12:36:09 +03:00
}
2020-06-17 16:35:06 +03:00
return composition ;
2019-09-14 12:36:09 +03:00
}
2020-06-17 16:35:06 +03:00
}
builder . append ( consume ( ) ) ;
}
return create < AST : : StringLiteral > ( builder . to_string ( ) ) ; // String Literal
}
RefPtr < AST : : Node > Parser : : parse_variable ( )
{
auto rule_start = push_start ( ) ;
if ( at_end ( ) )
return nullptr ;
if ( peek ( ) ! = ' $ ' )
return nullptr ;
consume ( ) ;
switch ( peek ( ) ) {
case ' $ ' :
case ' ? ' :
2020-08-04 07:57:25 +03:00
case ' * ' :
case ' # ' :
2020-06-17 16:35:06 +03:00
return create < AST : : SpecialVariable > ( consume ( ) ) ; // Variable Special
default :
break ;
}
auto name = consume_while ( is_word_character ) ;
if ( name . length ( ) = = 0 ) {
putback ( ) ;
return nullptr ;
2019-05-07 02:12:08 +03:00
}
2020-04-30 03:56:16 +03:00
2020-06-17 16:35:06 +03:00
return create < AST : : SimpleVariable > ( move ( name ) ) ; // Variable Simple
}
RefPtr < AST : : Node > Parser : : parse_evaluate ( )
{
auto rule_start = push_start ( ) ;
if ( at_end ( ) )
return nullptr ;
if ( peek ( ) ! = ' $ ' )
return nullptr ;
consume ( ) ;
2020-06-21 23:00:14 +03:00
if ( peek ( ) = = ' ( ' ) {
consume ( ) ;
auto inner = parse_pipe_sequence ( ) ;
2020-06-23 17:40:41 +03:00
if ( ! inner )
2020-06-28 17:12:57 +03:00
inner = create < AST : : SyntaxError > ( " Unexpected EOF in list " ) ;
2020-06-23 17:40:41 +03:00
if ( ! expect ( ' ) ' ) )
2020-06-28 17:12:57 +03:00
inner - > set_is_syntax_error ( * create < AST : : SyntaxError > ( " Expected a terminating close paren " ) ) ;
2020-09-16 03:37:14 +03:00
return create < AST : : Execute > ( inner . release_nonnull ( ) , true ) ;
2020-06-21 23:00:14 +03:00
}
2020-06-17 16:35:06 +03:00
auto inner = parse_expression ( ) ;
if ( ! inner ) {
2020-06-28 17:12:57 +03:00
inner = create < AST : : SyntaxError > ( " Expected a command " ) ;
2020-06-17 16:35:06 +03:00
} else {
if ( inner - > is_list ( ) ) {
2020-09-16 03:37:14 +03:00
auto execute_inner = create < AST : : Execute > ( inner . release_nonnull ( ) , true ) ;
inner = move ( execute_inner ) ;
2020-05-10 09:05:23 +03:00
} else {
2020-09-16 03:37:14 +03:00
auto dyn_inner = create < AST : : DynamicEvaluate > ( inner . release_nonnull ( ) ) ;
inner = move ( dyn_inner ) ;
2020-05-10 09:05:23 +03:00
}
2020-04-30 03:56:16 +03:00
}
2020-06-17 16:35:06 +03:00
return inner ;
}
2019-05-07 02:12:08 +03:00
2020-06-17 16:35:06 +03:00
RefPtr < AST : : Node > Parser : : parse_comment ( )
{
if ( at_end ( ) )
return nullptr ;
if ( peek ( ) ! = ' # ' )
return nullptr ;
consume ( ) ;
auto text = consume_while ( is_not ( ' \n ' ) ) ;
return create < AST : : Comment > ( move ( text ) ) ; // Comment
}
RefPtr < AST : : Node > Parser : : parse_bareword ( )
{
auto rule_start = push_start ( ) ;
StringBuilder builder ;
auto is_acceptable_bareword_character = [ ] ( char c ) {
2020-07-15 18:30:52 +03:00
return strchr ( " \\ \" '*$&#|() { } ? ; < > \ n " , c) == nullptr;
2020-06-17 16:35:06 +03:00
} ;
while ( ! at_end ( ) ) {
char ch = peek ( ) ;
if ( ch = = ' \\ ' ) {
consume ( ) ;
if ( ! at_end ( ) ) {
ch = consume ( ) ;
if ( is_acceptable_bareword_character ( ch ) )
builder . append ( ' \\ ' ) ;
2019-05-07 02:12:08 +03:00
}
2020-06-17 16:35:06 +03:00
builder . append ( ch ) ;
continue ;
2019-05-07 02:12:08 +03:00
}
2020-06-17 16:35:06 +03:00
if ( is_acceptable_bareword_character ( ch ) ) {
builder . append ( consume ( ) ) ;
continue ;
}
break ;
}
if ( builder . is_empty ( ) )
return nullptr ;
2020-06-23 17:40:41 +03:00
auto current_end = m_offset ;
2020-06-17 16:35:06 +03:00
auto string = builder . to_string ( ) ;
if ( string . starts_with ( ' ~ ' ) ) {
String username ;
2020-06-23 17:40:41 +03:00
RefPtr < AST : : Node > tilde , text ;
2020-06-17 16:35:06 +03:00
auto first_slash_index = string . index_of ( " / " ) ;
if ( first_slash_index . has_value ( ) ) {
username = string . substring_view ( 1 , first_slash_index . value ( ) - 1 ) ;
string = string . substring_view ( first_slash_index . value ( ) , string . length ( ) - first_slash_index . value ( ) ) ;
} else {
username = string . substring_view ( 1 , string . length ( ) - 1 ) ;
string = " " ;
}
2020-06-23 17:40:41 +03:00
// Synthesize a Tilde Node with the correct positioning information.
{
m_offset - = string . length ( ) ;
tilde = create < AST : : Tilde > ( move ( username ) ) ;
}
2020-06-17 16:35:06 +03:00
if ( string . is_empty ( ) )
return tilde ;
2020-06-23 17:40:41 +03:00
// Synthesize a BarewordLiteral Node with the correct positioning information.
{
m_offset = tilde - > position ( ) . end_offset ;
auto text_start = push_start ( ) ;
m_offset = current_end ;
text = create < AST : : BarewordLiteral > ( move ( string ) ) ;
}
2020-09-16 03:37:14 +03:00
return create < AST : : Juxtaposition > ( tilde . release_nonnull ( ) , text . release_nonnull ( ) ) ; // Juxtaposition Varible Bareword
2019-05-07 02:12:08 +03:00
}
2020-06-17 16:35:06 +03:00
if ( string . starts_with ( " \\ ~ " ) ) {
// Un-escape the tilde, but only at the start (where it would be an expansion)
2020-06-23 17:40:41 +03:00
string = string . substring ( 1 , string . length ( ) - 1 ) ;
2020-06-17 16:35:06 +03:00
}
2020-06-23 17:40:41 +03:00
return create < AST : : BarewordLiteral > ( move ( string ) ) ; // Bareword Literal
2020-06-17 16:35:06 +03:00
}
RefPtr < AST : : Node > Parser : : parse_glob ( )
{
auto rule_start = push_start ( ) ;
auto bareword_part = parse_bareword ( ) ;
if ( at_end ( ) )
return bareword_part ;
char ch = peek ( ) ;
if ( ch = = ' * ' | | ch = = ' ? ' ) {
consume ( ) ;
StringBuilder textbuilder ;
if ( bareword_part ) {
StringView text ;
2020-06-23 17:40:41 +03:00
if ( bareword_part - > is_bareword ( ) ) {
2020-06-17 16:35:06 +03:00
auto bareword = static_cast < AST : : BarewordLiteral * > ( bareword_part . ptr ( ) ) ;
text = bareword - > text ( ) ;
} else {
2020-06-23 17:40:41 +03:00
// FIXME: Allow composition of tilde+bareword with globs: '~/foo/bar/baz*'
putback ( ) ;
2020-06-28 17:12:57 +03:00
bareword_part - > set_is_syntax_error ( * create < AST : : SyntaxError > ( String : : format ( " Unexpected %s inside a glob " , bareword_part - > class_name ( ) . characters ( ) ) ) ) ;
2020-06-23 17:40:41 +03:00
return bareword_part ;
2020-06-17 16:35:06 +03:00
}
textbuilder . append ( text ) ;
}
textbuilder . append ( ch ) ;
auto glob_after = parse_glob ( ) ;
if ( glob_after ) {
if ( glob_after - > is_glob ( ) ) {
auto glob = static_cast < AST : : BarewordLiteral * > ( glob_after . ptr ( ) ) ;
textbuilder . append ( glob - > text ( ) ) ;
} else if ( glob_after - > is_bareword ( ) ) {
auto bareword = static_cast < AST : : BarewordLiteral * > ( glob_after . ptr ( ) ) ;
textbuilder . append ( bareword - > text ( ) ) ;
} else {
ASSERT_NOT_REACHED ( ) ;
}
}
return create < AST : : Glob > ( textbuilder . to_string ( ) ) ; // Glob
}
return bareword_part ;
}
StringView Parser : : consume_while ( Function < bool ( char ) > condition )
{
auto start_offset = m_offset ;
while ( ! at_end ( ) & & condition ( peek ( ) ) )
consume ( ) ;
return m_input . substring_view ( start_offset , m_offset - start_offset ) ;
2019-05-07 02:12:08 +03:00
}