comparison src/DLD-FUNCTIONS/dispatch.cc @ 5164:57077d0ddc8e

[project @ 2005-02-25 19:55:24 by jwe]
author jwe
date Fri, 25 Feb 2005 19:55:28 +0000
parents
children 240ed0328925
comparison
equal deleted inserted replaced
5163:9f3299378193 5164:57077d0ddc8e
1 /*
2
3 Copyright (C) 2001 John W. Eaton and Paul Kienzle
4
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2, or (at your option) any
8 later version.
9
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Octave; see the file COPYING. If not, write to the Free
17 Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <list>
26 #include <map>
27 #include <string>
28
29 #include "defun-dld.h"
30 #include "ov.h"
31 #include "ov-fcn.h"
32 #include "ov-typeinfo.h"
33 #include "pager.h"
34 #include "parse.h"
35 #include "symtab.h"
36 #include "variables.h"
37
38 // XXX FIXME XXX should be using a map from type_id->name, rather
39 // than type_name->name
40
41 template class std::map<std::string,std::string>;
42
43 typedef std::map<std::string,std::string> Table;
44
45 class
46 octave_dispatch : public octave_function
47 {
48 public:
49
50 // XXX FIXME XXX need to handle doc strings of dispatched functions, for
51 // example, by appending "for <f>(<type>,...) see <name>" for each
52 // time dispatch(f,type,name) is called.
53 octave_dispatch (const std::string &nm)
54 : octave_function (nm, "Overloaded function"), tab (), base (nm),
55 has_alias (false)
56 { }
57
58 // XXX FIXME XXX if we get deleted, we should restore the original
59 // symbol_record from base before dying.
60 ~octave_dispatch (void) { }
61
62 bool is_builtin_function (void) const { return true; }
63
64 octave_function *function_value (bool) { return this; }
65
66 octave_value do_index_op (const octave_value_list&, int)
67 {
68 error ("dispatch: do_index_op");
69 return octave_value ();
70 }
71
72 octave_value subsref (const std::string&,
73 const std::list<octave_value_list>&)
74 {
75 error ("dispatch: subsref (str, list)");
76 panic_impossible ();
77 return octave_value ();
78 }
79
80 octave_value_list subsref (const std::string& type,
81 const std::list<octave_value_list>& idx,
82 int nargout);
83
84 octave_value_list do_multi_index_op (int, const octave_value_list&);
85
86 void add (const std::string t, const std::string n);
87
88 void clear (const std::string t);
89
90 void print (std::ostream& os, bool pr_as_read=false) const;
91
92 private:
93
94 Table tab;
95 std::string base;
96 bool has_alias;
97
98 octave_dispatch (void)
99 : octave_function (), tab (), base (), has_alias (false) { }
100
101 DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
102
103 DECLARE_OCTAVE_ALLOCATOR
104 };
105
106 DEFINE_OCTAVE_ALLOCATOR (octave_dispatch);
107
108 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_dispatch,
109 "overloaded function", "function");
110
111 void
112 octave_dispatch::add (const std::string t, const std::string n)
113 {
114 if (tab.count (t) > 0 && tab[t] != n)
115 warning ("replacing %s(%s,...)->%s with %s",
116 base.c_str (), t.c_str (), tab[t].c_str (), n.c_str ());
117
118 tab[t] = n;
119
120 if (t == "any")
121 has_alias = true;
122 }
123
124 void
125 octave_dispatch::clear (const std::string t)
126 {
127 tab.erase (t);
128
129 if (t == "any")
130 has_alias = false;
131 }
132
133 octave_value_list
134 octave_dispatch::subsref (const std::string& type,
135 const std::list<octave_value_list>& idx,
136 int nargout)
137 {
138 octave_value_list retval;
139
140 switch (type[0])
141 {
142 case '(':
143 retval = do_multi_index_op (nargout, idx.front ());
144 break;
145
146 case '{':
147 case '.':
148 {
149 const std::string nm = type_name ();
150 error ("%s cannot be indexed with %c", nm.c_str (), type[0]);
151 }
152 break;
153
154 default:
155 panic_impossible ();
156 }
157
158 if (idx.size () > 1)
159 retval = retval(0).next_subsref (type, idx);
160
161 return retval;
162 }
163
164 static octave_function*
165 builtin (const std::string& base)
166 {
167 octave_function *fcn = 0;
168
169 // Check if we are overriding a builtin function. This is the
170 // case if builtin is protected.
171 symbol_record *builtin = fbi_sym_tab->lookup ("builtin:" + base, 0);
172
173 if (! builtin)
174 error ("builtin record has gone missing");
175
176 if (error_state)
177 return fcn;
178
179 if (builtin->is_read_only ())
180 {
181 // builtin is read only, so checking for updates is pointless
182 if (builtin->is_function ())
183 fcn = builtin->def().function_value ();
184 else
185 error ("builtin %s is not a function", base.c_str ());
186 }
187 else
188 {
189 // Check that builtin is up to date.
190
191 // Don't try to fight octave's function name handling
192 // mechanism. Instead, move dispatch record out of the way,
193 // and restore the builtin to its original name.
194 symbol_record *dispatch = fbi_sym_tab->lookup (base, 0);
195 if (! dispatch)
196 error ("dispatch record has gone missing");
197
198 dispatch->unprotect ();
199
200 fbi_sym_tab->rename (base, "dispatch:" + base);
201
202 fbi_sym_tab->rename ("builtin:" + base, base);
203
204 // check for updates to builtin function; ignore errors that
205 // appear (they interfere with renaming), and remove the updated
206 // name from the current symbol table. XXX FIXME XXX check that
207 // updating a function updates it in all contexts --- it may be
208 // that it is updated only in the current symbol table, and not
209 // the caller. I believe this won't be a problem because the
210 // caller will go through the same logic and end up with the
211 // newer version.
212 fcn = is_valid_function (base, "dispatch", 1);
213 int cache_error = error_state;
214 error_state = 0;
215 curr_sym_tab->clear_function (base);
216
217 // Move the builtin function out of the way and restore the
218 // dispatch fuction.
219 // XXX FIXME XXX what if builtin wants to protect itself?
220 symbol_record *found=fbi_sym_tab->lookup (base, 0);
221 bool readonly = found->is_read_only ();
222 found->unprotect ();
223 fbi_sym_tab->rename (base, "builtin:" + base);
224 fbi_sym_tab->rename ("dispatch:" + base, base);
225 if (readonly)
226 found->protect ();
227 dispatch->protect ();
228
229 // remember if there were any errors.
230 error_state = cache_error;
231 }
232
233 return fcn;
234 }
235
236 static bool
237 any_arg_is_magic_colon (const octave_value_list& args)
238 {
239 int nargin = args.length ();
240
241 for (int i = 0; i < nargin; i++)
242 if (args(i).is_magic_colon ())
243 return true;
244
245 return false;
246 }
247
248
249 octave_value_list
250 octave_dispatch::do_multi_index_op (int nargout, const octave_value_list& args)
251 {
252 octave_value_list retval;
253
254 if (error_state) return retval;
255
256 if (any_arg_is_magic_colon (args))
257 {
258 ::error ("invalid use of colon in function argument list");
259 return retval;
260 }
261
262 // If more than one argument, check if argument template matches any
263 // overloaded functions. Also provide a catch-all '*' type to provide
264 // single level pseudo rename and replace functionality.
265 if (args.length () > 0 && tab.count (args(0).type_name ()) > 0)
266 retval = feval (tab[args(0).type_name()], args, nargout);
267 else if (has_alias)
268 retval = feval (tab["any"], args, nargout);
269 else
270 {
271 octave_function *fcn = builtin (base);
272 if (! error_state && fcn)
273 retval = fcn->do_multi_index_op (nargout, args);
274 }
275
276 return retval;
277 }
278
279 void
280 octave_dispatch::print (std::ostream& os, bool) const
281 {
282 os << "Overloaded function " << base << std::endl;
283
284 for (Table::const_iterator it = tab.begin (); it != tab.end (); it++)
285 os << base << "(" << it->first << ",...)->"
286 << it->second << "(" << it->first << ",...)"
287 << std::endl;
288 }
289
290 DEFUN_DLD (builtin, args, nargout,
291 "-*- texinfo -*-\n\
292 @deftypefn {Loadable Function} {[@dots{}]} builtin (@var{f}, @dots{})\n\
293 Call the base function @var{f} even if @var{f} is overloaded to\n\
294 some other function for the given type signature.\n\
295 @end deftypefn\n\
296 @seealso{dispatch}")
297 {
298 octave_value_list retval;
299
300 int nargin = args.length ();
301
302 if (nargin > 0)
303 {
304 const std::string name (args(0).string_value ());
305
306 if (error_state)
307 return retval;
308
309 symbol_record *sr = fbi_sym_tab->lookup (name, 0);
310 if (sr->def().type_id () == octave_dispatch::static_type_id ())
311 {
312 octave_function *fcn = builtin (name);
313
314 if (!error_state && fcn)
315 retval = fcn->do_multi_index_op (nargout,
316 args.splice (0, 1, retval));
317 }
318 else
319 retval = feval (name, args, nargout);
320 }
321 else
322 print_usage ("builtin");
323
324 return retval;
325 }
326
327 DEFUN_DLD (dispatch_help, args, nargout,
328 "-*- texinfo -*-\n\
329 @deftypefn {Loadable Function} {} dispatch_help (@var{name}, @dots{})\n\
330 Delayed loading of help messages for dispatched functions.\n\
331 @end deftypefn\n\
332 @seealso{builtin, dispatch}")
333 {
334 octave_value_list retval;
335
336 int nargin = args.length ();
337
338 for (int i = 0; i < nargin; i++)
339 {
340 if (args(i).is_string ())
341 {
342 const std::string name (args(i).string_value ());
343
344 if (error_state)
345 return retval;
346
347 symbol_record *sr = fbi_sym_tab->lookup (name, false);
348
349 if (sr)
350 {
351 std::string help = sr->help ();
352
353 if (help[0] == '<' && help[1] == '>'
354 && sr->def().type_id () == octave_dispatch::static_type_id ())
355 {
356 builtin (name);
357
358 symbol_record *builtin_record
359 = fbi_sym_tab->lookup ("builtin:" + name, 0);
360
361 help.replace (0, 2, builtin_record->help ());
362
363 sr->document (help);
364 }
365 }
366 }
367 }
368
369 return feval ("builtin:help", args, nargout);
370 }
371
372 static void
373 dispatch_record (const std::string &f, const std::string &n,
374 const std::string &t)
375 {
376 // find the base function in the symbol table, loading it if it
377 // is not already there; if it is already a dispatch, then bonus
378
379 symbol_record *sr = fbi_sym_tab->lookup (f, true);
380
381 if (sr->def().type_id () != octave_dispatch::static_type_id ())
382 {
383 // Preserve mark_as_command status
384 bool iscommand = sr->is_command ();
385
386 // Not an overloaded name, so if only display or clear then we are done
387 if (t.empty ())
388 return;
389
390 // sr is the base symbol; rename it to keep it safe. When we need
391 // it we will rename it back again.
392 if (sr->is_read_only ())
393 {
394 sr->unprotect ();
395 fbi_sym_tab->rename (f, "builtin:" + f);
396 sr = fbi_sym_tab->lookup (f, true);
397 sr->protect ();
398 }
399 else
400 fbi_sym_tab->rename (f, "builtin:" + f);
401
402 std::string basedoc ("<>");
403
404 if (! sr->help().empty ())
405 basedoc = sr->help ();
406
407 // Problem: when a function is first called a new record
408 // is created for it in the current symbol table, so calling
409 // dispatch on a function that has already been called, we
410 // should also clear it from all existing symbol tables.
411 // This is too much work, so we will only do it for the
412 // top level symbol table. We can't use the clear_function()
413 // method, because it won't clear builtin functions. Instead
414 // we check if the symbol is a function and clear it then. This
415 // won't properly clear shadowed functions, or functions in
416 // other namespaces (such as the current, if called from a
417 // function).
418 symbol_record *local = top_level_sym_tab->lookup (f, false);
419 if (local && local->is_function ())
420 local->clear ();
421
422 // Build a new dispatch object based on the function definition
423 octave_dispatch *dispatch = new octave_dispatch (f);
424
425 // Create a symbol record for the dispatch object.
426 sr = fbi_sym_tab->lookup (f, true);
427 sr->unprotect ();
428 sr->define (octave_value (dispatch), symbol_record::BUILTIN_FUNCTION);
429 // std::cout << "iscommand('"<<f<<"')=" << iscommand << std::endl;
430 if (iscommand)
431 sr->mark_as_command();
432 sr->document (basedoc + "\n\nOverloaded function\n");
433 sr->make_eternal (); // XXX FIXME XXX why??
434 sr->mark_as_static ();
435 sr->protect ();
436 }
437
438 // clear/replace/extend the map with the new type-function pair
439 const octave_dispatch& rep
440 = reinterpret_cast<const octave_dispatch&> (sr->def().get_rep ());
441
442 if (t.empty ())
443 // XXX FIXME XXX should return the list if nargout > 1
444 rep.print (octave_stdout);
445 else if (n.empty ())
446 {
447 // XXX FIXME XXX should we eliminate the dispatch function if
448 // there are no more elements?
449 // XXX FIXME XXX should clear the " $t:\w+" from the help string.
450 // XXX FIXME XXX -- seems bad to cast away const here...
451 octave_dispatch& xrep = const_cast<octave_dispatch&> (rep);
452
453 xrep.clear (t);
454 }
455 else
456 {
457 // XXX FIXME XXX -- seems bad to cast away const here...
458 octave_dispatch& xrep = const_cast<octave_dispatch&> (rep);
459
460 xrep.add (t, n);
461
462 if (! sr->help().empty ())
463 sr->document (sr->help() + "\n " + n + "(" + t + ",...)");
464 }
465 }
466
467 DEFUN_DLD (dispatch, args, ,
468 "-*- texinfo -*-\n\
469 @deftypefn {Loadable Function} {} dispatch (@var{f}, @var{r}, @var{type})\n\
470 \n\
471 Replace the function @var{f} with a dispatch so that function @var{r}\n\
472 is called when @var{f} is called with the first argument of the named\n\
473 @var{type}. If the type is @var{any} then call @var{r} if no other type\n\
474 matches. The original function @var{f} is accessible using\n\
475 @code{builtin (@var{f}, @dots{}).\n\
476 \n\
477 If @var{r} is omitted, clear dispatch function associated with @var{type}.\n\
478 \n\
479 If both @var{r} and @var{type} are omitted, list dispatch functions\n\
480 for @var{f}\n\
481 @end deftypefn\n\
482 @seealso{builtin}")
483 {
484 octave_value retval;
485 int nargin = args.length ();
486
487 if (nargin < 1 || nargin > 3)
488 {
489 print_usage ("dispatch");
490 return retval;
491 }
492
493 std::string f, t, n;
494 if (nargin > 0)
495 f = args(0).string_value ();
496
497 if (nargin == 2)
498 t = args(1).string_value ();
499 else if (nargin > 2)
500 {
501 n = args(1).string_value ();
502 t = args(2).string_value ();
503 }
504
505 if (error_state)
506 return retval;
507
508 static bool register_type = true;
509
510 // register dispatch function type if you have not already done so
511 if (register_type)
512 {
513 octave_dispatch::register_type ();
514 register_type = false;
515 fbi_sym_tab->lookup("dispatch")->mark_as_static ();
516 dispatch_record ("help", "dispatch_help", "string");
517 }
518
519 dispatch_record (f, n, t);
520
521 return retval;
522 }
523
524 /*
525
526 %!test # builtin function replacement
527 %! dispatch('sin','length','string')
528 %! assert(sin('abc'),3)
529 %! assert(sin(0),0,10*eps);
530 %!test # 'any' function
531 %! dispatch('sin','exp','any')
532 %! assert(sin(0),1,eps);
533 %! assert(sin('abc'),3);
534 %!test # 'builtin' function
535 %! assert(builtin('sin',0),0,eps);
536 %! builtin('eval','x=1;');
537 %! assert(x,1);
538 %!test # clear function mapping
539 %! dispatch('sin','string')
540 %! dispatch('sin','any')
541 %! assert(sin(0),0,10*eps);
542 %!test # oct-file replacement
543 %! dispatch('fft','length','string')
544 %! assert(fft([1,1]),[2,0]);
545 %! assert(fft('abc'),3)
546 %! dispatch('fft','string');
547 %!test # m-file replacement
548 %! dispatch('hamming','length','string')
549 %! assert(hamming(1),1)
550 %! assert(hamming('abc'),3)
551 %! dispatch('hamming','string')
552
553 %!test # override preloaded builtin
554 %! evalin('base','cos(1);');
555 %! dispatch('cos','length','string')
556 %! evalin('base',"assert(cos('abc'),3)");
557 %! evalin('base',"assert(cos(0),1,eps)");
558 %! dispatch('cos','string')
559 %!test # override pre-loaded oct-file
560 %! evalin('base','qr(1);');
561 %! dispatch('qr','length','string')
562 %! evalin('base',"assert(qr('abc'),3)");
563 %! evalin('base',"assert(qr(1),1)");
564 %! dispatch('qr','string');
565 %!test # override pre-loaded m-file
566 %! evalin('base','hanning(1);');
567 %! dispatch('hanning','length','string')
568 %! evalin('base','assert(hanning("abc"),3)');
569 %! evalin('base','assert(hanning(1),1)');
570 %! dispatch('hanning','string');
571
572 XXX FIXME XXX I would rather not create dispatch_x/dispatch_y
573 in the current directory! I don't want them installed accidentally.
574
575 %!test # replace base m-file
576 %! system("echo 'function a=dispatch_x(a)'>dispatch_x.m");
577 %! dispatch('dispatch_x','length','string')
578 %! assert(dispatch_x(3),3)
579 %! assert(dispatch_x('a'),1)
580 %! pause(1);
581 %! system("echo 'function a=dispatch_x(a),++a;'>dispatch_x.m");
582 %! assert(dispatch_x(3),4)
583 %! assert(dispatch_x('a'),1)
584 %!test
585 %! system("rm dispatch_x.m");
586
587 %!test # replace dispatch m-file
588 %! system("echo 'function a=dispatch_y(a)'>dispatch_y.m");
589 %! dispatch('hello','dispatch_y','complex scalar')
590 %! assert(hello(3i),3i)
591 %! pause(1);
592 %! system("echo 'function a=dispatch_y(a),++a;'>dispatch_y.m");
593 %! assert(hello(3i),1+3i)
594 %!test
595 %! system("rm dispatch_y.m");
596
597 XXX FIXME XXX add tests for preservation of mark_as_command status.
598
599 */