# HG changeset patch # User John W. Eaton # Date 1208372996 14400 # Node ID 5b4d278ec828627fc0356ad2d39701d981ab16b0 # Parent 83ea845cda36586912c35a704fc2727ca988d0dd parse scripts completely before executing diff --git a/src/ChangeLog b/src/ChangeLog --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,83 @@ +2008-04-16 John W. Eaton + + * pt-walk.h (tree_walker::visit_function_def): New function. + * pt-pr-code.cc (tree_print_code::visit_function_def): New function. + * pt-pr-code.h: Provide decl. + * pt-bp.cc (tree_breakpoint::visit_function_def): New function. + * pt-bp.h: Provide decl. + * pt-check.cc (tree_checker::visit_function_def): New function. + * pt-check.h: Provide decl. + + * parse.y (finish_function): Return ptr to tree_function_def object + if curr_fcn_ptr is not defined. Avoid dereferencing invalid pointers. + (frob_function): Set curr_fcn_ptr only when reading a function file. + (function): Call finish function in both cases to generate value + of production. + + * pt-cmd.h, pt-cmd.cc (tree_function_def): New class. + + * parse.y (parse_and_execute): Delete. + * parse.h: Delete decl. + + * pt-pb.cc (tree_breakpoint::visit_octave_user_script): New function. + * pt-pb.h: Provide decl. + * pt-check.cc (tree_checker::visit_octave_user_script): New function. + * pt-check.h: Provide decl. + * pt-pr-code.cc (tree_print_code::visit_octave_user_script): + New function. + * pt-pr-code.h: Provide decl. + * pt-walk.h (visit_octave_user_script): New pure virtual function. + + * parse.y (make_script, process_leading_comments, + looking_at_function_keyword): New static functions. + (SCRIPT): New token. + (script): New non-terminal. + (command): Handle script here. + (parse_fcn_file): Eliminate EXEC_SCRIPT argument. Change all + callers. Parse scripts and return an octave_user_script rather + than executing them here. + (load_fcn_from_file): Handle script and function files the same. + (source_file): Call d_multi_index_op on object returned by + parse_fcn_file. + (is_function_file): Delete. + (gobble_leading_white_space): Don't recurse. Simplify. Eliminate + SKIP_CODE, IN_PARTS, and SAVE_COPYRIGHT arguments. Change all + callers. + (octave_function_ptr): Delete unused typedef. + (get_help_from_file): If help text isn't found by + gobble_leading_whitespace, parse file to find it. + + * lex.l (SCRIPT_FILE_BEGIN): New exclusive start state. + (prep_lexer_for_script): New function. + (grab_help_text): New arg, EOF. + (process_comment): New function. + ({CCHAR}): Use it. + * lex.h (prep_lexer_for_script): Provide decl. + + * lex.h, lex.cc, parse.y (lexical_feedback::beginning_of_function): + Delete data member and all uses. + + * ov-usr-fcn.cc (octave_user_script::~octave_user_script): + Move destructor here from ov-usr-fcn.h. Delete cmd_list. + (octave_user_script::subsref, octave_user_script::accept, + octave_user_script::traceback_error): New functions. + (octave_user_script::do_multi_index_op): + Execute cmd_list instead of calling source_file. + * ov-usr-fcn.h (octave_usr_script::cmd_list, + octave_usr_script::t_parsed, octave_usr_script::t_checked, + octave_usr_script::call_depth): New data members. + (octave_usr_script::octave_usr_script): Initialize all data members. + (octave_usr_script::octave_usr_script (const std::string&, const + std::string&, tree_statement_list *, const std::string&)): + New constructor. + (octave_usr_script::function_value, + octave_usr_script::user_script_value, mark_fcn_file_up_to_date, + octave_usr_script::stash_fcn_file_time, + octave_usr_script::time_parsed, octave_usr_script::time_checked, + octave_usr_script::subsref, octave_usr_script::body): New functions. + (octave_user_script::subsref, octave_user_script::accept, + octave_user_script::traceback_error): Provide decls. + 2008-04-14 Jaroslav Hajek * oct-stream.cc (octave_scan_1): Ensure digit following X is hex diff --git a/src/lex.h b/src/lex.h --- a/src/lex.h +++ b/src/lex.h @@ -50,6 +50,8 @@ // Is the given string a keyword? extern bool is_keyword (const std::string& s); +extern void prep_lexer_for_script (void); + // For communication between the lexer and parser. class @@ -72,10 +74,6 @@ // TRUE means we're in the middle of defining a loop. int looping; - // TRUE means we think we are looking at the beginning of a - // function definition. - bool beginning_of_function; - // TRUE means that we should convert spaces to a comma inside a // matrix definition. bool convert_spaces_to_comma; diff --git a/src/lex.l b/src/lex.l --- a/src/lex.l +++ b/src/lex.l @@ -26,6 +26,8 @@ %s COMMAND_START %s MATRIX_START +%x SCRIPT_FILE_BEGIN + %x NESTED_FUNCTION_END %x NESTED_FUNCTION_BEGIN @@ -242,7 +244,8 @@ static int is_keyword_token (const std::string& s); static void prep_for_function (void); static void prep_for_nested_function (void); -static std::string grab_help_text (void); +static std::string grab_help_text (bool& eof); +static int process_comment (char cchar, bool& eof); static bool match_any (char c, const char *s); static bool next_token_is_sep_op (void); static bool next_token_is_bin_op (bool spc_prev); @@ -285,6 +288,12 @@ NUMBER (({D}+\.?{D}*{EXPON}?)|(\.{D}+{EXPON}?)|(0[xX][0-9a-fA-F]+)) %% +. { + BEGIN (INITIAL); + yyunput (yytext[0], yytext); + COUNT_TOK_AND_RETURN (SCRIPT); + } + . { BEGIN (NESTED_FUNCTION_BEGIN); yyunput (yytext[0], yytext); @@ -621,60 +630,12 @@ %} {CCHAR} { - std::string help_txt; - - if (! help_buf.empty ()) - help_txt = help_buf.top (); - - if (help_txt.empty () - && lexer_flags.beginning_of_function - && nesting_level.none ()) - { - lexer_flags.beginning_of_function = false; - - std::string txt = grab_help_text (); - - if (! help_buf.empty ()) - help_buf.pop (); - - help_buf.push (txt); - - octave_comment_buffer::append (txt); - } - else - { - std::string buf; - - bool begin_comment = true; - - int c; - while ((c = yyinput ()) != EOF && c != '\n') - { - if (begin_comment && (c == '#' || c == '%')) - ; /* Skip leading comment characters. */ - else - buf += static_cast (c); - } - - octave_comment_buffer::append (buf); - } - - current_input_column = 1; - lexer_flags.quote_is_transpose = false; - lexer_flags.convert_spaces_to_comma = true; - - maybe_gripe_matlab_incompatible_comment (yytext[0]); - - if (YY_START == COMMAND_START) - BEGIN (INITIAL); - - if (nesting_level.none ()) - { - lexer_flags.doing_rawcommand = false; - COUNT_TOK_AND_RETURN ('\n'); - } - else if (nesting_level.is_bracket_or_brace ()) - COUNT_TOK_AND_RETURN (';'); + bool eof = false; + int tok = process_comment (yytext[0], eof); + if (eof) + TOK_RETURN (END_OF_INPUT); + else if (tok > 0) + COUNT_TOK_AND_RETURN (tok); } %{ @@ -964,7 +925,6 @@ lexer_flags.defining_func = true; lexer_flags.parsed_function_name = false; - lexer_flags.beginning_of_function = true; if (! (reading_fcn_file || reading_script_file)) input_line_number = 1; @@ -1180,7 +1140,7 @@ // duplicates some of this code! static std::string -grab_help_text (void) +grab_help_text (bool& eof) { std::string buf; @@ -1238,12 +1198,77 @@ done: - if (c) + if (c == EOF) + eof = true; + + if (c && ! eof) yyunput (c, yytext); return buf; } +static int +process_comment (char cchar, bool& eof) +{ + eof = false; + + std::string help_txt; + + if (! help_buf.empty ()) + help_txt = help_buf.top (); + + if (help_txt.empty () && nesting_level.none ()) + { + std::string txt = grab_help_text (eof); + + if (! help_buf.empty ()) + help_buf.pop (); + + help_buf.push (txt); + + octave_comment_buffer::append (txt); + } + else + { + std::string buf; + + bool begin_comment = true; + + int c; + while ((c = yyinput ()) != EOF && c != '\n') + { + if (begin_comment && (c == '#' || c == '%')) + ; /* Skip leading comment characters. */ + else + buf += static_cast (c); + } + + octave_comment_buffer::append (buf); + + if (c == EOF) + eof = true; + } + + current_input_column = 1; + lexer_flags.quote_is_transpose = false; + lexer_flags.convert_spaces_to_comma = true; + + maybe_gripe_matlab_incompatible_comment (cchar); + + if (YY_START == COMMAND_START) + BEGIN (INITIAL); + + if (nesting_level.none ()) + { + lexer_flags.doing_rawcommand = false; + return '\n'; + } + else if (nesting_level.is_bracket_or_brace ()) + return ';'; + else + return 0; +} + // Return 1 if the given character matches any character in the given // string. @@ -2347,7 +2372,6 @@ looping = 0; // Not initially defining a function. - beginning_of_function = false; defining_func = false; parsed_function_name = false; parsing_nested_function = 0; @@ -2426,6 +2450,11 @@ return retval; } +void +prep_lexer_for_script (void) +{ + BEGIN (SCRIPT_FILE_BEGIN); +} static void maybe_warn_separator_insert (char sep) diff --git a/src/oct-hist.cc b/src/oct-hist.cc --- a/src/oct-hist.cc +++ b/src/oct-hist.cc @@ -449,7 +449,7 @@ octave_set_interrupt_handler (old_interrupt_handler); - // Write the commands to the history file since parse_and_execute + // Write the commands to the history file since source_file // disables command line history while it executes. std::fstream file (name.c_str (), std::ios::in); @@ -488,7 +488,7 @@ Vecho_executing_commands = ECHO_CMD_LINE; input_from_tmp_history_file = true; - parse_and_execute (name); + source_file (name); unwind_protect::run_frame ("do_edit_history"); @@ -517,7 +517,7 @@ Vecho_executing_commands = ECHO_CMD_LINE; input_from_tmp_history_file = true; - parse_and_execute (name); + source_file (name); unwind_protect::run_frame ("do_run_history"); diff --git a/src/octave.cc b/src/octave.cc --- a/src/octave.cc +++ b/src/octave.cc @@ -289,7 +289,11 @@ input_from_startup_file = true; - int verbose = (verbose_flag && ! inhibit_startup_message); + std::string context; + + bool verbose = (verbose_flag && ! inhibit_startup_message); + + bool require_file = false; if (read_site_files) { @@ -298,9 +302,9 @@ // (if it exists), then from the file // $(prefix)/share/octave/$(version)/m/octaverc (if it exists). - parse_and_execute (Vlocal_site_defaults_file, verbose); + source_file (Vlocal_site_defaults_file, context, verbose, require_file); - parse_and_execute (Vsite_defaults_file, verbose); + source_file (Vsite_defaults_file, context, verbose, require_file); } if (read_init_files) @@ -324,7 +328,7 @@ if (! home_rc.empty ()) { - parse_and_execute (home_rc, verbose); + source_file (home_rc, context, verbose, require_file); // Names alone are not enough. @@ -352,7 +356,7 @@ local_rc = octave_env::make_absolute (initfile, curr_dir); } - parse_and_execute (local_rc, verbose); + source_file (local_rc, context, verbose, require_file); } } @@ -447,7 +451,11 @@ try { - parse_and_execute (fname, false, "octave"); + std::string context; + bool verbose = false; + bool require_file = true; + + source_file (fname, context, verbose, require_file, "octave"); } catch (octave_interrupt_exception) { diff --git a/src/ov-usr-fcn.cc b/src/ov-usr-fcn.cc --- a/src/ov-usr-fcn.cc +++ b/src/ov-usr-fcn.cc @@ -51,7 +51,29 @@ // Maximum nesting level for functions called recursively. static int Vmax_recursion_depth = 256; -// Scripts. +// User defined scripts. + +DEFINE_OCTAVE_ALLOCATOR (octave_user_script); + +DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_user_script, + "user-defined script", + "user-defined script"); + +octave_user_script::~octave_user_script (void) +{ + delete cmd_list; +} + +octave_value_list +octave_user_script::subsref (const std::string&, + const std::list&, int) +{ + octave_value_list retval; + + ::error ("invalid use of script in index expression"); + + return retval; +} octave_value_list octave_user_script::do_multi_index_op (int nargout, @@ -61,13 +83,37 @@ if (! error_state) { - if (args.length () == 0) + if (args.length () == 0 && nargout == 0) { - // FIXME -- I think we need a way to protect against - // recursion, but we can't use the same method as we use for - // functions. + if (cmd_list) + { + unwind_protect::begin_frame ("user_script_eval"); + + unwind_protect_int (call_depth); + call_depth++; + + if (call_depth <= Vmax_recursion_depth) + { + octave_call_stack::push (this); + + unwind_protect::add (octave_call_stack::unwind_pop, 0); + + cmd_list->eval (); - source_file (file_name); + if (tree_return_command::returning) + tree_return_command::returning = 0; + + if (tree_break_command::breaking) + tree_break_command::breaking--; + + if (error_state) + traceback_error (); + } + else + ::error ("max_recursion_limit exceeded"); + + unwind_protect::run_frame ("user_script_eval"); + } } else error ("invalid call to script"); @@ -76,6 +122,38 @@ return retval; } +void +octave_user_script::accept (tree_walker& tw) +{ + tw.visit_octave_user_script (*this); +} + +// FIXME -- this function is exactly the same as +// octave_user_function::traceback_error. + +void +octave_user_script::traceback_error (void) const +{ + if (error_state >= 0) + error_state = -1; + + if (my_name.empty ()) + { + if (file_name.empty ()) + ::error ("called from `?unknown?'"); + else + ::error ("called from file `%s'", file_name.c_str ()); + } + else + { + if (file_name.empty ()) + ::error ("called from `%s'", my_name.c_str ()); + else + ::error ("called from `%s' in file `%s'", + my_name.c_str (), file_name.c_str ()); + } +} + // User defined functions. DEFINE_OCTAVE_ALLOCATOR (octave_user_function); @@ -84,12 +162,6 @@ "user-defined function", "user-defined function"); -DEFINE_OCTAVE_ALLOCATOR (octave_user_script); - -DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_user_script, - "user-defined script", - "user-defined script"); - // Ugh. This really needs to be simplified (code/data? // extrinsic/intrinsic state?). diff --git a/src/ov-usr-fcn.h b/src/ov-usr-fcn.h --- a/src/ov-usr-fcn.h +++ b/src/ov-usr-fcn.h @@ -50,13 +50,23 @@ { public: - octave_user_script (void) { } + octave_user_script (void) + : octave_function (), cmd_list (0), file_name () { } + + octave_user_script (const std::string& fnm, const std::string& nm, + tree_statement_list *cmds, + const std::string& ds = std::string ()) + : octave_function (nm, ds), cmd_list (cmds), file_name (fnm) { } octave_user_script (const std::string& fnm, const std::string& nm, const std::string& ds = std::string ()) - : octave_function (nm, ds), file_name (fnm) { } + : octave_function (nm, ds), cmd_list (0), file_name (fnm) { } + + ~octave_user_script (void); - ~octave_user_script (void) { } + octave_function *function_value (bool = false) { return this; } + + octave_user_script *user_script_value (bool = false) { return this; } // Scripts and user functions are both considered "scripts" because // they are written in Octave's scripting language. @@ -65,16 +75,58 @@ void stash_fcn_file_name (const std::string& nm) { file_name = nm; } + void mark_fcn_file_up_to_date (const octave_time& t) { t_checked = t; } + + void stash_fcn_file_time (const octave_time& t) + { + t_parsed = t; + mark_fcn_file_up_to_date (t); + } + std::string fcn_file_name (void) const { return file_name; } + octave_time time_parsed (void) const { return t_parsed; } + + octave_time time_checked (void) const { return t_checked; } + + octave_value subsref (const std::string& type, + const std::list& idx) + { + octave_value_list tmp = subsref (type, idx, 1); + return tmp.length () > 0 ? tmp(0) : octave_value (); + } + + octave_value_list subsref (const std::string& type, + const std::list& idx, + int nargout); + octave_value_list do_multi_index_op (int nargout, const octave_value_list& args); + tree_statement_list *body (void) { return cmd_list; } + + void traceback_error (void) const; + + void accept (tree_walker& tw); + private: - // The name of the file to parse. + // The list of commands that make up the body of this function. + tree_statement_list *cmd_list; + + // The name of the file we parsed. std::string file_name; + // The time the file was parsed. + octave_time t_parsed; + + // The time the file was last checked to see if it needs to be + // parsed again. + octave_time t_checked; + + // Used to keep track of recursion depth. + int call_depth; + // No copying! octave_user_script (const octave_user_script& f); diff --git a/src/parse.h b/src/parse.h --- a/src/parse.h +++ b/src/parse.h @@ -81,13 +81,6 @@ // Keep a count of how many END tokens we expect. extern int end_tokens_expected; -extern OCTINTERP_API void -parse_and_execute (FILE *f); - -extern OCTINTERP_API void -parse_and_execute (const std::string& s, bool verbose = false, - const char *warn_for = 0); - extern OCTINTERP_API std::string get_help_from_file (const std::string& nm, bool& symbol_found, std::string& file); @@ -110,7 +103,9 @@ extern OCTINTERP_API void source_file (const std::string& file_name, - const std::string& context = std::string ()); + const std::string& context = std::string (), + bool verbose = false, bool require_file = true, + const std::string& warn_for = std::string ()); extern OCTINTERP_API octave_value_list feval (const std::string& name, diff --git a/src/parse.y b/src/parse.y --- a/src/parse.y +++ b/src/parse.y @@ -259,6 +259,10 @@ make_assign_op (int op, tree_argument_list *lhs, token *eq_tok, tree_expression *rhs); +// Define a script. +static void +make_script (tree_statement_list *cmds); + // Begin defining a function. static octave_user_function * start_function (tree_parameter_list *param_list, tree_statement_list *body); @@ -268,7 +272,7 @@ frob_function (const std::string& fname, octave_user_function *fcn); // Finish defining a function. -static void +static tree_function_def * finish_function (tree_parameter_list *ret_list, octave_user_function *fcn, octave_comment_list *lc); @@ -399,7 +403,7 @@ // Other tokens. %token END_OF_INPUT LEXICAL_ERROR -%token FCN +%token FCN SCRIPT // %token VARARGIN VARARGOUT %token CLOSE_BRACE @@ -424,7 +428,7 @@ %type param_list param_list1 param_list2 %type return_list return_list1 %type command select_command loop_command -%type jump_command except_command function +%type jump_command except_command function script %type if_command %type elseif_clause else_clause %type if_cmd_list1 if_cmd_list @@ -521,10 +525,7 @@ ; list1 : statement - { - lexer_flags.beginning_of_function = false; - $$ = new tree_statement_list ($1); - } + { $$ = new tree_statement_list ($1); } | list1 sep statement { set_stmt_print_flag ($1, $2, true); @@ -908,6 +909,8 @@ { $$ = $1; } | function { $$ = $1; } + | script + { $$ = $1; } ; // ===================== @@ -1206,6 +1209,17 @@ } ; +// =========== +// Script file +// =========== + +script : SCRIPT opt_list END_OF_INPUT + { + make_script ($2); + $$ = 0; + } + ; + // =================== // Function definition // =================== @@ -1216,15 +1230,13 @@ function : function_beg function1 { - $2->stash_leading_comment ($1); + $$ = finish_function (0, $2, $1); recover_from_parsing_function (); - $$ = 0; } | function_beg return_list '=' function1 { - finish_function ($2, $4, $1); + $$ = finish_function ($2, $4, $1); recover_from_parsing_function (); - $$ = 0; } ; @@ -2386,6 +2398,30 @@ return retval; } +// Define a function. + +static void +make_script (tree_statement_list *cmds) +{ + std::string doc_string; + + if (! help_buf.empty ()) + { + doc_string = help_buf.top (); + help_buf.pop (); + } + + octave_user_script *script + = new octave_user_script (curr_fcn_file_full_name, curr_fcn_file_name, + cmds, doc_string); + + octave_time now; + + script->stash_fcn_file_time (now); + + curr_fcn_ptr = script; +} + // Begin defining a function. static octave_user_function * @@ -2511,34 +2547,35 @@ symbol_table::reset_parent_scope (); } } - else if (! reading_fcn_file) - { - std::string nm = fcn->name (); - - symbol_table::install_cmdline_function (nm, octave_value (fcn)); - - // Make sure that any variable with the same name as the new - // function is cleared. - - symbol_table::varref (nm) = octave_value (); - } + else if (reading_fcn_file) + curr_fcn_ptr = fcn; else - curr_fcn_ptr = fcn; + curr_fcn_ptr = 0; return fcn; } -// Finish defining a function. - -static void +static tree_function_def * finish_function (tree_parameter_list *ret_list, octave_user_function *fcn, octave_comment_list *lc) { - ret_list->mark_as_formal_parameters (); - - fcn->stash_leading_comment (lc); - - fcn->define_ret_list (ret_list); + tree_function_def *retval = 0; + + if (ret_list) + ret_list->mark_as_formal_parameters (); + + if (fcn) + { + if (lc) + fcn->stash_leading_comment (lc); + + fcn->define_ret_list (ret_list); + } + + if (! curr_fcn_ptr) + retval = new tree_function_def (fcn); + + return retval; } static void @@ -2551,7 +2588,6 @@ symtab_context.pop (); lexer_flags.defining_func = false; - lexer_flags.beginning_of_function = false; lexer_flags.parsed_function_name = false; lexer_flags.looking_at_return_list = false; lexer_flags.looking_at_parameter_list = false; @@ -2767,77 +2803,6 @@ } } -void -parse_and_execute (FILE *f) -{ - unwind_protect::begin_frame ("parse_and_execute"); - - unwind_protect_ptr (global_command); - - YY_BUFFER_STATE old_buf = current_buffer (); - YY_BUFFER_STATE new_buf = create_buffer (f); - - unwind_protect::add (restore_input_buffer, old_buf); - unwind_protect::add (delete_input_buffer, new_buf); - - switch_to_buffer (new_buf); - - unwind_protect_bool (line_editing); - unwind_protect_bool (get_input_from_eval_string); - unwind_protect_bool (parser_end_of_input); - - line_editing = false; - get_input_from_eval_string = false; - parser_end_of_input = false; - - int retval; - do - { - reset_parser (); - - retval = yyparse (); - - if (retval == 0) - { - if (global_command) - { - global_command->eval (); - - delete global_command; - - global_command = 0; - - OCTAVE_QUIT; - - bool quit = (tree_return_command::returning - || tree_break_command::breaking); - - if (tree_return_command::returning) - tree_return_command::returning = 0; - - if (tree_break_command::breaking) - tree_break_command::breaking--; - - if (error_state) - { - error ("near line %d of file `%s'", input_line_number, - curr_fcn_file_full_name.c_str ()); - - break; - } - - if (quit) - break; - } - else if (parser_end_of_input) - break; - } - } - while (retval == 0); - - unwind_protect::run_frame ("parse_and_execute"); -} - static void safe_fclose (void *f) { @@ -2854,51 +2819,6 @@ fclose (static_cast (f)); } -void -parse_and_execute (const std::string& s, bool verbose, const char *warn_for) -{ - unwind_protect::begin_frame ("parse_and_execute_2"); - - unwind_protect_bool (reading_script_file); - unwind_protect_str (curr_fcn_file_full_name); - - reading_script_file = true; - curr_fcn_file_full_name = s; - - FILE *f = get_input_from_file (s, 0); - - if (f) - { - unwind_protect::add (safe_fclose, f); - - octave_user_script *script = new octave_user_script (s, s, ""); - octave_call_stack::push (script); - unwind_protect::add (octave_call_stack::unwind_pop_script, 0); - - unwind_protect_int (input_line_number); - unwind_protect_int (current_input_column); - - input_line_number = 0; - current_input_column = 1; - - if (verbose) - { - std::cout << "reading commands from " << s << " ... "; - reading_startup_message_printed = true; - std::cout.flush (); - } - - parse_and_execute (f); - - if (verbose) - std::cout << "done." << std::endl; - } - else if (warn_for) - error ("%s: unable to open file `%s'", warn_for, s.c_str ()); - - unwind_protect::run_frame ("parse_and_execute_2"); -} - static bool looks_like_copyright (const std::string& s) { @@ -2930,33 +2850,13 @@ } // Eat whitespace and comments from FFILE, returning the text of the -// comments read if it doesn't look like a copyright notice. If -// IN_PARTS, consider each block of comments separately; otherwise, -// grab them all at once. If UPDATE_POS is TRUE, line and column -// number information is updated. If SAVE_COPYRIGHT is TRUE, then -// comments that are recognized as a copyright notice are saved in the -// comment buffer. If SKIP_CODE is TRUE, then ignore code, otherwise -// stop at the first non-whitespace character that is not part of a +// comments read if it doesn't look like a copyright notice. The +// parser line and column number information is updated. Processing +// stops at the first non-whitespace character that is not part of a // comment. -// FIXME -- skipping code will fail for something like this: -// -// function foo (x) -// fprintf ('%d\n', x); -// -// % This is the help for foo. -// -// because we recognize the '%' in the fprintf format as a comment -// character. Fixing this will probably require actually parsing the -// file properly. - -// FIXME -- grab_help_text() in lex.l duplicates some of this -// code! - static std::string -gobble_leading_white_space (FILE *ffile, bool in_parts, - bool update_pos, bool save_copyright, - bool skip_code) +gobble_leading_white_space (FILE *ffile) { std::string help_txt; @@ -2980,8 +2880,7 @@ while ((c = text_getc (ffile)) != EOF) { - if (update_pos) - current_input_column++; + current_input_column++; if (begin_comment) { @@ -3006,38 +2905,32 @@ if (c == '\n') { - if (update_pos) - { - input_line_number++; - current_input_column = 0; - } + input_line_number++; + current_input_column = 0; in_comment = false; discard_space = true; - - if (in_parts) - { - if ((c = text_getc (ffile)) != EOF) - { - if (update_pos) - current_input_column--; - ungetc (c, ffile); - if (c == '\n') - break; - } - else - break; - } } } else { switch (c) { + case '\n': + input_line_number++; + current_input_column = 0; + // fall through... + case ' ': case '\t': - if (first_comments_seen) - have_help_text = true; + if (first_comments_seen && ! have_help_text) + { + if (looks_like_copyright (help_txt)) + help_txt.resize (0); + + if (! help_txt.empty ()) + have_help_text = true; + } break; case '%': @@ -3046,122 +2939,48 @@ in_comment = true; break; - case '\n': - if (first_comments_seen) - have_help_text = true; - if (update_pos) - { - input_line_number++; - current_input_column = 0; - } - continue; - default: - if (skip_code) - continue; - else - { - if (update_pos) - current_input_column--; - ungetc (c, ffile); - goto done; - } + current_input_column--; + ungetc (c, ffile); + goto done; } } } done: - if (! help_txt.empty ()) - { - if (looks_like_copyright (help_txt)) - { - if (save_copyright) - octave_comment_buffer::append (help_txt); - - help_txt.resize (0); - } - - if (in_parts && help_txt.empty ()) - help_txt = gobble_leading_white_space (ffile, in_parts, update_pos, - false, skip_code); - } - return help_txt; } -std::string -get_help_from_file (const std::string& nm, bool& symbol_found, - std::string& file) +static void +process_leading_comments (FILE *fptr) { - std::string retval; - - file = fcn_file_in_path (nm); - - if (! file.empty ()) - { - symbol_found = true; - - FILE *fptr = fopen (file.c_str (), "r"); - - if (fptr) - { - unwind_protect::add (safe_fclose, fptr); - - retval = gobble_leading_white_space (fptr, true, true, false, true); - - unwind_protect::run (); - } - } - - return retval; + std::string txt = gobble_leading_white_space (fptr); + + help_buf.push (txt); + + octave_comment_buffer::append (txt); } -std::string -get_help_from_file (const std::string& nm, bool& symbol_found) +static bool +looking_at_function_keyword (FILE *ffile) { - std::string file; - return get_help_from_file (nm, symbol_found, file); -} - -static int -is_function_file (FILE *ffile) -{ - int status = 0; + bool status = false; long pos = ftell (ffile); - gobble_leading_white_space (ffile, false, false, false, false); - char buf [10]; fgets (buf, 10, ffile); - int len = strlen (buf); + size_t len = strlen (buf); if (len > 8 && strncmp (buf, "function", 8) == 0 && ! (isalnum (buf[8]) || buf[8] == '_')) - status = 1; + status = true; fseek (ffile, pos, SEEK_SET); return status; } -static int -is_function_file (const std::string& fname) -{ - int retval = 0; - - FILE *fid = fopen (fname.c_str (), "r"); - - if (fid) - { - retval = is_function_file (fid); - - fclose (fid); - } - - return retval; -} - static void restore_command_history (void *) { @@ -3174,11 +2993,10 @@ command_editor::set_input_stream (static_cast (f)); } -typedef octave_function * octave_function_ptr; - static octave_function * parse_fcn_file (const std::string& ff, const std::string& dispatch_type, - bool exec_script, bool force_script = false) + bool force_script = false, bool require_file = true, + const std::string& warn_for = std::string ()) { unwind_protect::begin_frame ("parse_fcn_file"); @@ -3210,96 +3028,89 @@ parent_function_name = ""; current_class_name = dispatch_type; + // The next four lines must be in this order. + unwind_protect::add (restore_command_history, 0); + + // FIXME -- we shouldn't need both the + // command_history object and the + // Vsaving_history variable... + command_history::ignore_entries (); + + unwind_protect_bool (Vsaving_history); + + Vsaving_history = false; + FILE *ffile = get_input_from_file (ff, 0); unwind_protect::add (safe_fclose, ffile); if (ffile) { - // Check to see if this file defines a function or is just a - // list of commands. - - if (! force_script && is_function_file (ffile)) + process_leading_comments (ffile); + + std::string file_type; + + bool parsing_script = false; + + if (! force_script && looking_at_function_keyword (ffile)) { - // FIXME -- we shouldn't need both the - // command_history object and the - // Vsaving_history variable... - command_history::ignore_entries (); - - unwind_protect::add (restore_command_history, 0); + file_type = "function"; unwind_protect_int (Vecho_executing_commands); - unwind_protect_bool (Vsaving_history); unwind_protect_bool (reading_fcn_file); unwind_protect_bool (get_input_from_eval_string); unwind_protect_bool (parser_end_of_input); Vecho_executing_commands = ECHO_OFF; - Vsaving_history = false; reading_fcn_file = true; get_input_from_eval_string = false; parser_end_of_input = false; - - YY_BUFFER_STATE old_buf = current_buffer (); - YY_BUFFER_STATE new_buf = create_buffer (ffile); - - unwind_protect::add (restore_input_buffer, old_buf); - unwind_protect::add (delete_input_buffer, new_buf); - - switch_to_buffer (new_buf); - - unwind_protect_ptr (curr_fcn_ptr); - curr_fcn_ptr = 0; - - reset_parser (); - - std::string txt - = gobble_leading_white_space (ffile, true, true, true, false); - - help_buf.push (txt); - - octave_comment_buffer::append (txt); - - // FIXME -- this should not be necessary. - gobble_leading_white_space (ffile, false, true, false, false); - - lexer_flags.parsing_class_method = ! dispatch_type.empty (); - - int status = yyparse (); - - fcn_ptr = curr_fcn_ptr; - - if (status != 0) - error ("parse error while reading function file %s", ff.c_str ()); } - else if (exec_script) + else { + file_type = "script"; + // The value of `reading_fcn_file' will be restored to the // proper value when we unwind from this frame. reading_fcn_file = old_reading_fcn_file_state; - // FIXME -- we shouldn't need both the - // command_history object and the - // Vsaving_history variable... - command_history::ignore_entries (); - - unwind_protect::add (restore_command_history, 0); - - unwind_protect_bool (Vsaving_history); unwind_protect_bool (reading_script_file); - Vsaving_history = false; reading_script_file = true; - octave_user_script *script = new octave_user_script (ff, ff, ""); - octave_call_stack::push (script); - unwind_protect::add (octave_call_stack::unwind_pop_script, 0); - - parse_and_execute (ffile); + parsing_script = true; } + + YY_BUFFER_STATE old_buf = current_buffer (); + YY_BUFFER_STATE new_buf = create_buffer (ffile); + + unwind_protect::add (restore_input_buffer, old_buf); + unwind_protect::add (delete_input_buffer, new_buf); + + switch_to_buffer (new_buf); + + unwind_protect_ptr (curr_fcn_ptr); + curr_fcn_ptr = 0; + + reset_parser (); + + if (parsing_script) + prep_lexer_for_script (); + + lexer_flags.parsing_class_method = ! dispatch_type.empty (); + + int status = yyparse (); + + fcn_ptr = curr_fcn_ptr; + + if (status != 0) + error ("parse error while reading %s file %s", + file_type.c_str(), ff.c_str ()); } - else + else if (require_file) error ("no such file, `%s'", ff.c_str ()); + else if (! warn_for.empty ()) + error ("%s: unable to open file `%s'", warn_for.c_str (), ff.c_str ()); unwind_protect::run_frame ("parse_fcn_file"); @@ -3307,6 +3118,52 @@ } std::string +get_help_from_file (const std::string& nm, bool& symbol_found, + std::string& file) +{ + std::string retval; + + file = fcn_file_in_path (nm); + + if (! file.empty ()) + { + symbol_found = true; + + FILE *fptr = fopen (file.c_str (), "r"); + + if (fptr) + { + unwind_protect::add (safe_fclose, fptr); + + retval = gobble_leading_white_space (fptr); + + if (retval.empty ()) + { + octave_function *fcn = parse_fcn_file (file, ""); + + if (fcn) + { + retval = fcn->doc_string (); + + delete fcn; + } + } + + unwind_protect::run (); + } + } + + return retval; +} + +std::string +get_help_from_file (const std::string& nm, bool& symbol_found) +{ + std::string file; + return get_help_from_file (nm, symbol_found, file); +} + +std::string lookup_autoload (const std::string& nm) { std::string retval; @@ -3402,20 +3259,15 @@ retval = octave_dynamic_loader::load_mex (nm, file, fcn_file_from_relative_lookup); else if (len > 2) { - if (is_function_file (file)) - { - // These are needed by yyparse. - - unwind_protect_str (curr_fcn_file_name); - unwind_protect_str (curr_fcn_file_full_name); - - curr_fcn_file_name = nm; - curr_fcn_file_full_name = file; - - retval = parse_fcn_file (file, dispatch_type, false, autoloading); - } - else - retval = new octave_user_script (file, fcn_name); + // These are needed by yyparse. + + unwind_protect_str (curr_fcn_file_name); + unwind_protect_str (curr_fcn_file_full_name); + + curr_fcn_file_name = nm; + curr_fcn_file_full_name = file; + + retval = parse_fcn_file (file, dispatch_type, autoloading); } if (retval) @@ -3530,7 +3382,8 @@ } void -source_file (const std::string& file_name, const std::string& context) +source_file (const std::string& file_name, const std::string& context, + bool verbose, bool require_file, const std::string& warn_for) { std::string file_full_name = file_ops::tilde_expand (file_name); @@ -3557,9 +3410,31 @@ if (! error_state) { - parse_fcn_file (file_full_name, "", true, true); - - if (error_state) + octave_function *fcn = parse_fcn_file (file_full_name, "", true, + require_file, warn_for); + + if (! error_state) + { + if (fcn && fcn->is_user_script ()) + { + octave_value_list args; + + if (verbose) + { + std::cout << "executing commands from " << file_full_name << " ... "; + reading_startup_message_printed = true; + std::cout.flush (); + } + + fcn->do_multi_index_op (0, args); + + if (verbose) + std::cout << "done." << std::endl; + + delete fcn; + } + } + else error ("source: error sourcing file `%s'", file_full_name.c_str ()); } diff --git a/src/pt-bp.cc b/src/pt-bp.cc --- a/src/pt-bp.cc +++ b/src/pt-bp.cc @@ -270,6 +270,12 @@ } void +tree_breakpoint::visit_octave_user_script (octave_user_script&) +{ + // FIXME -- should anything happen here? +} + +void tree_breakpoint::visit_octave_user_function (octave_user_function&) { // We should not visit octave user functions because the function we @@ -289,6 +295,18 @@ // Do nothing. } +void +tree_breakpoint::visit_function_def (tree_function_def& fdef) +{ + if (found) + return; + + octave_function *fcn = fdef.function (); + + if (fcn) + fcn->accept (*this); +} + void tree_breakpoint::visit_identifier (tree_identifier& id) { diff --git a/src/pt-bp.h b/src/pt-bp.h --- a/src/pt-bp.h +++ b/src/pt-bp.h @@ -69,12 +69,16 @@ void visit_complex_for_command (tree_complex_for_command&); + void visit_octave_user_script (octave_user_script&); + void visit_octave_user_function (octave_user_function&); void visit_octave_user_function_header (octave_user_function&); void visit_octave_user_function_trailer (octave_user_function&); + void visit_function_def (tree_function_def&); + void visit_identifier (tree_identifier&); void visit_if_clause (tree_if_clause&); diff --git a/src/pt-check.cc b/src/pt-check.cc --- a/src/pt-check.cc +++ b/src/pt-check.cc @@ -181,6 +181,15 @@ } void +tree_checker::visit_octave_user_script (octave_user_script& fcn) +{ + tree_statement_list *cmd_list = fcn.body (); + + if (cmd_list) + cmd_list->accept (*this); +} + +void tree_checker::visit_octave_user_function (octave_user_function& fcn) { tree_statement_list *cmd_list = fcn.body (); @@ -190,6 +199,15 @@ } void +tree_checker::visit_function_def (tree_function_def& fdef) +{ + octave_function *fcn = fdef.function (); + + if (fcn) + fcn->accept (*this); +} + +void tree_checker::visit_identifier (tree_identifier& /* id */) { } diff --git a/src/pt-check.h b/src/pt-check.h --- a/src/pt-check.h +++ b/src/pt-check.h @@ -58,8 +58,12 @@ void visit_complex_for_command (tree_complex_for_command&); + void visit_octave_user_script (octave_user_script&); + void visit_octave_user_function (octave_user_function&); + void visit_function_def (tree_function_def&); + void visit_identifier (tree_identifier&); void visit_if_clause (tree_if_clause&); diff --git a/src/pt-cmd.cc b/src/pt-cmd.cc --- a/src/pt-cmd.cc +++ b/src/pt-cmd.cc @@ -42,6 +42,38 @@ tw.visit_no_op_command (*this); } +// Function definition. + +void +tree_function_def::eval (void) +{ + octave_function *f = function (); + + if (f) + { + std::string nm = f->name (); + + symbol_table::install_cmdline_function (nm, fcn); + + // Make sure that any variable with the same name as the new + // function is cleared. + + symbol_table::varref (nm) = octave_value (); + } +} + +tree_command * +tree_function_def::dup (symbol_table::scope_id) +{ + return new tree_function_def (fcn, line (), column ()); +} + +void +tree_function_def::accept (tree_walker& tw) +{ + tw.visit_function_def (*this); +} + /* ;;; Local Variables: *** ;;; mode: C++ *** diff --git a/src/pt-cmd.h b/src/pt-cmd.h --- a/src/pt-cmd.h +++ b/src/pt-cmd.h @@ -28,6 +28,7 @@ class tree_walker; +#include "ov-fcn.h" #include "pt.h" #include "pt-bp.h" #include "symtab.h" @@ -88,6 +89,40 @@ tree_no_op_command& operator = (const tree_no_op_command&); }; +// Function definition. + +class +tree_function_def : public tree_command +{ +public: + + tree_function_def (octave_function *f, int l = -1, int c = -1) + : tree_command (l, c), fcn (f) { } + + ~tree_function_def (void) { } + + void eval (void); + + tree_command *dup (symbol_table::scope_id scope); + + void accept (tree_walker& tw); + + octave_function *function (void) { return fcn.function_value (); } + +private: + + octave_value fcn; + + tree_function_def (const octave_value& v, int l = -1, int c = -1) + : tree_command (l, c), fcn (v) { } + + // No copying! + + tree_function_def (const tree_function_def&); + + tree_function_def& operator = (const tree_function_def&); +}; + #endif /* diff --git a/src/pt-pr-code.cc b/src/pt-pr-code.cc --- a/src/pt-pr-code.cc +++ b/src/pt-pr-code.cc @@ -284,6 +284,17 @@ } void +tree_print_code::visit_octave_user_script (octave_user_script& fcn) +{ + reset (); + + tree_statement_list *cmd_list = fcn.body (); + + if (cmd_list) + cmd_list->accept (*this); +} + +void tree_print_code::visit_octave_user_function (octave_user_function& fcn) { reset (); @@ -407,6 +418,17 @@ } void +tree_print_code::visit_function_def (tree_function_def& fdef) +{ + indent (); + + octave_function *fcn = fdef.function (); + + if (fcn) + fcn->accept (*this); +} + +void tree_print_code::visit_identifier (tree_identifier& id) { indent (); diff --git a/src/pt-pr-code.h b/src/pt-pr-code.h --- a/src/pt-pr-code.h +++ b/src/pt-pr-code.h @@ -75,12 +75,16 @@ void visit_complex_for_command (tree_complex_for_command&); + void visit_octave_user_script (octave_user_script&); + void visit_octave_user_function (octave_user_function&); void visit_octave_user_function_header (octave_user_function&); void visit_octave_user_function_trailer (octave_user_function&); + void visit_function_def (tree_function_def&); + void visit_identifier (tree_identifier&); void visit_if_clause (tree_if_clause&); diff --git a/src/pt-walk.h b/src/pt-walk.h --- a/src/pt-walk.h +++ b/src/pt-walk.h @@ -35,7 +35,9 @@ class tree_decl_init_list; class tree_simple_for_command; class tree_complex_for_command; +class octave_user_script; class octave_user_function; +class tree_function_def; class tree_identifier; class tree_if_clause; class tree_if_command; @@ -102,9 +104,15 @@ visit_complex_for_command (tree_complex_for_command&) = 0; virtual void + visit_octave_user_script (octave_user_script&) = 0; + + virtual void visit_octave_user_function (octave_user_function&) = 0; virtual void + visit_function_def (tree_function_def&) = 0; + + virtual void visit_identifier (tree_identifier&) = 0; virtual void