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"
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 ) ,
2014-01-02 20:37:50 +00:00
cancellation_requested ( false ) ,
2014-03-21 04:31:47 +00:00
is_within_fish_initialization ( false )
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
2014-10-01 03:58:45 +00:00
// Types TOP and SUBST are not considered blocks for the purposes of `status -b`
if ( type ! = TOP & & type ! = SUBST )
{
is_block = 1 ;
}
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 ;
2014-10-01 03:58:45 +00:00
// Figure out if `status -b` should consider us to be in a block now
int new_is_block = 0 ;
for ( std : : vector < block_t * > : : const_iterator it = block_stack . begin ( ) , end = block_stack . end ( ) ; it ! = end ; + + it )
{
const enum block_type_t type = ( * it ) - > type ( ) ;
if ( type ! = TOP & & type ! = SUBST )
{
new_is_block = 1 ;
break ;
}
}
is_block = new_is_block ;
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
{
2011-12-27 06:51:34 +00:00
forbidden_function . pop_back ( ) ;
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
}
2014-03-18 21:14:32 +00:00
void parser_t : : expand_argument_list ( 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 ;
2014-03-27 18:17:05 +00:00
if ( ! parse_tree_from_string ( arg_list_src , parse_flag_none , & tree , NULL /* errors */ , symbol_freestanding_argument_list ) )
2014-03-17 15:45:25 +00:00
{
/* 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 ) ;
2014-03-27 18:17:05 +00:00
assert ( arg_list - > type = = symbol_freestanding_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 ) ;
2014-03-22 00:13:33 +00:00
if ( expand_string ( arg_src , output_arg_list , eflags , NULL ) = = 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-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-18 21:14:32 +00:00
bool is_interactive = get_is_interactive ( ) ;
bool skip_caret = is_interactive & & ! is_function ( ) ;
2014-03-17 05:06:32 +00:00
/* Use an error with empty text */
assert ( source_offset > = 0 ) ;
parse_error_t empty_error = { } ;
empty_error . source_start = source_offset ;
2014-03-18 21:14:32 +00:00
wcstring line_info = empty_error . describe_with_prefix ( context - > get_source ( ) , prefix , is_interactive , skip_caret ) ;
2014-03-17 05:06:32 +00:00
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
}
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 ) ;
}
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-18 21:42:38 +00:00
int parser_t : : eval ( 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 ;
2014-10-13 03:09:45 +00:00
parse_error_list_t error_list ;
if ( ! parse_tree_from_string ( cmd , parse_flag_none , & tree , this - > show_errors ? & error_list : NULL ) )
2014-03-01 01:54:05 +00:00
{
2014-10-13 03:09:45 +00:00
if ( this - > show_errors )
{
/* Get a backtrace */
wcstring backtrace_and_desc ;
this - > get_backtrace ( cmd , error_list , & backtrace_and_desc ) ;
/* Print it */
fprintf ( stderr , " %ls " , backtrace_and_desc . c_str ( ) ) ;
}
2014-03-01 01:54:05 +00:00
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 */
if ( ! tree . empty ( ) )
2012-11-18 10:23:22 +00:00
{
2014-04-28 00:23:19 +00:00
this - > eval_block_node ( 0 , io , block_type ) ;
2014-03-02 04:33:26 +00:00
}
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 ;
2006-05-21 19:25:24 +00:00
}
2014-03-18 21:14:32 +00:00
bool parser_t : : detect_errors_in_argument_list ( const wcstring & arg_list_src , wcstring * out , const wchar_t * prefix )
2006-05-21 19:25:24 +00:00
{
2014-03-18 21:14:32 +00:00
bool errored = false ;
parse_error_list_t errors ;
2013-02-22 14:34:30 +00:00
2014-03-18 21:14:32 +00:00
/* Use empty string for the prefix if it's NULL */
if ( prefix = = NULL )
2012-11-18 10:23:22 +00:00
{
2014-03-18 21:14:32 +00:00
prefix = L " " ;
}
2012-11-18 10:23:22 +00:00
2014-03-18 21:14:32 +00:00
/* Parse the string as an argument list */
parse_node_tree_t tree ;
2014-03-27 18:17:05 +00:00
if ( ! parse_tree_from_string ( arg_list_src , parse_flag_none , & tree , & errors , symbol_freestanding_argument_list ) )
2014-03-18 21:14:32 +00:00
{
/* Failed to parse. */
errored = true ;
}
2014-03-31 17:01:39 +00:00
2014-03-18 21:14:32 +00:00
if ( ! errored )
{
/* Get the root argument list */
assert ( ! tree . empty ( ) ) ;
const parse_node_t * arg_list = & tree . at ( 0 ) ;
2014-03-27 18:17:05 +00:00
assert ( arg_list - > type = = symbol_freestanding_argument_list ) ;
2012-11-18 10:23:22 +00:00
2014-03-18 21:14:32 +00:00
/* Extract arguments from it */
while ( arg_list ! = NULL & & ! errored )
{
const parse_node_t * arg_node = tree . next_node_in_node_list ( * arg_list , symbol_argument , & arg_list ) ;
if ( arg_node ! = NULL )
2012-11-19 00:30:30 +00:00
{
2014-03-18 21:14:32 +00:00
const wcstring arg_src = arg_node - > get_source ( arg_list_src ) ;
if ( parse_util_detect_errors_in_argument ( * arg_node , arg_src , & errors ) )
2012-11-19 08:31:03 +00:00
{
2014-03-18 21:14:32 +00:00
errored = true ;
2012-11-19 08:31:03 +00:00
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
}
2014-03-18 21:14:32 +00:00
if ( ! errors . empty ( ) & & out ! = NULL )
{
out - > assign ( errors . at ( 0 ) . describe_with_prefix ( arg_list_src , prefix , false /* not interactive */ , false /* don't skip caret */ ) ) ;
}
return errored ;
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-03-31 17:01:39 +00:00
2014-03-22 00:13:33 +00:00
const bool is_interactive = get_is_interactive ( ) ;
2014-03-31 17:01:39 +00:00
2014-03-22 00:13:33 +00:00
// Determine if we want to try to print a caret to point at the source error
// The err.source_start <= src.size() check is due to the nasty way that slices work,
// which is by rewriting the source (!)
size_t which_line = 0 ;
bool skip_caret = true ;
if ( err . source_start ! = SOURCE_LOCATION_UNKNOWN & & err . source_start < = src . size ( ) )
{
// Determine which line we're on
which_line = 1 + std : : count ( src . begin ( ) , src . begin ( ) + err . source_start , L ' \n ' ) ;
2014-03-31 17:01:39 +00:00
2014-03-22 00:13:33 +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
skip_caret = ( is_interactive & & which_line = = 1 & & err . source_start = = 0 ) ;
}
2014-03-31 17:01:39 +00:00
2014-03-22 00:13:33 +00:00
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-03-22 00:13:33 +00:00
if ( which_line > 0 )
{
prefix = format_string ( _ ( L " %ls (line %lu): " ) , user_presentable_path ( filename ) . c_str ( ) , which_line ) ;
}
else
{
prefix = format_string ( _ ( L " %ls: " ) , user_presentable_path ( filename ) . c_str ( ) ) ;
}
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-18 21:14:32 +00:00
const wcstring description = err . describe_with_prefix ( src , prefix , is_interactive , skip_caret ) ;
2014-03-15 20:07:19 +00:00
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
}