mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 12:53:13 +00:00
Major update to the ahead-of-time syntax checker
darcs-hash:20060521192524-ac50b-48713f826558e66ef21046d1bb779623cc2fd97a.gz
This commit is contained in:
parent
bbf2a3836f
commit
1c2cbb00bc
4 changed files with 362 additions and 63 deletions
24
expand.c
24
expand.c
|
@ -76,27 +76,6 @@ parameter expansion.
|
|||
*/
|
||||
#define COMPLETE_LAST_DESC _( L"Last background job")
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_DESC _( L"The '$' character begins a variable name. The character '%lc', which directly followed a '$', is not allowed as a part of a variable name, and variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'.")
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_NULL_DESC _( L"The '$' begins a variable name. It was given at the end of an argument. Variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'.")
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_BRACKET_DESC _( L"Did you mean {$VARIABLE}? The '$' character begins a variable name. A bracket, which directly followed a '$', is not allowed as a part of a variable name, and variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'." )
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_PARAN_DESC _( L"Did you mean (COMMAND)? In fish, the '$' character is only used for accessing variables. To learn more about command substitution in fish, type 'help expand-command-substitution'.")
|
||||
|
||||
|
||||
/**
|
||||
String in process expansion denoting ourself
|
||||
*/
|
||||
|
@ -804,8 +783,7 @@ static int expand_variables( wchar_t *in, array_list_t *out, int last_idx )
|
|||
{
|
||||
error( SYNTAX_ERROR,
|
||||
-1,
|
||||
COMPLETE_VAR_NULL_DESC,
|
||||
in[stop_pos] );
|
||||
COMPLETE_VAR_NULL_DESC );
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
21
expand.h
21
expand.h
|
@ -112,6 +112,27 @@ enum
|
|||
/** String containing the character for separating two array elements */
|
||||
#define ARRAY_SEP_STR L"\x1e"
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_DESC _( L"The '$' character begins a variable name. The character '%lc', which directly followed a '$', is not allowed as a part of a variable name, and variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'.")
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_NULL_DESC _( L"The '$' begins a variable name. It was given at the end of an argument. Variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'.")
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_BRACKET_DESC _( L"Did you mean {$VARIABLE}? The '$' character begins a variable name. A bracket, which directly followed a '$', is not allowed as a part of a variable name, and variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'." )
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_PARAN_DESC _( L"Did you mean (COMMAND)? In fish, the '$' character is only used for accessing variables. To learn more about command substitution in fish, type 'help expand-command-substitution'.")
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
|
332
parser.c
332
parser.c
|
@ -76,7 +76,7 @@ The fish parser. Contains functions for parsing code.
|
|||
/**
|
||||
Error message for short circuit command error.
|
||||
*/
|
||||
#define COND_ERR_MSG _( L"Short circuit command requires additional command")
|
||||
#define COND_ERR_MSG _( L"Pipe or short circuit command requires additional command")
|
||||
|
||||
/**
|
||||
Error message on reaching maximum recusrion depth
|
||||
|
@ -2542,10 +2542,237 @@ int eval( const wchar_t *cmd, io_data_t *io, int block_type )
|
|||
return code;
|
||||
}
|
||||
|
||||
int parser_test( wchar_t * buff,
|
||||
static int parser_test_argument( const wchar_t *arg, int babble )
|
||||
{
|
||||
wchar_t *unesc;
|
||||
wchar_t *pos;
|
||||
int err=0;
|
||||
|
||||
const wchar_t *paran_begin, *paran_end;
|
||||
wchar_t *arg_cpy = wcsdup( arg );
|
||||
int do_loop = 1;
|
||||
|
||||
while( do_loop )
|
||||
{
|
||||
switch( parse_util_locate_cmdsubst(arg_cpy,
|
||||
¶n_begin,
|
||||
¶n_end,
|
||||
0 ) )
|
||||
{
|
||||
case -1:
|
||||
err=1;
|
||||
if( babble )
|
||||
{
|
||||
error( SYNTAX_ERROR,
|
||||
-1,
|
||||
L"Mismatched parans" );
|
||||
print_errors();
|
||||
}
|
||||
free( arg_cpy );
|
||||
return 1;
|
||||
|
||||
case 0:
|
||||
do_loop = 0;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
{
|
||||
|
||||
wchar_t *subst = wcsndup( paran_begin+1, paran_end-paran_begin-1 );
|
||||
string_buffer_t tmp;
|
||||
sb_init( &tmp );
|
||||
|
||||
sb_append_substring( &tmp, arg_cpy, paran_begin - arg_cpy);
|
||||
sb_append_char( &tmp, INTERNAL_SEPARATOR);
|
||||
sb_append( &tmp, paran_end+1);
|
||||
|
||||
// debug( 1, L"%ls -> %ls %ls", arg_cpy, subst, tmp.buff );
|
||||
|
||||
err |= parser_test( subst, babble );
|
||||
|
||||
free( subst );
|
||||
free( arg_cpy );
|
||||
arg_cpy = (wchar_t *)tmp.buff;
|
||||
|
||||
/*
|
||||
Do _not_ call sb_destroy on this stringbuffer - it's
|
||||
buffer is used as the new 'arg_cpy'.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
unesc = unescape( arg_cpy, 1 );
|
||||
free( arg_cpy );
|
||||
|
||||
/*
|
||||
Check for invalid variable expansions
|
||||
*/
|
||||
for( pos = unesc; *pos; pos++ )
|
||||
{
|
||||
switch( *pos )
|
||||
{
|
||||
case VARIABLE_EXPAND:
|
||||
case VARIABLE_EXPAND_SINGLE:
|
||||
{
|
||||
switch( *(pos+1))
|
||||
{
|
||||
case BRACKET_BEGIN:
|
||||
{
|
||||
err=1;
|
||||
if( babble )
|
||||
{
|
||||
error( SYNTAX_ERROR,
|
||||
-1,
|
||||
COMPLETE_VAR_BRACKET_DESC );
|
||||
print_errors();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case INTERNAL_SEPARATOR:
|
||||
{
|
||||
err=1;
|
||||
if( babble )
|
||||
{
|
||||
error( SYNTAX_ERROR,
|
||||
-1,
|
||||
COMPLETE_VAR_PARAN_DESC );
|
||||
print_errors();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0:
|
||||
{
|
||||
err=1;
|
||||
if( babble )
|
||||
{
|
||||
error( SYNTAX_ERROR,
|
||||
-1,
|
||||
COMPLETE_VAR_NULL_DESC );
|
||||
print_errors();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if( !iswalnum(*(pos+1)) &&
|
||||
*(pos+1)!=L'_' &&
|
||||
*(pos+1)!=VARIABLE_EXPAND &&
|
||||
*(pos+1)!=VARIABLE_EXPAND_SINGLE )
|
||||
{
|
||||
err=1;
|
||||
if( babble )
|
||||
{
|
||||
error( SYNTAX_ERROR,
|
||||
-1,
|
||||
COMPLETE_VAR_DESC,
|
||||
*(pos+1) );
|
||||
print_errors();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
free( unesc );
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
int parser_test_args(const wchar_t * buff,
|
||||
int babble )
|
||||
{
|
||||
tokenizer tok;
|
||||
tokenizer *previous_tokenizer = current_tokenizer;
|
||||
int previous_pos = current_tokenizer_pos;
|
||||
int do_loop = 1;
|
||||
int err = 0;
|
||||
|
||||
current_tokenizer = &tok;
|
||||
|
||||
for( tok_init( &tok, buff, 0 );
|
||||
do_loop && tok_has_next( &tok );
|
||||
tok_next( &tok ) )
|
||||
{
|
||||
current_tokenizer_pos = tok_get_pos( &tok );
|
||||
switch( tok_last_type( &tok ) )
|
||||
{
|
||||
|
||||
case TOK_STRING:
|
||||
{
|
||||
err |= parser_test_argument( tok_last( &tok ), babble );
|
||||
break;
|
||||
}
|
||||
|
||||
case TOK_END:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case TOK_ERROR:
|
||||
{
|
||||
if( babble )
|
||||
{
|
||||
error( SYNTAX_ERROR,
|
||||
tok_get_pos( &tok ),
|
||||
TOK_ERR_MSG,
|
||||
tok_last(&tok) );
|
||||
print_errors();
|
||||
}
|
||||
err=1;
|
||||
do_loop=0;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if( babble )
|
||||
{
|
||||
error( SYNTAX_ERROR,
|
||||
tok_get_pos( &tok ),
|
||||
UNEXPECTED_TOKEN_ERR_MSG,
|
||||
tok_get_desc( tok_last_type(&tok)) );
|
||||
print_errors();
|
||||
}
|
||||
err=1;
|
||||
do_loop=0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tok_destroy( &tok );
|
||||
|
||||
current_tokenizer=previous_tokenizer;
|
||||
current_tokenizer_pos = previous_pos;
|
||||
|
||||
error_code=0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int parser_test( const wchar_t * buff,
|
||||
int babble )
|
||||
{
|
||||
tokenizer tok;
|
||||
/*
|
||||
Set to one if a command name has been given for the currently
|
||||
parsed process specification
|
||||
*/
|
||||
int had_cmd=0;
|
||||
int count = 0;
|
||||
int err=0;
|
||||
|
@ -2554,9 +2781,15 @@ int parser_test( wchar_t * buff,
|
|||
static int block_pos[BLOCK_MAX_COUNT];
|
||||
static int block_type[BLOCK_MAX_COUNT];
|
||||
int is_pipeline = 0;
|
||||
/*
|
||||
Set to one if the currently specified process can not be used inside a pipeline
|
||||
*/
|
||||
int forbid_pipeline = 0;
|
||||
/*
|
||||
Set to one if an additional process specification is needed
|
||||
*/
|
||||
int needs_cmd=0;
|
||||
int require_additional_commands=0;
|
||||
void *context = halloc( 0, 0 );
|
||||
|
||||
current_tokenizer = &tok;
|
||||
|
||||
|
@ -2576,7 +2809,23 @@ int parser_test( wchar_t * buff,
|
|||
int mark = tok_get_pos( &tok );
|
||||
had_cmd = 1;
|
||||
|
||||
if( require_additional_commands )
|
||||
if( !expand_one( context,
|
||||
wcsdup( tok_last( &tok ) ),
|
||||
EXPAND_SKIP_SUBSHELL | EXPAND_SKIP_VARIABLES ) )
|
||||
{
|
||||
err=1;
|
||||
if( babble )
|
||||
{
|
||||
error( SYNTAX_ERROR,
|
||||
tok_get_pos( &tok ),
|
||||
ILLEGAL_CMD_ERR_MSG,
|
||||
tok_last( &tok ) );
|
||||
|
||||
print_errors();
|
||||
}
|
||||
}
|
||||
|
||||
if( needs_cmd )
|
||||
{
|
||||
if( contains_str( tok_last(&tok),
|
||||
L"end",
|
||||
|
@ -2593,7 +2842,7 @@ int parser_test( wchar_t * buff,
|
|||
}
|
||||
}
|
||||
|
||||
require_additional_commands--;
|
||||
needs_cmd=0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2638,7 +2887,6 @@ int parser_test( wchar_t * buff,
|
|||
|
||||
// debug( 2, L"add block of type %d after cmd %ls\n", block_type[count], tok_last(&tok) );
|
||||
|
||||
|
||||
block_pos[count] = current_tokenizer_pos;
|
||||
tok_next( &tok );
|
||||
count++;
|
||||
|
@ -2658,9 +2906,6 @@ int parser_test( wchar_t * buff,
|
|||
had_cmd = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
The short circuit commands requires _two_ additional commands.
|
||||
*/
|
||||
if( contains_str( tok_last( &tok ),
|
||||
L"or",
|
||||
L"and",
|
||||
|
@ -2679,12 +2924,11 @@ int parser_test( wchar_t * buff,
|
|||
|
||||
}
|
||||
}
|
||||
require_additional_commands=1;
|
||||
}
|
||||
|
||||
/*
|
||||
There are a lot of situations where pipelines
|
||||
are forbidden, inclusing when using the exec
|
||||
are forbidden, including when using the exec
|
||||
builtin.
|
||||
*/
|
||||
if( parser_is_pipe_forbidden( tok_last( &tok ) ) )
|
||||
|
@ -2795,6 +3039,11 @@ int parser_test( wchar_t * buff,
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
err = parser_test_argument( tok_last( &tok ), babble );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2840,7 +3089,31 @@ int parser_test( wchar_t * buff,
|
|||
|
||||
case TOK_PIPE:
|
||||
{
|
||||
if( forbid_pipeline )
|
||||
if( !had_cmd )
|
||||
{
|
||||
err=1;
|
||||
if( babble )
|
||||
{
|
||||
if( tok_get_pos(&tok)>0 && buff[tok_get_pos(&tok)-1] == L'|' )
|
||||
{
|
||||
error( SYNTAX_ERROR,
|
||||
tok_get_pos( &tok ),
|
||||
CMD_OR_ERR_MSG,
|
||||
tok_get_desc( tok_last_type(&tok) ) );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
error( SYNTAX_ERROR,
|
||||
tok_get_pos( &tok ),
|
||||
CMD_ERR_MSG,
|
||||
tok_get_desc( tok_last_type(&tok)));
|
||||
}
|
||||
|
||||
print_errors();
|
||||
}
|
||||
}
|
||||
else if( forbid_pipeline )
|
||||
{
|
||||
err=1;
|
||||
if( babble )
|
||||
|
@ -2852,22 +3125,37 @@ int parser_test( wchar_t * buff,
|
|||
print_errors();
|
||||
}
|
||||
}
|
||||
needs_cmd=0;
|
||||
is_pipeline=1;
|
||||
else
|
||||
{
|
||||
needs_cmd=1;
|
||||
is_pipeline=1;
|
||||
had_cmd=0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case TOK_BACKGROUND:
|
||||
{
|
||||
if( needs_cmd && !had_cmd )
|
||||
if( !had_cmd )
|
||||
{
|
||||
err = 1;
|
||||
if( babble )
|
||||
{
|
||||
error( SYNTAX_ERROR,
|
||||
tok_get_pos( &tok ),
|
||||
CMD_ERR_MSG,
|
||||
tok_get_desc( tok_last_type(&tok)));
|
||||
if( tok_get_pos(&tok)>0 && buff[tok_get_pos(&tok)-1] == L'&' )
|
||||
{
|
||||
error( SYNTAX_ERROR,
|
||||
tok_get_pos( &tok ),
|
||||
CMD_AND_ERR_MSG,
|
||||
tok_get_desc( tok_last_type(&tok) ) );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
error( SYNTAX_ERROR,
|
||||
tok_get_pos( &tok ),
|
||||
CMD_ERR_MSG,
|
||||
tok_get_desc( tok_last_type(&tok)));
|
||||
}
|
||||
|
||||
print_errors();
|
||||
}
|
||||
|
@ -2898,7 +3186,7 @@ int parser_test( wchar_t * buff,
|
|||
}
|
||||
}
|
||||
|
||||
if( require_additional_commands )
|
||||
if( needs_cmd )
|
||||
{
|
||||
err=1;
|
||||
if( babble )
|
||||
|
@ -2928,6 +3216,8 @@ int parser_test( wchar_t * buff,
|
|||
|
||||
error_code=0;
|
||||
|
||||
halloc_free( context );
|
||||
|
||||
return err | ((count!=0)<<1);
|
||||
}
|
||||
|
||||
|
|
16
parser.h
16
parser.h
|
@ -315,10 +315,20 @@ const wchar_t *parser_get_block_desc( int block );
|
|||
|
||||
|
||||
/**
|
||||
Test if the specified string can be parsed, or if more bytes need to be read first.
|
||||
The result has the first bit set if the string contains errors, and the second bit is set if the string contains an unclosed block.
|
||||
Test if the specified string can be parsed, or if more bytes need
|
||||
to be read first. The result has the first bit set if the string
|
||||
contains errors, and the second bit is set if the string contains
|
||||
an unclosed block.
|
||||
*/
|
||||
int parser_test( wchar_t * buff, int babble );
|
||||
int parser_test( const wchar_t * buff, int babble );
|
||||
|
||||
/**
|
||||
Test if the specified string can be parsed as an argument list,
|
||||
e.g. sent to eval_args. The result has the first bit set if the
|
||||
string contains errors, and the second bit is set if the string
|
||||
contains an unclosed block.
|
||||
*/
|
||||
int parser_test_args( const wchar_t * buff, int babble );
|
||||
|
||||
/**
|
||||
Returns the full path of the specified directory. If the \c in is a
|
||||
|
|
Loading…
Reference in a new issue