# HG changeset patch # User John W. Eaton # Date 1584991789 14400 # Node ID 7a8c69c4eb55f744bec893d25cbed233884c8e69 # Parent 9a3deb17b4eae7c5920fa61914353e43611cef63 convert obsolete octave_fcn_inline object to @inline class Use a legacy @class object to provide inline function objects instead of using a built-in class derived from the octave_function_handle class. * scripts/legacy/@inline: New directory containing the following files: argnames.m, char.m, feval.m, formula.m, inline.m, subsref.m, vectorize.m. * scripts/legacy/@inline/module.mk: New file. * scripts/legacy/module.mk, scripts/module.mk: Update. * test/inline-fcn.tst: New tests. * test/module.mk: Update. * ov-fcn-inline.h, ov-fcn-inline.cc: Delete. Remove all uses. * libinterp/octave-value/module.mk: Update. * external.txi, func.txi, octave.texi, plot.txi, quad.txi: Eliminate discusion of inline function objects. * fplot.m, __ezplot__.m: Use isa to identify inline objects instead of instead of checking typeinfo. * ov-fcn-handle.cc, ov-typeinfo.cc: Fix tests. * ov-base.h, ov-base.cc (octave_base_value::fcn_inline_value): Delete. * ov.h, ov.cc (octave_value::fcn_inline_value): Delete. (octave_value::xfcn_inline_value): Delete value extractor. * ov-class.cc, ov-class.h (octave_inline, octave_inline_fcn): New classes that allow us to preserve the is_inline_function and function_value methods that were previously available in the octave_fcn_inline class. (F__inline_ctor__): New function for final construction of inline objects. * ls-hdf5.cc, ls-mat5.cc, ls-oct-binary.cc, ls-oct-text.cc: Handle loading of inline function objects from old files as special cases. New inline function objects will be saved and loaded as ordinary @class objects. diff -r 9a3deb17b4ea -r 7a8c69c4eb55 doc/interpreter/external.txi --- a/doc/interpreter/external.txi Sat Apr 25 13:17:11 2020 -0400 +++ b/doc/interpreter/external.txi Mon Mar 23 15:29:49 2020 -0400 @@ -917,8 +917,6 @@ @item Anonymous Function Handle -@item Inline Function - @item String @end enumerate @@ -938,8 +936,6 @@ @result{} 0.84147 funcdemo (@@(x) sin (x), 1) @result{} 0.84147 -funcdemo (inline ("sin (x)"), 1) -@result{} 0.84147 funcdemo ("sin", 1) @result{} 0.84147 funcdemo (@@atan2, 1, 1) @@ -1698,8 +1694,7 @@ @end group @end example -Note that it is not possible to use function handles or inline functions -within a mex-file. +Note that it is not possible to use function handles within a mex-file. @c @node Application Programming Interface for Mex-Files @c @subsection Application Programming Interface for Mex-Files diff -r 9a3deb17b4ea -r 7a8c69c4eb55 doc/interpreter/func.txi --- a/doc/interpreter/func.txi Sat Apr 25 13:17:11 2020 -0400 +++ b/doc/interpreter/func.txi Mon Mar 23 15:29:49 2020 -0400 @@ -40,7 +40,7 @@ * Validating Arguments:: * Function Files:: * Script Files:: -* Function Handles Anonymous Functions Inline Functions:: +* Function Handles and Anonymous Functions:: * Commands:: * Organization of Functions:: @end menu @@ -1733,11 +1733,10 @@ @end group @end example -@node Function Handles Anonymous Functions Inline Functions -@section Function Handles, Anonymous Functions, Inline Functions +@node Function Handles and Anonymous Functions +@section Function Handles and Anonymous Functions @cindex handle, function handles @cindex anonymous functions -@cindex inline, inline functions It can be very convenient store a function in a variable so that it can be passed to a different function. For example, a function that @@ -1747,7 +1746,6 @@ @menu * Function Handles:: * Anonymous Functions:: -* Inline Functions:: @end menu @node Function Handles @@ -1887,34 +1885,6 @@ @xref{Operator Overloading}, for a list of operators which also have a functional form. -@node Inline Functions -@subsection Inline Functions - -An inline function is created from a string containing the function -body using the @code{inline} function. The following code defines the -function @math{f(x) = x^2 + 2}. - -@example -f = inline ("x^2 + 2"); -@end example - -@noindent -After this it is possible to evaluate @math{f} at any @math{x} by -writing @code{f(x)}. - -@strong{Caution}: @sc{matlab} has begun the process of deprecating inline -functions. At some point in the future support will be dropped and eventually -Octave will follow @sc{matlab} and also remove inline functions. Use anonymous -functions in all new code. - -@DOCSTRING(inline) - -@DOCSTRING(argnames) - -@DOCSTRING(formula) - -@DOCSTRING(symvar) - @node Commands @section Commands diff -r 9a3deb17b4ea -r 7a8c69c4eb55 doc/interpreter/octave.texi --- a/doc/interpreter/octave.texi Sat Apr 25 13:17:11 2020 -0400 +++ b/doc/interpreter/octave.texi Mon Mar 23 15:29:49 2020 -0400 @@ -411,7 +411,7 @@ * Default Arguments:: * Function Files:: * Script Files:: -* Function Handles Anonymous Functions Inline Functions:: +* Function Handles and Anonymous Functions:: * Commands:: * Organization of Functions:: @@ -446,11 +446,10 @@ * HTML Markup:: * LaTeX Markup:: -Function Handles Anonymous Functions Inline Functions +Function Handles and Anonymous Functions * Function Handles:: * Anonymous Functions:: -* Inline Functions:: Errors and Warnings diff -r 9a3deb17b4ea -r 7a8c69c4eb55 doc/interpreter/plot.txi --- a/doc/interpreter/plot.txi Sat Apr 25 13:17:11 2020 -0400 +++ b/doc/interpreter/plot.txi Mon Mar 23 15:29:49 2020 -0400 @@ -330,12 +330,11 @@ @subsubsection Two-dimensional Function Plotting @cindex plotting, two-dimensional functions -Octave can plot a function from a function handle, inline function, or -string defining the function without the user needing to explicitly -create the data to be plotted. The function @code{fplot} also generates -two-dimensional plots with linear axes using a function name and limits -for the range of the x-coordinate instead of the x and y data. For -example, +Octave can plot a function from a function handle or string defining the +function without the user needing to explicitly create the data to be +plotted. The function @code{fplot} also generates two-dimensional plots +with linear axes using a function name and limits for the range of the +x-coordinate instead of the x and y data. For example, @example @group @@ -1871,7 +1870,7 @@ as an anonymous function, or as a string representing an Octave command. The latter syntax is not recommended since syntax errors will only occur when the string is evaluated. -@xref{Function Handles Anonymous Functions Inline Functions, , Function Handles section}. +@xref{Function Handles and Anonymous Functions, , Function Handles section}. This can then be associated with an object either at the object's creation, or later with the @code{set} function. For example, diff -r 9a3deb17b4ea -r 7a8c69c4eb55 doc/interpreter/quad.txi --- a/doc/interpreter/quad.txi Sat Apr 25 13:17:11 2020 -0400 +++ b/doc/interpreter/quad.txi Mon Mar 23 15:29:49 2020 -0400 @@ -140,18 +140,12 @@ is reasonably accurate (to see why, examine what happens to the result if you move the lower bound to 0.1, then 0.01, then 0.001, etc.). -The function @qcode{"f"} can be the string name of a function, a function -handle, or an inline function. These options make it quite easy to do -integration without having to fully define a function in an m-file. For -example: +The function @qcode{"f"} can be the string name of a function or a +function handle. These options make it quite easy to do integration +without having to fully define a function in an m-file. For example: @example @group -# Verify integral (x^3) = x^4/4 -f = inline ("x.^3"); -quadgk (f, 0, 1) - @result{} 0.25000 - # Verify gamma function = (n-1)! for n = 4 f = @@(x) x.^3 .* exp (-x); quadcc (f, 0, Inf) diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/corefcn/cellfun.cc --- a/libinterp/corefcn/cellfun.cc Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/corefcn/cellfun.cc Mon Mar 23 15:29:49 2020 -0400 @@ -452,7 +452,7 @@ // more specific function class, so this can result in fewer polymorphic // function calls as the function gets called for each value of the array. { - if (func.is_function_handle ()) + if (func.is_function_handle () || func.is_inline_function ()) { // We can't check for overloads now. Is there something else we // should be doing instead? @@ -1156,7 +1156,7 @@ if (! symbol_table_lookup) { - if (func.is_function_handle ()) + if (func.is_function_handle () || func.class_name () == "inline") { // We can't check for overloads now. Is there something // else we should be doing instead? diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/corefcn/interpreter-private.cc --- a/libinterp/corefcn/interpreter-private.cc Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/corefcn/interpreter-private.cc Mon Mar 23 15:29:49 2020 -0400 @@ -44,7 +44,7 @@ #include "load-path.h" #include "load-save.h" #include "ov.h" -#include "ov-fcn-inline.h" +#include "ovl.h" #include "pager.h" #include "symtab.h" @@ -208,6 +208,9 @@ return get_function_handle (interp, arg, parameter_names); } + // May return a function handle object, inline function object, or + // function object. + octave_value get_function_handle (interpreter& interp, const octave_value& arg, const std::list& parameter_names) @@ -228,12 +231,19 @@ if (fcn.is_defined ()) return fcn; - fcn = octave_value (new octave_fcn_inline (fstr, parameter_names)); - // Possibly warn here that passing the function body in a // character string is discouraged. - return fcn; + octave_value_list args (parameter_names.size () + 1); + octave_idx_type i = 0; + args(i++) = fstr; + for (const auto& pname : parameter_names) + args(i++) = pname; + + octave_value_list tmp = interp.feval ("inline", args, 1); + + if (tmp.length () > 0) + return tmp(0); } return octave_value (); diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/corefcn/interpreter.cc --- a/libinterp/corefcn/interpreter.cc Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/corefcn/interpreter.cc Mon Mar 23 15:29:49 2020 -0400 @@ -1526,7 +1526,7 @@ { return feval (val.function_value (), args, nargout); } - else if (val.is_function_handle ()) + else if (val.is_function_handle () || val.is_inline_function ()) { // This covers function handles, inline functions, and anonymous // functions. diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/corefcn/ls-hdf5.cc --- a/libinterp/corefcn/ls-hdf5.cc Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/corefcn/ls-hdf5.cc Mon Mar 23 15:29:49 2020 -0400 @@ -54,6 +54,7 @@ #include "defun.h" #include "error.h" #include "errwarn.h" +#include "interpreter.h" #include "interpreter-private.h" #include "load-save.h" #include "oct-hdf5.h" @@ -433,6 +434,212 @@ return type_id; } +static herr_t +load_inline_fcn (hid_t loc_id, const char *name, octave_value& retval) +{ +#if defined (HAVE_HDF5) + + hid_t group_hid, data_hid, space_hid, type_hid, type_class_hid, st_id; + hsize_t rank; + int slen; + +#if defined (HAVE_HDF5_18) + group_hid = H5Gopen (loc_id, name, octave_H5P_DEFAULT); +#else + group_hid = H5Gopen (loc_id, name); +#endif + if (group_hid < 0) return -1; + +#if defined (HAVE_HDF5_18) + data_hid = H5Dopen (group_hid, "args", octave_H5P_DEFAULT); +#else + data_hid = H5Dopen (group_hid, "args"); +#endif + space_hid = H5Dget_space (data_hid); + rank = H5Sget_simple_extent_ndims (space_hid); + + if (rank != 2) + { + H5Dclose (data_hid); + H5Sclose (space_hid); + H5Gclose (group_hid); + return -1; + } + + OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank); + OCTAVE_LOCAL_BUFFER (hsize_t, maxdims, rank); + + H5Sget_simple_extent_dims (space_hid, hdims, maxdims); + + octave_value_list args (hdims[1]+1); + + OCTAVE_LOCAL_BUFFER (char, s1, hdims[0] * hdims[1]); + + if (H5Dread (data_hid, H5T_NATIVE_UCHAR, octave_H5S_ALL, octave_H5S_ALL, + octave_H5P_DEFAULT, s1) < 0) + { + H5Dclose (data_hid); + H5Sclose (space_hid); + H5Gclose (group_hid); + return -1; + } + + H5Dclose (data_hid); + H5Sclose (space_hid); + + for (size_t i = 0; i < hdims[1]; i++) + args(i+1) = std::string (s1 + i*hdims[0]); + +#if defined (HAVE_HDF5_18) + data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT); +#else + data_hid = H5Dopen (group_hid, "nm"); +#endif + + if (data_hid < 0) + { + H5Gclose (group_hid); + return -1; + } + + type_hid = H5Dget_type (data_hid); + type_class_hid = H5Tget_class (type_hid); + + if (type_class_hid != H5T_STRING) + { + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return -1; + } + + space_hid = H5Dget_space (data_hid); + rank = H5Sget_simple_extent_ndims (space_hid); + + if (rank != 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return -1; + } + + slen = H5Tget_size (type_hid); + if (slen < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return -1; + } + + OCTAVE_LOCAL_BUFFER (char, nm_tmp, slen); + + // create datatype for (null-terminated) string to read into: + st_id = H5Tcopy (H5T_C_S1); + H5Tset_size (st_id, slen); + + if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL, + octave_H5P_DEFAULT, nm_tmp) < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Gclose (group_hid); + return -1; + } + H5Tclose (st_id); + H5Dclose (data_hid); + // NAME is obsolete and unused. + // std::string name (nm_tmp); + +#if defined (HAVE_HDF5_18) + data_hid = H5Dopen (group_hid, "iftext", octave_H5P_DEFAULT); +#else + data_hid = H5Dopen (group_hid, "iftext"); +#endif + + if (data_hid < 0) + { + H5Gclose (group_hid); + return -1; + } + + type_hid = H5Dget_type (data_hid); + type_class_hid = H5Tget_class (type_hid); + + if (type_class_hid != H5T_STRING) + { + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return -1; + } + + space_hid = H5Dget_space (data_hid); + rank = H5Sget_simple_extent_ndims (space_hid); + + if (rank != 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return -1; + } + + slen = H5Tget_size (type_hid); + if (slen < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return -1; + } + + OCTAVE_LOCAL_BUFFER (char, iftext_tmp, slen); + + // create datatype for (null-terminated) string to read into: + st_id = H5Tcopy (H5T_C_S1); + H5Tset_size (st_id, slen); + + if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL, + octave_H5P_DEFAULT, iftext_tmp) < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Gclose (group_hid); + return -1; + } + H5Tclose (st_id); + H5Dclose (data_hid); + + args(0) = std::string (iftext_tmp); + + octave::interpreter& interp + = octave::__get_interpreter__ ("load_inline_fcn"); + + octave_value_list tmp = interp.feval ("inline", args, 1); + + if (tmp.length () > 0) + { + retval = tmp(0); + return 1; + } + +#else + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + octave_unused_parameter (retval); + + warn_load ("hdf5"); +#endif + + return -1; +} + // This function is designed to be passed to H5Giterate, which calls it // on each data item in an HDF5 file. For the item whose name is NAME in // the group GROUP_ID, this function sets dv->tc to an Octave representation @@ -534,9 +741,16 @@ H5Tclose (st_id); H5Dclose (data_id); - d->tc = type_info.lookup_type (typ); + if (std::string (typ) == "inline function") + { + retval = load_inline_fcn (subgroup_id, name, d->tc); + } + else + { + d->tc = type_info.lookup_type (typ); - retval = (d->tc.load_hdf5 (subgroup_id, "value") ? 1 : -1); + retval = (d->tc.load_hdf5 (subgroup_id, "value") ? 1 : -1); + } // check for OCTAVE_GLOBAL attribute: d->global = hdf5_check_attr (subgroup_id, "OCTAVE_GLOBAL"); @@ -545,6 +759,10 @@ } else { + // It seems that this block only applies to an old list type + // and that we shouldn't need to handle the old inline + // function type here. + // an HDF5 group is treated as an octave structure by // default (since that preserves name information), and an // octave list otherwise. @@ -565,6 +783,10 @@ } else if (info.type == H5G_DATASET && ident_valid) { + // It seems that this block only applies to an old version of + // Octave HDF5 files and that it is probably not important to + // handle the old inline function type here. + // For backwards compatibility. #if defined (HAVE_HDF5_18) data_id = H5Dopen (group_id, name, octave_H5P_DEFAULT); diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/corefcn/ls-mat5.cc --- a/libinterp/corefcn/ls-mat5.cc Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/corefcn/ls-mat5.cc Mon Mar 23 15:29:49 2020 -0400 @@ -65,7 +65,6 @@ #include "oct-map.h" #include "ov-cell.h" #include "ov-class.h" -#include "ov-fcn-inline.h" #include "ov.h" #include "ovl.h" #include "pager.h" @@ -1174,60 +1173,50 @@ if (isclass) { - if (classname == "inline") + octave::cdef_manager& cdm = interp.get_cdef_manager (); + + if (cdm.find_class (classname, false, true).ok ()) { - // inline is not an object in Octave but rather an - // overload of a function handle. Special case. - tc = new octave_fcn_inline (m.contents ("expr")(0).string_value (), - m.contents ("args")(0).string_value ()); + tc = m; + warning_with_id ("Octave:load:classdef-to-struct", + "load: classdef element has been converted to a struct"); } else { - octave::cdef_manager& cdm = interp.get_cdef_manager (); - - if (cdm.find_class (classname, false, true).ok ()) + octave_class *cls + = new octave_class (m, classname, + std::list ()); + + if (cls->reconstruct_exemplar ()) { - tc = m; - warning_with_id ("Octave:load:classdef-to-struct", - "load: classdef element has been converted to a struct"); + + if (! cls->reconstruct_parents ()) + warning_with_id ("Octave:load:classdef-object-inheritance", + "load: unable to reconstruct object inheritance"); + + tc = cls; + + octave::load_path& lp = interp.get_load_path (); + + if (lp.find_method (classname, "loadobj") != "") + { + try + { + octave_value_list tmp = octave::feval ("loadobj", tc, 1); + + tc = tmp(0); + } + catch (const octave::execution_exception&) + { + goto data_read_error; + } + } } else { - octave_class *cls - = new octave_class (m, classname, - std::list ()); - - if (cls->reconstruct_exemplar ()) - { - - if (! cls->reconstruct_parents ()) - warning_with_id ("Octave:load:classdef-object-inheritance", - "load: unable to reconstruct object inheritance"); - - tc = cls; - - octave::load_path& lp = interp.get_load_path (); - - if (lp.find_method (classname, "loadobj") != "") - { - try - { - octave_value_list tmp = octave::feval ("loadobj", tc, 1); - - tc = tmp(0); - } - catch (const octave::execution_exception&) - { - goto data_read_error; - } - } - } - else - { - tc = m; - warning_with_id ("Octave:load:classdef-to-struct", - "load: element has been converted to a structure"); - } + tc = m; + warning_with_id ("Octave:load:classdef-to-struct", + "load: element has been converted to a structure"); } } } diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/corefcn/ls-oct-binary.cc --- a/libinterp/corefcn/ls-oct-binary.cc Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/corefcn/ls-oct-binary.cc Mon Mar 23 15:29:49 2020 -0400 @@ -44,6 +44,7 @@ #include "defun.h" #include "error.h" #include "errwarn.h" +#include "interpreter.h" #include "interpreter-private.h" #include "load-save.h" #include "ls-oct-binary.h" @@ -56,6 +57,78 @@ #include "variables.h" #include "version.h" +static bool +load_inline_fcn (std::istream& is, bool swap, octave::mach_info::float_format, + octave_value& retval) +{ + int32_t nargs; + if (! is.read (reinterpret_cast (&nargs), 4)) + return false; + if (swap) + swap_bytes<4> (&nargs); + + if (nargs < 1) + return false; + else + { + int32_t tmp; + octave_value_list args (nargs+1); + for (int i = 0; i < nargs; i++) + { + if (! is.read (reinterpret_cast (&tmp), 4)) + return false; + if (swap) + swap_bytes<4> (&tmp); + + OCTAVE_LOCAL_BUFFER (char, ctmp, tmp+1); + is.read (ctmp, tmp); + args(i+1) = std::string (ctmp); + + if (! is) + return false; + } + + if (! is.read (reinterpret_cast (&tmp), 4)) + return false; + if (swap) + swap_bytes<4> (&tmp); + + OCTAVE_LOCAL_BUFFER (char, ctmp1, tmp+1); + is.read (ctmp1, tmp); + // NAME is obsolete and unused. + // std::string name (ctmp1); + + if (! is) + return false; + + if (! is.read (reinterpret_cast (&tmp), 4)) + return false; + if (swap) + swap_bytes<4> (&tmp); + + OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1); + is.read (ctmp2, tmp); + + if (is) + { + args(0) = std::string (ctmp2); + + octave::interpreter& interp + = octave::__get_interpreter__ ("load_inline_fcn"); + + octave_value_list tmp = interp.feval ("inline", args, 1); + + if (tmp.length () > 0) + { + retval = tmp(0); + return true; + } + } + } + + return false; +} + // Extract one value (scalar, matrix, string, etc.) from stream IS and // place it in TC, returning the name of the variable. If the value // is tagged as global in the file, return TRUE in GLOBAL. If SWAP @@ -238,6 +311,15 @@ error ("load: trouble reading binary file '%s'", filename.c_str ()); s[len] = '\0'; std::string typ = s; + + if (typ == "inline function") + { + // Special case for loading old octave_inline_fcn objects. + if (! load_inline_fcn (is, swap, fmt, tc)) + error ("load: trouble reading binary file '%s'", filename.c_str ()); + return retval; + } + tc = type_info.lookup_type (typ); } break; diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/corefcn/ls-oct-text.cc --- a/libinterp/corefcn/ls-oct-text.cc Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/corefcn/ls-oct-text.cc Mon Mar 23 15:29:49 2020 -0400 @@ -52,6 +52,7 @@ #include "defun.h" #include "error.h" #include "errwarn.h" +#include "interpreter.h" #include "interpreter-private.h" #include "load-save.h" #include "ls-ascii-helper.h" @@ -235,6 +236,53 @@ #define SUBSTRING_COMPARE_EQ(s, pos, n, t) (s.substr (pos, n) == (t)) +static octave_value +load_inline_fcn (std::istream& is, const std::string& filename) +{ + int nargs; + if (extract_keyword (is, "nargs", nargs, true)) + { + std::string name; + octave_value_list args (nargs+1); + for (int i = 0; i < nargs; i++) + { + std::string tmp; + is >> tmp; + args(i+1) = tmp; + } + is >> name; + if (name == "0") + name = ""; + + skip_preceeding_newline (is); + + std::string buf; + + if (is) + { + + // Get a line of text whitespace characters included, + // leaving newline in the stream. + buf = read_until_newline (is, true); + } + + if (is) + { + args(0) = std::string (buf); + + octave::interpreter& interp + = octave::__get_interpreter__ ("load_inline_fcn"); + + octave_value_list tmp = interp.feval ("inline", args, 1); + + if (tmp.length () > 0) + return tmp(0); + } + } + + error ("load: trouble reading ascii file '%s'", filename.c_str ()); +} + std::string read_text_data (std::istream& is, const std::string& filename, bool& global, octave_value& tc, octave_idx_type count, @@ -280,6 +328,12 @@ // Special case for backward compatibility. A small bit of cruft if (SUBSTRING_COMPARE_EQ (typ, 0, 12, "string array")) tc = charMatrix (); + else if (SUBSTRING_COMPARE_EQ (typ, 0, 15, "inline function")) + { + // Special case for loading old octave_inline_fcn objects. + tc = load_inline_fcn (is, filename); + return name; + } else { octave::type_info& type_info diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/octave-value/module.mk --- a/libinterp/octave-value/module.mk Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/octave-value/module.mk Mon Mar 23 15:29:49 2020 -0400 @@ -43,7 +43,6 @@ %reldir%/ov-cx-mat.h \ %reldir%/ov-dld-fcn.h \ %reldir%/ov-fcn-handle.h \ - %reldir%/ov-fcn-inline.h \ %reldir%/ov-fcn.h \ %reldir%/ov-float.h \ %reldir%/ov-flt-complex.h \ @@ -108,7 +107,6 @@ %reldir%/ov-cx-mat.cc \ %reldir%/ov-dld-fcn.cc \ %reldir%/ov-fcn-handle.cc \ - %reldir%/ov-fcn-inline.cc \ %reldir%/ov-fcn.cc \ %reldir%/ov-float.cc \ %reldir%/ov-flt-complex.cc \ diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/octave-value/ov-base.cc --- a/libinterp/octave-value/ov-base.cc Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/octave-value/ov-base.cc Mon Mar 23 15:29:49 2020 -0400 @@ -914,15 +914,6 @@ return nullptr; } -octave_fcn_inline * -octave_base_value::fcn_inline_value (bool silent) -{ - if (! silent) - err_wrong_type_arg ("octave_base_value::fcn_inline_value()", type_name ()); - - return nullptr; -} - octave_value_list octave_base_value::list_value (void) const { diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/octave-value/ov-base.h --- a/libinterp/octave-value/ov-base.h Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/octave-value/ov-base.h Mon Mar 23 15:29:49 2020 -0400 @@ -66,7 +66,6 @@ class octave_user_script; class octave_user_code; class octave_fcn_handle; -class octave_fcn_inline; class octave_value_list; enum builtin_type_t @@ -631,8 +630,6 @@ virtual octave_fcn_handle * fcn_handle_value (bool silent = false); - virtual octave_fcn_inline * fcn_inline_value (bool silent = false); - virtual octave_value_list list_value (void) const; virtual octave_value convert_to_str (bool pad = false, bool force = false, diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/octave-value/ov-class.cc --- a/libinterp/octave-value/ov-class.cc Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/octave-value/ov-class.cc Mon Mar 23 15:29:49 2020 -0400 @@ -28,6 +28,7 @@ #endif #include +#include #include #include "Array-util.h" @@ -2028,3 +2029,117 @@ return octave_value(); } + +// The following classes allow us to define "inline" function objects as +// legacy @class objects (as they appear to be in Matlab) while +// preserving the is_inline_function and function_value methods that +// were previously available in the octave_fcn_inline class. However, +// inline function objects no longer behave as octave_fcn_handle objects +// so calling is_function_handle for them no longer returns true. I see +// no reasonable way to preserve that behavior. The goal here is to +// allow most code that used the old octave_inline_fcn object to +// continue to work while eliminating the octave_inline_fcn class that +// was derived from the octave_fcn_handle class. Making that change +// appears to be necessary to properly fix function handle behavior and +// improve Matlab compatibility. It's unfortunate if this change causes +// trouble, but I see no better fix. Ultimately, we should replace all +// uses of "inline" function objects with anonymous functions. + +class octave_inline; + +// The following class can be removed once the +// octave_value::function_value method is removed. + +class +octave_inline_fcn : public octave_function +{ +public: + + octave_inline_fcn (octave_inline *obj) : m_inline_obj (obj) { } + + // No copying! + + octave_inline_fcn (const octave_inline_fcn& ob) = delete; + + octave_inline_fcn& operator = (const octave_inline_fcn& ob) = delete; + + ~octave_inline_fcn (void) = default; + + // We don't need to override both forms of the call method. The using + // declaration will avoid warnings about partially-overloaded virtual + // functions. + using octave_function::call; + + octave_value_list + call (octave::tree_evaluator& tw, int nargout = 0, + const octave_value_list& args = octave_value_list ()); + +private: + + octave_inline *m_inline_obj; +}; + +// Once the octave_inline_fcn class is removed, we should also be able +// to eliminate the octave_inline class below and replace the +// octave_value::is_inline_function method with +// +// bool octave_value::is_inline_function (void) const +// { +// return class_name () == "inline"; +// } + +class +octave_inline : public octave_class +{ +public: + + octave_inline (const octave_map& m) + : octave_class (m, "inline"), m_fcn_obj (new octave_inline_fcn (this)) + { } + + octave_inline (const octave_inline&) = default; + + ~octave_inline (void) = default; + + octave_base_value * clone (void) const { return new octave_inline (*this); } + + octave_base_value * empty_clone (void) const + { + return new octave_inline (octave_map (map_keys ())); + } + + bool is_inline_function (void) const { return true; } + + octave_function * function_value (bool) + { + return m_fcn_obj.get (); + } + +private: + + std::shared_ptr m_fcn_obj; +}; + +octave_value_list +octave_inline_fcn::call (octave::tree_evaluator& tw, int nargout, + const octave_value_list& args) +{ + octave::interpreter& interp = tw.get_interpreter (); + + return interp.feval (octave_value (m_inline_obj, true), args, nargout); +} + + +DEFUN (__inline_ctor__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {} __inline_ctor__ (@var{prop_struct}) +Internal function. + +Implements final construction for inline objects. +@seealso{inline} +@end deftypefn */) +{ + // Input validation has already been done in input.m. + + return octave_value (new octave_inline (args(0).map_value ())); +} diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/octave-value/ov-class.h --- a/libinterp/octave-value/ov-class.h Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/octave-value/ov-class.h Mon Mar 23 15:29:49 2020 -0400 @@ -59,6 +59,11 @@ parent_list (), obsolete_copies (0) { } + octave_class (const octave_map& m, const std::string& id) + : octave_base_value (), map (m), c_name (id), + parent_list (), obsolete_copies (0) + { } + octave_class (const octave_map& m, const std::string& id, const std::list& plist) : octave_base_value (), map (m), c_name (id), diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/octave-value/ov-fcn-handle.cc --- a/libinterp/octave-value/ov-fcn-handle.cc Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/octave-value/ov-fcn-handle.cc Mon Mar 23 15:29:49 2020 -0400 @@ -2052,12 +2052,10 @@ } /* -%!shared fh, finline +%!shared fh %! fh = @(x) x; -%! finline = inline ("x"); %!assert (is_function_handle (fh)) -%!assert (is_function_handle (finline)) %!assert (! is_function_handle ({fh})) %!assert (! is_function_handle (1)) diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/octave-value/ov-fcn-inline.cc --- a/libinterp/octave-value/ov-fcn-inline.cc Sat Apr 25 13:17:11 2020 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1003 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2004-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or . -// -// This file is part of Octave. -// -// Octave 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 3 of the License, or -// (at your option) any later version. -// -// Octave 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 Octave; see the file COPYING. If not, see -// . -// -// In addition to the terms of the GPL, you are permitted to link -// this program with any Open Source program, as defined by the -// Open Source Initiative (www.opensource.org) -// -//////////////////////////////////////////////////////////////////////// - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include -#include -#include -#include - -#include "oct-locbuf.h" - -#include "defun.h" -#include "error.h" -#include "errwarn.h" -#include "interpreter-private.h" -#include "interpreter.h" -#include "oct-hdf5.h" -#include "oct-map.h" -#include "ov-base.h" -#include "ov-fcn-inline.h" -#include "ov-usr-fcn.h" -#include "parse.h" -#include "pr-output.h" -#include "variables.h" - -#include "byte-swap.h" -#include "ls-ascii-helper.h" -#include "ls-oct-text.h" -#include "ls-hdf5.h" -#include "ls-utils.h" - - -DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_fcn_inline, - "inline function", - "function_handle"); - -octave_fcn_inline::octave_fcn_inline (const std::string& f, - const string_vector& a, - const std::string& n) - : octave_fcn_handle (n), m_text (f), m_args (a) -{ - // Form a string representing the function. - - std::ostringstream buf; - - buf << "@("; - - for (int i = 0; i < m_args.numel (); i++) - { - if (i > 0) - buf << ", "; - - buf << m_args(i); - } - - buf << ") " << m_text; - - octave::interpreter& interp - = octave::__get_interpreter__ ("octave_fcn_inline::octave_fcn_inline"); - - int parse_status; - octave_value anon_fcn_handle - = interp.eval_string (buf.str (), true, parse_status); - - if (parse_status == 0) - { - octave_fcn_handle *fh = anon_fcn_handle.fcn_handle_value (); - - if (fh) - { - m_fcn = fh->fcn_val (); - - octave_user_function *uf = m_fcn.user_function_value (); - - if (uf) - { - octave::tree_evaluator& tw = interp.get_evaluator (); - - octave_function *curr_fcn = tw.current_function (); - - if (curr_fcn) - { - octave::symbol_scope parent_scope - = curr_fcn->parent_fcn_scope (); - - if (! parent_scope) - parent_scope = curr_fcn->scope (); - - uf->stash_parent_fcn_scope (parent_scope); - } - } - } - } - - if (m_fcn.is_undefined ()) - error ("inline: unable to define function"); -} - -// This function is supplied to allow a Matlab style class structure -// to be returned.. -octave_map -octave_fcn_inline::map_value (void) const -{ - octave_scalar_map m; - - m.assign ("version", 1.0); - m.assign ("isEmpty", 0.0); - m.assign ("expr", fcn_text ()); - - string_vector args = fcn_arg_names (); - - m.assign ("numArgs", args.numel ()); - m.assign ("args", args); - - std::ostringstream buf; - - for (int i = 0; i < args.numel (); i++) - buf << args(i) << " = INLINE_INPUTS_{" << i + 1 << "}; "; - - m.assign ("inputExpr", buf.str ()); - - return m; -} - -bool -octave_fcn_inline::save_ascii (std::ostream& os) -{ - os << "# nargs: " << m_args.numel () << "\n"; - for (int i = 0; i < m_args.numel (); i++) - os << m_args(i) << "\n"; - if (m_name.length () < 1) - // Write an invalid value to flag empty fcn handle name. - os << "0\n"; - else - os << m_name << "\n"; - os << m_text << "\n"; - return true; -} - -bool -octave_fcn_inline::load_ascii (std::istream& is) -{ - int nargs; - if (extract_keyword (is, "nargs", nargs, true)) - { - m_args.resize (nargs); - for (int i = 0; i < nargs; i++) - is >> m_args(i); - is >> m_name; - if (m_name == "0") - m_name = ""; - - skip_preceeding_newline (is); - - std::string buf; - - if (is) - { - - // Get a line of text whitespace characters included, - // leaving newline in the stream. - buf = read_until_newline (is, true); - } - - m_text = buf; - - octave_fcn_inline tmp (m_text, m_args, m_name); - m_fcn = tmp.m_fcn; - - return true; - } - else - return false; -} - -bool -octave_fcn_inline::save_binary (std::ostream& os, bool) -{ - int32_t tmp = m_args.numel (); - os.write (reinterpret_cast (&tmp), 4); - for (int i = 0; i < m_args.numel (); i++) - { - tmp = m_args(i).length (); - os.write (reinterpret_cast (&tmp), 4); - os.write (m_args(i).c_str (), m_args(i).length ()); - } - tmp = m_name.length (); - os.write (reinterpret_cast (&tmp), 4); - os.write (m_name.c_str (), m_name.length ()); - tmp = m_text.length (); - os.write (reinterpret_cast (&tmp), 4); - os.write (m_text.c_str (), m_text.length ()); - return true; -} - -bool -octave_fcn_inline::load_binary (std::istream& is, bool swap, - octave::mach_info::float_format) -{ - int32_t nargs; - if (! is.read (reinterpret_cast (&nargs), 4)) - return false; - if (swap) - swap_bytes<4> (&nargs); - - if (nargs < 1) - return false; - else - { - int32_t tmp; - m_args.resize (nargs); - for (int i = 0; i < nargs; i++) - { - if (! is.read (reinterpret_cast (&tmp), 4)) - return false; - if (swap) - swap_bytes<4> (&tmp); - - OCTAVE_LOCAL_BUFFER (char, ctmp, tmp+1); - is.read (ctmp, tmp); - m_args(i) = std::string (ctmp); - - if (! is) - return false; - } - - if (! is.read (reinterpret_cast (&tmp), 4)) - return false; - if (swap) - swap_bytes<4> (&tmp); - - OCTAVE_LOCAL_BUFFER (char, ctmp1, tmp+1); - is.read (ctmp1, tmp); - m_name = std::string (ctmp1); - - if (! is) - return false; - - if (! is.read (reinterpret_cast (&tmp), 4)) - return false; - if (swap) - swap_bytes<4> (&tmp); - - OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1); - is.read (ctmp2, tmp); - m_text = std::string (ctmp2); - - if (! is) - return false; - - octave_fcn_inline ftmp (m_text, m_args, m_name); - m_fcn = ftmp.m_fcn; - } - return true; -} - -bool -octave_fcn_inline::save_hdf5 (octave_hdf5_id loc_id, const char *name, - bool /* save_as_floats */) -{ - bool retval = false; - -#if defined (HAVE_HDF5) - - hid_t group_hid = -1; - -#if defined (HAVE_HDF5_18) - group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT, - octave_H5P_DEFAULT); -#else - group_hid = H5Gcreate (loc_id, name, 0); -#endif - if (group_hid < 0) return false; - - size_t len = 0; - for (int i = 0; i < m_args.numel (); i++) - if (len < m_args(i).length ()) - len = m_args(i).length (); - - hid_t space_hid, data_hid, type_hid; - space_hid = data_hid = type_hid = -1; - - // FIXME: Is there a better way of saving string vectors, - // than a null padded matrix? - - OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2); - - // Octave uses column-major, while HDF5 uses row-major ordering - hdims[1] = m_args.numel (); - hdims[0] = len + 1; - - space_hid = H5Screate_simple (2, hdims, nullptr); - if (space_hid < 0) - { - H5Gclose (group_hid); - return false; - } -#if defined (HAVE_HDF5_18) - data_hid = H5Dcreate (group_hid, "args", H5T_NATIVE_CHAR, space_hid, - octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT); -#else - data_hid = H5Dcreate (group_hid, "args", H5T_NATIVE_CHAR, space_hid, - octave_H5P_DEFAULT); -#endif - if (data_hid < 0) - { - H5Sclose (space_hid); - H5Gclose (group_hid); - return false; - } - - OCTAVE_LOCAL_BUFFER (char, s, m_args.numel () * (len + 1)); - - // Save the args as a null terminated list - for (int i = 0; i < m_args.numel (); i++) - { - const char *cptr = m_args(i).c_str (); - for (size_t j = 0; j < m_args(i).length (); j++) - s[i*(len+1)+j] = *cptr++; - s[m_args(i).length ()] = '\0'; - } - - retval = H5Dwrite (data_hid, H5T_NATIVE_CHAR, octave_H5S_ALL, octave_H5S_ALL, - octave_H5P_DEFAULT, s) >= 0; - - H5Dclose (data_hid); - H5Sclose (space_hid); - - if (! retval) - { - H5Gclose (group_hid); - return false; - } - - // attach the type of the variable - type_hid = H5Tcopy (H5T_C_S1); - H5Tset_size (type_hid, m_name.length () + 1); - if (type_hid < 0) - { - H5Gclose (group_hid); - return false; - } - - hdims[0] = 0; - space_hid = H5Screate_simple (0, hdims, nullptr); - if (space_hid < 0) - { - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } -#if defined (HAVE_HDF5_18) - data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid, - octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT); -#else - data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid, - octave_H5P_DEFAULT); -#endif - if (data_hid < 0 - || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL, - octave_H5P_DEFAULT, m_name.c_str ()) < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - H5Dclose (data_hid); - - // attach the type of the variable - H5Tset_size (type_hid, m_text.length () + 1); - if (type_hid < 0) - { - H5Gclose (group_hid); - return false; - } - -#if defined (HAVE_HDF5_18) - data_hid = H5Dcreate (group_hid, "iftext", type_hid, space_hid, - octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT); -#else - data_hid = H5Dcreate (group_hid, "iftext", type_hid, space_hid, - octave_H5P_DEFAULT); -#endif - if (data_hid < 0 - || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL, - octave_H5P_DEFAULT, m_text.c_str ()) < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - - H5Dclose (data_hid); - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - -#else - octave_unused_parameter (loc_id); - octave_unused_parameter (name); - - warn_save ("hdf5"); -#endif - - return retval; -} - -bool -octave_fcn_inline::load_hdf5 (octave_hdf5_id loc_id, const char *name) -{ -#if defined (HAVE_HDF5) - - hid_t group_hid, data_hid, space_hid, type_hid, type_class_hid, st_id; - hsize_t rank; - int slen; - -#if defined (HAVE_HDF5_18) - group_hid = H5Gopen (loc_id, name, octave_H5P_DEFAULT); -#else - group_hid = H5Gopen (loc_id, name); -#endif - if (group_hid < 0) return false; - -#if defined (HAVE_HDF5_18) - data_hid = H5Dopen (group_hid, "args", octave_H5P_DEFAULT); -#else - data_hid = H5Dopen (group_hid, "args"); -#endif - space_hid = H5Dget_space (data_hid); - rank = H5Sget_simple_extent_ndims (space_hid); - - if (rank != 2) - { - H5Dclose (data_hid); - H5Sclose (space_hid); - H5Gclose (group_hid); - return false; - } - - OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank); - OCTAVE_LOCAL_BUFFER (hsize_t, maxdims, rank); - - H5Sget_simple_extent_dims (space_hid, hdims, maxdims); - - m_args.resize (hdims[1]); - - OCTAVE_LOCAL_BUFFER (char, s1, hdims[0] * hdims[1]); - - if (H5Dread (data_hid, H5T_NATIVE_UCHAR, octave_H5S_ALL, octave_H5S_ALL, - octave_H5P_DEFAULT, s1) < 0) - { - H5Dclose (data_hid); - H5Sclose (space_hid); - H5Gclose (group_hid); - return false; - } - - H5Dclose (data_hid); - H5Sclose (space_hid); - - for (size_t i = 0; i < hdims[1]; i++) - m_args(i) = std::string (s1 + i*hdims[0]); - -#if defined (HAVE_HDF5_18) - data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT); -#else - data_hid = H5Dopen (group_hid, "nm"); -#endif - - if (data_hid < 0) - { - H5Gclose (group_hid); - return false; - } - - type_hid = H5Dget_type (data_hid); - type_class_hid = H5Tget_class (type_hid); - - if (type_class_hid != H5T_STRING) - { - H5Tclose (type_hid); - H5Dclose (data_hid); - H5Gclose (group_hid); - return false; - } - - space_hid = H5Dget_space (data_hid); - rank = H5Sget_simple_extent_ndims (space_hid); - - if (rank != 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Dclose (data_hid); - H5Gclose (group_hid); - return false; - } - - slen = H5Tget_size (type_hid); - if (slen < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Dclose (data_hid); - H5Gclose (group_hid); - return false; - } - - OCTAVE_LOCAL_BUFFER (char, nm_tmp, slen); - - // create datatype for (null-terminated) string to read into: - st_id = H5Tcopy (H5T_C_S1); - H5Tset_size (st_id, slen); - - if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL, - octave_H5P_DEFAULT, nm_tmp) < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - H5Tclose (st_id); - H5Dclose (data_hid); - m_name = nm_tmp; - -#if defined (HAVE_HDF5_18) - data_hid = H5Dopen (group_hid, "iftext", octave_H5P_DEFAULT); -#else - data_hid = H5Dopen (group_hid, "iftext"); -#endif - - if (data_hid < 0) - { - H5Gclose (group_hid); - return false; - } - - type_hid = H5Dget_type (data_hid); - type_class_hid = H5Tget_class (type_hid); - - if (type_class_hid != H5T_STRING) - { - H5Tclose (type_hid); - H5Dclose (data_hid); - H5Gclose (group_hid); - return false; - } - - space_hid = H5Dget_space (data_hid); - rank = H5Sget_simple_extent_ndims (space_hid); - - if (rank != 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Dclose (data_hid); - H5Gclose (group_hid); - return false; - } - - slen = H5Tget_size (type_hid); - if (slen < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Dclose (data_hid); - H5Gclose (group_hid); - return false; - } - - OCTAVE_LOCAL_BUFFER (char, iftext_tmp, slen); - - // create datatype for (null-terminated) string to read into: - st_id = H5Tcopy (H5T_C_S1); - H5Tset_size (st_id, slen); - - if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL, - octave_H5P_DEFAULT, iftext_tmp) < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - H5Tclose (st_id); - H5Dclose (data_hid); - m_text = iftext_tmp; - - octave_fcn_inline ftmp (m_text, m_args, m_name); - m_fcn = ftmp.m_fcn; - - return true; - -#else - octave_unused_parameter (loc_id); - octave_unused_parameter (name); - - warn_load ("hdf5"); - - return false; -#endif -} - -void -octave_fcn_inline::print (std::ostream& os, bool pr_as_read_syntax) -{ - print_raw (os, pr_as_read_syntax); - newline (os); -} - -void -octave_fcn_inline::print_raw (std::ostream& os, bool pr_as_read_syntax) const -{ - std::ostringstream buf; - - if (m_name.empty ()) - buf << "f("; - else - buf << m_name << '('; - - for (int i = 0; i < m_args.numel (); i++) - { - if (i) - buf << ", "; - - buf << m_args(i); - } - - buf << ") = " << m_text; - - octave_print_internal (os, buf.str (), pr_as_read_syntax, - current_print_indent_level ()); -} - -octave_value -octave_fcn_inline::convert_to_str_internal (bool, bool, char type) const -{ - return octave_value (fcn_text (), type); -} - -DEFUNX ("inline", Finline, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {} inline (@var{str}) -@deftypefnx {} {} inline (@var{str}, @var{arg1}, @dots{}) -@deftypefnx {} {} inline (@var{str}, @var{n}) -Create an inline function from the character string @var{str}. - -If called with a single argument, the arguments of the generated function -are extracted from the function itself. The generated function arguments -will then be in alphabetical order. It should be noted that i and j are -ignored as arguments due to the ambiguity between their use as a variable or -their use as an built-in constant. All arguments followed by a parenthesis -are considered to be functions. If no arguments are found, a function -taking a single argument named @code{x} will be created. - -If the second and subsequent arguments are character strings, they are the -names of the arguments of the function. - -If the second argument is an integer @var{n}, the arguments are -@qcode{"x"}, @qcode{"P1"}, @dots{}, @qcode{"P@var{N}"}. - -Programming Note: The use of @code{inline} is discouraged and it may be -removed from a future version of Octave. The preferred way to create -functions from strings is through the use of anonymous functions -(@pxref{Anonymous Functions}) or @code{str2func}. -@seealso{argnames, formula, vectorize, str2func} -@end deftypefn */) -{ - int nargin = args.length (); - - if (nargin == 0) - print_usage (); - - std::string fun = args(0).xstring_value ("inline: STR argument must be a string"); - - string_vector fargs; - - if (nargin == 1) - { - bool is_arg = false; - bool in_string = false; - std::string tmp_arg; - size_t i = 0; - size_t fun_length = fun.length (); - - while (i < fun_length) - { - bool terminate_arg = false; - char c = fun[i++]; - - if (in_string) - { - if (c == '\'' || c == '\"') - in_string = false; - } - else if (c == '\'' || c == '\"') - { - in_string = true; - if (is_arg) - terminate_arg = true; - } - else if (! isalpha (c) && c != '_') - if (! is_arg) - continue; - else if (isdigit (c)) - tmp_arg.append (1, c); - else - { - // Before we do anything remove trailing whitespaces. - while (i < fun_length && isspace (c)) - c = fun[i++]; - - // Do we have a variable or a function? - if (c != '(') - terminate_arg = true; - else - { - tmp_arg = ""; - is_arg = false; - } - } - else if (! is_arg) - { - if (c == 'e' || c == 'E') - { - // possible number in exponent form, not arg - if (isdigit (fun[i]) || fun[i] == '-' || fun[i] == '+') - continue; - } - is_arg = true; - tmp_arg.append (1, c); - } - else - { - tmp_arg.append (1, c); - } - - if (terminate_arg || (i == fun_length && is_arg)) - { - bool have_arg = false; - - for (int j = 0; j < fargs.numel (); j++) - if (tmp_arg == fargs (j)) - { - have_arg = true; - break; - } - - if (! have_arg && tmp_arg != "i" && tmp_arg != "j" - && tmp_arg != "NaN" && tmp_arg != "nan" - && tmp_arg != "Inf" && tmp_arg != "inf" - && tmp_arg != "NA" && tmp_arg != "pi" - && tmp_arg != "e" && tmp_arg != "eps") - fargs.append (tmp_arg); - - tmp_arg = ""; - is_arg = false; - } - } - - // Sort the arguments into ASCII order. - fargs.sort (); - - if (fargs.isempty ()) - fargs.append (std::string ("x")); - - } - else if (nargin == 2 && args(1).isnumeric ()) - { - if (! args(1).is_scalar_type ()) - error ("inline: N must be an integer"); - - int n = args(1).xint_value ("inline: N must be an integer"); - - if (n < 0) - error ("inline: N must be a positive integer or zero"); - - fargs.resize (n+1); - - fargs(0) = "x"; - - for (int i = 1; i < n+1; i++) - { - std::ostringstream buf; - buf << 'P' << i; - fargs(i) = buf.str (); - } - } - else - { - fargs.resize (nargin - 1); - - for (int i = 1; i < nargin; i++) - { - std::string s = args(i).xstring_value ("inline: additional arguments must be strings"); - fargs(i-1) = s; - } - } - - return ovl (new octave_fcn_inline (fun, fargs)); -} - -/* -%!shared fn -%! fn = inline ("x.^2 + 1"); -%!assert (feval (fn, 6), 37) -%!assert (fn (6), 37) -%!assert (feval (inline ("sum (x(:))"), [1 2; 3 4]), 10) -%!assert (feval (inline ("sqrt (x^2 + y^2)", "x", "y"), 3, 4), 5) -%!assert (feval (inline ("exp (P1*x) + P2", 3), 3, 4, 5), exp(3*4) + 5) - -## Test input validation -%!error inline () -%!error inline (1) -%!error inline ("2", ones (2,2)) -%!error inline ("2", -1) -%!error inline ("2", "x", -1, "y") -*/ - -DEFUN (formula, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {} formula (@var{fun}) -Return a character string representing the inline function @var{fun}. - -Note that @code{char (@var{fun})} is equivalent to -@code{formula (@var{fun})}. -@seealso{char, argnames, inline, vectorize} -@end deftypefn */) -{ - if (args.length () != 1) - print_usage (); - - octave_fcn_inline *fn = args(0).fcn_inline_value (true); - - if (! fn) - error ("formula: FUN must be an inline function"); - - return ovl (fn->fcn_text ()); -} - -/* -%!assert (formula (fn), "x.^2 + 1") -%!assert (formula (fn), char (fn)) - -## Test input validation -%!error formula () -%!error formula (1, 2) -%!error formula (1) -*/ - -DEFUN (argnames, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {} argnames (@var{fun}) -Return a cell array of character strings containing the names of the -arguments of the inline function @var{fun}. -@seealso{inline, formula, vectorize} -@end deftypefn */) -{ - if (args.length () != 1) - print_usage (); - - octave_fcn_inline *fn = args(0).fcn_inline_value (true); - - if (! fn) - error ("argnames: FUN must be an inline function"); - - string_vector t1 = fn->fcn_arg_names (); - - Cell t2 (dim_vector (t1.numel (), 1)); - - for (int i = 0; i < t1.numel (); i++) - t2(i) = t1(i); - - return ovl (t2); -} - -/* -%!assert (argnames (fn), {"x"}) -%!assert (argnames (inline ("1e-3*y + 2e4*z")), {"y"; "z"}) -%!assert (argnames (inline ("2", 2)), {"x"; "P1"; "P2"}) - -## Test input validation -%!error argnames () -%!error argnames (1, 2) -%!error argnames (1) -*/ - -DEFUN (vectorize, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {} vectorize (@var{fun}) -Create a vectorized version of the inline function @var{fun} by replacing -all occurrences of @code{*}, @code{/}, etc., with @code{.*}, @code{./}, etc. - -This may be useful, for example, when using inline functions with numerical -integration or optimization where a vector-valued function is expected. - -@example -@group -fcn = vectorize (inline ("x^2 - 1")) - @result{} fcn = f(x) = x.^2 - 1 -quadv (fcn, 0, 3) - @result{} 6 -@end group -@end example -@seealso{inline, formula, argnames} -@end deftypefn */) -{ - if (args.length () != 1) - print_usage (); - - std::string old_func; - octave_fcn_inline *old = nullptr; - bool func_is_string = true; - - if (args(0).is_string ()) - old_func = args(0).string_value (); - else - { - func_is_string = false; - - old = args(0).fcn_inline_value (true); - if (! old) - error ("vectorize: FUN must be a string or inline function"); - - old_func = old->fcn_text (); - } - - size_t len = old_func.length (); - std::string new_func; - new_func.reserve (len + 10); - - size_t i = 0; - while (i < len) - { - char t1 = old_func[i]; - - if (t1 == '*' || t1 == '/' || t1 == '\\' || t1 == '^') - { - if (i && old_func[i-1] != '.') - new_func.push_back ('.'); - - // Special case for ** operator. - if (t1 == '*' && i < (len - 1) && old_func[i+1] == '*') - { - new_func.push_back ('*'); - i++; - } - } - new_func.push_back (t1); - i++; - } - - if (func_is_string) - return ovl (new_func); - else - return ovl (new octave_fcn_inline (new_func, old->fcn_arg_names ())); -} - -/* -%!assert (char (vectorize (fn)), "x.^2 + 1") -%!assert (char (vectorize (inline ("1e-3*y + 2e4*z"))), "1e-3.*y + 2e4.*z") -%!assert (char (vectorize (inline ("2**x^5"))), "2.**x.^5") -%!assert (vectorize ("x.^2 + 1"), "x.^2 + 1") -%!assert (vectorize ("1e-3*y + 2e4*z"), "1e-3.*y + 2e4.*z") -%!assert (vectorize ("2**x^5"), "2.**x.^5") - -## Test input validation -%!error vectorize () -%!error vectorize (1, 2) -%!error vectorize (1) -*/ diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/octave-value/ov-fcn-inline.h --- a/libinterp/octave-value/ov-fcn-inline.h Sat Apr 25 13:17:11 2020 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2004-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or . -// -// This file is part of Octave. -// -// Octave 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 3 of the License, or -// (at your option) any later version. -// -// Octave 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 Octave; see the file COPYING. If not, see -// . -// -//////////////////////////////////////////////////////////////////////// - -#if ! defined (octave_ov_fcn_inline_h) -#define octave_ov_fcn_inline_h 1 - -#include "octave-config.h" - -#include -#include - -#include "ov-base.h" -#include "ov-base-mat.h" -#include "ov-fcn.h" -#include "ov-typeinfo.h" -#include "ov-fcn-handle.h" - -// Inline functions. - -class -OCTINTERP_API -octave_fcn_inline : public octave_fcn_handle -{ -public: - - octave_fcn_inline (void) - : octave_fcn_handle (), m_text (), m_args () { } - - octave_fcn_inline (const std::string& f, const string_vector& a, - const std::string& n = ""); - - octave_fcn_inline (const octave_fcn_inline& fi) - : octave_fcn_handle (fi), m_text (fi.m_text), m_args (fi.m_args) { } - - ~octave_fcn_inline (void) = default; - - octave_base_value * clone (void) const - { return new octave_fcn_inline (*this); } - octave_base_value * empty_clone (void) const - { return new octave_fcn_inline (); } - - bool is_inline_function (void) const { return true; } - - octave_fcn_inline * fcn_inline_value (bool = false) { return this; } - - std::string fcn_text (void) const { return m_text; } - - string_vector fcn_arg_names (void) const { return m_args; } - - octave_value convert_to_str_internal (bool, bool, char) const; - - octave_map map_value (void) const; - - bool save_ascii (std::ostream& os); - - bool load_ascii (std::istream& is); - - bool save_binary (std::ostream& os, bool save_as_floats); - - bool load_binary (std::istream& is, bool swap, - octave::mach_info::float_format fmt); - - bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats); - - bool load_hdf5 (octave_hdf5_id loc_id, const char *name); - - void print (std::ostream& os, bool pr_as_read_syntax = false); - - void print_raw (std::ostream& os, bool pr_as_read_syntax = false) const; - -private: - - DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA - - // The expression of an inline function. - std::string m_text; - - // The args of an inline function. - string_vector m_args; -}; - -#endif diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/octave-value/ov-typeinfo.cc --- a/libinterp/octave-value/ov-typeinfo.cc Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/octave-value/ov-typeinfo.cc Mon Mar 23 15:29:49 2020 -0400 @@ -991,8 +991,6 @@ %!assert (typeinfo (@sin), "function handle") %!assert (typeinfo (@(x) x), "function handle") -%!assert (typeinfo (inline ("x^2")), "inline function") - %!assert (typeinfo (single (1)), "float scalar") %!assert (typeinfo (single (i)), "float complex scalar") %!assert (typeinfo (single ([1, 2])), "float matrix") diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/octave-value/ov.cc --- a/libinterp/octave-value/ov.cc Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/octave-value/ov.cc Mon Mar 23 15:29:49 2020 -0400 @@ -75,7 +75,6 @@ #include "ov-dld-fcn.h" #include "ov-usr-fcn.h" #include "ov-fcn-handle.h" -#include "ov-fcn-inline.h" #include "ov-typeinfo.h" #include "ov-null-mat.h" #include "ov-lazy-idx.h" @@ -1694,12 +1693,6 @@ return rep->fcn_handle_value (silent); } -octave_fcn_inline * -octave_value::fcn_inline_value (bool silent) const -{ - return rep->fcn_inline_value (silent); -} - octave_value_list octave_value::list_value (void) const { @@ -2119,7 +2112,6 @@ XVALUE_EXTRACTOR (octave_user_script *, xuser_script_value, user_script_value) XVALUE_EXTRACTOR (octave_user_code *, xuser_code_value, user_code_value) XVALUE_EXTRACTOR (octave_fcn_handle *, xfcn_handle_value, fcn_handle_value) -XVALUE_EXTRACTOR (octave_fcn_inline *, xfcn_inline_value, fcn_inline_value) XVALUE_EXTRACTOR (octave_value_list, xlist_value, list_value) @@ -2962,7 +2954,6 @@ octave_user_function::register_type (ti); octave_dld_function::register_type (ti); octave_fcn_handle::register_type (ti); - octave_fcn_inline::register_type (ti); octave_float_scalar::register_type (ti); octave_float_complex::register_type (ti); octave_float_matrix::register_type (ti); diff -r 9a3deb17b4ea -r 7a8c69c4eb55 libinterp/octave-value/ov.h --- a/libinterp/octave-value/ov.h Sat Apr 25 13:17:11 2020 -0400 +++ b/libinterp/octave-value/ov.h Mon Mar 23 15:29:49 2020 -0400 @@ -57,7 +57,6 @@ class octave_function; class octave_user_function; class octave_fcn_handle; -class octave_fcn_inline; class octave_value_list; #include "oct-stream.h" @@ -974,8 +973,6 @@ octave_fcn_handle * fcn_handle_value (bool silent = false) const; - octave_fcn_inline * fcn_inline_value (bool silent = false) const; - octave_value_list list_value (void) const; ColumnVector column_vector_value (bool frc_str_conv = false, @@ -1193,7 +1190,6 @@ octave_user_script * xuser_script_value (const char *fmt, ...) const; octave_user_code * xuser_code_value (const char *fmt, ...) const; octave_fcn_handle * xfcn_handle_value (const char *fmt, ...) const; - octave_fcn_inline * xfcn_inline_value (const char *fmt, ...) const; octave_value_list xlist_value (const char *fmt, ...) const; diff -r 9a3deb17b4ea -r 7a8c69c4eb55 scripts/legacy/@inline/argnames.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/argnames.m Mon Mar 23 15:29:49 2020 -0400 @@ -0,0 +1,16 @@ +## -*- texinfo -*- +## @deftypefn {} {} argnames (@var{fun}) +## Return a cell array of character strings containing the names of the +## arguments of the inline function @var{fun}. +## @seealso{inline, formula, vectorize} +## @end deftypefn + +function args = argnames (obj) + + if (nargin != 1) + print_usage (); + endif + + args = obj.args; + +endfunction diff -r 9a3deb17b4ea -r 7a8c69c4eb55 scripts/legacy/@inline/char.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/char.m Mon Mar 23 15:29:49 2020 -0400 @@ -0,0 +1,18 @@ +## -*- texinfo -*- +## @deftypefn {} {} char (@var{fun}) +## Return a character string representing the inline function @var{fun}. +## +## Note that @code{char (@var{fun})} is equivalent to +## @code{formula (@var{fun})}. +## @seealso{char, argnames, inline, vectorize} +## @end deftypefn + +function expr = char (obj) + + if (nargin != 1) + print_usage (); + endif + + expr = obj.expr; + +endfunction diff -r 9a3deb17b4ea -r 7a8c69c4eb55 scripts/legacy/@inline/feval.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/feval.m Mon Mar 23 15:29:49 2020 -0400 @@ -0,0 +1,36 @@ +######################################################################## +## +## Copyright (C) 2020 The Octave Project Developers +## +## See the file COPYRIGHT.md in the top-level directory of this +## distribution or . +## +## This file is part of Octave. +## +## Octave 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 3 of the License, or +## (at your option) any later version. +## +## Octave 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 Octave; see the file COPYING. If not, see +## . +## +######################################################################## + +function retval = feval (fcn, varargin) + + if (nargin < 1) + print_usage (); + endif + + fh = eval (sprintf ("@(%s) %s", strjoin (fcn.args, ","), fcn.expr)); + + retval = fh (varargin{:}); + +endfunction diff -r 9a3deb17b4ea -r 7a8c69c4eb55 scripts/legacy/@inline/formula.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/formula.m Mon Mar 23 15:29:49 2020 -0400 @@ -0,0 +1,18 @@ +## -*- texinfo -*- +## @deftypefn {} {} formula (@var{fun}) +## Return a character string representing the inline function @var{fun}. +## +## Note that @code{char (@var{fun})} is equivalent to +## @code{formula (@var{fun})}. +## @seealso{char, argnames, inline, vectorize} +## @end deftypefn + +function expr = formula (obj) + + if (nargin != 1) + print_usage (); + endif + + expr = obj.expr; + +endfunction diff -r 9a3deb17b4ea -r 7a8c69c4eb55 scripts/legacy/@inline/inline.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/inline.m Mon Mar 23 15:29:49 2020 -0400 @@ -0,0 +1,190 @@ +######################################################################## +## +## Copyright (C) 2020 The Octave Project Developers +## +## See the file COPYRIGHT.md in the top-level directory of this +## distribution or . +## +## This file is part of Octave. +## +## Octave 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 3 of the License, or +## (at your option) any later version. +## +## Octave 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 Octave; see the file COPYING. If not, see +## . +## +######################################################################## + +## -*- texinfo -*- +## @deftypefn {} {} inline (@var{str}) +## @deftypefnx {} {} inline (@var{str}, @var{arg1}, @dots{}) +## @deftypefnx {} {} inline (@var{str}, @var{n}) +## +## This function is obsolete. Use anonymous functions +## (@pxref{Anonymous Functions}) instead. +## +## Create an inline function from the character string @var{str}. +## +## If called with a single argument, the arguments of the generated +## function are extracted from the function itself. The generated +## function arguments will then be in alphabetical order. It should be +## noted that i and j are ignored as arguments due to the ambiguity +## between their use as a variable or their use as an built-in constant. +## All arguments followed by a parenthesis are considered to be +## functions. If no arguments are found, a function taking a single +## argument named @code{x} will be created. +## +## If the second and subsequent arguments are character strings, they +## are the names of the arguments of the function. +## +## If the second argument is an integer @var{n}, the arguments are +## @qcode{"x"}, @qcode{"P1"}, @dots{}, @qcode{"P@var{N}"}. +## +## @strong{Caution:} the use of @code{inline} is discouraged and it may +## be removed from a future version of Octave. The preferred way to +## create functions from strings is through the use of anonymous +## functions (@pxref{Anonymous Functions}) or @code{str2func}. +## @seealso{argnames, formula, vectorize, str2func} +## @end deftypefn + +function obj = inline (expr, varargin) + + if (nargin == 0) + print_usage (); + endif + + if (! ischar (expr)) + error ("inline: EXPR must be a string"); + endif + + if (nargin == 1) + args = parse_expr_for_args (expr); + elseif (nargin == 2) + n = varargin{1}; + if (isnumeric (n)) + if (isscalar (n) && fix (n) == n) + if (n > 0) + args = strsplit (["x", sprintf(":P%d", 1:n)], ":"); + else + error ("inline: N must be a positive integer"); + endif + else + error ("inline: N must be an integer"); + endif + else + args = {"x"}; + endif + elseif (iscellstr (varargin)) + args = varargin; + else + error ("inline: additional arguments must be strings"); + endif + + p.expr = expr; + p.args = args(:); + p.numArgs = numel (args); + tmp = [args; num2cell(1:numel(args))]; + p.inputExpr = sprintf ("%s = INLINE_INPUTS_{%d}; ", tmp{:}); + p.isEmpty = false; + p.version = 1; + + obj = __inline_ctor__ (p); + +endfunction + +## The following function was translated directly from the original C++ +## version. Yes, it will be slow, but the use of inline functions is +## strongly discouraged anyway, and most expressions will probably be +## short. It may also be buggy. Well, don't use this object! Use +## function handles instead! + +function args = parse_expr_for_args (expr) + + persistent symbols_to_skip = {"i", "j", "NaN", "nan", "Inf", "inf", ... + "NA", "pi", "e", "eps"}; + + is_arg = false; + in_string = false; + tmp_arg = ""; + i = 1; + expr_length = length (expr); + args = {}; + + while (i <= expr_length) + + terminate_arg = false; + c = expr(i++); + + if (in_string) + if (c == "'" || c == '"') + in_string = false; + endif + elseif (c == "'" || c == '"') + in_string = true; + if (is_arg) + terminate_arg = true; + endif + elseif (! isalpha (c) && c != "_") + if (! is_arg) + continue; + elseif (isdigit (c)) + tmp_arg(end+1) = c; + else + ## Before we do anything remove trailing whitespaces. + while (i <= expr_length && isspace (c)) + c = expr(i++); + endwhile + + ## Do we have a variable or a function? + if (c != "(") + terminate_arg = true; + else + tmp_arg = ""; + is_arg = false; + endif + endif + elseif (! is_arg) + if (c == "e" || c == "E") + ## Possible number in exponent form, not arg. + if (isdigit (expr(i)) || expr(i) == "-" || expr(i) == "+") + continue; + endif + endif + is_arg = true; + tmp_arg(end+1) = c; + else + tmp_arg(end+1) = c; + endif + + if (terminate_arg || (i == expr_length+1 && is_arg)) + have_arg = false; + if (any (strcmp (tmp_arg, args))) + have_arg = true; + endif + + if (! (have_arg || any (strcmp (tmp_arg, symbols_to_skip)))) + args{end+1} = tmp_arg; + endif + + tmp_arg = ""; + is_arg = false; + endif + + endwhile + + ## Sort the arguments into ASCII order. + args = sort (args); + + if (isempty (args)) + args = {"x"}; + endif + +endfunction diff -r 9a3deb17b4ea -r 7a8c69c4eb55 scripts/legacy/@inline/module.mk --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/module.mk Mon Mar 23 15:29:49 2020 -0400 @@ -0,0 +1,11 @@ +## Automake fails to process "include %reldir%/module.mk" in the directory +## above. All of the commands which would normally be in this file were +## manually placed in scripts/legacy/module.mk to avoid using the "include" +## directive. +## +## This is an Automake bug. Automake has switched to a Perl backend which uses +## the following pattern to detect a path: +## +## my $PATH_PATTERN = '(\w|[+/.-])+'; +## +## This pattern only includes alphanumeric, '_', and [+/.-], but not "@". diff -r 9a3deb17b4ea -r 7a8c69c4eb55 scripts/legacy/@inline/subsref.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/subsref.m Mon Mar 23 15:29:49 2020 -0400 @@ -0,0 +1,52 @@ +######################################################################## +## +## Copyright (C) 2020 The Octave Project Developers +## +## See the file COPYRIGHT.md in the top-level directory of this +## distribution or . +## +## This file is part of Octave. +## +## Octave 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 3 of the License, or +## (at your option) any later version. +## +## Octave 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 Octave; see the file COPYING. If not, see +## . +## +######################################################################## + +## -*- texinfo -*- +## @deftypefn {} {@var{value} =} subsref (@var{fcn}, @var{idx}) +## Perform subscripted function call on the inline function object @var{fcn}. +## @end deftypefn + +function retval = subsref (fcn, idx) + + if (nargin != 2) + print_usage (); + endif + + if (isempty (idx)) + error ("@inline/subsref: missing index"); + endif + + if (strcmp (idx(1).type, "()")) + args = idx.subs; + if (numel (args) > 0) + retval = feval (fcn, args{:}); + else + retval = feval (fcn); + endif + else + error ("@inline/subsref: invalid subscript type"); + endif + +endfunction diff -r 9a3deb17b4ea -r 7a8c69c4eb55 scripts/legacy/@inline/vectorize.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/vectorize.m Mon Mar 23 15:29:49 2020 -0400 @@ -0,0 +1,62 @@ +## -*- texinfo -*- +## @deftypefn {} {} vectorize (@var{fun}) +## Create a vectorized version of the inline function @var{fun} by +## replacing all occurrences of @code{*}, @code{/}, etc., with +## @code{.*}, @code{./}, etc. +## +## This may be useful, for example, when using inline functions with +## numerical integration or optimization where a vector-valued function +## is expected. +## +## @example +## @group +## fcn = vectorize (inline ("x^2 - 1")) +## @result{} fcn = f(x) = x.^2 - 1 +## quadv (fcn, 0, 3) +## @result{} 6 +## @end group +## @end example +## @seealso{inline, formula, argnames} +## @end deftypefn + +## The following function was translated directly from the original C++ +## version. Yes, it will be slow, but the use of inline functions is +## strongly discouraged anyway, and most expressions will probably be +## short. It may also be buggy. Well, don't use this object! Use +## function handles instead! + +function fcn = vectorize (obj) + + if (nargin != 1) + print_usage (); + endif + + new_expr = ""; + + expr = obj.expr; + len = length (expr); + i = 1; + + while (i <= len) + c = expr(i); + + if (c == "*" || c == "/" || c == "\\" || c == "^") + if (i > 1 && expr(i-1) != ".") + new_expr(end+1) = "."; + endif + + ## Special case for ** operator. + if (c == '*' && i < (len - 1) && expr(i+1) == '*') + new_expr(end+1) = "*"; + i++; + endif + endif + + new_expr(end+1) = c; + i++; + + endwhile + + fcn = inline (new_expr); + +endfunction diff -r 9a3deb17b4ea -r 7a8c69c4eb55 scripts/legacy/module.mk --- a/scripts/legacy/module.mk Sat Apr 25 13:17:11 2020 -0400 +++ b/scripts/legacy/module.mk Mon Mar 23 15:29:49 2020 -0400 @@ -12,6 +12,31 @@ %reldir%/strread.m \ %reldir%/textread.m +## include %reldir%/@inline/module.mk +## The include above fails because Automake cannot process the '@' character. +## As a work around, the contents of %reldir%/@inline/module.mk are placed directly +## in this module.mk file. +scripts_EXTRA_DIST += %reldir%/@inline/module.mk +######################## include %reldir%/@inline/module.mk ######################## +FCN_FILE_DIRS += %reldir%/@inline + +%canon_reldir%_@inline_FCN_FILES = \ + %reldir%/@inline/argnames.m \ + %reldir%/@inline/formula.m \ + %reldir%/@inline/inline.m \ + %reldir%/@inline/vectorize.m + +%canon_reldir%_@inlinedir = $(fcnfiledir)/@inline + +%canon_reldir%_@inline_DATA = $(%canon_reldir%_@inline_FCN_FILES) + +FCN_FILES += $(%canon_reldir%_@inline_FCN_FILES) + +PKG_ADD_FILES += %reldir%/@inline/PKG_ADD + +DIRSTAMP_FILES += %reldir%/@inline/$(octave_dirstamp) +####################### end include %reldir%/@inline/module.mk ##################### + %canon_reldir%dir = $(fcnfiledir)/legacy %canon_reldir%_DATA = $(%canon_reldir%_FCN_FILES) diff -r 9a3deb17b4ea -r 7a8c69c4eb55 scripts/module.mk --- a/scripts/module.mk Sat Apr 25 13:17:11 2020 -0400 +++ b/scripts/module.mk Mon Mar 23 15:29:49 2020 -0400 @@ -45,7 +45,7 @@ ## The include above fails because Automake cannot process the '@' character. ## As a work around, the contents of %reldir%/@ftp/module.mk are placed directly ## in this module.mk file. -%canon_reldir%_EXTRA_DIST += %reldir%/@ftp/module.mk +scripts_EXTRA_DIST += %reldir%/@ftp/module.mk ######################## include %reldir%/@ftp/module.mk ######################## FCN_FILE_DIRS += %reldir%/@ftp diff -r 9a3deb17b4ea -r 7a8c69c4eb55 scripts/plot/draw/fplot.m --- a/scripts/plot/draw/fplot.m Sat Apr 25 13:17:11 2020 -0400 +++ b/scripts/plot/draw/fplot.m Mon Mar 23 15:29:49 2020 -0400 @@ -83,11 +83,9 @@ ## ## @code{fplot} performance is better when the function accepts and returns a ## vector argument. Consider this when writing user-defined functions and use -## element-by-element operators such as @code{.*}, @code{./}, etc. See the -## function @code{vectorize} for potentially converting inline or anonymous -## functions to vectorized versions. +## element-by-element operators such as @code{.*}, @code{./}, etc. ## -## @seealso{ezplot, plot, vectorize} +## @seealso{ezplot, plot} ## @end deftypefn function [X, Y] = fplot (varargin) @@ -99,8 +97,8 @@ endif fn = varargin{1}; - if (strcmp (typeinfo (fn), "inline function")) - fn = vectorize (fn); + if (isa (fn, "inline")) + fn = vectorize (inline (fn)); nam = formula (fn); elseif (is_function_handle (fn)) nam = func2str (fn); diff -r 9a3deb17b4ea -r 7a8c69c4eb55 scripts/plot/draw/private/__ezplot__.m --- a/scripts/plot/draw/private/__ezplot__.m Sat Apr 25 13:17:11 2020 -0400 +++ b/scripts/plot/draw/private/__ezplot__.m Mon Mar 23 15:29:49 2020 -0400 @@ -84,7 +84,7 @@ endif endif - if (strcmp (typeinfo (fun), "inline function")) + if (isa (fun, "inline")) argids = argnames (fun); if (isplot && length (argids) == 2) nargs = 2; @@ -147,7 +147,7 @@ yarg = args{2}; endif else - error ("%s: F must be string, inline function, or function handle", ezfunc); + error ("%s: F must be a string or function handle", ezfunc); endif if (nargin > 2 || (nargin == 2 && isplot)) @@ -165,7 +165,7 @@ error ("%s: expecting a function of %d arguments", ezfunc, nargs); endif fstry = formula (funy); - elseif (strcmp (typeinfo (funy), "inline function")) + elseif (isa (funy, "inline")) parametric = true; if (numel (argnames (funy)) != nargs) error ("%s: expecting a function of %d arguments", ezfunc, nargs); @@ -210,7 +210,7 @@ error ("%s: expecting a function of %d arguments", ezfunc, nargs); endif fstrz = formula (funz); - elseif (strcmp (typeinfo (funz), "inline function")) + elseif (isa (funz, "inline")) if (numel (argnames (funz)) != nargs) error ("%s: expecting a function of %d arguments", ezfunc, nargs); endif diff -r 9a3deb17b4ea -r 7a8c69c4eb55 test/inline-fcn.tst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/inline-fcn.tst Mon Mar 23 15:29:49 2020 -0400 @@ -0,0 +1,33 @@ +## INLINE contstructor + +%!shared fn +%! fn = inline ("x.^2 + 1"); +%!assert (feval (fn, 6), 37) +%!assert (fn (6), 37) +%!assert (feval (inline ("sum (x(:))"), [1 2; 3 4]), 10) +%!assert (feval (inline ("sqrt (x^2 + y^2)", "x", "y"), 3, 4), 5) +%!assert (feval (inline ("exp (P1*x) + P2", 3), 3, 4, 5), exp(3*4) + 5) + +## Test input validation +%!error inline () +%!error inline (1) +%!error inline ("2", ones (2,2)) +%!error inline ("2", -1) +%!error inline ("2", "x", -1, "y") + +## FORMULA + +%!assert (formula (fn), "x.^2 + 1") +%!assert (formula (fn), char (fn)) + +## ARGNAMES + +%!assert (argnames (fn), {"x"}) +%!assert (argnames (inline ("1e-3*y + 2e4*z")), {"y"; "z"}) +%!assert (argnames (inline ("2", 2)), {"x"; "P1"; "P2"}) + +## VECTORIZE + +%!assert (formula (vectorize (fn)), "x.^2 + 1") +%!assert (formula (vectorize (inline ("1e-3*y + 2e4*z"))), "1e-3.*y + 2e4.*z") +%!assert (formula (vectorize (inline ("2**x^5"))), "2.**x.^5") diff -r 9a3deb17b4ea -r 7a8c69c4eb55 test/module.mk --- a/test/module.mk Sat Apr 25 13:17:11 2020 -0400 +++ b/test/module.mk Mon Mar 23 15:29:49 2020 -0400 @@ -31,6 +31,7 @@ %reldir%/global.tst \ %reldir%/if.tst \ %reldir%/index.tst \ + %reldir%/inline-fcn.tst \ %reldir%/integer.tst \ %reldir%/io.tst \ %reldir%/jit.tst \