diff --git a/src/language.yacc b/src/language.yacc new file mode 100644 index 0000000000000000000000000000000000000000..c473d5bc0d47e3968e124212fbdf0c02e6147bc1 --- /dev/null +++ b/src/language.yacc @@ -0,0 +1,1203 @@ +/*\ +||| This file a part of Pike, and is copyright by Fredrik Hubinette +||| Pike is distributed as GPL (General Public License) +||| See the files COPYING and DISCLAIMER for more information. +\*/ +%pure_parser + +/* + * These values are used by the stack machine, and can not be directly + * called from Pike. + */ +%token F_ADD_256 F_ADD_512 F_ADD_768 F_ADD_1024 F_ADD_256X +%token F_PREFIX_256 F_PREFIX_512 F_PREFIX_768 F_PREFIX_1024 +%token F_PREFIX_CHARX256 F_PREFIX_WORDX256 F_PREFIX_24BITX256 +%token F_POP_VALUE F_POP_N_ELEMS F_MARK F_MARK2 +%token F_CALL_LFUN F_CALL_LFUN_AND_POP + +%token F_BRANCH F_BRANCH_WHEN_ZERO F_BRANCH_WHEN_NON_ZERO +%token F_BRANCH_WHEN_LT F_BRANCH_WHEN_GT +%token F_BRANCH_WHEN_LE F_BRANCH_WHEN_GE +%token F_BRANCH_WHEN_EQ F_BRANCH_WHEN_NE +%token F_INC_LOOP F_DEC_LOOP +%token F_INC_NEQ_LOOP F_DEC_NEQ_LOOP + +%token F_INDEX F_INDIRECT F_STRING_INDEX F_LOCAL_INDEX +%token F_POS_INT_INDEX F_NEG_INT_INDEX +%token F_LTOSVAL F_LTOSVAL2 +%token F_PUSH_ARRAY +%token F_RANGE F_COPY_VALUE + +/* + * Basic value pushing + */ +%token F_LFUN F_GLOBAL F_LOCAL +%token F_GLOBAL_LVALUE F_LOCAL_LVALUE +%token F_CLEAR_LOCAL +%token F_CONSTANT F_FLOAT F_STRING +%token F_NUMBER F_NEG_NUMBER F_CONST_1 F_CONST0 F_CONST1 F_BIGNUM +/* + * These are the predefined functions that can be accessed from Pike. + */ + +%token F_INC F_DEC F_POST_INC F_POST_DEC F_INC_AND_POP F_DEC_AND_POP +%token F_INC_LOCAL F_INC_LOCAL_AND_POP F_POST_INC_LOCAL +%token F_DEC_LOCAL F_DEC_LOCAL_AND_POP F_POST_DEC_LOCAL +%token F_RETURN F_DUMB_RETURN F_RETURN_0 F_THROW_ZERO + +%token F_ASSIGN F_ASSIGN_AND_POP +%token F_ASSIGN_LOCAL F_ASSIGN_LOCAL_AND_POP +%token F_ASSIGN_GLOBAL F_ASSIGN_GLOBAL_AND_POP +%token F_ADD F_SUBTRACT +%token F_MULTIPLY F_DIVIDE F_MOD + +%token F_LT F_GT F_EQ F_GE F_LE F_NE +%token F_NEGATE F_NOT F_COMPL +%token F_AND F_OR F_XOR +%token F_LSH F_RSH +%token F_LAND F_LOR + +%token F_SWITCH F_SSCANF F_CATCH +%token F_CAST +%token F_FOREACH + +%token F_SIZEOF F_SIZEOF_LOCAL + +/* + * These are token values that needn't have an associated code for the + * compiled file + */ + +%token F_MAX_OPCODE +%token F_ADD_EQ +%token F_AND_EQ +%token F_APPLY +%token F_ARG_LIST +%token F_ARRAY_ID +%token F_ARROW +%token F_BREAK +%token F_CASE +%token F_CLASS +%token F_COLON_COLON +%token F_COMMA +%token F_CONTINUE +%token F_DEFAULT +%token F_DIV_EQ +%token F_DO +%token F_DOT_DOT +%token F_DOT_DOT_DOT +%token F_PREDEF +%token F_EFUN_CALL +%token F_ELSE +%token F_FLOAT_ID +%token F_FOR +%token F_FUNCTION_ID +%token F_GAUGE +%token F_IDENTIFIER +%token F_IF +%token F_INHERIT +%token F_INLINE +%token F_INT_ID +%token F_LAMBDA +%token F_MULTISET_ID +%token F_MULTISET_END +%token F_MULTISET_START +%token F_LOCAL +%token F_LSH_EQ +%token F_LVALUE_LIST +%token F_MAPPING_ID +%token F_MIXED_ID +%token F_MOD_EQ +%token F_MULT_EQ +%token F_NO_MASK +%token F_OBJECT_ID +%token F_OR_EQ +%token F_PRIVATE +%token F_PROGRAM_ID +%token F_PROTECTED +%token F_PUBLIC +%token F_RSH_EQ +%token F_STATIC +%token F_STATUS +%token F_STRING_ID +%token F_SUBSCRIPT +%token F_SUB_EQ +%token F_TYPEOF +%token F_VAL_LVAL +%token F_VARARGS +%token F_VOID_ID +%token F_WHILE +%token F_XOR_EQ + +%token F_ALIGN +%token F_POINTER +%token F_LABEL + +%token F_MAX_INSTR + +%right '=' +%right '?' +%left F_LOR +%left F_LAND +%left '|' +%left '^' +%left '&' +%left F_EQ F_NE +%left '>' F_GE '<' F_LE /* nonassoc? */ +%left F_LSH F_RSH +%left '+' '-' +%left '*' '%' '/' +%right F_NOT '~' +%nonassoc F_INC F_DEC + + +%{ +/* This is the grammar definition of Pike. */ + +#include "global.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +#include "interpret.h" +#include "array.h" +#include "object.h" +#include "stralloc.h" +#include "las.h" +#include "interpret.h" +#include "lex.h" +#include "program.h" +#include "pike_types.h" +#include "constants.h" +#include "macros.h" +#include "error.h" +#include "docode.h" + +#define YYMAXDEPTH 600 + +static void push_locals(); +static void pop_locals(); +void free_all_local_names(); +void add_local_name(struct pike_string *,struct pike_string *); + +/* + * The names and types of arguments and auto variables. + */ +struct locals *local_variables; + +static int varargs; +static INT32 current_modifiers; + +void fix_comp_stack(int sp) +{ + if(comp_stackp>sp) + { + yyerror("Compiler stack fixed."); + comp_stackp=sp; + }else if(comp_stackp<sp){ + fatal("Compiler stack frame underflow."); + } +} + +%} +%union +{ + int number; + FLOAT_TYPE fnum; + unsigned int address; /* Address of an instruction */ + struct pike_string *string; + char *str; + unsigned short type; + struct node_s *n; + struct efun *efun; +} + +%type <fnum> F_FLOAT +%type <number> modifiers modifier optional_dot_dot_dot +%type <number> assign F_NUMBER F_LOCAL arguments arguments2 +%type <number> optional_stars modifier_list +%type <string> F_IDENTIFIER F_STRING string_constant low_string +%type <string> optional_identifier cast simple_type +%type <string> optional_rename_inherit + +%type <number> F_ARRAY_ID F_BREAK F_CASE F_CATCH F_CONTINUE F_DEFAULT F_DO +%type <number> F_PREDEF F_ELSE F_FLOAT_ID F_FOR F_FOREACH F_FUNCTION_ID F_GAUGE +%type <number> F_IF F_INHERIT F_INLINE F_INT_ID F_LAMBDA F_MULTISET_ID F_MAPPING_ID +%type <number> F_MIXED_ID F_NO_MASK F_OBJECT_ID F_PRIVATE F_PROGRAM_ID +%type <number> F_PROTECTED F_PUBLIC F_RETURN F_SSCANF F_STATIC +%type <number> F_STRING_ID F_SWITCH F_VARARGS F_VOID_ID F_WHILE + +/* The following symbos return type information */ + +%type <n> string expr01 expr00 comma_expr comma_expr_or_zero +%type <n> expr2 expr1 expr3 expr0 expr4 catch lvalue_list +%type <n> lambda for_expr block assoc_pair new_local_name +%type <n> expr_list2 m_expr_list m_expr_list2 statement gauge sscanf +%type <n> for do cond optional_else_part while statements +%type <n> local_name_list class catch_arg comma_expr_or_maxint +%type <n> unused2 foreach unused switch case return expr_list default +%type <n> continue break block_or_semi typeof +%% + +all: program; + +program: program def optional_semi_colon + | /* empty */ ; + +optional_semi_colon: /* empty */ + | ';' { yyerror("Extra ';'. Ignored."); }; + +string_constant: low_string + | string_constant '+' low_string + { + $$=add_shared_strings($1,$3); + free_string($1); + free_string($3); + } + ; + +optional_rename_inherit: ':' F_IDENTIFIER { $$=$2; } + | { $$=0; } + ; + +inheritance: modifiers F_INHERIT string_constant optional_rename_inherit ';' + { + simple_do_inherit($3,$1,$4); + }; + +block_or_semi: block { $$ = mknode(F_ARG_LIST,$1,mknode(F_RETURN,mkintnode(0),0)); } + | ';' { $$ = NULL;} + ; + + +type_or_error: simple_type + { + if(local_variables->current_type) free_string(local_variables->current_type); + local_variables->current_type=$1; + } + | /* empty */ + { + yyerror("Missing type."); + copy_shared_string(local_variables->current_type, + mixed_type_string); + } + + +def: modifiers type_or_error optional_stars F_IDENTIFIER '(' arguments ')' + { + int e; + /* construct the function type */ + push_finished_type(local_variables->current_type); + while($3--) push_type(T_ARRAY); + + if(local_variables->current_return_type) + free_string(local_variables->current_return_type); + local_variables->current_return_type=pop_type(); + + push_finished_type(local_variables->current_return_type); + + e=$6-1; + if(varargs) + { + push_finished_type(local_variables->variable[e].type); + e--; + varargs=0; + pop_type_stack(); + }else{ + push_type(T_VOID); + } + push_type(T_MANY); + for(; e>=0; e--) + { + push_finished_type(local_variables->variable[e].type); + if($1 & ID_VARARGS) + { + push_type(T_VOID); + push_type(T_OR); + } + } + push_type(T_FUNCTION); + + $<string>$=pop_type(); + define_function($4, + $<string>$, + $1, + IDENTIFIER_PIKE_FUNCTION, + 0); + } + block_or_semi + { + int e; + if($9) + { + union idptr tmp; + int args, vargs; + for(e=0; e<$6; e++) + { + if(!local_variables->variable[e].name || + !local_variables->variable[e].name->len) + { + my_yyerror("Missing name for argument %d",e); + } + } + + tmp.offset=PC; + args=count_arguments($<string>8); + if(args < 0) + { + args=~args; + vargs=IDENTIFIER_VARARGS; + }else{ + vargs=0; + } + ins_byte(local_variables->max_number_of_locals, A_PROGRAM); + ins_byte(args, A_PROGRAM); + dooptcode($4, $9, $6); + + define_function($4, + $<string>8, + $1, + IDENTIFIER_PIKE_FUNCTION | vargs, + &tmp); + } + if(local_variables->current_return_type) + { + free_string(local_variables->current_return_type); + local_variables->current_return_type=0; + } + free_all_local_names(); + free_string($4); + free_string($<string>8); + } + | modifiers type_or_error name_list ';' {} + | inheritance {} + | error + { + reset_type_stack(); + if(num_parse_error>5) YYACCEPT; + } ; + + +optional_dot_dot_dot: F_DOT_DOT_DOT { $$=1; } + | /* empty */ { $$=0; } + ; + +optional_identifier: F_IDENTIFIER + | /* empty */ { $$=make_shared_string(""); } + ; + + +new_arg_name: type optional_dot_dot_dot optional_identifier + { + if(varargs) yyerror("Can't define more arguments after ..."); + + if($2) + { + push_type(T_ARRAY); + varargs=1; + } + if(islocal($3) >= 0) + my_yyerror("Variable '%s' appear twice in argument list.", + $3->str); + + add_local_name($3, pop_type()); + }; + +arguments: /* empty */ optional_comma { $$=0; } + | arguments2 optional_comma { $$=$1; } + ; + +arguments2: new_arg_name { $$ = 1; } + | arguments2 ',' new_arg_name { $$ = $1 + 1; } + ; + +modifier: F_NO_MASK { $$ = ID_NOMASK; } + | F_STATIC { $$ = ID_STATIC; } + | F_PRIVATE { $$ = ID_PRIVATE; } + | F_PUBLIC { $$ = ID_PUBLIC; } + | F_VARARGS { $$ = ID_VARARGS; } + | F_PROTECTED { $$ = ID_PROTECTED; } + | F_INLINE { $$ = ID_INLINE | ID_NOMASK; } + ; + +modifiers: modifier_list { $$=current_modifiers=$1; } + +modifier_list: /* empty */ { $$ = 0; } + | modifier modifier_list + { + $$ = $1 | $2; + } + ; + +optional_stars: optional_stars '*' { $$=$1 + 1; } + | /* empty */ { $$=0; } + ; + +cast: '(' type ')' { $$=pop_type(); } ; + +type: type '*' { push_type(T_ARRAY); } + | type2 + ; + +simple_type: type2 { $$=pop_type(); } + +type2: type2 '|' type3 { push_type(T_OR); } + | type3 + ; + +type3: F_INT_ID { push_type(T_INT); } + | F_FLOAT_ID { push_type(T_FLOAT); } + | F_STRING_ID { push_type(T_STRING); } + | F_OBJECT_ID { push_type(T_OBJECT); } + | F_PROGRAM_ID { push_type(T_PROGRAM); } + | F_VOID_ID { push_type(T_VOID); } + | F_MIXED_ID { push_type(T_MIXED); } + | F_MAPPING_ID opt_mapping_type { push_type(T_MAPPING); } + | F_ARRAY_ID opt_array_type { push_type(T_ARRAY); } + | F_MULTISET_ID opt_array_type { push_type(T_MULTISET); } + | F_FUNCTION_ID opt_function_type { push_type(T_FUNCTION); } + ; + +opt_function_type: '(' + { + type_stack_mark(); + type_stack_mark(); + } + function_type_list + optional_dot_dot_dot + ':' + { + if ($4) + { + push_type(T_MANY); + type_stack_reverse(); + }else{ + type_stack_reverse(); + push_type(T_MANY); + push_type(T_VOID); + } + type_stack_mark(); + } + type ')' + { + type_stack_reverse(); + type_stack_reverse(); + } + | { + push_type(T_MIXED); + push_type(T_MIXED); + push_type(T_MANY); + }; + +function_type_list: /* Empty */ optional_comma + | function_type_list2 optional_comma + ; + +function_type_list2: type + | function_type_list2 ',' + { + type_stack_reverse(); + type_stack_mark(); + } + type + ; + +opt_array_type: '(' type ')' + | { push_type(T_MIXED); } + ; + +opt_mapping_type: '(' + { + type_stack_mark(); + type_stack_mark(); + } + type ':' + { + type_stack_reverse(); + type_stack_mark(); + } + type + { + type_stack_reverse(); + type_stack_reverse(); + } + ')' + | { + push_type(T_MIXED); + push_type(T_MIXED); + } + ; + + + +name_list: new_name + | name_list ',' new_name; + +new_name: optional_stars F_IDENTIFIER + { + struct pike_string *type; + push_finished_type(local_variables->current_type); + while($1--) push_type(T_ARRAY); + type=pop_type(); + define_variable($2, type, current_modifiers); + free_string(type); + free_string($2); + } + | optional_stars F_IDENTIFIER '=' + { + struct pike_string *type; + push_finished_type(local_variables->current_type); + while($1--) push_type(T_ARRAY); + type=pop_type(); + $<number>$=define_variable($2, type, current_modifiers); + free_string(type); + } + expr0 + { + init_node=mknode(F_ARG_LIST,init_node, + mkcastnode(void_type_string, mknode(F_ASSIGN,$5,mkidentifiernode($<number>4)))); + free_string($2); + } ; + + +new_local_name: optional_stars F_IDENTIFIER + { + push_finished_type(local_variables->current_type); + while($1--) push_type(T_ARRAY); + add_local_name($2, pop_type()); + $$=mkcastnode(void_type_string, + mknode(F_ASSIGN,mkintnode(0), + mklocalnode(islocal($2)))); + } + | optional_stars F_IDENTIFIER '=' expr0 + { + push_finished_type(local_variables->current_type); + while($1--) push_type(T_ARRAY); + add_local_name($2, pop_type()); + $$=mkcastnode(void_type_string, + mknode(F_ASSIGN,$4, + mklocalnode(islocal($2)))); + + }; + + +block:'{' + { + $<number>$=local_variables->current_number_of_locals; + } + statements + '}' + { + while(local_variables->current_number_of_locals > $<number>2) + { + int e; + e=--(local_variables->current_number_of_locals); + free_string(local_variables->variable[e].name); + free_string(local_variables->variable[e].type); + } + $$=$3; + } ; + +local_name_list: new_local_name + | local_name_list ',' new_local_name + { + $$=mknode(F_ARG_LIST,$1,$3); + } + ; + +statements: { $$=0; } + | statements statement + { + $$=mknode(F_ARG_LIST,$1,mkcastnode(void_type_string,$2)); + } + ; + +statement: unused2 ';' { $$=$1; } + | simple_type + { + if(local_variables->current_type) free_string(local_variables->current_type); + local_variables->current_type=$1; + } local_name_list ';' { $$=$3; } + | cond + | while + | do + | for + | switch + | case + | default + | return ';' + | block {} + | foreach + | break ';' + | continue ';' + | error ';' { $$=0; } + | ';' { $$=0; } + ; + + +break: F_BREAK { $$=mknode(F_BREAK,0,0); } ; +default: F_DEFAULT ':' { $$=mknode(F_DEFAULT,0,0); } ; +continue: F_CONTINUE { $$=mknode(F_CONTINUE,0,0); } ; + +lambda: F_LAMBDA + { + push_locals(); + $<number>$=comp_stackp; + + if(local_variables->current_return_type) + free_string(local_variables->current_return_type); + copy_shared_string(local_variables->current_return_type,any_type_string); + } + '(' arguments ')' block + { + struct pike_string *type; + char buf[40]; + int f,e,args,vargs; + union idptr func; + struct pike_string *name; + + setup_fake_program(); + fix_comp_stack($<number>2); + + push_type(T_MIXED); + + e=$4-1; + if(varargs) + { + push_finished_type(local_variables->variable[e].type); + e--; + varargs=0; + pop_type_stack(); + }else{ + push_type(T_VOID); + } + push_type(T_MANY); + for(; e>=0; e--) + push_finished_type(local_variables->variable[e].type); + + push_type(T_FUNCTION); + + type=pop_type(); + func.offset=PC; + + args=count_arguments(type); + if(args < 0) + { + args=~args; + vargs=IDENTIFIER_VARARGS; + }else{ + vargs=0; + } + ins_byte(local_variables->max_number_of_locals, A_PROGRAM); + ins_byte(args, A_PROGRAM); + + sprintf(buf,"__lambda_%ld", + (long)fake_program.num_identifier_references); + name=make_shared_string(buf); + dooptcode(name,mknode(F_ARG_LIST,$6,mknode(F_RETURN,mkintnode(0),0)),$4); + f=define_function(name, + type, + 0, + IDENTIFIER_PIKE_FUNCTION | vargs, + &func); + free_string(name); + free_string(type); + pop_locals(); + $$=mkidentifiernode(f); + } ; + +class: F_CLASS '{' + { + start_new_program(); + } + program + '}' + { + struct svalue s; + s.u.program=end_program(); + if(!s.u.program) + { + yyerror("Class definition failed."); + s.type=T_INT; + s.subtype=0; + } else { + s.type=T_PROGRAM; + s.subtype=0; + } + $$=mksvaluenode(&s); + free_svalue(&s); + } ; + +cond: F_IF '(' comma_expr ')' + statement + optional_else_part + { + $$=mknode('?',$3,mknode(':',$5,$6)); + $$->line_number=$1; + $$=mkcastnode(void_type_string,$$); + $$->line_number=$1; + } + ; + +optional_else_part: { $$=0; } + | F_ELSE statement { $$=$2; } + ; + +foreach: F_FOREACH '(' expr0 ',' expr4 ')' + statement + { + $$=mknode(F_FOREACH,mknode(F_VAL_LVAL,$3,$5),$7); + $$->line_number=$1; + } ; + +do: F_DO statement F_WHILE '(' comma_expr ')' ';' + { + $$=mknode(F_DO,$2,$5); + $$->line_number=$1; + } ; + + +for: F_FOR '(' unused ';' for_expr ';' unused ')' + statement + { + int i=current_line; + current_line=$1; + $$=mknode(F_ARG_LIST,mkcastnode(void_type_string,$3),mknode(F_FOR,$5,mknode(':',$9,$7))); + current_line=i; + } ; + + +while: F_WHILE '(' comma_expr ')' + statement + { + int i=current_line; + current_line=$1; + $$=mknode(F_FOR,$3,mknode(':',$5,NULL)); + current_line=i; + } ; + +for_expr: /* EMPTY */ { $$=mkintnode(1); } + | comma_expr; + +switch: F_SWITCH '(' comma_expr ')' + statement + { + $$=mknode(F_SWITCH,$3,$5); + $$->line_number=$1; + } ; + +case: F_CASE comma_expr ':' + { + $$=mknode(F_CASE,$2,0); + } + | F_CASE comma_expr F_DOT_DOT comma_expr ':' + { + $$=mknode(F_CASE,$2,$4); + } + ; + +return: F_RETURN + { + if(!match_types(local_variables->current_return_type, + void_type_string)) + { + yyerror("Must return a value for a non-void function."); + } + $$=mknode(F_RETURN,mkintnode(0),0); + } + | F_RETURN comma_expr + { + $$=mknode(F_RETURN,$2,0); + }; + +unused: { $$=0; } + | unused2 + ; + +unused2: comma_expr + { + $$=mkcastnode(void_type_string,$1); + } + ; + +comma_expr: expr0 + | unused2 ',' expr0 + { + $$ = mknode(F_ARG_LIST,mkcastnode(void_type_string,$1),$3); + } + ; + +expr00: expr0 + | '@' expr0 + { + $$=mknode(F_PUSH_ARRAY,$2,0); + }; + +expr0: expr01 + | expr4 '=' expr0 + { + $$=mknode(F_ASSIGN,$3,$1); + } + | expr4 assign expr0 + { + $$=mknode($2,$1,$3); + } + | error assign expr01 + { + $$=0; + }; + +expr01: expr1 { $$ = $1; } + | expr1 '?' expr01 ':' expr01 + { + $$=mknode('?',$1,mknode(':',$3,$5)); + }; + +assign: F_AND_EQ { $$=F_AND_EQ; } + | F_OR_EQ { $$=F_OR_EQ; } + | F_XOR_EQ { $$=F_XOR_EQ; } + | F_LSH_EQ { $$=F_LSH_EQ; } + | F_RSH_EQ { $$=F_RSH_EQ; } + | F_ADD_EQ { $$=F_ADD_EQ; } + | F_SUB_EQ { $$=F_SUB_EQ; } + | F_MULT_EQ{ $$=F_MULT_EQ; } + | F_MOD_EQ { $$=F_MOD_EQ; } + | F_DIV_EQ { $$=F_DIV_EQ; } + ; + +optional_comma: | ',' ; + +expr_list: { $$=0; } + | expr_list2 optional_comma { $$=$1; } + ; + + +expr_list2: expr00 + | expr_list2 ',' expr00 { $$=mknode(F_ARG_LIST,$1,$3); } + ; + +m_expr_list: { $$=0; } + | m_expr_list2 optional_comma { $$=$1; } + ; + +m_expr_list2: assoc_pair + | m_expr_list2 ',' assoc_pair { $$=mknode(F_ARG_LIST,$1,$3); } + ; + +assoc_pair: expr0 ':' expr1 + { + $$=mknode(F_ARG_LIST,$1,$3); + } ; + +expr1: expr2 + | expr1 F_LOR expr1 { $$=mknode(F_LOR,$1,$3); } + | expr1 F_LAND expr1 { $$=mknode(F_LAND,$1,$3); } + | expr1 '|' expr1 { $$=mkopernode("`|",$1,$3); } + | expr1 '^' expr1 { $$=mkopernode("`^",$1,$3); } + | expr1 '&' expr1 { $$=mkopernode("`&",$1,$3); } + | expr1 F_EQ expr1 { $$=mkopernode("`==",$1,$3); } + | expr1 F_NE expr1 { $$=mkopernode("`!=",$1,$3); } + | expr1 '>' expr1 { $$=mkopernode("`>",$1,$3); } + | expr1 F_GE expr1 { $$=mkopernode("`>=",$1,$3); } + | expr1 '<' expr1 { $$=mkopernode("`<",$1,$3); } + | expr1 F_LE expr1 { $$=mkopernode("`<=",$1,$3); } + | expr1 F_LSH expr1 { $$=mkopernode("`<<",$1,$3); } + | expr1 F_RSH expr1 { $$=mkopernode("`>>",$1,$3); } + | expr1 '+' expr1 { $$=mkopernode("`+",$1,$3); } + | expr1 '-' expr1 { $$=mkopernode("`-",$1,$3); } + | expr1 '*' expr1 { $$=mkopernode("`*",$1,$3); } + | expr1 '%' expr1 { $$=mkopernode("`%",$1,$3); } + | expr1 '/' expr1 { $$=mkopernode("`/",$1,$3); } + ; + +expr2: expr3 + | cast expr2 + { + $$=mkcastnode($1,$2); + free_string($1); + } + | F_INC expr4 { $$=mknode(F_INC,$2,0); } + | F_DEC expr4 { $$=mknode(F_DEC,$2,0); } + | F_NOT expr2 { $$=mkopernode("`!",$2,0); } + | '~' expr2 { $$=mkopernode("`~",$2,0); } + | '-' expr2 { $$=mkopernode("`-",$2,0); } + ; + +expr3: expr4 + | expr4 F_INC { $$=mknode(F_POST_INC,$1,0); } + | expr4 F_DEC { $$=mknode(F_POST_DEC,$1,0); } + ; + +expr4: string + | F_NUMBER { $$=mkintnode($1); } + | F_FLOAT { $$=mkfloatnode($1); } + | catch + | gauge + | typeof + | sscanf + | lambda + | class + | F_IDENTIFIER + { + int i; + struct efun *f; + if((i=islocal($1))>=0) + { + $$=mklocalnode(i); + }else if((i=isidentifier($1))>=0){ + $$=mkidentifiernode(i); + }else if((f=lookup_efun($1))){ + $$=mkconstantsvaluenode(&f->function); + }else{ + my_yyerror("'%s' undefined.",$1->str); + $$=0; + } + free_string($1); + } + | F_PREDEF F_COLON_COLON F_IDENTIFIER + { + struct efun *f; + f=lookup_efun($3); + if(!f) + { + my_yyerror("Unknown efun: %s.",$3->str); + $$=mkintnode(0); + }else{ + $$=mksvaluenode(&f->function); + } + free_string($3); + } + | expr4 '(' expr_list ')' { $$=mkapplynode($1,$3); } + | expr4 '[' expr0 ']' { $$=mknode(F_INDEX,$1,$3); } + | expr4 '[' comma_expr_or_zero F_DOT_DOT comma_expr_or_maxint ']' + { + $$=mknode(F_RANGE,$1,mknode(F_ARG_LIST,$3,$5)); + } + | '(' comma_expr ')' { $$=$2; } + | '(' '{' expr_list '}' ')' + { $$=mkefuncallnode("aggregate",$3); } + | '(' '[' m_expr_list ']' ')' + { $$=mkefuncallnode("aggregate_mapping",$3); }; + | F_MULTISET_START expr_list F_MULTISET_END + { $$=mkefuncallnode("aggregate_multiset",$2); } + | expr4 F_ARROW F_IDENTIFIER + { + $$=mknode(F_INDEX,$1,mkstrnode($3)); + free_string($3); + } + | F_IDENTIFIER F_COLON_COLON F_IDENTIFIER + { + int f; + struct reference *idp; + + setup_fake_program(); + f=reference_inherited_identifier($1,$3); + idp=fake_program.identifier_references+f; + if (f<0 || ID_FROM_PTR(&fake_program,idp)->func.offset == -1) + { + my_yyerror("Undefined identifier %s::%s", $1->str,$3->str); + $$=mkintnode(0); + } else { + $$=mkidentifiernode(f); + } + + free_string($1); + free_string($3); + } + | F_COLON_COLON F_IDENTIFIER + { + int e,i; + + $$=0; + setup_fake_program(); + for(e=1;e<(int)fake_program.num_inherits;e++) + { + if(fake_program.inherits[e].inherit_level!=1) continue; + i=low_reference_inherited_identifier(e,$2); + if(i==-1) continue; + if($$) + { + $$=mknode(F_ARG_LIST,$$,mkidentifiernode(i)); + }else{ + $$=mkidentifiernode(i); + } + } + if(!$$) + { + $$=mkintnode(0); + }else{ + if($$->token==F_ARG_LIST) $$=mkefuncallnode("aggregate",$$); + } + free_string($2); + } + ; + +comma_expr_or_zero: /* empty */ { $$=mkintnode(0); } + | comma_expr + ; + +comma_expr_or_maxint: /* empty */ { $$=mkintnode(0x7fffffff); } + | comma_expr + ; + +gauge: F_GAUGE catch_arg + { + $$=mkopernode("`-", + mkopernode("`-", + mknode(F_INDEX,mkefuncallnode("rusage",0), + mkintnode(GAUGE_RUSAGE_INDEX)), + mknode(F_ARG_LIST,$2, + mknode(F_INDEX,mkefuncallnode("rusage",0), + mkintnode(GAUGE_RUSAGE_INDEX)))),0); + } ; + +typeof: F_TYPEOF '(' expr0 ')' + { + node *tmp; + tmp=mknode(F_ARG_LIST,$3,0); + $$=mkstrnode(describe_type($3->type)); + free_node(tmp); + } ; + +catch_arg: '(' comma_expr ')' { $$=$2; } + | block + ; + +catch: F_CATCH catch_arg + { + $$=mknode(F_CATCH,$2,NULL); + } ; + +sscanf: F_SSCANF '(' expr0 ',' expr0 lvalue_list ')' + { + $$=mknode(F_SSCANF,mknode(F_ARG_LIST,$3,$5),$6); + } + + +lvalue_list: /* empty */ { $$ = 0; } + | ',' expr4 lvalue_list + { + $$ = mknode(F_LVALUE_LIST,$2,$3); + } ; + +low_string: F_STRING + | low_string F_STRING + { + $$=add_shared_strings($1,$2); + free_string($1); + free_string($2); + } + ; + +string: low_string + { + $$=mkstrnode($1); + free_string($1); + } ; + + +%% + +void yyerror(char *str) +{ + extern int num_parse_error; + + if (num_parse_error > 5) return; + num_parse_error++; + + if ( get_master() ) + { + sp->type = T_STRING; + copy_shared_string(sp->u.string, current_file); + sp++; + sp->type = T_INT; + sp->u.integer = current_line; + sp++; + sp->type = T_STRING; + sp->u.string = make_shared_string(str); + sp++; + SAFE_APPLY_MASTER("compile_error",3); + pop_stack(); + }else{ + (void)fprintf(stderr, "%s:%ld: %s\n", + current_file->str, + (long)current_line, + str); + fflush(stderr); + } +} + +/* argument must be a shared string (no need to free it) */ +void add_local_name(struct pike_string *str, + struct pike_string *type) +{ + if (local_variables->current_number_of_locals == MAX_LOCAL) + { + yyerror("Too many local variables"); + }else { + local_variables->variable[local_variables->current_number_of_locals].type = type; + local_variables->variable[local_variables->current_number_of_locals].name = str; + local_variables->current_number_of_locals++; + if(local_variables->current_number_of_locals > + local_variables->max_number_of_locals) + { + local_variables->max_number_of_locals= + local_variables->current_number_of_locals; + } + } +} + +/* argument must be a shared string */ +int islocal(struct pike_string *str) +{ + int e; + for(e=local_variables->current_number_of_locals-1;e>=0;e--) + if(local_variables->variable[e].name==str) + return e; + return -1; +} + +void free_all_local_names() +{ + int e; + + for (e=0; e<local_variables->current_number_of_locals; e++) + { + if(local_variables->variable[e].name) + { + free_string(local_variables->variable[e].name); + free_string(local_variables->variable[e].type); + } + local_variables->variable[e].name=0; + local_variables->variable[e].type=0; + } + local_variables->current_number_of_locals = 0; + local_variables->max_number_of_locals = 0; +} + +static void push_locals() +{ + struct locals *l; + l=ALLOC_STRUCT(locals); + l->current_type=0; + l->current_return_type=0; + l->next=local_variables; + l->current_number_of_locals=0; + l->max_number_of_locals=0; + local_variables=l; +} + +static void pop_locals() +{ + struct locals *l; + free_all_local_names(); + l=local_variables->next; + if(local_variables->current_type) + free_string(local_variables->current_type); + if(local_variables->current_return_type) + free_string(local_variables->current_return_type); + free((char *)local_variables); + + local_variables=l; + /* insert check if ( local->next == parent locals ) here */ +}