Mercurial > octave
changeset 32894:9104b087dc26
maint: merge stable to default
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Thu, 01 Feb 2024 00:34:24 -0500 |
parents | 837450b0ee50 (current diff) 2faec354b977 (diff) |
children | dc3ff08bfd3f 212bb363f1fa |
files | libinterp/octave-value/cdef-class.h libinterp/octave-value/cdef-object.h libinterp/parse-tree/lex.h libinterp/parse-tree/oct-parse.yy libinterp/parse-tree/pt-eval.cc |
diffstat | 11 files changed, 358 insertions(+), 80 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/help.cc Wed Jan 31 00:26:04 2024 -0500 +++ b/libinterp/corefcn/help.cc Thu Feb 01 00:34:24 2024 -0500 @@ -232,24 +232,15 @@ return h; } -std::string -help_system::which (const std::string& name, - std::string& type) const +bool +help_system::get_which_info_from_fcn (const std::string& name, const octave_value& ov_fcn, std::string& file, std::string& type) const { - std::string file; - - if (name.empty ()) - return file; - + file = ""; type = ""; - symbol_table& symtab = m_interpreter.get_symbol_table (); - - octave_value val = symtab.find_function (name); - - if (val.is_defined ()) + if (ov_fcn.is_function ()) { - octave_function *fcn = val.function_value (); + octave_function *fcn = ov_fcn.function_value (); if (fcn) { @@ -269,11 +260,10 @@ } else { - file = fcn->fcn_file_name (); if (! file.empty ()) - type = val.is_user_script () ? "script" : "function"; + type = ov_fcn.is_user_script () ? "script" : "function"; else { if (fcn->is_user_function ()) @@ -285,6 +275,8 @@ } } } + + return true; } else { @@ -293,24 +285,147 @@ load_path& lp = m_interpreter.get_load_path (); file = lp.find_fcn_file (name); + + if (file.empty ()) + return false; } } - if (file.empty ()) + return false; +} + +// FIXME: There is a lot of duplication between the following function +// and help_system::raw_help_from_symbol_table. Some refactoring would +// probably be useful. + +std::string +help_system::which (const std::string& name, + std::string& type) const +{ + std::string file; + + if (name.empty ()) + return file; + + type = ""; + + symbol_table& symtab = m_interpreter.get_symbol_table (); + + size_t pos = name.find ('.'); + + if (pos == std::string::npos) { - // File query. + // Simple name. If not found, continue looking for packages and + // classes. + + octave_value ov_fcn = symtab.find_function (name); + + if (get_which_info_from_fcn (name, ov_fcn, file, type)) + return file; + } + + // NAME contains '.' and must match the following pattern: + // + // (package.)*(package|classname).(property|method)* + // + // Start by looking up the full name. It could be either a package or + // a class and we are done. Otherwise, strip the final component and + // lookup that name. If it is a package, look for a function. If it + // is a class, look for a property or method. + + cdef_manager& cdm = m_interpreter.get_cdef_manager (); - load_path& lp = m_interpreter.get_load_path (); + // FIXME: In the following search we may load classes. Is that really + // what we want, or should we just search the loadpath for + // +pkga/+pkgb/classname/file.m, etc. and attempt to extract help text + // without actually installing packages and classes into the fcn_info + // table? + + // Is NAME a class? + + cdef_class cls = cdm.find_class (name, false, true); + + if (cls.ok ()) + { + // FIXME: Return documentation for the class or the class + // constructor? + + file = cls.file_name (); + type = "classdef class"; + + return file; + } + + cdef_package pkg = cdm.find_package (name, false, true); - // For compatibility: "file." queries "file". - if (name.size () > 1 && name[name.size () - 1] == '.') - file = lp.find_file (name.substr (0, name.size () - 1)); - else - file = lp.find_file (name); + if (pkg.ok ()) + { + // FIXME: How to get the fill name of a package? + file = pkg.get_name (); + type = "package"; + + return file; + } + + // Strip final component (might be a property or method name). + + pos = name.rfind ('.'); + std::string prefix = name.substr (0, pos); + std::string nm = name.substr (pos+1); + + // Is PREFIX the name of a class? + + cls = cdm.find_class (prefix, false, true); + + if (cls.ok ()) + { + // FIXME: Should we only find public methods here? + + octave_value ov_meth = cls.get_method (nm); + + if (get_which_info_from_fcn (nm, ov_meth, file, type)) + return file; + + // FIXME: Should we only find public properties here? + + cdef_property prop = cls.find_property (nm); - file = sys::env::make_absolute (file); + if (prop.ok ()) + { + file = cls.file_name (); + type = "class property"; + + return file; + } } + // Or is PREFIX the name of a package? + + pkg = cdm.find_package (prefix, false, true); + + if (pkg.ok ()) + { + octave_value ov_fcn = pkg.find (nm); + + if (get_which_info_from_fcn (nm, ov_fcn, file, type)) + return file; + } + + // File query. + + load_path& lp = m_interpreter.get_load_path (); + + // For compatibility: "file." queries "file". + if (name.size () > 1 && name[name.size () - 1] == '.') + file = lp.find_file (name.substr (0, name.size () - 1)); + else + file = lp.find_file (name); + + file = sys::env::make_absolute (file); + + if (! file.empty ()) + type = "file"; + return file; } @@ -545,51 +660,168 @@ return retval; } +static bool +get_help_from_fcn (const std::string& fcn_nm, const octave_value& ov_fcn, std::string& help, std::string& what, bool& symbol_found) +{ + symbol_found = false; + + help = ""; + what = ""; + + if (ov_fcn.is_function ()) + { + octave_function *fcn = ov_fcn.function_value (); + + help = fcn->doc_string (fcn_nm); + what = fcn->fcn_file_name (); + + if (what.empty ()) + what = fcn->is_user_function () ? "command-line function" : "built-in function"; + + symbol_found = true; + } + + return symbol_found; +} + +// FIXME: There is a lot of duplication between the following function +// and help_system::which. Some refactoring would probably be useful. + bool -help_system::raw_help_from_symbol_table (const std::string& nm, - std::string& h, std::string& w, - bool& symbol_found) const +help_system::raw_help_from_symbol_table (const std::string& name, std::string& help, std::string& what, bool& symbol_found) const { - std::string meth_nm; - symbol_table& symtab = m_interpreter.get_symbol_table (); - octave_value val = symtab.find_function (nm); + size_t pos = name.find ('.'); + + if (pos == std::string::npos) + { + // Simple name. If not found, continue looking for packages and + // classes. + + octave_value ov_fcn = symtab.find_function (name); + + // FIXME: it seems like there is a lot of potential for confusion + // because is_function can also return true for + // octave_classdef_meta objects. - if (! val.is_defined ()) - { - std::size_t pos = nm.rfind ('.'); + if (! ov_fcn.is_classdef_meta () + && get_help_from_fcn (name, ov_fcn, help, what, symbol_found)) + return true; + } + + // If NAME does not contain '.', then it should be a package or a + // class name. + // + // If NAME contains '.' it should match the following pattern: + // + // (package.)*(package|classname).(property|method)* + // + // Start by looking up the full name. It could be either a package or + // a class and we are done. Otherwise, strip the final component and + // lookup that name. If it is a package, look for a function. If it + // is a class, look for a property or method. + + cdef_manager& cdm = m_interpreter.get_cdef_manager (); - if (pos != std::string::npos) + // FIXME: In the following search we may load classes. Is that really + // what we want, or should we just search the loadpath for + // +pkga/+pkgb/classname/file.m, etc. and attempt to extract help text + // without actually installing packages and classes into the fcn_info + // table? + + // Is NAME a class? + + cdef_class cls = cdm.find_class (name, false, true); + + if (cls.ok ()) + { + // Is the class documented? + + help = cls.doc_string (); + + if (! help.empty ()) { - meth_nm = nm.substr (pos+1); + what = "class"; + + symbol_found = true; + return true; + } + + // Look for constructor. - val = symtab.find_function (nm.substr (0, pos)); + pos = name.rfind ('.'); + std::string nm = name.substr (pos+1); + + octave_value ov_meth = cls.get_method (nm); + + if (get_help_from_fcn (nm, ov_meth, help, what, symbol_found)) + { + what = "constructor"; + + return true; } } - if (val.is_defined ()) + cdef_package pkg = cdm.find_package (name, false, true); + + if (pkg.ok ()) { - octave_function *fcn = val.function_value (); + help = "package " + name; + what = "package"; + + symbol_found = true; + return true; + } + + // Strip final component (might be a property or method name). + + pos = name.rfind ('.'); + std::string prefix = name.substr (0, pos); + std::string nm = name.substr (pos+1); + + // Is PREFIX the name of a class? + + cls = cdm.find_class (prefix, false, true); - if (fcn) + if (cls.ok ()) + { + // FIXME: Should we only find public methods here? + + octave_value ov_meth = cls.get_method (nm); + + if (get_help_from_fcn (nm, ov_meth, help, what, symbol_found)) + return true; + + // FIXME: Should we only find public properties here? + + cdef_property prop = cls.find_property (nm); + + if (prop.ok ()) { - // FCN may actually be a classdef_meta object. + // FIXME: is it supposed to be possible to document + // properties? + + help = prop.doc_string (); + what = "class property"; symbol_found = true; - - h = fcn->doc_string (meth_nm); - - w = fcn->fcn_file_name (); - - if (w.empty ()) - w = fcn->is_user_function () ? "command-line function" - : "built-in function"; - return true; } } + // Or is PREFIX the name of a package? + + pkg = cdm.find_package (prefix, false, true); + + if (pkg.ok ()) + { + octave_value ov_fcn = pkg.find (nm); + + if (get_help_from_fcn (nm, ov_fcn, help, what, symbol_found)) + return true; + } + return false; }
--- a/libinterp/corefcn/help.h Wed Jan 31 00:26:04 2024 -0500 +++ b/libinterp/corefcn/help.h Thu Feb 01 00:34:24 2024 -0500 @@ -187,6 +187,8 @@ return old_val; } + bool get_which_info_from_fcn (const std::string& name, const octave_value& ov_fcn, std::string& file, std::string& type) const; + string_vector local_functions () const; bool raw_help_from_symbol_table (const std::string& nm,
--- a/libinterp/octave-value/cdef-class.cc Wed Jan 31 00:26:04 2024 -0500 +++ b/libinterp/octave-value/cdef-class.cc Thu Feb 01 00:34:24 2024 -0500 @@ -1128,6 +1128,8 @@ cdef_property prop = cdm.make_property (retval, prop_name); + prop.doc_string (prop_p->doc_string ()); + #if DEBUG_TRACE std::cerr << "property: " << prop_p->ident ()->name () << std::endl;
--- a/libinterp/octave-value/cdef-class.h Wed Jan 31 00:26:04 2024 -0500 +++ b/libinterp/octave-value/cdef-class.h Thu Feb 01 00:34:24 2024 -0500 @@ -158,10 +158,6 @@ bool is_meta_class () const { return m_meta; } - void doc_string (const std::string& txt) { m_doc_string = txt; } - - std::string doc_string () const { return m_doc_string; } - void file_name (const std::string& nm) { m_file_name = nm; } std::string file_name () const { return m_file_name; } @@ -191,8 +187,6 @@ std::string m_directory; - std::string m_doc_string; - std::string m_file_name; // The methods defined by this class. @@ -402,10 +396,6 @@ bool is_meta_class () const { return get_rep ()->is_meta_class (); } - void doc_string (const std::string& txt) { get_rep ()->doc_string (txt); } - - std::string doc_string () const { return get_rep ()->doc_string (); } - void file_name (const std::string& nm) { get_rep ()->file_name (nm); } std::string file_name () const { return get_rep ()->file_name (); }
--- a/libinterp/octave-value/cdef-object.h Wed Jan 31 00:26:04 2024 -0500 +++ b/libinterp/octave-value/cdef-object.h Thu Feb 01 00:34:24 2024 -0500 @@ -619,6 +619,10 @@ virtual bool is_package () const { return false; } + void doc_string (const std::string& txt) { m_doc_string = txt; } + + std::string doc_string () const { return m_doc_string; } + virtual octave_value_list meta_subsref (const std::string& /* type */, const std::list<octave_value_list>& /* idx */, @@ -636,6 +640,8 @@ protected: + std::string m_doc_string; + // Restricted copying! cdef_meta_object_rep (const cdef_meta_object_rep& obj) : handle_cdef_object (obj) @@ -667,6 +673,10 @@ bool is_package () const { return get_rep ()->is_package (); } + void doc_string (const std::string& txt) { get_rep ()->doc_string (txt); } + + std::string doc_string () const { return get_rep ()->doc_string (); } + octave_value_list meta_subsref (const std::string& type, const std::list<octave_value_list>& idx, int nargout)
--- a/libinterp/parse-tree/lex.h Wed Jan 31 00:26:04 2024 -0500 +++ b/libinterp/parse-tree/lex.h Thu Feb 01 00:34:24 2024 -0500 @@ -302,6 +302,7 @@ m_string_text (), m_current_input_line (), m_comment_text (), + m_classdef_help_text (), m_help_text (), m_function_text (), m_fcn_file_name (), @@ -478,6 +479,9 @@ // The current comment text. std::string m_comment_text; + // The current classdef help text. + std::string m_classdef_help_text; + // The current help text. std::string m_help_text;
--- a/libinterp/parse-tree/lex.ll Wed Jan 31 00:26:04 2024 -0500 +++ b/libinterp/parse-tree/lex.ll Thu Feb 01 00:34:24 2024 -0500 @@ -2205,6 +2205,7 @@ m_string_text = ""; m_current_input_line = ""; m_comment_text = ""; + m_classdef_help_text = ""; m_help_text = ""; m_function_text = ""; m_fcn_file_name = "";
--- a/libinterp/parse-tree/oct-parse.yy Wed Jan 31 00:26:04 2024 -0500 +++ b/libinterp/parse-tree/oct-parse.yy Thu Feb 01 00:34:24 2024 -0500 @@ -1652,19 +1652,17 @@ } ; -function : function_beg stash_comment fcn_name - opt_param_list opt_sep function_body function_end +function : function_beg stash_comment fcn_name opt_param_list opt_sep stash_comment function_body function_end { OCTAVE_YYUSE ($5); - $$ = parser.make_function ($1, nullptr, $3, $4, $6, $7, $2); - } - | function_beg stash_comment return_list '=' fcn_name - opt_param_list opt_sep function_body function_end + $$ = parser.make_function ($1, nullptr, $3, $4, $7, $8, $2, $6); + } + | function_beg stash_comment return_list '=' fcn_name opt_param_list opt_sep stash_comment function_body function_end { OCTAVE_YYUSE ($4, $7); - $$ = parser.make_function ($1, $3, $5, $6, $8, $9, $2); + $$ = parser.make_function ($1, $3, $5, $6, $9, $10, $2, $8); } ; @@ -1834,6 +1832,9 @@ YYABORT; } + lexer.m_classdef_help_text = lexer.m_help_text; + lexer.m_help_text = ""; + // Create invalid parent scope. lexer.m_symtab_context.push (octave::symbol_scope::anonymous ()); lexer.m_parsing_classdef = true; @@ -1844,19 +1845,25 @@ } ; -classdef : classdef_beg stash_comment attr_list identifier opt_sep superclass_list class_body END - { - OCTAVE_YYUSE ($5); - - octave::comment_list *lc = $2; +classdef : classdef_beg attr_list identifier opt_sep stash_comment superclass_list stash_comment class_body END + { + OCTAVE_YYUSE ($4); + + octave::comment_list *lc = $5; octave::comment_list *tc = lexer.get_comment (); + if (lexer.m_classdef_help_text.empty () && $7 && ! $7->empty ()) + { + const octave::comment_elt& elt = $7->front (); + lexer.m_classdef_help_text = elt.text (); + } + lexer.m_parsing_classdef = false; - if (! ($$ = parser.make_classdef ($1, $3, $4, $6, $7, $8, + if (! ($$ = parser.make_classdef ($1, $2, $3, $6, $8, $9, lc, tc))) { - // make_classdef deleted $3, $4, $6, $7, LC, and + // make_classdef deleted $2, $3, $6, $8, LC, and // TC. YYABORT; } @@ -2010,6 +2017,8 @@ properties_beg : PROPERTIES { + lexer.m_help_text = ""; + lexer.m_classdef_element_names_are_keywords = false; $$ = $1; } @@ -2086,6 +2095,8 @@ methods_beg : METHODS { + lexer.m_help_text = ""; + lexer.m_classdef_element_names_are_keywords = false; $$ = $1; } @@ -2170,6 +2181,8 @@ events_beg : EVENTS { + lexer.m_help_text = ""; + lexer.m_classdef_element_names_are_keywords = false; $$ = $1; } @@ -2222,6 +2235,8 @@ enumeration_beg : ENUMERATION { + lexer.m_help_text = ""; + lexer.m_classdef_element_names_are_keywords = false; $$ = $1; } @@ -3948,8 +3963,19 @@ tree_parameter_list *param_list, tree_statement_list *body, tree_statement *end_fcn_stmt, - comment_list *lc) - { + comment_list *lc, comment_list *bc) + { + // If we are looking at a classdef method and there is a comment + // prior to the function keyword and another after, choose the one + // inside the function definition for compatibility with Matlab. + + if (m_lexer.m_parsing_classdef && ! m_lexer.m_help_text.empty () && bc && ! bc->empty ()) + { + const octave::comment_elt& elt = bc->front (); + m_lexer.m_help_text = elt.text (); + } + + int l = fcn_tok->line (); int c = fcn_tok->column (); @@ -4378,8 +4404,11 @@ body = new tree_classdef_body (); retval = new tree_classdef (m_lexer.m_symtab_context.curr_scope (), + m_lexer.m_classdef_help_text, a, id, sc, body, lc, tc, m_curr_package_name, full_name, l, c); + + m_lexer.m_classdef_help_text = ""; } else {
--- a/libinterp/parse-tree/parse.h Wed Jan 31 00:26:04 2024 -0500 +++ b/libinterp/parse-tree/parse.h Thu Feb 01 00:34:24 2024 -0500 @@ -389,7 +389,7 @@ make_function (token *fcn_tok, tree_parameter_list *ret_list, tree_identifier *id, tree_parameter_list *param_list, tree_statement_list *body, tree_statement *end_fcn_stmt, - comment_list *lc); + comment_list *lc, comment_list *bc); // Begin defining a function. OCTINTERP_API octave_user_function *
--- a/libinterp/parse-tree/pt-classdef.h Wed Jan 31 00:26:04 2024 -0500 +++ b/libinterp/parse-tree/pt-classdef.h Thu Feb 01 00:34:24 2024 -0500 @@ -679,13 +679,14 @@ { public: - tree_classdef (const symbol_scope& scope, + tree_classdef (const symbol_scope& scope, const std::string& help_text, tree_classdef_attribute_list *a, tree_identifier *i, tree_classdef_superclass_list *sc, tree_classdef_body *b, comment_list *lc, comment_list *tc, const std::string& pn = "", const std::string& fn = "", int l = -1, int c = -1) - : tree_command (l, c), m_scope (scope), m_attr_list (a), m_id (i), + : tree_command (l, c), m_scope (scope), m_help_text (help_text), + m_attr_list (a), m_id (i), m_supclass_list (sc), m_element_list (b), m_lead_comm (lc), m_trail_comm (tc), m_pack_name (pn), m_file_name (fn) { } @@ -726,7 +727,7 @@ std::string doc_string () const { - return m_element_list ? m_element_list->doc_string () : ""; + return m_help_text; } void accept (tree_walker& tw) @@ -742,6 +743,8 @@ symbol_scope m_scope; + std::string m_help_text; + tree_classdef_attribute_list *m_attr_list; tree_identifier *m_id;
--- a/libinterp/parse-tree/pt-eval.cc Wed Jan 31 00:26:04 2024 -0500 +++ b/libinterp/parse-tree/pt-eval.cc Thu Feb 01 00:34:24 2024 -0500 @@ -3688,6 +3688,11 @@ m_call_stack.set_location (stmt->line (), stmt->column ()); retval = expr->evaluate_n (*this, nargout); + + // Don't allow a comma-separated list to escape (see bug #64783). + + if (nargout <= 1 && retval.length () == 1 && retval(0).is_cs_list ()) + retval = retval(0).list_value (); } } else