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