2014-02-28 10:15:24 +00:00
/** \file parser.c
2005-09-20 13:26:39 +00:00
2006-11-18 21:24:59 +00:00
The fish parser . Contains functions for parsing and evaluating code .
2005-09-20 13:26:39 +00:00
*/
2006-08-11 01:18:35 +00:00
# include "config.h"
2005-09-20 13:26:39 +00:00
# include <stdlib.h>
# include <stdio.h>
# include <wchar.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# include <errno.h>
# include <fcntl.h>
# include <termios.h>
# include <pwd.h>
# include <dirent.h>
# include <signal.h>
2012-02-28 15:50:09 +00:00
# include <algorithm>
2005-09-20 13:26:39 +00:00
2006-02-28 13:17:16 +00:00
# include "fallback.h"
2005-09-20 13:26:39 +00:00
# include "util.h"
2006-02-28 13:17:16 +00:00
2005-09-20 13:26:39 +00:00
# include "common.h"
# include "wutil.h"
# include "proc.h"
# include "parser.h"
2007-04-22 09:50:26 +00:00
# include "parser_keywords.h"
2005-09-20 13:26:39 +00:00
# include "tokenizer.h"
# include "exec.h"
# include "wildcard.h"
# include "function.h"
# include "builtin.h"
# include "env.h"
# include "expand.h"
# include "reader.h"
# include "sanity.h"
# include "env_universal.h"
2005-10-05 22:37:08 +00:00
# include "event.h"
2006-02-02 15:23:56 +00:00
# include "intern.h"
2006-02-05 13:10:35 +00:00
# include "parse_util.h"
2006-10-19 11:50:23 +00:00
# include "path.h"
2006-10-29 21:09:11 +00:00
# include "signal.h"
2012-01-16 16:56:47 +00:00
# include "complete.h"
2013-12-12 02:34:28 +00:00
# include "parse_tree.h"
2013-12-26 21:24:10 +00:00
# include "parse_execution.h"
2005-09-20 13:26:39 +00:00
/**
Error message for tokenizer error . The tokenizer message is
appended to this message .
*/
2006-01-04 12:51:02 +00:00
# define TOK_ERR_MSG _( L"Tokenizer error: '%ls'")
2005-09-20 13:26:39 +00:00
2006-01-04 12:51:02 +00:00
/**
Error for evaluating in illegal scope
*/
2006-01-11 14:17:35 +00:00
# define INVALID_SCOPE_ERR_MSG _( L"Tried to evaluate commands using invalid block type '%ls'" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Error for wrong token type
2005-12-14 03:39:39 +00:00
*/
2006-01-04 12:51:02 +00:00
# define UNEXPECTED_TOKEN_ERR_MSG _( L"Unexpected token of type '%ls'")
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
While block description
2012-11-18 10:23:22 +00:00
*/
2006-06-16 12:56:16 +00:00
# define WHILE_BLOCK N_( L"'while' block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
For block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define FOR_BLOCK N_( L"'for' block" )
2006-01-04 12:51:02 +00:00
2012-11-18 10:23:22 +00:00
/**
Breakpoint block
2006-11-11 10:54:00 +00:00
*/
2012-11-18 10:23:22 +00:00
# define BREAKPOINT_BLOCK N_( L"Block created by breakpoint" )
2006-11-11 10:54:00 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
If block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define IF_BLOCK N_( L"'if' conditional block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Function definition block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define FUNCTION_DEF_BLOCK N_( L"function definition block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Function invocation block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define FUNCTION_CALL_BLOCK N_( L"function invocation block" )
2006-01-04 12:51:02 +00:00
2007-04-22 22:10:33 +00:00
/**
Function invocation block description
*/
# define FUNCTION_CALL_NO_SHADOW_BLOCK N_( L"function invocation block with no variable shadowing" )
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Switch block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define SWITCH_BLOCK N_( L"'switch' block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Fake block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define FAKE_BLOCK N_( L"unexecutable block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Top block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define TOP_BLOCK N_( L"global root block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Command substitution block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define SUBST_BLOCK N_( L"command substitution block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Begin block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define BEGIN_BLOCK N_( L"'begin' unconditional block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Source block description
2006-01-28 11:34:40 +00:00
*/
2006-06-16 12:56:16 +00:00
# define SOURCE_BLOCK N_( L"Block created by the . builtin" )
2006-01-28 11:34:40 +00:00
2006-02-01 15:49:11 +00:00
/**
2006-04-20 18:35:02 +00:00
Source block description
2006-02-01 15:49:11 +00:00
*/
2006-06-16 12:56:16 +00:00
# define EVENT_BLOCK N_( L"event handler block" )
2006-02-01 15:49:11 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Unknown block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define UNKNOWN_BLOCK N_( L"unknown / invalid block" )
2006-01-04 12:51:02 +00:00
2005-12-14 01:07:12 +00:00
2006-06-17 13:07:08 +00:00
/**
Datastructure to describe a block type , like while blocks , command substitution blocks , etc .
*/
2006-06-16 12:56:16 +00:00
struct block_lookup_entry
{
2006-07-03 10:46:47 +00:00
2012-11-19 00:30:30 +00:00
/**
The block type id . The legal values are defined in parser . h .
*/
block_type_t type ;
/**
The name of the builtin that creates this type of block , if any .
*/
const wchar_t * name ;
/**
A description of this block type
*/
const wchar_t * desc ;
2006-06-16 12:56:16 +00:00
}
2012-11-19 00:30:30 +00:00
;
2006-06-16 12:56:16 +00:00
2006-06-17 13:07:08 +00:00
/**
List of all legal block types
*/
2010-10-08 00:43:57 +00:00
static const struct block_lookup_entry block_lookup [ ] =
2006-06-16 12:56:16 +00:00
{
2013-01-24 22:59:52 +00:00
{ WHILE , L " while " , WHILE_BLOCK } ,
{ FOR , L " for " , FOR_BLOCK } ,
{ IF , L " if " , IF_BLOCK } ,
{ FUNCTION_DEF , L " function " , FUNCTION_DEF_BLOCK } ,
{ FUNCTION_CALL , 0 , FUNCTION_CALL_BLOCK } ,
{ FUNCTION_CALL_NO_SHADOW , 0 , FUNCTION_CALL_NO_SHADOW_BLOCK } ,
{ SWITCH , L " switch " , SWITCH_BLOCK } ,
{ FAKE , 0 , FAKE_BLOCK } ,
{ TOP , 0 , TOP_BLOCK } ,
{ SUBST , 0 , SUBST_BLOCK } ,
{ BEGIN , L " begin " , BEGIN_BLOCK } ,
{ SOURCE , L " . " , SOURCE_BLOCK } ,
{ EVENT , 0 , EVENT_BLOCK } ,
{ BREAKPOINT , L " breakpoint " , BREAKPOINT_BLOCK } ,
{ ( block_type_t ) 0 , 0 , 0 }
2012-01-23 05:57:30 +00:00
} ;
2013-12-16 23:33:20 +00:00
// Given a file path, return something nicer. Currently we just "unexpand" tildes.
static wcstring user_presentable_path ( const wcstring & path )
{
return replace_home_directory_with_tilde ( path ) ;
}
2012-05-06 20:36:51 +00:00
parser_t : : parser_t ( enum parser_type_t type , bool errors ) :
2012-01-23 05:57:30 +00:00
parser_type ( type ) ,
2012-05-06 20:36:51 +00:00
show_errors ( errors ) ,
2012-01-23 05:57:30 +00:00
error_code ( 0 ) ,
err_pos ( 0 ) ,
2014-01-02 20:37:50 +00:00
cancellation_requested ( false ) ,
2014-02-20 18:57:13 +00:00
is_within_fish_initialization ( false ) ,
2012-01-23 05:57:30 +00:00
current_tokenizer ( NULL ) ,
2014-03-16 21:49:51 +00:00
current_tokenizer_pos ( 0 )
2012-01-23 05:40:08 +00:00
{
}
2005-09-20 13:26:39 +00:00
2012-06-04 21:20:01 +00:00
/* A pointer to the principal parser (which is a static local) */
static parser_t * s_principal_parser = NULL ;
2012-01-23 05:40:08 +00:00
parser_t & parser_t : : principal_parser ( void )
{
2012-02-28 02:43:24 +00:00
ASSERT_IS_NOT_FORKED_CHILD ( ) ;
2012-01-23 05:40:08 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-05-06 20:36:51 +00:00
static parser_t parser ( PARSER_TYPE_GENERAL , true ) ;
2012-06-04 21:20:01 +00:00
if ( ! s_principal_parser )
{
s_principal_parser = & parser ;
}
2012-01-23 05:40:08 +00:00
return parser ;
}
2005-09-20 13:26:39 +00:00
2014-02-20 18:57:13 +00:00
void parser_t : : set_is_within_fish_initialization ( bool flag )
{
is_within_fish_initialization = flag ;
}
2012-06-04 21:20:01 +00:00
void parser_t : : skip_all_blocks ( void )
{
/* Tell all blocks to skip */
if ( s_principal_parser )
{
2014-01-02 20:37:50 +00:00
s_principal_parser - > cancellation_requested = true ;
2012-06-04 22:10:35 +00:00
//write(2, "Cancelling blocks\n", strlen("Cancelling blocks\n"));
2013-12-21 01:41:21 +00:00
for ( size_t i = 0 ; i < s_principal_parser - > block_count ( ) ; i + + )
2012-06-04 21:20:01 +00:00
{
2013-12-21 01:41:21 +00:00
s_principal_parser - > block_at_index ( i ) - > skip = true ;
2012-06-04 21:20:01 +00:00
}
}
}
2013-12-21 01:41:21 +00:00
void parser_t : : push_block ( block_t * new_current )
2005-09-20 13:26:39 +00:00
{
2013-12-21 01:41:21 +00:00
const enum block_type_t type = new_current - > type ( ) ;
new_current - > src_lineno = parser_t : : get_lineno ( ) ;
2014-03-16 23:45:00 +00:00
const wchar_t * filename = parser_t : : current_filename ( ) ;
if ( filename ! = NULL )
{
new_current - > src_filename = intern ( filename ) ;
}
2012-11-18 10:23:22 +00:00
2013-12-21 01:41:21 +00:00
const block_t * old_current = this - > current_block ( ) ;
if ( old_current & & old_current - > skip )
2014-03-02 21:46:30 +00:00
{
new_current - > skip = true ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
New blocks should be skipped if the outer block is skipped ,
except TOP ans SUBST block , which open up new environments . Fake
blocks should always be skipped . Rather complicated . . . : - (
*/
2013-12-21 01:41:21 +00:00
new_current - > skip = old_current ? old_current - > skip : 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Type TOP and SUBST are never skipped
*/
if ( type = = TOP | | type = = SUBST )
{
2013-12-21 01:41:21 +00:00
new_current - > skip = 0 ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Fake blocks and function definition blocks are never executed
*/
if ( type = = FAKE | | type = = FUNCTION_DEF )
{
2013-12-21 01:41:21 +00:00
new_current - > skip = 1 ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2013-12-21 01:41:21 +00:00
new_current - > job = 0 ;
new_current - > loop_status = LOOP_NORMAL ;
2014-01-15 09:40:40 +00:00
2013-12-21 01:41:21 +00:00
this - > block_stack . push_back ( new_current ) ;
2012-11-19 00:30:30 +00:00
2013-12-21 01:41:21 +00:00
if ( ( new_current - > type ( ) ! = FUNCTION_DEF ) & &
( new_current - > type ( ) ! = FAKE ) & &
( new_current - > type ( ) ! = TOP ) )
2012-11-19 00:30:30 +00:00
{
env_push ( type = = FUNCTION_CALL ) ;
2013-12-21 01:41:21 +00:00
new_current - > wants_pop_env = true ;
2012-11-19 00:30:30 +00:00
}
2005-09-20 13:26:39 +00:00
}
2012-01-23 05:40:08 +00:00
void parser_t : : pop_block ( )
2005-09-20 13:26:39 +00:00
{
2013-12-21 01:41:21 +00:00
if ( block_stack . empty ( ) )
2012-11-19 00:30:30 +00:00
{
debug ( 1 ,
L " function %s called on empty block stack. " ,
__func__ ) ;
bugreport ( ) ;
return ;
}
2012-11-18 10:23:22 +00:00
2013-12-21 01:41:21 +00:00
block_t * old = block_stack . back ( ) ;
block_stack . pop_back ( ) ;
2012-11-18 10:23:22 +00:00
2012-02-08 01:06:45 +00:00
if ( old - > wants_pop_env )
env_pop ( ) ;
2012-11-18 10:23:22 +00:00
2012-02-10 03:26:44 +00:00
delete old ;
2005-09-20 13:26:39 +00:00
}
2013-12-26 20:24:00 +00:00
void parser_t : : pop_block ( const block_t * expected )
{
assert ( expected = = this - > current_block ( ) ) ;
this - > pop_block ( ) ;
}
2012-11-19 00:30:30 +00:00
const wchar_t * parser_t : : get_block_desc ( int block ) const
2005-09-20 13:26:39 +00:00
{
2013-01-24 22:59:52 +00:00
for ( size_t i = 0 ; block_lookup [ i ] . desc ; i + + )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( block_lookup [ i ] . type = = block )
{
return _ ( block_lookup [ i ] . desc ) ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
return _ ( UNKNOWN_BLOCK ) ;
2005-09-20 13:26:39 +00:00
}
2014-03-16 23:45:00 +00:00
wcstring parser_t : : block_stack_description ( ) const
{
wcstring result ;
size_t idx = this - > block_count ( ) ;
size_t spaces = 0 ;
while ( idx - - )
{
if ( spaces > 0 )
{
result . push_back ( L ' \n ' ) ;
}
for ( size_t j = 0 ; j < spaces ; j + + )
{
result . push_back ( L ' ' ) ;
}
result . append ( this - > block_at_index ( idx ) - > description ( ) ) ;
spaces + + ;
}
return result ;
}
2013-12-21 01:41:21 +00:00
const block_t * parser_t : : block_at_index ( size_t idx ) const
{
/* 0 corresponds to the last element in our vector */
size_t count = block_stack . size ( ) ;
return idx < count ? block_stack . at ( count - idx - 1 ) : NULL ;
}
block_t * parser_t : : block_at_index ( size_t idx )
{
size_t count = block_stack . size ( ) ;
return idx < count ? block_stack . at ( count - idx - 1 ) : NULL ;
}
const block_t * parser_t : : current_block ( ) const
{
return block_stack . empty ( ) ? NULL : block_stack . back ( ) ;
}
block_t * parser_t : : current_block ( )
{
return block_stack . empty ( ) ? NULL : block_stack . back ( ) ;
}
2012-11-19 00:30:30 +00:00
void parser_t : : forbid_function ( const wcstring & function )
2005-09-20 13:26:39 +00:00
{
2012-01-30 06:06:58 +00:00
forbidden_function . push_back ( function ) ;
2005-09-20 13:26:39 +00:00
}
2012-01-23 05:40:08 +00:00
void parser_t : : allow_function ( )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
/*
if ( al_peek ( & forbidden_function ) )
debug ( 2 , L " Allow %ls \n " , al_peek ( & forbidden_function ) ) ;
*/
2011-12-27 06:51:34 +00:00
forbidden_function . pop_back ( ) ;
2005-09-20 13:26:39 +00:00
}
2013-12-12 02:34:28 +00:00
void parser_t : : error ( int ec , size_t p , const wchar_t * str , . . . )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
va_list va ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
CHECK ( str , ) ;
2005-12-07 16:06:47 +00:00
2012-11-19 00:30:30 +00:00
error_code = ec ;
2014-01-15 09:40:40 +00:00
2013-12-16 00:44:05 +00:00
// note : p may be -1
2013-12-12 02:34:28 +00:00
err_pos = static_cast < int > ( p ) ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
va_start ( va , str ) ;
2012-02-10 02:43:36 +00:00
err_buff = vformat_string ( str , va ) ;
2012-11-19 00:30:30 +00:00
va_end ( va ) ;
2005-09-20 13:26:39 +00:00
}
2006-01-23 20:40:14 +00:00
/**
Print profiling information to the specified stream
*/
2013-01-13 23:49:32 +00:00
static void print_profile ( const std : : vector < profile_item_t * > & items ,
2012-11-19 00:30:30 +00:00
FILE * out )
2005-09-20 13:26:39 +00:00
{
2014-02-09 22:04:43 +00:00
for ( size_t pos = 0 ; pos < items . size ( ) ; pos + + )
2012-11-18 10:23:22 +00:00
{
2013-01-13 23:49:32 +00:00
const profile_item_t * me , * prev ;
size_t i ;
int my_time ;
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
me = items . at ( pos ) ;
if ( ! me - > skipped )
2012-11-19 00:30:30 +00:00
{
2013-01-13 23:49:32 +00:00
my_time = me - > parse + me - > exec ;
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
for ( i = pos + 1 ; i < items . size ( ) ; i + + )
2012-11-19 00:30:30 +00:00
{
2013-01-13 23:49:32 +00:00
prev = items . at ( i ) ;
if ( prev - > skipped )
{
continue ;
}
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
if ( prev - > level < = me - > level )
{
break ;
}
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
if ( prev - > level > me - > level + 1 )
{
continue ;
}
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
my_time - = prev - > parse ;
my_time - = prev - > exec ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
if ( me - > cmd . size ( ) > 0 )
2012-11-19 00:30:30 +00:00
{
2013-01-13 23:49:32 +00:00
if ( fwprintf ( out , L " %d \t %d \t " , my_time , me - > parse + me - > exec ) < 0 )
2012-11-19 00:30:30 +00:00
{
wperror ( L " fwprintf " ) ;
return ;
}
2013-01-13 23:49:32 +00:00
for ( i = 0 ; i < me - > level ; i + + )
{
if ( fwprintf ( out , L " - " ) < 0 )
{
wperror ( L " fwprintf " ) ;
return ;
}
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
}
if ( fwprintf ( out , L " > %ls \n " , me - > cmd . c_str ( ) ) < 0 )
{
wperror ( L " fwprintf " ) ;
return ;
}
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2005-09-20 13:26:39 +00:00
}
2014-02-09 22:04:43 +00:00
void parser_t : : emit_profiling ( const char * path ) const
2005-09-20 13:26:39 +00:00
{
2014-02-09 22:04:43 +00:00
/* Save profiling information. OK to not use CLO_EXEC here because this is called while fish is dying (and hence will not fork) */
FILE * f = fopen ( path , " w " ) ;
if ( ! f )
2012-11-18 10:23:22 +00:00
{
2014-02-09 22:04:43 +00:00
debug ( 1 ,
_ ( L " Could not write profiling information to file '%s' " ) ,
path ) ;
}
else
{
if ( fwprintf ( f ,
_ ( L " Time \t Sum \t Command \n " ) ,
profile_items . size ( ) ) < 0 )
2012-11-19 00:30:30 +00:00
{
2014-02-09 22:04:43 +00:00
wperror ( L " fwprintf " ) ;
2012-11-19 00:30:30 +00:00
}
else
{
2014-02-09 22:04:43 +00:00
print_profile ( profile_items , f ) ;
}
2012-11-19 00:30:30 +00:00
2014-02-09 22:04:43 +00:00
if ( fclose ( f ) )
{
wperror ( L " fclose " ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2005-09-20 13:26:39 +00:00
}
2005-10-08 02:00:08 +00:00
/**
2012-03-04 06:08:34 +00:00
Print error message to string if an error has occured while parsing
2006-06-20 00:50:10 +00:00
\ param target the buffer to write to
2012-12-16 04:23:24 +00:00
\ param prefix : The string token to prefix the each line with . Usually the name of the command trying to parse something .
2005-10-08 02:00:08 +00:00
*/
2012-11-19 00:30:30 +00:00
void parser_t : : print_errors ( wcstring & target , const wchar_t * prefix )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
CHECK ( prefix , ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( error_code & & ! err_buff . empty ( ) )
{
int tmp ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
append_format ( target , L " %ls: %ls \n " , prefix , err_buff . c_str ( ) ) ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
tmp = current_tokenizer_pos ;
current_tokenizer_pos = err_pos ;
2005-09-20 13:26:39 +00:00
2014-03-16 21:49:51 +00:00
target . append ( this - > current_line ( ) ) ;
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
current_tokenizer_pos = tmp ;
}
2005-09-20 13:26:39 +00:00
}
2006-06-20 00:50:10 +00:00
/**
Print error message to stderr if an error has occured while parsing
*/
2012-01-23 05:57:30 +00:00
void parser_t : : print_errors_stderr ( )
2006-06-02 02:15:17 +00:00
{
2012-11-19 00:30:30 +00:00
if ( error_code & & ! err_buff . empty ( ) )
{
debug ( 0 , L " %ls " , err_buff . c_str ( ) ) ;
int tmp ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
tmp = current_tokenizer_pos ;
current_tokenizer_pos = err_pos ;
2012-11-18 10:23:22 +00:00
2014-03-16 21:49:51 +00:00
wcstring current_line = this - > current_line ( ) ;
fwprintf ( stderr , L " %ls " , current_line . c_str ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
current_tokenizer_pos = tmp ;
}
2006-06-02 02:15:17 +00:00
}
2014-03-17 15:45:25 +00:00
void parser_t : : eval_args ( const wcstring & arg_list_src , std : : vector < completion_t > & output_arg_list )
2005-09-20 13:26:39 +00:00
{
2012-02-28 02:43:24 +00:00
expand_flags_t eflags = 0 ;
if ( ! show_errors )
eflags | = EXPAND_NO_DESCRIPTIONS ;
2012-05-06 20:51:11 +00:00
if ( this - > parser_type ! = PARSER_TYPE_GENERAL )
2012-02-28 02:43:24 +00:00
eflags | = EXPAND_SKIP_CMDSUBST ;
2012-11-18 10:23:22 +00:00
2014-03-17 15:45:25 +00:00
/* Suppress calling proc_push_interactive off of the main thread. */
2012-02-26 02:54:49 +00:00
if ( this - > parser_type = = PARSER_TYPE_GENERAL )
2014-03-17 15:45:25 +00:00
{
2012-02-26 02:54:49 +00:00
proc_push_interactive ( 0 ) ;
2014-03-17 15:45:25 +00:00
}
2012-11-18 10:23:22 +00:00
2014-03-17 15:45:25 +00:00
/* Parse the string as an argument list */
parse_node_tree_t tree ;
if ( ! parse_tree_from_string ( arg_list_src , parse_flag_none , & tree , NULL /* errors */ , symbol_argument_list ) )
{
/* Failed to parse. Here we expect to have reported any errors in test_args */
return ;
}
2012-11-18 10:23:22 +00:00
2014-03-17 15:45:25 +00:00
/* Get the root argument list */
assert ( ! tree . empty ( ) ) ;
const parse_node_t * arg_list = & tree . at ( 0 ) ;
assert ( arg_list - > type = = symbol_argument_list ) ;
2012-11-18 10:23:22 +00:00
2014-03-17 15:45:25 +00:00
/* Extract arguments from it */
while ( arg_list ! = NULL )
2012-11-18 10:23:22 +00:00
{
2014-03-17 15:45:25 +00:00
const parse_node_t * arg_node = tree . next_node_in_node_list ( * arg_list , symbol_argument , & arg_list ) ;
if ( arg_node ! = NULL )
2012-11-18 10:23:22 +00:00
{
2014-03-17 15:45:25 +00:00
const wcstring arg_src = arg_node - > get_source ( arg_list_src ) ;
if ( expand_string ( arg_src , output_arg_list , eflags ) = = EXPAND_ERROR )
2012-11-19 08:31:03 +00:00
{
2014-03-17 15:45:25 +00:00
/* Failed to expand a string */
2012-11-19 08:31:03 +00:00
break ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2006-06-02 02:15:17 +00:00
2012-02-26 02:54:49 +00:00
if ( this - > parser_type = = PARSER_TYPE_GENERAL )
2014-03-17 15:45:25 +00:00
{
2012-02-26 02:54:49 +00:00
proc_pop_interactive ( ) ;
2014-03-17 15:45:25 +00:00
}
2005-09-20 13:26:39 +00:00
}
2013-12-21 01:44:37 +00:00
void parser_t : : stack_trace ( size_t block_idx , wcstring & buff ) const
2012-11-18 10:23:22 +00:00
{
/*
2012-11-19 00:30:30 +00:00
Check if we should end the recursion
2012-11-18 10:23:22 +00:00
*/
2013-12-21 01:41:21 +00:00
if ( block_idx > = this - > block_count ( ) )
2012-11-19 00:30:30 +00:00
return ;
2014-01-15 09:40:40 +00:00
2013-12-21 01:41:21 +00:00
const block_t * b = this - > block_at_index ( block_idx ) ;
2012-11-19 00:30:30 +00:00
if ( b - > type ( ) = = EVENT )
{
/*
This is an event handler
*/
2012-08-27 05:42:29 +00:00
const event_block_t * eb = static_cast < const event_block_t * > ( b ) ;
2012-11-19 00:30:30 +00:00
wcstring description = event_get_desc ( eb - > event ) ;
append_format ( buff , _ ( L " in event handler: %ls \n " ) , description . c_str ( ) ) ;
buff . append ( L " \n " ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
2012-12-16 04:23:24 +00:00
Stop recursing at event handler . No reason to believe that
2012-11-19 00:30:30 +00:00
any other code is relevant .
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
It might make sense in the future to continue printing the
stack trace of the code that invoked the event , if this is a
programmatic event , but we can ' t currently detect that .
*/
return ;
}
2012-11-18 10:23:22 +00:00
2014-03-16 21:49:51 +00:00
if ( b - > type ( ) = = FUNCTION_CALL | | b - > type ( ) = = FUNCTION_CALL_NO_SHADOW | | b - > type ( ) = = SOURCE | | b - > type ( ) = = SUBST )
2012-11-19 00:30:30 +00:00
{
/*
These types of blocks should be printed
*/
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
int i ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
switch ( b - > type ( ) )
{
2012-11-19 08:31:03 +00:00
case SOURCE :
{
const source_block_t * sb = static_cast < const source_block_t * > ( b ) ;
const wchar_t * source_dest = sb - > source_file ;
2013-12-16 23:33:20 +00:00
append_format ( buff , _ ( L " from sourcing file %ls \n " ) , user_presentable_path ( source_dest ) . c_str ( ) ) ;
2012-11-19 08:31:03 +00:00
break ;
}
case FUNCTION_CALL :
2014-03-16 21:49:51 +00:00
case FUNCTION_CALL_NO_SHADOW :
2012-11-19 08:31:03 +00:00
{
const function_block_t * fb = static_cast < const function_block_t * > ( b ) ;
2013-12-16 23:33:20 +00:00
append_format ( buff , _ ( L " in function '%ls' \n " ) , fb - > name . c_str ( ) ) ;
2012-11-19 08:31:03 +00:00
break ;
}
case SUBST :
{
append_format ( buff , _ ( L " in command substitution \n " ) ) ;
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
default : /* Can't get here */
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
const wchar_t * file = b - > src_filename ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( file )
{
append_format ( buff ,
2013-12-16 23:33:20 +00:00
_ ( L " \t called on line %d of file %ls \n " ) ,
2012-11-19 00:30:30 +00:00
b - > src_lineno ,
2013-12-16 23:33:20 +00:00
user_presentable_path ( file ) . c_str ( ) ) ;
2012-11-19 00:30:30 +00:00
}
2014-02-20 18:57:13 +00:00
else if ( is_within_fish_initialization )
{
append_format ( buff , _ ( L " \t called during startup \n " ) ) ;
}
2012-11-19 00:30:30 +00:00
else
{
2014-02-20 18:57:13 +00:00
append_format ( buff , _ ( L " \t called on standard input \n " ) ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( b - > type ( ) = = FUNCTION_CALL )
2012-08-27 05:42:29 +00:00
{
const function_block_t * fb = static_cast < const function_block_t * > ( b ) ;
const process_t * const process = fb - > process ;
2012-11-19 00:30:30 +00:00
if ( process - > argv ( 1 ) )
{
wcstring tmp ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
for ( i = 1 ; process - > argv ( i ) ; i + + )
{
2012-02-22 20:00:02 +00:00
if ( i > 1 )
tmp . push_back ( L ' ' ) ;
tmp . append ( process - > argv ( i ) ) ;
2012-11-19 00:30:30 +00:00
}
append_format ( buff , _ ( L " \t with parameter list '%ls' \n " ) , tmp . c_str ( ) ) ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
append_format ( buff , L " \n " ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Recursively print the next block
*/
2013-12-21 01:41:21 +00:00
parser_t : : stack_trace ( block_idx + 1 , buff ) ;
2006-01-26 14:48:10 +00:00
}
2006-06-08 23:55:57 +00:00
/**
2006-10-28 16:41:22 +00:00
Returns the name of the currently evaluated function if we are
currently evaluating a function , null otherwise . This is tested by
moving down the block - scope - stack , checking every block if it is of
type FUNCTION_CALL .
2006-06-08 23:55:57 +00:00
*/
2012-01-23 05:57:30 +00:00
const wchar_t * parser_t : : is_function ( ) const
2006-01-26 14:48:10 +00:00
{
2012-02-08 01:06:45 +00:00
// PCA: Have to make this a string somehow
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-18 10:23:22 +00:00
2013-12-21 01:41:21 +00:00
const wchar_t * result = NULL ;
for ( size_t block_idx = 0 ; block_idx < this - > block_count ( ) ; block_idx + + )
2012-11-18 10:23:22 +00:00
{
2013-12-21 01:41:21 +00:00
const block_t * b = this - > block_at_index ( block_idx ) ;
2014-03-16 21:49:51 +00:00
if ( b - > type ( ) = = FUNCTION_CALL | | b - > type ( ) = = FUNCTION_CALL_NO_SHADOW )
2012-11-19 00:30:30 +00:00
{
2012-08-27 05:42:29 +00:00
const function_block_t * fb = static_cast < const function_block_t * > ( b ) ;
2013-12-21 01:41:21 +00:00
result = fb - > name . c_str ( ) ;
break ;
2012-11-19 00:30:30 +00:00
}
2014-03-16 23:45:00 +00:00
else if ( b - > type ( ) = = SOURCE )
{
/* If a function sources a file, obviously that function's offset doesn't contribute */
break ;
}
2012-11-18 10:23:22 +00:00
}
2013-12-21 01:41:21 +00:00
return result ;
2006-01-26 14:48:10 +00:00
}
2012-01-23 05:40:08 +00:00
int parser_t : : get_lineno ( ) const
2006-01-26 14:48:10 +00:00
{
2014-03-02 21:11:17 +00:00
int lineno = - 1 ;
if ( ! execution_contexts . empty ( ) )
2014-03-02 00:04:13 +00:00
{
2014-03-02 21:11:17 +00:00
lineno = execution_contexts . back ( ) - > get_current_line_number ( ) ;
2014-03-16 23:45:00 +00:00
/* If we are executing a function, we have to add in its offset */
const wchar_t * function_name = is_function ( ) ;
if ( function_name ! = NULL )
{
lineno + = function_get_definition_offset ( function_name ) ;
}
2014-03-02 00:04:13 +00:00
}
2012-11-19 00:30:30 +00:00
return lineno ;
2006-01-26 14:48:10 +00:00
}
2012-08-05 00:44:14 +00:00
int parser_t : : line_number_of_character_at_offset ( size_t idx ) const
{
2012-11-19 00:30:30 +00:00
if ( ! current_tokenizer )
2012-08-05 00:44:14 +00:00
return - 1 ;
2012-11-18 10:23:22 +00:00
2012-08-05 00:44:14 +00:00
int result = current_tokenizer - > line_number_of_character_at_offset ( idx ) ;
//assert(result == parse_util_lineno(tok_string( current_tokenizer ), idx));
return result ;
}
2012-01-23 05:40:08 +00:00
const wchar_t * parser_t : : current_filename ( ) const
2006-01-26 14:48:10 +00:00
{
2012-02-02 23:05:08 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-18 10:23:22 +00:00
2013-12-21 01:41:21 +00:00
for ( size_t i = 0 ; i < this - > block_count ( ) ; i + + )
2012-11-18 10:23:22 +00:00
{
2013-12-21 01:41:21 +00:00
const block_t * b = this - > block_at_index ( i ) ;
2014-03-16 23:45:00 +00:00
if ( b - > type ( ) = = FUNCTION_CALL | | b - > type ( ) = = FUNCTION_CALL_NO_SHADOW )
2012-11-19 00:30:30 +00:00
{
2012-08-27 05:42:29 +00:00
const function_block_t * fb = static_cast < const function_block_t * > ( b ) ;
2012-11-19 00:30:30 +00:00
return function_get_definition_file ( fb - > name ) ;
}
2014-03-16 23:45:00 +00:00
else if ( b - > type ( ) = = SOURCE )
{
const source_block_t * sb = static_cast < const source_block_t * > ( b ) ;
return sb - > source_file ;
}
2012-11-18 10:23:22 +00:00
}
2014-01-15 09:40:40 +00:00
2014-01-01 23:29:56 +00:00
/* We query a global array for the current file name, but only do that if we are the principal parser */
if ( this = = & principal_parser ( ) )
{
return reader_current_filename ( ) ;
}
return NULL ;
2006-01-26 14:48:10 +00:00
}
2014-03-16 21:49:51 +00:00
wcstring parser_t : : current_line ( )
2005-09-20 13:26:39 +00:00
{
2014-03-17 05:06:32 +00:00
if ( execution_contexts . empty ( ) )
2012-11-18 10:23:22 +00:00
{
2014-03-17 05:06:32 +00:00
return wcstring ( ) ;
2012-11-18 10:23:22 +00:00
}
2014-03-17 05:06:32 +00:00
const parse_execution_context_t * context = execution_contexts . back ( ) ;
assert ( context ! = NULL ) ;
2006-05-10 11:54:31 +00:00
2014-03-17 05:06:32 +00:00
int source_offset = context - > get_current_source_offset ( ) ;
if ( source_offset < 0 )
2012-11-19 00:30:30 +00:00
{
2014-03-17 05:06:32 +00:00
return wcstring ( ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2014-03-17 05:06:32 +00:00
const int lineno = this - > get_lineno ( ) ;
const wchar_t * file = this - > current_filename ( ) ;
2012-11-18 10:23:22 +00:00
2014-03-17 05:06:32 +00:00
wcstring prefix ;
2006-01-30 19:53:10 +00:00
2014-03-17 05:06:32 +00:00
/* If we are not going to print a stack trace, at least print the line number and filename */
2012-11-19 00:30:30 +00:00
if ( ! get_is_interactive ( ) | | is_function ( ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( file )
2014-02-20 18:57:13 +00:00
{
2014-03-17 05:06:32 +00:00
append_format ( prefix , _ ( L " %ls (line %d): " ) , user_presentable_path ( file ) . c_str ( ) , lineno ) ;
2014-02-20 18:57:13 +00:00
}
else if ( is_within_fish_initialization )
{
2014-03-17 05:06:32 +00:00
append_format ( prefix , L " %ls: " , _ ( L " Startup " ) , lineno ) ;
2014-02-20 18:57:13 +00:00
}
2012-11-19 00:30:30 +00:00
else
2014-02-20 18:57:13 +00:00
{
2014-03-17 05:06:32 +00:00
append_format ( prefix , L " %ls: " , _ ( L " Standard input " ) , lineno ) ;
2014-02-20 18:57:13 +00:00
}
2012-11-19 00:30:30 +00:00
}
2014-03-17 05:06:32 +00:00
bool skip_caret = get_is_interactive ( ) & & ! is_function ( ) ;
/* Use an error with empty text */
assert ( source_offset > = 0 ) ;
parse_error_t empty_error = { } ;
empty_error . source_start = source_offset ;
wcstring line_info = empty_error . describe_with_prefix ( context - > get_source ( ) , prefix , skip_caret ) ;
if ( ! line_info . empty ( ) )
2012-11-19 00:30:30 +00:00
{
2014-03-17 05:06:32 +00:00
line_info . push_back ( L ' \n ' ) ;
2012-11-18 10:23:22 +00:00
}
2014-03-17 05:06:32 +00:00
parser_t : : stack_trace ( 0 , line_info ) ;
return line_info ;
2005-09-20 13:26:39 +00:00
}
2012-01-23 05:40:08 +00:00
const wchar_t * parser_t : : get_buffer ( ) const
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
return tok_string ( current_tokenizer ) ;
2005-09-20 13:26:39 +00:00
}
2013-12-12 02:34:28 +00:00
int parser_t : : is_help ( const wchar_t * s , int min_match )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
CHECK ( s , 0 ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
size_t len = wcslen ( s ) ;
2006-10-28 16:41:22 +00:00
2012-11-19 00:30:30 +00:00
min_match = maxi ( min_match , 3 ) ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
return ( wcscmp ( L " -h " , s ) = = 0 ) | |
2012-12-21 01:37:09 +00:00
( len > = ( size_t ) min_match & & ( wcsncmp ( L " --help " , s , len ) = = 0 ) ) ;
2005-09-20 13:26:39 +00:00
}
2013-12-27 09:38:43 +00:00
void parser_t : : job_add ( job_t * job )
{
assert ( job ! = NULL ) ;
assert ( job - > first_process ! = NULL ) ;
this - > my_job_list . push_front ( job ) ;
}
2013-12-26 21:24:10 +00:00
job_t * parser_t : : job_create ( const io_chain_t & io )
2012-02-28 02:43:24 +00:00
{
2013-12-26 21:24:10 +00:00
job_t * res = new job_t ( acquire_job_id ( ) , io ) ;
2012-02-28 02:43:24 +00:00
this - > my_job_list . push_front ( res ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
job_set_flag ( res ,
JOB_CONTROL ,
( job_control_mode = = JOB_CONTROL_ALL ) | |
( ( job_control_mode = = JOB_CONTROL_INTERACTIVE ) & & ( get_is_interactive ( ) ) ) ) ;
2012-02-28 02:43:24 +00:00
return res ;
}
2012-11-19 00:30:30 +00:00
bool parser_t : : job_remove ( job_t * j )
2012-02-28 02:43:24 +00:00
{
job_list_t : : iterator iter = std : : find ( my_job_list . begin ( ) , my_job_list . end ( ) , j ) ;
2012-11-19 00:30:30 +00:00
if ( iter ! = my_job_list . end ( ) )
{
2012-02-28 02:43:24 +00:00
my_job_list . erase ( iter ) ;
return true ;
2012-11-19 00:30:30 +00:00
}
else
{
debug ( 1 , _ ( L " Job inconsistency " ) ) ;
sanity_lose ( ) ;
2012-02-28 02:43:24 +00:00
return false ;
}
}
void parser_t : : job_promote ( job_t * job )
{
2013-01-30 10:22:38 +00:00
signal_block ( ) ;
2013-01-31 23:57:08 +00:00
2014-01-15 09:40:40 +00:00
job_list_t : : iterator loc = std : : find ( my_job_list . begin ( ) , my_job_list . end ( ) , job ) ;
2012-02-28 02:43:24 +00:00
assert ( loc ! = my_job_list . end ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-02-28 02:43:24 +00:00
/* Move the job to the beginning */
my_job_list . splice ( my_job_list . begin ( ) , my_job_list , loc ) ;
2013-01-30 10:22:38 +00:00
signal_unblock ( ) ;
2012-02-28 02:43:24 +00:00
}
job_t * parser_t : : job_get ( job_id_t id )
{
job_iterator_t jobs ( my_job_list ) ;
job_t * job ;
2012-11-19 00:30:30 +00:00
while ( ( job = jobs . next ( ) ) )
{
if ( id < = 0 | | job - > job_id = = id )
2012-02-28 02:43:24 +00:00
return job ;
2012-11-19 00:30:30 +00:00
}
return NULL ;
2012-02-28 02:43:24 +00:00
}
2012-11-19 00:30:30 +00:00
job_t * parser_t : : job_get_from_pid ( int pid )
2012-02-28 02:43:24 +00:00
{
job_iterator_t jobs ;
job_t * job ;
2012-11-19 00:30:30 +00:00
while ( ( job = jobs . next ( ) ) )
{
if ( job - > pgid = = pid )
return job ;
}
return 0 ;
2012-02-28 02:43:24 +00:00
}
2014-02-09 22:04:43 +00:00
profile_item_t * parser_t : : create_profile_item ( )
{
profile_item_t * result = NULL ;
if ( g_profiling_active )
{
result = new profile_item_t ( ) ;
profile_items . push_back ( result ) ;
}
return result ;
}
2005-09-20 13:26:39 +00:00
2014-03-01 01:54:05 +00:00
int parser_t : : eval_new_parser ( const wcstring & cmd , const io_chain_t & io , enum block_type_t block_type )
2005-09-20 13:26:39 +00:00
{
2014-03-01 01:54:05 +00:00
CHECK_BLOCK ( 1 ) ;
2012-11-18 10:23:22 +00:00
2014-03-01 01:54:05 +00:00
if ( block_type ! = TOP & & block_type ! = SUBST )
2012-11-18 10:23:22 +00:00
{
2014-03-01 01:54:05 +00:00
debug ( 1 , INVALID_SCOPE_ERR_MSG , parser_t : : get_block_desc ( block_type ) ) ;
bugreport ( ) ;
return 1 ;
}
2012-11-18 10:23:22 +00:00
2014-03-01 01:54:05 +00:00
/* Parse the source into a tree, if we can */
parse_node_tree_t tree ;
if ( ! parse_tree_from_string ( cmd , parse_flag_none , & tree , NULL ) )
{
return 1 ;
}
2012-11-19 08:31:03 +00:00
2014-03-16 23:45:00 +00:00
//print_stderr(block_stack_description());
2012-11-19 00:30:30 +00:00
2014-03-01 01:54:05 +00:00
/* Determine the initial eval level. If this is the first context, it's -1; otherwise it's the eval level of the top context. This is sort of wonky because we're stitching together a global notion of eval level from these separate objects. A better approach would be some profile object that all contexts share, and that tracks the eval levels on its own. */
int exec_eval_level = ( execution_contexts . empty ( ) ? - 1 : execution_contexts . back ( ) - > current_eval_level ( ) ) ;
2013-02-27 20:03:30 +00:00
2014-03-01 01:54:05 +00:00
/* Append to the execution context stack */
parse_execution_context_t * ctx = new parse_execution_context_t ( tree , cmd , this , exec_eval_level ) ;
execution_contexts . push_back ( ctx ) ;
2012-11-19 00:30:30 +00:00
2014-03-02 04:33:26 +00:00
/* Execute the first node */
int result = 1 ;
if ( ! tree . empty ( ) )
2012-11-18 10:23:22 +00:00
{
2014-03-02 04:33:26 +00:00
result = this - > eval_block_node ( 0 , io , block_type ) ;
}
2012-11-18 10:23:22 +00:00
2014-03-01 01:54:05 +00:00
/* Clean up the execution context stack */
assert ( ! execution_contexts . empty ( ) & & execution_contexts . back ( ) = = ctx ) ;
execution_contexts . pop_back ( ) ;
delete ctx ;
2012-11-18 10:23:22 +00:00
2014-03-01 01:54:05 +00:00
return 0 ;
}
2012-11-18 10:23:22 +00:00
2014-03-01 01:54:05 +00:00
int parser_t : : eval_block_node ( node_offset_t node_idx , const io_chain_t & io , enum block_type_t block_type )
{
/* Paranoia. It's a little frightening that we're given only a node_idx and we interpret this in the topmost execution context's tree. What happens if two trees were to be interleaved? Fortunately that cannot happen (yet); in the future we probably want some sort of reference counted trees.
*/
parse_execution_context_t * ctx = execution_contexts . back ( ) ;
assert ( ctx ! = NULL ) ;
2012-11-18 10:23:22 +00:00
2014-03-01 01:54:05 +00:00
CHECK_BLOCK ( 1 ) ;
2012-11-18 10:23:22 +00:00
2014-03-01 01:54:05 +00:00
/* Handle cancellation requests. If our block stack is currently empty, then we already did successfully cancel (or there was nothing to cancel); clear the flag. If our block stack is not empty, we are still in the process of cancelling; refuse to evaluate anything */
if ( this - > cancellation_requested )
{
if ( ! block_stack . empty ( ) )
{
return 1 ;
}
else
{
this - > cancellation_requested = false ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2014-03-01 01:54:05 +00:00
/* Only certain blocks are allowed */
if ( ( block_type ! = TOP ) & &
( block_type ! = SUBST ) )
2012-11-18 10:23:22 +00:00
{
2014-03-01 01:54:05 +00:00
debug ( 1 ,
INVALID_SCOPE_ERR_MSG ,
parser_t : : get_block_desc ( block_type ) ) ;
bugreport ( ) ;
return 1 ;
}
2012-11-18 10:23:22 +00:00
2014-03-01 01:54:05 +00:00
/* Not sure why we reap jobs here */
job_reap ( 0 ) ;
2012-11-18 10:23:22 +00:00
2014-03-01 01:54:05 +00:00
/* Start it up */
const block_t * const start_current_block = current_block ( ) ;
block_t * scope_block = new scope_block_t ( block_type ) ;
this - > push_block ( scope_block ) ;
int result = ctx - > eval_node_at_offset ( node_idx , scope_block , io ) ;
2012-11-18 10:23:22 +00:00
2014-03-01 01:54:05 +00:00
/* Clean up the block stack */
this - > pop_block ( ) ;
while ( start_current_block ! = current_block ( ) )
{
if ( current_block ( ) = = NULL )
{
debug ( 0 ,
_ ( L " End of block mismatch. Program terminating. " ) ) ;
bugreport ( ) ;
FATAL_EXIT ( ) ;
break ;
2012-11-19 00:30:30 +00:00
}
2014-03-01 01:54:05 +00:00
this - > pop_block ( ) ;
2012-11-18 10:23:22 +00:00
}
2014-03-01 01:54:05 +00:00
/* Reap again */
job_reap ( 0 ) ;
2006-11-02 13:45:37 +00:00
2014-03-01 01:54:05 +00:00
return result ;
2005-09-20 13:26:39 +00:00
2014-03-01 01:54:05 +00:00
}
2013-12-20 22:37:40 +00:00
2013-12-09 05:54:06 +00:00
int parser_t : : eval ( const wcstring & cmd_str , const io_chain_t & io , enum block_type_t block_type )
2005-09-20 13:26:39 +00:00
{
2014-03-01 01:54:05 +00:00
return this - > eval_new_parser ( cmd_str , io , block_type ) ;
2005-09-20 13:26:39 +00:00
}
2006-06-16 12:56:16 +00:00
2006-06-08 23:55:57 +00:00
/**
\ return the block type created by the specified builtin , or - 1 on error .
*/
2012-11-19 00:30:30 +00:00
block_type_t parser_get_block_type ( const wcstring & cmd )
2006-05-26 11:25:25 +00:00
{
2013-01-24 22:59:52 +00:00
for ( size_t i = 0 ; block_lookup [ i ] . desc ; i + + )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( block_lookup [ i ] . name & & cmd = = block_lookup [ i ] . name )
{
return block_lookup [ i ] . type ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
return ( block_type_t ) - 1 ;
2006-06-16 12:56:16 +00:00
}
/**
2006-08-22 14:38:31 +00:00
\ return the block command that createa the specified block type , or null on error .
2006-06-16 12:56:16 +00:00
*/
2012-11-19 00:30:30 +00:00
const wchar_t * parser_get_block_command ( int type )
2006-06-16 12:56:16 +00:00
{
2013-01-24 22:59:52 +00:00
for ( size_t i = 0 ; block_lookup [ i ] . desc ; i + + )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( block_lookup [ i ] . type = = type )
{
return block_lookup [ i ] . name ;
}
2012-11-18 10:23:22 +00:00
}
2013-01-24 22:59:52 +00:00
return NULL ;
2006-05-26 11:25:25 +00:00
}
2006-06-08 23:55:57 +00:00
/**
Test if this argument contains any errors . Detected errors include
2006-07-21 01:08:31 +00:00
syntax errors in command substitutions , improperly escaped
2006-06-08 23:55:57 +00:00
characters and improper use of the variable expansion operator .
*/
2012-11-19 00:30:30 +00:00
int parser_t : : parser_test_argument ( const wchar_t * arg , wcstring * out , const wchar_t * prefix , int offset )
2006-05-21 19:25:24 +00:00
{
2012-11-19 00:30:30 +00:00
int err = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
wchar_t * paran_begin , * paran_end ;
wchar_t * arg_cpy ;
int do_loop = 1 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
CHECK ( arg , 1 ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
arg_cpy = wcsdup ( arg ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
while ( do_loop )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
switch ( parse_util_locate_cmdsubst ( arg_cpy ,
& paran_begin ,
& paran_end ,
2013-07-17 08:35:30 +00:00
false ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
case - 1 :
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
offset ,
L " Mismatched parenthesis " ) ;
this - > print_errors ( * out , prefix ) ;
}
free ( arg_cpy ) ;
return err ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case 0 :
do_loop = 0 ;
break ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case 1 :
{
2012-11-18 10:23:22 +00:00
2013-12-13 02:18:07 +00:00
const wcstring subst ( paran_begin + 1 , paran_end ) ;
2012-11-19 08:31:03 +00:00
wcstring tmp ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
tmp . append ( arg_cpy , paran_begin - arg_cpy ) ;
tmp . push_back ( INTERNAL_SEPARATOR ) ;
tmp . append ( paran_end + 1 ) ;
2012-11-18 10:23:22 +00:00
// debug( 1, L"%ls -> %ls %ls", arg_cpy, subst, tmp.buff );
2013-12-13 02:18:07 +00:00
parse_error_list_t errors ;
2013-12-16 00:05:37 +00:00
err | = parse_util_detect_errors ( subst , & errors ) ;
2013-12-13 02:18:07 +00:00
if ( out & & ! errors . empty ( ) )
{
out - > append ( parse_errors_description ( errors , subst , prefix ) ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
free ( arg_cpy ) ;
arg_cpy = wcsdup ( tmp . c_str ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
break ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2013-11-25 06:57:49 +00:00
wcstring unesc ;
if ( ! unescape_string ( arg_cpy , & unesc , UNESCAPE_SPECIAL ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( out )
{
error ( SYNTAX_ERROR ,
offset ,
L " Invalid token '%ls' " , arg_cpy ) ;
print_errors ( * out , prefix ) ;
}
return 1 ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
else
2012-11-18 10:23:22 +00:00
{
2013-11-25 06:57:49 +00:00
/* Check for invalid variable expansions */
const size_t unesc_size = unesc . size ( ) ;
for ( size_t idx = 0 ; idx < unesc_size ; idx + + )
2012-11-18 10:23:22 +00:00
{
2013-11-25 06:57:49 +00:00
switch ( unesc . at ( idx ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
case VARIABLE_EXPAND :
case VARIABLE_EXPAND_SINGLE :
2012-11-19 00:30:30 +00:00
{
2013-11-25 06:57:49 +00:00
wchar_t next_char = ( idx + 1 < unesc_size ? unesc . at ( idx + 1 ) : L ' \0 ' ) ;
2012-11-19 08:31:03 +00:00
2013-11-25 06:57:49 +00:00
if ( next_char ! = VARIABLE_EXPAND & &
next_char ! = VARIABLE_EXPAND_SINGLE & &
! wcsvarchr ( next_char ) )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
err = 1 ;
if ( out )
{
2013-11-25 06:57:49 +00:00
expand_variable_error ( * this , unesc , idx , offset ) ;
2012-11-19 08:31:03 +00:00
print_errors ( * out , prefix ) ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
break ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
}
2012-11-19 00:30:30 +00:00
free ( arg_cpy ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
return err ;
2012-11-18 10:23:22 +00:00
2006-05-21 19:25:24 +00:00
}
2012-11-19 00:30:30 +00:00
int parser_t : : test_args ( const wchar_t * buff , wcstring * out , const wchar_t * prefix )
2006-05-21 19:25:24 +00:00
{
2012-11-19 00:30:30 +00:00
int do_loop = 1 ;
int err = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
CHECK ( buff , 1 ) ;
2012-11-18 10:23:22 +00:00
2012-11-22 01:48:35 +00:00
tokenizer_t tok ( buff , 0 ) ;
2013-02-27 20:03:30 +00:00
scoped_push < tokenizer_t * > tokenizer_push ( & current_tokenizer , & tok ) ;
scoped_push < int > tokenizer_pos_push ( & current_tokenizer_pos ) ;
2013-02-22 14:34:30 +00:00
2012-11-22 01:48:35 +00:00
for ( ; do_loop & & tok_has_next ( & tok ) ; tok_next ( & tok ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
current_tokenizer_pos = tok_get_pos ( & tok ) ;
switch ( tok_last_type ( & tok ) )
{
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_STRING :
{
err | = parser_test_argument ( tok_last ( & tok ) , out , prefix , tok_get_pos ( & tok ) ) ;
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_END :
{
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_ERROR :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
TOK_ERR_MSG ,
tok_last ( & tok ) ) ;
print_errors ( * out , prefix ) ;
}
err = 1 ;
do_loop = 0 ;
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
default :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
UNEXPECTED_TOKEN_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
print_errors ( * out , prefix ) ;
}
err = 1 ;
do_loop = 0 ;
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
}
2012-11-19 00:30:30 +00:00
error_code = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
return err ;
2006-05-21 19:25:24 +00:00
}
2013-01-24 22:59:52 +00:00
// helper type used in parser::test below
2013-01-31 23:57:08 +00:00
struct block_info_t
{
2013-01-24 22:59:52 +00:00
int position ; //tokenizer position
block_type_t type ; //type of the block
} ;
2013-12-16 00:05:37 +00:00
void parser_t : : get_backtrace ( const wcstring & src , const parse_error_list_t & errors , wcstring * output ) const
2013-12-13 02:18:07 +00:00
{
2013-12-16 00:05:37 +00:00
assert ( output ! = NULL ) ;
if ( ! errors . empty ( ) )
2013-12-12 02:34:28 +00:00
{
2013-12-27 09:38:43 +00:00
const parse_error_t & err = errors . at ( 0 ) ;
2014-01-15 09:40:40 +00:00
2013-12-16 00:05:37 +00:00
// Determine which line we're on
assert ( err . source_start < = src . size ( ) ) ;
size_t which_line = 1 + std : : count ( src . begin ( ) , src . begin ( ) + err . source_start , L ' \n ' ) ;
2014-01-15 09:40:40 +00:00
2014-02-17 22:51:51 +00:00
// Don't include the caret if we're interactive, this is the first line of text, and our source is at its beginning, because then it's obvious
bool skip_caret = ( get_is_interactive ( ) & & which_line = = 1 & & err . source_start = = 0 ) ;
wcstring prefix ;
2013-12-16 00:05:37 +00:00
const wchar_t * filename = this - > current_filename ( ) ;
if ( filename )
2013-12-12 02:34:28 +00:00
{
2014-02-17 22:51:51 +00:00
prefix = format_string ( _ ( L " %ls (line %lu): " ) , user_presentable_path ( filename ) . c_str ( ) , which_line ) ;
2013-12-12 02:34:28 +00:00
}
2013-12-16 00:05:37 +00:00
else
{
2014-02-17 22:51:51 +00:00
prefix = L " fish: " ;
2013-12-16 00:05:37 +00:00
}
2014-01-15 09:40:40 +00:00
2014-03-15 20:07:19 +00:00
const wcstring description = err . describe_with_prefix ( src , prefix , skip_caret ) ;
if ( ! description . empty ( ) )
{
output - > append ( description ) ;
output - > push_back ( L ' \n ' ) ;
}
2013-12-21 01:45:49 +00:00
this - > stack_trace ( 0 , * output ) ;
2013-12-12 02:34:28 +00:00
}
}
2012-08-27 06:16:20 +00:00
block_t : : block_t ( block_type_t t ) :
2012-11-19 00:30:30 +00:00
block_type ( t ) ,
skip ( ) ,
tok_pos ( ) ,
2013-12-24 21:17:24 +00:00
node_offset ( NODE_OFFSET_INVALID ) ,
2012-11-19 00:30:30 +00:00
loop_status ( ) ,
job ( ) ,
src_filename ( ) ,
src_lineno ( ) ,
wants_pop_env ( false ) ,
2013-12-21 01:41:21 +00:00
event_blocks ( )
2012-08-27 05:42:29 +00:00
{
}
block_t : : ~ block_t ( )
{
2012-08-27 06:16:20 +00:00
}
2014-03-16 23:45:00 +00:00
wcstring block_t : : description ( ) const
{
wcstring result ;
switch ( this - > type ( ) )
{
case WHILE :
result . append ( L " while " ) ;
break ;
case FOR :
result . append ( L " for " ) ;
break ;
case IF :
result . append ( L " if " ) ;
break ;
case FUNCTION_DEF :
result . append ( L " function_def " ) ;
break ;
case FUNCTION_CALL :
result . append ( L " function_call " ) ;
break ;
case FUNCTION_CALL_NO_SHADOW :
result . append ( L " function_call_no_shadow " ) ;
break ;
case SWITCH :
result . append ( L " switch " ) ;
break ;
case FAKE :
result . append ( L " fake " ) ;
break ;
case SUBST :
result . append ( L " substitution " ) ;
break ;
case TOP :
result . append ( L " top " ) ;
break ;
case BEGIN :
result . append ( L " begin " ) ;
break ;
case SOURCE :
result . append ( L " source " ) ;
break ;
case EVENT :
result . append ( L " event " ) ;
break ;
case BREAKPOINT :
result . append ( L " breakpoint " ) ;
break ;
default :
append_format ( result , L " unknown type %ld " , ( long ) this - > type ( ) ) ;
break ;
}
if ( this - > src_lineno > = 0 )
{
append_format ( result , L " (line %d) " , this - > src_lineno ) ;
}
if ( this - > src_filename ! = NULL )
{
append_format ( result , L " (file %ls) " , this - > src_filename ) ;
}
return result ;
}
2012-08-27 06:16:20 +00:00
/* Various block constructors */
2014-03-02 21:46:30 +00:00
if_block_t : : if_block_t ( ) : block_t ( IF )
2012-08-27 06:16:20 +00:00
{
}
2012-12-22 17:38:28 +00:00
event_block_t : : event_block_t ( const event_t & evt ) :
2012-08-27 06:16:20 +00:00
block_t ( EVENT ) ,
event ( evt )
{
}
2013-01-30 10:22:38 +00:00
function_block_t : : function_block_t ( const process_t * p , const wcstring & n , bool shadows ) :
2012-11-19 00:30:30 +00:00
block_t ( shadows ? FUNCTION_CALL : FUNCTION_CALL_NO_SHADOW ) ,
2012-08-27 06:16:20 +00:00
process ( p ) ,
2012-09-01 19:29:00 +00:00
name ( n )
2012-08-27 06:16:20 +00:00
{
}
source_block_t : : source_block_t ( const wchar_t * src ) :
2012-09-01 19:29:00 +00:00
block_t ( SOURCE ) ,
source_file ( src )
2012-08-27 06:16:20 +00:00
{
}
2014-03-02 21:46:30 +00:00
for_block_t : : for_block_t ( ) : block_t ( FOR )
2012-08-27 06:16:20 +00:00
{
}
2014-03-02 21:46:30 +00:00
while_block_t : : while_block_t ( ) : block_t ( WHILE )
2012-08-27 06:16:20 +00:00
{
}
2014-03-02 21:46:30 +00:00
switch_block_t : : switch_block_t ( ) : block_t ( SWITCH )
2012-08-27 06:16:20 +00:00
{
}
2014-03-02 21:46:30 +00:00
fake_block_t : : fake_block_t ( ) : block_t ( FAKE )
2012-08-27 06:16:20 +00:00
{
}
2014-03-02 21:46:30 +00:00
scope_block_t : : scope_block_t ( block_type_t type ) : block_t ( type )
2012-08-27 06:16:20 +00:00
{
assert ( type = = BEGIN | | type = = TOP | | type = = SUBST ) ;
}
2014-03-02 21:46:30 +00:00
breakpoint_block_t : : breakpoint_block_t ( ) : block_t ( BREAKPOINT )
2012-08-27 06:16:20 +00:00
{
2012-08-27 05:42:29 +00:00
}
2013-01-04 21:09:01 +00:00
2013-12-26 21:24:10 +00:00
bool parser_use_ast ( void )
{
env_var_t var = env_get_string ( L " fish_new_parser " ) ;
if ( var . missing_or_empty ( ) )
{
2014-02-28 05:06:39 +00:00
return 1 ;
2013-12-26 21:24:10 +00:00
}
else
{
return from_string < bool > ( var ) ;
}
}