/* G-Cows - scripting language for creation of web sites Copyright (C) 2002, 2003, 2004, 2005, 2006 Andrea Sozzi This file is part of G-Cows. G-Cows is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. G-Cows is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* There are 3 shift/reduce conflicts due to array notation. They are solved by shift. */ %{ #include "common.H" #include "error.H" #include "language.H" extern char *yytext; extern int cowswitch (string new_buffer); extern int check_file (string new_buffer); extern void unput_brace (); extern int yylex (void); extern Tree *tree; void yyerror (const char *str); int yywrap () { return 1; } static bool inside_var_type = false; static string type_into = ""; %} %union { char *str; Gen_type *gen; Node *node; }; %expect 3 %token G_CONST TYPE_STRING %token VAR VAR_TYPE %token UNDEF PRINT ECHO AT_ECHO %token VERBATIM INCLUDE %token CMD EVALCMD EXEC EVALEXEC %token TOBOOL TOINT TOSTRING TOARRAY %token ISINT ISSTRING ISARRAY %token IF ELIF IFDEF ELIFDEF %token IFNDEF ELIFNDEF ELSE %token DATE FDATE FSIZE ADJ_LINK %token INPUT_FILE ABS_INPUT_FILE OUTPUT_FILE ABS_OUTPUT_FILE %token SUBSTR FIND RFIND FINDFIRSTOF %token FINDLASTOF FINDFIRSTNOTOF FINDLASTNOTOF CHAR %token STARTSWITH ENDSWITH %token INSERT REPLACE REPLACE_ALL %token TRIM LTRIM RTRIM %token TO_LOWER TO_UPPER %token LENGTH RAISEERR RAISEWARN TERM %token TOKENIZE JOIN DELETE PUSH %token FOR WHILE FOREACH IN %token CPLUS CMINUS CTIMES %token PP MM RANDOM %token FILEEXISTS FILL %token END_SCRIPT END_FILE END_TYPE %type expr stmt stmt_list var list item %type if elif if_seq %type ifdef elifdef ifdef_seq %type ifndef elifndef ifndef_seq %nonassoc IFX %nonassoc ELSE ELIF ELIFDEF ELIFNDEF %left '$' %left ',' %right '=' CPLUS CMINUS CTIMES CDIV CMOD CPOW %left OR %left AND %left EQ WEQ NEQ WNEQ %left '<' '>' GE LE %left '+' '-' %left '*' '/' '%' %left POW %right UMINUS '!' PP MM %right '[' %% input: /* empty */ | input stmt { tree->start (); } ; stmt: ';' { $$ = tree->push_node (new N_empty () ); } | END_SCRIPT { $$ = tree->push_node ( new N_end () ); } | END_FILE { $$ = tree->push_node ( new N_eof () ); } | END_TYPE { $$ = tree->push_node (new N_empty () ); inside_var_type=false; type_into=""; } | expr ';' { $$ = $1; } | UNDEF '(' var ')' ';' { $$ = tree->push_node ( new N_undef ($3) ); } | TYPE_STRING { if (inside_var_type) { N_var *var1=new N_var (type_into,'s'); N_var *var2=new N_var (type_into,'s'); tree->push_node (var1); tree->push_node (var2); $$=tree->push_node (new N_assign (var1, new N_plus (var2, new N_const ($1)))); } else { $$ = tree->push_node (new N_echo (new N_const ($1)) ); } } | VAR_TYPE { inside_var_type=true; type_into=string($1); N_var *var=new N_var (type_into,'s'); N_const *con=new N_const (new Gen_type ("")); tree->push_node (var); tree->push_node (con); $$ = tree->push_node (new N_assign (var, con, true)); } | AT_ECHO expr ';' { N_var *var1 = new N_var (type_into,'s'); N_var *var2 = new N_var (type_into,'s'); tree->push_node (var1); tree->push_node (var2); if (inside_var_type) { N_var *var1 = new N_var (type_into,'s'); N_var *var2 = new N_var (type_into,'s'); tree->push_node (var1); tree->push_node (var2); $$ = tree->push_node (new N_assign (var1, new N_plus (var2, $2))); } else { $$ = tree->push_node ( new N_echo ($2) ); } } | VERBATIM '('expr ')' ';' { $$ = tree->push_node ( new N_verbatim ($3) ); } | INCLUDE '(' expr ')' ';' { /* EXPERIMENTAL FEATURE: while in deps-mode, if --fast set, doesn't open file */ if (tree->is_deps ()) ((Deps_tree *)tree)->unset_first_file (); $$ = tree->push_node ( new N_include ($3) ); string file = ($3->exec ()).get_string (); if (tree->is_exec () || tree->is_deps () ) { Msg::inst()->save_buffer (); if ( ! cowswitch (file.c_str ())) { if (set_f_w_dir (file)) { Msg::inst()->sys_fatal ("can't change directory to " + get_dir_from_file_name (file), get_dir_from_file_name (file)); } if (tree->is_deps ()) { ((Deps_tree *)tree)->refresh (file); } } } else { // Even if we don't open the file, we check for its existence if (check_file (file.c_str ()) != EXIT_SUCCESS) { Msg::inst()->sys_fatal ( file + ": No such file or directory"); } unput_brace (); } } | INCLUDE '(' expr ',' list ')' ';' { if (tree->is_deps ()) ((Deps_tree *)tree)->unset_first_file (); $$ = tree->push_node ( new N_arg_include ($3, $5) ); string file = ($3->exec ()).get_string (); if (tree->is_exec () || tree->is_deps () ) { Msg::inst()->save_buffer (); if ( ! cowswitch (file.c_str ())) { if (set_f_w_dir (file)) { Msg::inst()->sys_fatal ("can't change directory to " + get_dir_from_file_name (file), get_dir_from_file_name (file)); } if (tree->is_deps ()) { ((Deps_tree *)tree)->refresh (file); } } } else { // Even if we don't open the file, we check for its existence if (check_file (file.c_str ()) != EXIT_SUCCESS) { Msg::inst()->sys_fatal (get_dir_from_file_name (file) + ": No such file or directory"); } unput_brace (); } } | if_seq %prec IFX { $$ = $1; } | if_seq ELSE stmt { $$ = tree->push_node ( new N_if_else ($1, $3) ); } | ifdef_seq %prec IFX { $$ = $1; } | ifdef_seq ELSE stmt { $$ = tree->push_node ( new N_if_else ($1, $3) ); } | ifndef_seq %prec IFX { $$ = $1; } | ifndef_seq ELSE stmt { $$ = tree->push_node ( new N_if_else ($1, $3) ); } | '{' stmt_list '}' { $$ = $2; } // | '{' '}' {} // $$ = tree->push_node (new N_empty () ); | FOR '(' expr ';' expr ';' expr ')' stmt { $$ = tree->push_node (new N_for ($3, $5, $7, $9) ); } | WHILE '(' expr ')' stmt { $$ = tree->push_node (new N_while ($3, $5) ); } | FOREACH '(' var IN expr ')' stmt { $$ = tree->push_node (new N_foreach ($3, $5, $7) ); } ; stmt_list: stmt { $$ = $1; } | stmt_list stmt { $$ = tree->push_node ( new N_stmt ($1, $2) ); } ; expr: G_CONST { $$ = tree->push_node ( new N_const ($1) ); } | '(' expr ')' { $$ = $2; } | var {$$ = $1; } | var '=' expr { $$ = tree->push_node ( new N_assign ($1, $3) ); } | var '=' '{' '}' { $$ = tree->push_node ( new N_empty_array ($1) ); } | '-' expr %prec UMINUS { $$ = tree->push_node ( new N_uminus ($2)); } | expr '>' expr { $$ = tree->push_node (new N_gt ($1, $3)); } | expr '<' expr { $$ = tree->push_node (new N_lt ($1, $3)); } | expr GE expr { $$ = tree->push_node (new N_ge ($1, $3)); } | expr LE expr { $$ = tree->push_node (new N_le ($1, $3)); } | expr '+' expr { $$ = tree->push_node (new N_plus ($1, $3)); } | expr '-' expr { $$ = tree->push_node (new N_minus ($1, $3)); } | expr '*' expr { $$ = tree->push_node (new N_times ($1, $3)); } | expr '/' expr { $$ = tree->push_node (new N_div ($1, $3)); } | expr '%' expr { $$ = tree->push_node (new N_mod ($1, $3)); } | expr POW expr { $$ = tree->push_node (new N_pow ($1, $3)); } | var PP { $$ = tree->push_node (new N_epp ($1, 1, true)); } | var MM { $$ = tree->push_node (new N_epp ($1, -1, true)); } | PP expr { $$ = tree->push_node (new N_epp ($2, 1, false)); } | MM expr { $$ = tree->push_node (new N_epp ($2, -1, false)); } | expr CPLUS expr { $$=tree->push_node (new N_assign_rec ($1, new N_plus ($1, $3)) ); } | expr CMINUS expr { $$=tree->push_node (new N_assign_rec ($1, new N_minus ($1, $3)) ); } | expr CTIMES expr { $$=tree->push_node (new N_assign_rec ($1, new N_times ($1, $3)) ); } | expr CDIV expr { $$=tree->push_node (new N_assign_rec ($1, new N_div ($1, $3)) ); } | expr CMOD expr { $$=tree->push_node (new N_assign_rec ($1, new N_mod ($1, $3)) ); } | expr CPOW expr { $$=tree->push_node (new N_assign_rec ($1, new N_pow ($1, $3)) ); } | '!' expr { $$ = tree->push_node ( new N_not ($2)); } | expr EQ expr { $$ = tree->push_node (new N_eq ($1, $3)); } | expr WEQ expr { $$ = tree->push_node (new N_w_eq ($1, $3)); } | expr NEQ expr { $$ = tree->push_node (new N_neq ($1, $3)); } | expr WNEQ expr { $$ = tree->push_node (new N_w_neq ($1, $3)); } | expr AND expr { $$ = tree->push_node (new N_and ($1, $3)); } | expr OR expr { $$ = tree->push_node (new N_or ($1, $3)); } | '{' list '}' { $$ = tree->push_node (new N_fix_list ($2)); } | ECHO '(' expr ')' { $$ = tree->push_node (new N_echo ($3)); } | PRINT '(' expr ')' { $$ = tree->push_node (new N_print ($3)); } | DATE '(' expr ')' { $$ = tree->push_node (new N_date ($3)); } | FDATE '(' expr ',' expr ')' { $$ = tree->push_node (new N_fdate ($3, $5)); } | FSIZE '(' expr ',' expr ')' { $$ = tree->push_node (new N_fsize ($3, $5)); } | ADJ_LINK '(' ')' { $$ = tree->push_node (new N_adj_link () );} | INPUT_FILE '(' ')' { $$ = tree->push_node (new N_input () ); } | INPUT_FILE '(' G_CONST ')' { $$ = tree->push_node (new N_input ($3) ); } | ABS_INPUT_FILE '(' ')' { $$ = tree->push_node (new N_abs_input () ); } | OUTPUT_FILE '(' ')' { $$ = tree->push_node (new N_output () ); } | OUTPUT_FILE '(' G_CONST ')' { $$ = tree->push_node (new N_output ($3) ); } | ABS_OUTPUT_FILE '(' ')' { $$ = tree->push_node (new N_abs_output () ); } | SUBSTR '(' expr ',' expr ')' { $$ = tree->push_node (new N_substr ($3, $5));} | SUBSTR '(' expr ',' expr ',' expr ')' { $$ = tree->push_node (new N_substr ($3, $5, $7)); } | FIND '(' expr ',' expr ')' { $$ = tree->push_node (new N_find ($3, $5,NULL));} | FIND '(' expr ',' expr ',' expr ')' { $$ = tree->push_node (new N_find ($3, $5, $7)); } | FINDFIRSTOF '(' expr ',' expr ')' { $$ = tree->push_node (new N_find_match ($3, $5, false));} | FINDLASTOF '(' expr ',' expr ',' expr ')' { $$ = tree->push_node (new N_find_match ($3, $5, true)); } | FINDFIRSTNOTOF '(' expr ',' expr ')' { $$ = tree->push_node (new N_find_match ($3, $5, false, true));} | FINDLASTNOTOF '(' expr ',' expr ',' expr ')' { $$ = tree->push_node (new N_find_match ($3, $5, true, true)); } | RFIND '(' expr ',' expr ')' { $$ = tree->push_node (new N_find ($3, $5, NULL, true)); } | RFIND '(' expr ',' expr ',' expr ')' { $$ = tree->push_node (new N_find ($3, $5, $7, true)); } | CHAR '(' expr ',' expr ')' { $$ = tree->push_node (new N_char ($3, $5)); } | STARTSWITH '(' expr ',' expr ')' { $$ = tree->push_node (new N_startswith ($3, $5)); } | ENDSWITH '(' expr ',' expr ')' { $$ = tree->push_node (new N_endswith ($3, $5)); } | INSERT '(' expr ',' expr ',' expr ')' { $$ = tree->push_node (new N_insert ($3, $5, $7) ); } | REPLACE '(' expr ',' expr ',' expr ')' { $$ = tree->push_node (new N_replace ($3, $5, $7, false) ); } | REPLACE_ALL '(' expr ',' expr ',' expr ')' { $$ = tree->push_node (new N_replace ($3, $5, $7, true) ); } | TRIM '(' expr ')' { $$ = tree->push_node (new N_trim ($3)); } | LTRIM '(' expr ')' { $$ = tree->push_node (new N_ltrim ($3)); } | RTRIM '(' expr ')' { $$ = tree->push_node (new N_rtrim ($3)); } | TO_LOWER '(' expr ')' { $$ = tree->push_node (new N_ch_case ($3, true)); } | TO_UPPER '(' expr ')' { $$ = tree->push_node (new N_ch_case ($3)); } | LENGTH '(' expr ')' { $$ = tree->push_node (new N_length ($3)); } | RAISEERR '(' expr ')' { $$ = tree->push_node (new N_raiseerr ($3)); } | RAISEERR '(' expr ',' expr ')' { $$ = tree->push_node (new N_raiseerr ($3, $5)); } | RAISEWARN '(' expr ')' { $$ = tree->push_node (new N_raisewarn ($3)); } | RAISEWARN '(' expr ',' expr ')' { $$ = tree->push_node (new N_raisewarn ($3, $5)); } | TERM '(' expr ')' { $$ = tree->push_node (new N_term ($3)); } | CMD '(' expr ')' { $$ = tree->push_node (new N_cmd ($3) ); } | EVALCMD '(' expr ')' { $$ = tree->push_node (new N_cmd ($3, true)); } | EXEC '(' expr ')' { $$ = tree->push_node (new N_exec ($3) ); } | EVALEXEC '(' expr ')' { $$ = tree->push_node (new N_exec ($3, true)); } | EXEC '(' expr ',' list ')' { $$ = tree->push_node (new N_exec ($3, $5)); } | EVALEXEC '(' expr ',' list ')' { $$ = tree->push_node (new N_exec ($3, $5, true)); } | TOBOOL '(' expr ')' { $$ = tree->push_node ( new N_tobool ($3) ); } | TOINT '(' expr ')' { $$ = tree->push_node ( new N_toint ($3) ); } | TOSTRING '(' expr ')' { $$ = tree->push_node ( new N_tostring ($3) ); } | TOARRAY '(' expr ')' { $$ = tree->push_node ( new N_toarray ($3) ); } | ISINT '(' expr ')' { $$ = tree->push_node ( new N_isint ($3) ); } | ISSTRING '(' expr ')' { $$ = tree->push_node ( new N_isstring ($3) ); } | ISARRAY '(' expr ')' { $$ = tree->push_node ( new N_isarray ($3) ); } | TOKENIZE '(' expr ',' expr ')' { $$ = tree->push_node (new N_tok ($3, $5));} | JOIN '(' expr ',' expr ')' { $$ = tree->push_node (new N_join ($3, $5));} | DELETE '(' expr ',' expr ')' { $$ = tree->push_node (new N_del ($3, $5, NULL)); } | DELETE '(' expr ',' expr ',' expr ')' { $$ = tree->push_node (new N_del ($3, $5, $7)); } | PUSH '(' expr ',' expr ')' { $$ = tree->push_node (new N_push ($3, $5, NULL)); } | PUSH '(' expr ',' expr ',' expr ')' { $$ = tree->push_node (new N_push ($3, $5, $7)); } | RANDOM '(' expr ',' expr ')' { $$ = tree->push_node (new N_random ($3, $5)); } | FILEEXISTS '(' expr ')' { $$ = tree->push_node (new N_fileexists ($3)); } | FILL '(' expr ',' list ')' { $$ = tree->push_node (new N_fill ($3, $5)); } ; if_seq: if { $$ = $1; } | if_seq elif { $$ = tree->push_node (new N_cond_seq ($1, $2)); } ; if: IF '(' expr ')' stmt { $$ = tree->push_node (new N_if ($3, $5)); } ; elif: ELIF '(' expr ')' stmt { $$ = tree->push_node (new N_if ($3, $5)); } ; ifdef_seq: ifdef { $$ = $1; } | ifdef_seq elifdef { $$ = tree->push_node (new N_cond_seq ($1, $2)); } ; ifdef: IFDEF '(' var ')' stmt { $$ = tree->push_node (new N_ifdef ($3, $5)); } ; elifdef: ELIFDEF '(' var ')' stmt { $$ = tree->push_node (new N_ifdef ($3, $5)); } ; ifndef_seq: ifndef { $$ = $1; } | ifndef_seq elifndef { $$ = tree->push_node (new N_cond_seq ($1, $2)); } ; ifndef: IFNDEF '(' var ')' stmt { $$ = tree->push_node (new N_ifndef ($3, $5)); } ; elifndef: ELIFNDEF '(' var ')' stmt { $$ = tree->push_node (new N_ifndef ($3, $5)); } ; var: VAR { $$ = tree->push_node (new N_var ($1, 's')); } | '$' '(' expr ')' { $$ = tree->push_node (new N_ind_expr ($3, 's')); } | VAR '[' ']' { $$ = tree->push_node (new N_var ($1, 'a')); } | '$' '(' expr ')' '[' ']' { $$=tree->push_node (new N_ind_expr ($3, 'a')); } | item { $$ = $1; } ; item: //var '[' expr ']' { $$ = tree->push_node (new N_var ($1, $3)); } VAR '[' expr ']' { $$ = tree->push_node (new N_var ($1, $3)); } | expr '[' expr ']' { $$ = tree->push_node (new N_var ($1, $3)); } | '$' '(' expr ')' '[' expr ']' { $$=tree->push_node (new N_ind_expr ($3, $6)); } | item '[' expr ']' { $$ = tree->push_node (new N_var ($1, $3)); } ; list: expr { $$ = $1; } | list ',' expr { $$ = tree->push_node (new N_list ($1, $3) ); } ; %% void yyerror (const char *str) { string err_token = string (yytext); if (err_token == "") { Msg::inst()->error (string (str) ); } else { unsigned int eol_pos = err_token.find ("\n"); if (eol_pos != string::npos) { err_token = err_token.substr (0, eol_pos); int n_eol=0; int start = -1; string token_string (yytext); while ((start=token_string.find ("\n", start + 1) )!=(int)string::npos) n_eol++; Msg::inst()->error (n_eol, string (str) + " before `" + err_token + "'"); } else { Msg::inst()->error (string (str) + " before `" + err_token + "'"); } } }