1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2007-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING.  If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 //! @file op-class.cc
27 //! Unary and binary operators for classdef and old style classes.
28 
29 #if defined (HAVE_CONFIG_H)
30 #  include "config.h"
31 #endif
32 
33 #include "oct-time.h"
34 
35 #include "errwarn.h"
36 #include "interpreter-private.h"
37 #include "load-path.h"
38 #include "ovl.h"
39 #include "ov.h"
40 #include "ov-class.h"
41 #include "ov-typeinfo.h"
42 #include "ops.h"
43 #include "symtab.h"
44 #include "parse.h"
45 
46 //! Default unary class operator.
47 //!
48 //! @param a operand
49 //! @param opname operator name
50 
51 static octave_value
oct_unop_default(const octave_value & a,const std::string & opname)52 oct_unop_default (const octave_value& a, const std::string& opname)
53 {
54   std::string class_name = a.class_name ();
55 
56   octave_value meth
57     = octave::__get_symbol_table__ ("oct_unop_" + opname)
58       .find_method (opname, class_name);
59 
60   if (meth.is_defined ())
61     {
62       // Call overloaded unary class operator.
63       octave_value_list tmp = octave::feval (meth.function_value (),
64                                              ovl (a), 1);
65 
66       // Return first element if present.
67       if (tmp.length () > 0)
68         return tmp(0);
69 
70       return octave_value ();
71     }
72 
73   // Matlab compatibility:  If (conjugate) transpose is not overloaded and
74   // the number of dimensions is maximal two, just transpose the array of
75   // that class.
76 
77   if ((opname == "transpose") || (opname == "ctranspose"))
78     {
79       if (a.ndims () > 2)
80         error ("%s not defined for N-D objects of %s class", opname.c_str (),
81                class_name.c_str ());
82 
83       if (a.is_classdef_object ())
84         {
85           // FIXME: Default transposition for classdef arrays.
86 
87           error ("%s method not defined for %s class", opname.c_str (),
88                  class_name.c_str ());
89         }
90       else
91         {
92           const octave_class& v
93             = dynamic_cast<const octave_class&> (a.get_rep ());
94 
95           return octave_value (v.map_value ().transpose (),
96                                v.class_name (),
97                                v.parent_class_name_list ());
98         }
99     }
100   else
101     error ("%s method not defined for %s class", opname.c_str (),
102            class_name.c_str ());
103 }
104 
105 //! Helper macro to define unary class operators.
106 
107 #define DEF_CLASS_UNOP(opname)                 \
108   static octave_value                          \
109   oct_unop_ ## opname (const octave_value& a)  \
110   {                                            \
111     return oct_unop_default (a, #opname);      \
112   }
113 
114 DEF_CLASS_UNOP (not)         // !a or ~a
DEF_CLASS_UNOP(uplus)115 DEF_CLASS_UNOP (uplus)       // +a
116 DEF_CLASS_UNOP (uminus)      // -a
117 DEF_CLASS_UNOP (transpose)   //  a.'
118 DEF_CLASS_UNOP (ctranspose)  //  a'
119 #undef DEF_CLASS_UNOP
120 
121 //! Default binary class operator.
122 //!
123 //! @param a1 first  operand
124 //! @param a2 second operand
125 //! @param opname operator name
126 //!
127 //! The operator precedence is as follows:
128 //!
129 //! 1.   If exactly one of the operands is a user defined class object, then
130 //!      the class method of that operand is invoked.
131 //!
132 //! 2.   If both operands are user defined class objects, then
133 //! 2.1  The superior class method is invoked.
134 //! 2.2  The leftmost class method is invoked if both classes are the same
135 //!      or their precedence is not defined by superiorto/inferiorto.
136 
137 static octave_value
138 oct_binop_default (const octave_value& a1, const octave_value& a2,
139                    const std::string& opname)
140 {
141   octave::symbol_table& symtab
142     = octave::__get_symbol_table__ ("oct_binop_" + opname);
143 
144   // Dispatch to first (leftmost) operand by default.
145   std::string dispatch_type = a1.class_name ();
146 
147   // Determine, if second operand takes precedence (see rules above).
148   if (! a1.isobject ()
149       || (a1.isobject () && a2.isobject ()
150           && symtab.is_superiorto (a2.class_name (), dispatch_type)))
151     dispatch_type = a2.class_name ();
152 
153   octave_value meth = symtab.find_method (opname, dispatch_type);
154 
155   if (meth.is_undefined ())
156     error ("%s method not defined for %s class", opname.c_str (),
157            dispatch_type.c_str ());
158 
159   octave_value_list tmp = octave::feval (meth.function_value (),
160                                          ovl (a1, a2), 1);
161 
162   if (tmp.length () > 0)
163     return tmp(0);
164 
165   return octave_value ();
166 }
167 
168 //! Helper macro to define binary class operators.
169 
170 #define DEF_CLASS_BINOP(opname)                                          \
171   static octave_value                                                    \
172   oct_binop_ ## opname (const octave_value& a1, const octave_value& a2)  \
173   {                                                                      \
174     return oct_binop_default (a1, a2, #opname);                          \
175   }
176 
177 DEF_CLASS_BINOP (plus)     // a1 + a2
DEF_CLASS_BINOP(minus)178 DEF_CLASS_BINOP (minus)    // a1 - a2
179 DEF_CLASS_BINOP (mtimes)   // a1 * a2
180 DEF_CLASS_BINOP (mrdivide) // a1 / a2
181 DEF_CLASS_BINOP (mpower)   // a1 ^ a2
182 DEF_CLASS_BINOP (mldivide) // a1 \ a2
183 DEF_CLASS_BINOP (lt)       // a1 <  a2
184 DEF_CLASS_BINOP (le)       // a1 <= a2
185 DEF_CLASS_BINOP (eq)       // a1 <= a2
186 DEF_CLASS_BINOP (ge)       // a1 >= a2
187 DEF_CLASS_BINOP (gt)       // a1 >  a2
188 DEF_CLASS_BINOP (ne)       // a1 ~= a2 or a1 != a2
189 DEF_CLASS_BINOP (times)    // a1 .* a2
190 DEF_CLASS_BINOP (rdivide)  // a1 ./ a2
191 DEF_CLASS_BINOP (power)    // a1 .^ a2
192 DEF_CLASS_BINOP (ldivide)  // a1 .\ a2
193 DEF_CLASS_BINOP (and)      // a1 & a2
194 DEF_CLASS_BINOP (or)       // a1 | a2
195 #undef DEF_CLASS_BINOP
196 
197 void
198 install_class_ops (octave::type_info& ti)
199 {
200   ti.install_unary_class_op (octave_value::op_not,       oct_unop_not);
201   ti.install_unary_class_op (octave_value::op_uplus,     oct_unop_uplus);
202   ti.install_unary_class_op (octave_value::op_uminus,    oct_unop_uminus);
203   ti.install_unary_class_op (octave_value::op_transpose, oct_unop_transpose);
204   ti.install_unary_class_op (octave_value::op_hermitian, oct_unop_ctranspose);
205 
206   ti.install_binary_class_op (octave_value::op_add,     oct_binop_plus);
207   ti.install_binary_class_op (octave_value::op_sub,     oct_binop_minus);
208   ti.install_binary_class_op (octave_value::op_mul,     oct_binop_mtimes);
209   ti.install_binary_class_op (octave_value::op_div,     oct_binop_mrdivide);
210   ti.install_binary_class_op (octave_value::op_pow,     oct_binop_mpower);
211   ti.install_binary_class_op (octave_value::op_ldiv,    oct_binop_mldivide);
212   ti.install_binary_class_op (octave_value::op_lt,      oct_binop_lt);
213   ti.install_binary_class_op (octave_value::op_le,      oct_binop_le);
214   ti.install_binary_class_op (octave_value::op_eq,      oct_binop_eq);
215   ti.install_binary_class_op (octave_value::op_ge,      oct_binop_ge);
216   ti.install_binary_class_op (octave_value::op_gt,      oct_binop_gt);
217   ti.install_binary_class_op (octave_value::op_ne,      oct_binop_ne);
218   ti.install_binary_class_op (octave_value::op_el_mul,  oct_binop_times);
219   ti.install_binary_class_op (octave_value::op_el_div,  oct_binop_rdivide);
220   ti.install_binary_class_op (octave_value::op_el_pow,  oct_binop_power);
221   ti.install_binary_class_op (octave_value::op_el_ldiv, oct_binop_ldivide);
222   ti.install_binary_class_op (octave_value::op_el_and,  oct_binop_and);
223   ti.install_binary_class_op (octave_value::op_el_or,   oct_binop_or);
224 }
225