Mercurial > hg > octave-lojdl
changeset 16270:3b0381ea8737 classdef
maint: periodic merge of default to classdef
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Mon, 11 Mar 2013 14:31:56 -0400 |
parents | dbbef00202ff (diff) 488b0fef52c5 (current diff) |
children | 4cf2c4ae8df5 |
files | libinterp/parse-tree/lex.ll |
diffstat | 42 files changed, 6805 insertions(+), 241 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/interp-core/pt-jit.cc +++ b/libinterp/interp-core/pt-jit.cc @@ -655,6 +655,12 @@ } void +jit_convert::visit_funcall (tree_funcall&) +{ + throw jit_fail_exception (); +} + +void jit_convert::visit_parameter_list (tree_parameter_list&) { throw jit_fail_exception ();
--- a/libinterp/interp-core/pt-jit.h +++ b/libinterp/interp-core/pt-jit.h @@ -127,6 +127,8 @@ void visit_fcn_handle (tree_fcn_handle&); + void visit_funcall (tree_funcall&); + void visit_parameter_list (tree_parameter_list&); void visit_postfix_expression (tree_postfix_expression&);
--- a/libinterp/interpfcn/symtab.cc +++ b/libinterp/interpfcn/symtab.cc @@ -36,6 +36,7 @@ #include "dirfns.h" #include "input.h" #include "load-path.h" +#include "ov-classdef.h" #include "ov-fcn.h" #include "ov-usr-fcn.h" #include "pager.h" @@ -392,6 +393,31 @@ class_constructors[name] = retval; } } + else + { + // Classdef constructors can be defined anywhere in the path, not + // necessarily in @-folders. Look for a normal function and load it. + // If the loaded function is a classdef constructor, store it as such + // and restore function_on_path to its previous value. + + octave_value old_function_on_path = function_on_path; + + octave_value maybe_cdef_ctor = find_user_function (); + + if (maybe_cdef_ctor.is_defined ()) + { + octave_function *fcn = maybe_cdef_ctor.function_value (true); + + if (fcn && fcn->is_classdef_constructor ()) + { + retval = maybe_cdef_ctor; + + class_constructors[name] = retval; + + function_on_path = old_function_on_path; + } + } + } return retval; } @@ -406,43 +432,53 @@ retval = load_class_constructor (); else { - std::string dir_name; + octave_function *cm = cdef_manager::find_method_symbol (name, + dispatch_type); - std::string file_name = load_path::find_method (dispatch_type, name, - dir_name); + if (cm) + retval = octave_value (cm); - if (! file_name.empty ()) + if (! retval.is_defined ()) { - octave_function *fcn = load_fcn_from_file (file_name, dir_name, - dispatch_type); + std::string dir_name; - if (fcn) + std::string file_name = load_path::find_method (dispatch_type, name, + dir_name); + + if (! file_name.empty ()) { - retval = octave_value (fcn); + octave_function *fcn = load_fcn_from_file (file_name, dir_name, + dispatch_type); - class_methods[dispatch_type] = retval; - } - } + if (fcn) + { + retval = octave_value (fcn); - if (retval.is_undefined ()) - { - // Search parent classes + class_methods[dispatch_type] = retval; + } + } - const std::list<std::string>& plist = parent_classes (dispatch_type); - - std::list<std::string>::const_iterator it = plist.begin (); - - while (it != plist.end ()) + if (retval.is_undefined ()) { - retval = find_method (*it); + // Search parent classes - if (retval.is_defined ()) + const std::list<std::string>& plist = + parent_classes (dispatch_type); + + std::list<std::string>::const_iterator it = plist.begin (); + + while (it != plist.end ()) { - class_methods[dispatch_type] = retval; - break; + retval = find_method (*it); + + if (retval.is_defined ()) + { + class_methods[dispatch_type] = retval; + break; + } + + it++; } - - it++; } } }
--- a/libinterp/octave-value/module.mk +++ b/libinterp/octave-value/module.mk @@ -36,6 +36,7 @@ octave-value/ov-cell.h \ octave-value/ov-ch-mat.h \ octave-value/ov-class.h \ + octave-value/ov-classdef.h \ octave-value/ov-colon.h \ octave-value/ov-complex.h \ octave-value/ov-cs-list.h \ @@ -94,6 +95,7 @@ octave-value/ov-cell.cc \ octave-value/ov-ch-mat.cc \ octave-value/ov-class.cc \ + octave-value/ov-classdef.cc \ octave-value/ov-colon.cc \ octave-value/ov-complex.cc \ octave-value/ov-cs-list.cc \
new file mode 100644 --- /dev/null +++ b/libinterp/octave-value/ov-classdef.cc @@ -0,0 +1,3113 @@ +/* + +Copyright (C) 2012 Michael Goffioul + +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 +<http://www.gnu.org/licenses/>. + +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <algorithm> + +#include "defun.h" +#include "ov-builtin.h" +#include "ov-classdef.h" +#include "ov-fcn-handle.h" +#include "ov-typeinfo.h" +#include "pt-assign.h" +#include "pt-classdef.h" +#include "pt-funcall.h" +#include "pt-misc.h" +#include "pt-stmt.h" +#include "pt-walk.h" +#include "singleton-cleanup.h" +#include "symtab.h" +#include "toplev.h" + +#include "Array.cc" + +static void +gripe_method_access (const std::string& from, const cdef_method& meth) +{ + octave_value acc = meth.get ("Access"); + std::string acc_s; + + if (acc.is_string ()) + acc_s = acc.string_value (); + else + acc_s = "class-restricted"; + + error ("%s: method `%s' has %s access and cannot be run in this context", + from.c_str (), meth.get_name ().c_str (), acc_s.c_str ()); +} + +static void +gripe_property_access (const std::string& from, const cdef_property& prop, + bool is_set = false) +{ + octave_value acc = prop.get (is_set ? "SetAccess" : "GetAccess"); + std::string acc_s; + + if (acc.is_string ()) + acc_s = acc.string_value (); + else + acc_s = "class-restricted"; + + if (is_set) + error ("%s: property `%s' has %s access and cannot be set in this context", + from.c_str (), prop.get_name ().c_str (), acc_s.c_str ()); + else + error ("%s: property `%s' has %s access and cannot be obtained in this context", + from.c_str (), prop.get_name ().c_str (), acc_s.c_str ()); +} + +static std::string +get_base_name (const std::string& nm) +{ + std::string::size_type pos = nm.find_last_of ('.'); + + if (pos != std::string::npos) + return nm.substr (pos + 1); + + return nm; +} + +static void +make_function_of_class (const std::string& class_name, + const octave_value& fcn) +{ + octave_function *of = fcn.function_value (); + + if (! error_state) + { + of->stash_dispatch_class (class_name); + + octave_user_function *uf = of->user_function_value (true); + + if (! error_state && uf) + { + if (get_base_name (class_name) == uf->name ()) + { + uf->mark_as_class_constructor (); + uf->mark_as_classdef_constructor (); + } + else + uf->mark_as_class_method (); + } + } +} + +static void +make_function_of_class (const cdef_class& cls, const octave_value& fcn) +{ + make_function_of_class (cls.get_name (), fcn); +} + +static octave_value +make_fcn_handle (octave_builtin::fcn ff, const std::string& nm) +{ + octave_value fcn (new octave_builtin (ff, nm)); + + octave_value fcn_handle (new octave_fcn_handle (fcn, nm)); + + return fcn_handle; +} + +inline octave_value_list +execute_ov (octave_value val, const octave_value_list& args, int nargout) +{ + std::list<octave_value_list> idx (1, args); + + std::string type ("("); + + return val.subsref (type, idx, nargout); +} + +static cdef_class +lookup_class (const std::string& name, bool error_if_not_found = true, + bool load_if_not_found = true) +{ + return cdef_manager::find_class (name, error_if_not_found, + load_if_not_found); +} + +static cdef_class +lookup_class (const cdef_class& cls) +{ + // FIXME: placeholder for the time being, the purpose + // is to centralized any class update activity here. + + return cls; +} + +static cdef_class +lookup_class (const octave_value& ov) +{ + if (ov.is_string()) + return lookup_class (ov.string_value ()); + else + { + cdef_class cls (to_cdef (ov)); + + if (! error_state) + return lookup_class (cls); + } + + return cdef_class (); +} + +static std::list<cdef_class> +lookup_classes (const Cell& cls_list) +{ + std::list<cdef_class> retval; + + for (int i = 0; i < cls_list.numel (); i++) + { + cdef_class c = lookup_class (cls_list(i)); + + if (! error_state) + retval.push_back (c); + else + { + retval.clear (); + break; + } + } + + return retval; +} + +static octave_value +to_ov (const std::list<cdef_class>& class_list) +{ + Cell cls (class_list.size (), 1); + int i = 0; + + for (std::list<cdef_class>::const_iterator it = class_list.begin (); + it != class_list.end (); ++it, ++i) + cls(i) = to_ov (*it); + + return octave_value (cls); +} + +static bool +is_superclass (const cdef_class& clsa, const cdef_class& clsb, + bool allow_equal = true, int max_depth = -1) +{ + bool retval = false; + + if (allow_equal && clsa == clsb) + retval = true; + else if (max_depth != 0) + { + Cell c = clsb.get ("SuperClasses").cell_value (); + + for (int i = 0; ! error_state && ! retval && i < c.numel (); i++) + { + cdef_class cls = lookup_class (c(i)); + + if (! error_state) + retval = is_superclass (clsa, cls, true, + max_depth < 0 ? max_depth : max_depth-1); + } + } + + return retval; +} + +inline bool +is_strict_superclass (const cdef_class& clsa, const cdef_class& clsb) +{ return is_superclass (clsa, clsb, false); } + +inline bool +is_direct_superclass (const cdef_class& clsa, const cdef_class& clsb) +{ return is_superclass (clsa, clsb, false, 1); } + +static octave_value_list +class_get_properties (const octave_value_list& args, int /* nargout */) +{ + octave_value_list retval; + + if (args.length () == 1 && args(0).type_name () == "object") + { + cdef_class cls (to_cdef (args(0))); + + retval(0) = cls.get_properties (); + } + + return retval; +} + +static cdef_class +get_class_context (std::string& name, bool& in_constructor) +{ + cdef_class cls; + + octave_function* fcn = octave_call_stack::current (); + + in_constructor = false; + + if (fcn && + (fcn->is_class_method () + || fcn->is_classdef_constructor () + || fcn->is_anonymous_function_of_class () + || (fcn->is_private_function () + && ! fcn->dispatch_class ().empty ()))) + { + cls = lookup_class (fcn->dispatch_class ()); + if (! error_state) + { + name = fcn->name (); + in_constructor = fcn->is_classdef_constructor (); + } + } + + return cls; +} + +inline cdef_class +get_class_context (void) +{ + std::string dummy_string; + bool dummy_bool; + + return get_class_context (dummy_string, dummy_bool); +} + +static bool +check_access (const cdef_class& cls, const octave_value& acc) +{ + if (acc.is_string ()) + { + std::string acc_s = acc.string_value (); + + if (acc_s == "public") + return true; + + cdef_class ctx = get_class_context (); + + // The access is private or protected, this requires a + // valid class context. + + if (! error_state && ctx.ok ()) + { + if (acc_s == "private") + return (ctx == cls); + else if (acc_s == "protected") + return is_superclass (cls, ctx); + else + panic_impossible (); + } + } + else if (acc.is_cell ()) + { + Cell acc_c = acc.cell_value (); + + cdef_class ctx = get_class_context (); + + // At this point, a class context is always required. + + if (! error_state && ctx.ok ()) + { + if (ctx == cls) + return true; + + for (int i = 0; ! error_state && i < acc.numel (); i++) + { + cdef_class acc_cls (to_cdef (acc_c(i))); + + if (! error_state) + { + if (is_superclass (acc_cls, ctx)) + return true; + } + } + } + } + else + error ("invalid property/method access in class `%s'", + cls.get_name ().c_str ()); + + return false; +} + +static octave_value_list +class_get_methods (const octave_value_list& args, int /* nargout */) +{ + octave_value_list retval; + + if (args.length () == 1 && args(0).type_name () == "object") + { + cdef_class cls (to_cdef (args(0))); + + retval(0) = cls.get_methods (); + } + + return retval; +} + +static octave_value_list +class_get_superclasses (const octave_value_list& args, int /* nargout */) +{ + octave_value_list retval; + + if (args.length () == 1 && args(0).type_name () == "object" + && args(0).class_name () == "meta.class") + { + cdef_class cls (to_cdef (args(0))); + + Cell classes = cls.get ("SuperClasses").cell_value (); + + retval(0) = to_ov (lookup_classes (classes)); + } + + return retval; +} + +static octave_value_list +class_get_inferiorclasses (const octave_value_list& args, int /* nargout */) +{ + octave_value_list retval; + + if (args.length () == 1 && args(0).type_name () == "object" + && args(0).class_name () == "meta.class") + { + cdef_class cls (to_cdef (args(0))); + + Cell classes = cls.get ("InferiorClasses").cell_value (); + + retval(0) = to_ov (lookup_classes (classes)); + } + + return retval; +} + +static octave_value_list +class_fromName (const octave_value_list& args, int /* nargout */) +{ + octave_value_list retval; + + if (args.length () == 1) + { + std::string name = args(0).string_value (); + + if (! error_state) + retval(0) = to_ov (lookup_class (name)); + else + error ("fromName: invalid class name, expected a string value"); + } + else + error ("fromName: invalid number of parameters"); + + return retval; +} + +static octave_value_list +class_fevalStatic (const octave_value_list& args, int nargout) +{ + octave_value_list retval; + + if (args.length () > 1 && args(0).type_name () == "object") + { + cdef_class cls (to_cdef (args(0))); + + if (! error_state) + { + std::string meth_name = args(1).string_value (); + + if (! error_state) + { + cdef_method meth = cls.find_method (meth_name); + + if (meth.ok ()) + { + if (meth.is_static ()) + retval = meth.execute (args.splice (0, 2), nargout, + true, "fevalStatic"); + else + error ("fevalStatic: method `%s' is not static", + meth_name.c_str ()); + } + else + error ("fevalStatic: method not found: %s", + meth_name.c_str ()); + } + else + error ("fevalStatic: invalid method name, expected a string value"); + } + error ("fevalStatic: invalid object, expected a meta.class object"); + } + else + error ("fevalStatic: invalid arguments"); + + return retval; +} + +static octave_value_list +class_getConstant (const octave_value_list& args, int /* nargout */) +{ + octave_value_list retval; + + if (args.length () == 2 && args(0).type_name () == "object" + && args(0).class_name () == "meta.class") + { + cdef_class cls = to_cdef (args(0)); + + if (! error_state) + { + std::string prop_name = args(1).string_value (); + + if (! error_state) + { + cdef_property prop = cls.find_property (prop_name); + + if (prop.ok ()) + { + if (prop.is_constant ()) + retval(0) = prop.get_value (true, "getConstant"); + else + error ("getConstant: property `%s' is not constant", + prop_name.c_str ()); + } + else + error ("getConstant: property not found: %s", + prop_name.c_str ()); + } + else + error ("getConstant: invalid property name, expected a string value"); + } + else + error ("getConstant: invalid object, expected a meta.class object"); + } + else + error ("getConstant: invalid arguments"); + + return retval; +} + +#define META_CLASS_CMP(OP, CLSA, CLSB, FUN) \ +static octave_value_list \ +class_ ## OP (const octave_value_list& args, int /* nargout */) \ +{ \ + octave_value_list retval; \ +\ + if (args.length () == 2 \ + && args(0).type_name () == "object" && args(1).type_name () == "object" \ + && args(0).class_name () == "meta.class" && args(1).class_name () == "meta.class") \ + { \ + cdef_class clsa = to_cdef (args(0)); \ +\ + cdef_class clsb = to_cdef (args(1)); \ +\ + if (! error_state) \ + retval(0) = FUN (CLSA, CLSB); \ + else \ + error (#OP ": invalid objects, expected meta.class objects"); \ + } \ + else \ + error (#OP ": invalid arguments"); \ +\ + return retval; \ +} + +META_CLASS_CMP (lt, clsb, clsa, is_strict_superclass) +META_CLASS_CMP (le, clsb, clsa, is_superclass) +META_CLASS_CMP (gt, clsa, clsb, is_strict_superclass) +META_CLASS_CMP (ge, clsa, clsb, is_superclass) +META_CLASS_CMP (eq, clsa, clsb, operator==) +META_CLASS_CMP (ne, clsa, clsb, operator!=) + +octave_value_list +property_get_defaultvalue (const octave_value_list& args, int /* nargout */) +{ + octave_value_list retval; + + if (args.length () == 1 && args(0).type_name () == "object") + { + cdef_property prop (to_cdef (args(0))); + + retval(0) = prop.get ("DefaultValue"); + + if (! retval(0).is_defined ()) + error_with_id ("Octave:class:NotDefaultDefined", + "no default value for property `%s'", + prop.get_name ().c_str ()); + } + + return retval; +} + +static octave_value_list +handle_delete (const octave_value_list& /* args */, int /* nargout */) +{ + octave_value_list retval; + + // FIXME: implement this + + return retval; +} + +static cdef_class +make_class (const std::string& name, + const std::list<cdef_class>& super_list = std::list<cdef_class> ()) +{ + cdef_class cls (name, super_list); + + cls.set_class (cdef_class::meta_class ()); + cls.put ("Abstract", false); + cls.put ("ConstructOnLoad", false); + cls.put ("ContainingPackage", Matrix ()); + cls.put ("Description", std::string ()); + cls.put ("DetailedDescription", std::string ()); + cls.put ("Events", Cell ()); + cls.put ("Hidden", false); + cls.put ("InferiorClasses", Cell ()); + cls.put ("Methods", Cell ()); + cls.put ("Properties", Cell ()); + cls.put ("Sealed", false); + + if (name == "handle") + { + cls.put ("HandleCompatible", true); + cls.mark_as_handle_class (); + } + else if (super_list.empty ()) + { + cls.put ("HandleCompatible", false); + } + else + { + bool all_handle_compatible = true; + bool has_handle_class = false; + + for (std::list<cdef_class>::const_iterator it = super_list.begin (); + it != super_list.end (); ++it) + { + all_handle_compatible = all_handle_compatible && it->get ("HandleCompatible").bool_value (); + has_handle_class = has_handle_class || it->is_handle_class (); + } + + if (has_handle_class && ! all_handle_compatible) + ::error ("%s: cannot mix handle and non-HandleCompatible classes", + name.c_str ()); + else + { + cls.put ("HandleCompatible", all_handle_compatible); + if (has_handle_class) + cls.mark_as_handle_class (); + } + } + + if (error_state) + return cdef_class (); + + if (! name.empty ()) + cdef_manager::register_class (cls); + + return cls; +} + +static cdef_class +make_class (const std::string& name, const cdef_class& super) +{ + return make_class (name, std::list<cdef_class> (1, super)); +} + +static cdef_class +make_meta_class (const std::string& name, const cdef_class& super) +{ + cdef_class cls = make_class (name, super); + + cls.put ("Sealed", true); + cls.mark_as_meta_class (); + + return cls; +} + +static cdef_property +make_property (const cdef_class& cls, const std::string& name, + const octave_value& get_method = Matrix (), + const std::string& get_access = "public", + const octave_value& set_method = Matrix (), + const std::string& set_access = "public") +{ + cdef_property prop (name); + + prop.set_class (cdef_class::meta_property ()); + prop.put ("Description", std::string ()); + prop.put ("DetailedDescription", std::string ()); + prop.put ("Abstract", false); + prop.put ("Constant", false); + prop.put ("GetAccess", get_access); + prop.put ("SetAccess", set_access); + prop.put ("Dependent", false); + prop.put ("Transient", false); + prop.put ("Hidden", false); + prop.put ("GetObservable", false); + prop.put ("SetObservable", false); + prop.put ("GetMethod", get_method); + prop.put ("SetMethod", set_method); + prop.put ("DefiningClass", to_ov (cls)); + prop.put ("DefaultValue", octave_value ()); + prop.put ("HasDefault", false); + + std::string class_name = cls.get_name (); + + if (! get_method.is_empty ()) + make_function_of_class (class_name, get_method); + if (! set_method.is_empty ()) + make_function_of_class (class_name, set_method); + + return prop; +} + +inline cdef_property +make_attribute (const cdef_class& cls, const std::string& name) +{ + return make_property (cls, name, Matrix (), "public", Matrix (), "private"); +} + +static cdef_method +make_method (const cdef_class& cls, const std::string& name, + const octave_value& fcn,const std::string& m_access = "public", + bool is_static = false) +{ + cdef_method meth (name); + + meth.set_class (cdef_class::meta_method ()); + meth.put ("Abstract", false); + meth.put ("Access", m_access); + meth.put ("DefiningClass", to_ov (cls)); + meth.put ("Description", std::string ()); + meth.put ("DetailedDescription", std::string ()); + meth.put ("Hidden", false); + meth.put ("Sealed", true); + meth.put ("Static", is_static); + + if (fcn.is_defined ()) + make_function_of_class (cls, fcn); + + meth.set_function (fcn); + + return meth; +} + +inline cdef_method +make_method (const cdef_class& cls, const std::string& name, + octave_builtin::fcn ff, const std::string& m_access = "public", + bool is_static = false) +{ + octave_value fcn (new octave_builtin (ff, name)); + + return make_method (cls, name, fcn, m_access, is_static); +} + +static cdef_package +make_package (const std::string& nm, + const std::string& parent = std::string ()) +{ + cdef_package pack ("meta.package"); + + pack.set_class (cdef_class::meta_package ()); + pack.put ("Name", nm); + if (parent.empty ()) + pack.put ("ContainingPackage", Matrix ()); + else + pack.put ("ContainingPackage", to_ov (cdef_manager::find_package (parent))); + + if (! nm.empty ()) + cdef_manager::register_package (pack); + + return pack; +} + +//---------------------------------------------------------------------------- + +DEFINE_OCTAVE_ALLOCATOR (octave_classdef); + +int octave_classdef::t_id (-1); + +const std::string octave_classdef::t_name ("object"); + +void +octave_classdef::register_type (void) +{ + t_id = octave_value_typeinfo::register_type + (octave_classdef::t_name, "<unknown>", octave_value (new octave_classdef ())); +} + +octave_value_list +octave_classdef::subsref (const std::string& type, + const std::list<octave_value_list>& idx, + int nargout) +{ + size_t skip = 0; + octave_value_list retval; + + // FIXME: should check "subsref" method first + + retval = object.subsref (type, idx, nargout, skip, cdef_class ()); + + if (! error_state) + { + if (type.length () > skip && idx.size () > skip) + retval = retval(0).next_subsref (nargout, type, idx, skip); + } + + return retval; +} + +octave_value +octave_classdef::subsasgn (const std::string& type, + const std::list<octave_value_list>& idx, + const octave_value& rhs) +{ + return object.subsasgn (type, idx, rhs); +} + +octave_value +octave_classdef::undef_subsasgn (const std::string& type, + const std::list<octave_value_list>& idx, + const octave_value& rhs) +{ + if (type.length () == 1 && type[0] == '(') + { + object = object.make_array (); + + if (! error_state) + return subsasgn (type, idx, rhs); + } + else + return octave_base_value::undef_subsasgn (type, idx, rhs); + + return octave_value (); +} + +//---------------------------------------------------------------------------- + +class octave_classdef_meta : public octave_function +{ +public: + octave_classdef_meta (const cdef_meta_object& obj) + : object (obj) { } + + ~octave_classdef_meta (void) + { object.meta_release (); } + + octave_function* function_value (bool = false) { return this; } + + octave_value_list + subsref (const std::string& type, + const std::list<octave_value_list>& idx, + int nargout) + { return object.meta_subsref (type, idx, nargout); } + + octave_value + subsref (const std::string& type, + const std::list<octave_value_list>& idx) + { + octave_value_list retval; + + retval = subsref (type, idx, 1); + + return (retval.length () > 0 ? retval(0) : octave_value ()); + } + + octave_value_list + do_multi_index_op (int nargout, const octave_value_list& idx) + { + // Emulate ()-type meta subsref + + std::list<octave_value_list> l (1, idx); + std::string type ("("); + + return subsref (type, l, nargout); + } + + bool is_postfix_index_handled (char type) const + { return object.meta_is_postfix_index_handled (type); } + +private: + cdef_meta_object object; +}; + +//---------------------------------------------------------------------------- + +class octave_classdef_superclass_ref : public octave_function +{ +public: + octave_classdef_superclass_ref (const octave_value_list& a) + : octave_function (), args (a) { } + + ~octave_classdef_superclass_ref (void) { } + + octave_value_list + subsref (const std::string& type, + const std::list<octave_value_list>& idx, + int nargout) + { + size_t skip = 0; + octave_value_list retval; + + switch (type[0]) + { + case '(': + skip = 1; + retval = do_multi_index_op (type.length () > 1 ? 1 : nargout, + idx.front ()); + break; + default: + retval = do_multi_index_op (1, octave_value_list ()); + break; + } + + if (! error_state) + { + if (type.length () > skip && idx.size () > skip + && retval.length () > 0) + retval = retval(0).next_subsref (nargout, type, idx, skip); + } + + return retval; + } + + octave_value + subsref (const std::string& type, + const std::list<octave_value_list>& idx) + { + octave_value_list retval; + + retval = subsref (type, idx, 1); + + return (retval.length () > 0 ? retval(0) : octave_value ()); + } + + octave_value_list + do_multi_index_op (int nargout, const octave_value_list& idx) + { + octave_value_list retval; + + std::string meth_name; + bool in_constructor; + cdef_class ctx; + + ctx = get_class_context (meth_name, in_constructor); + + if (! error_state && ctx.ok ()) + { + std::string mname = args(0).string_value (); + std::string pname = args(1).string_value (); + std::string cname = args(2).string_value (); + + std::string cls_name = (pname.empty () ? + cname : pname + "." + cname); + cdef_class cls = lookup_class (cls_name); + + if (! error_state) + { + if (in_constructor) + { + if (is_direct_superclass (cls, ctx)) + { + if (is_constructed_object (mname)) + { + octave_value& sym = symbol_table::varref (mname); + + cls.run_constructor (to_cdef_ref (sym), idx); + + retval(0) = sym; + } + else + ::error ("cannot call superclass constructor with " + "variable `%s'", mname.c_str ()); + } + else + ::error ("`%s' is not a direct superclass of `%s'", + cls_name.c_str (), ctx.get_name ().c_str ()); + } + else + { + if (mname == meth_name) + { + if (is_strict_superclass (cls, ctx)) + { + // I see 2 possible implementations here: + // 1) use cdef_object::subsref with a different class + // context; this avoids duplicating codem but + // assumes the object is always the first argument + // 2) lookup the method manually and call + // cdef_method::execute; this duplicates part of + // logic in cdef_object::subsref, but avoid the + // assumption of 1) + // Not being sure about the assumption of 1), I + // go with option 2) for the time being. + + cdef_method meth = cls.find_method (meth_name, false); + + if (meth.ok ()) + retval = meth.execute (idx, nargout, true, + meth_name); + else + ::error ("no method `%s' found in superclass `%s'", + meth_name.c_str (), cls_name.c_str ()); + } + else + ::error ("`%s' is not a superclass of `%s'", + cls_name.c_str (), ctx.get_name ().c_str ()); + } + else + ::error ("method name mismatch (`%s' != `%s')", + mname.c_str (), meth_name.c_str ()); + } + } + } + else if (! error_state) + ::error ("superclass calls can only occur in methods or constructors"); + + return retval; + } + +private: + bool is_constructed_object (const std::string nm) + { + octave_function *of = octave_call_stack::current (); + + if (of->is_classdef_constructor ()) + { + octave_user_function *uf = of->user_function_value (true); + + if (uf) + { + tree_parameter_list *ret_list = uf->return_list (); + + if (ret_list && ret_list->length () == 1) + return (ret_list->front ()->name () == nm); + } + } + + return false; + } + +private: + octave_value_list args; +}; + +//---------------------------------------------------------------------------- + +string_vector +cdef_object_rep::map_keys (void) const +{ + cdef_class cls = get_class (); + + if (cls.ok ()) + return cls.get_names (); + + return string_vector (); +} + +octave_value_list +cdef_object_scalar::subsref (const std::string& type, + const std::list<octave_value_list>& idx, + int nargout, size_t& skip, + const cdef_class& context) +{ + skip = 0; + + cdef_class cls = (context.ok () ? context : get_class ()); + + octave_value_list retval; + + if (! cls.ok ()) + return retval; + + switch (type[0]) + { + case '.': + { + std::string name = (idx.front ())(0).string_value (); + + cdef_method meth = cls.find_method (name); + + if (meth.ok ()) + { + int _nargout = (type.length () > 2 ? 1 : nargout); + + octave_value_list args; + + skip = 1; + + if (type.length () > 1 && type[1] == '(') + { + std::list<octave_value_list>::const_iterator it = idx.begin (); + + args = *++it; + + skip++; + } + + if (meth.is_static ()) + retval = meth.execute (args, _nargout, true, "subsref"); + else + { + refcount++; + retval = meth.execute (cdef_object (this), args, _nargout, + true, "subsref"); + } + } + + if (skip == 0 && ! error_state) + { + cdef_property prop = cls.find_property (name); + + if (prop.ok ()) + { + if (prop.is_constant ()) + retval(0) = prop.get_value (true, "subsref"); + else + { + refcount++; + retval(0) = prop.get_value (cdef_object (this), + true, "subsref"); + } + + skip = 1; + } + else + error ("subsref: unknown method or property: %s", name.c_str ()); + } + break; + } + default: + error ("object cannot be indexed with `%c'", type[0]); + break; + } + + return retval; +} + +octave_value +cdef_object_scalar::subsasgn (const std::string& type, + const std::list<octave_value_list>& idx, + const octave_value& rhs) +{ + octave_value retval; + + cdef_class cls = get_class (); + + switch (type[0]) + { + case '.': + { + std::string name = (idx.front ())(0).string_value (); + + if (! error_state) + { + cdef_property prop = cls.find_property (name); + + if (prop.ok ()) + { + if (type.length () == 1) + { + refcount++; + + cdef_object obj (this); + + prop.set_value (obj, rhs, true, "subsasgn"); + + if (! error_state) + retval = to_ov (obj); + } + else + { + } + + if (! error_state) + { + } + } + else + error ("subsasgn: unknown property: %s", name.c_str ()); + } + } + break; + + default: + panic_impossible (); + break; + } + + return retval; +} + +void +cdef_object_scalar::mark_for_construction (const cdef_class& cls) +{ + std::string cls_name = cls.get_name (); + + Cell supcls = cls.get ("SuperClasses").cell_value (); + + if (! error_state) + { + std::list<cdef_class> supcls_list = lookup_classes (supcls); + + if (! error_state) + ctor_list[cls] = supcls_list; + } +} + +octave_value_list +cdef_object_array::subsref (const std::string& type, + const std::list<octave_value_list>& idx, + int /* nargout */, size_t& skip, + const cdef_class& /* context */) +{ + octave_value_list retval; + + skip = 1; + + switch (type[0]) + { + case '(': + { + const octave_value_list& ival = idx.front (); + bool is_scalar = true; + Array<idx_vector> iv (dim_vector (1, ival.length ())); + + for (int i = 0; ! error_state && i < ival.length (); i++) + { + iv(i) = ival(i).index_vector (); + if (! error_state) + is_scalar = is_scalar && iv(i).is_scalar (); + } + + if (! error_state) + { + Array<cdef_object> ires = array.index (iv); + + if (! error_state) + { + if (is_scalar) + retval(0) = to_ov (ires(0)); + else + { + cdef_object array_obj (new cdef_object_array (ires)); + + array_obj.set_class (get_class ()); + + retval(0) = to_ov (array_obj); + } + } + } + } + break; + + default: + ::error ("can't perform indexing operation on array of %s objects", + class_name ().c_str ()); + break; + } + + return retval; +} + +octave_value +cdef_object_array::subsasgn (const std::string& type, + const std::list<octave_value_list>& idx, + const octave_value& rhs) +{ + octave_value retval; + + switch (type[0]) + { + case '(': + if (type.length () == 1) + { + cdef_object rhs_obj = to_cdef (rhs); + + if (! error_state) + { + if (rhs_obj.get_class () == get_class ()) + { + const octave_value_list& ival = idx.front (); + bool is_scalar = true; + Array<idx_vector> iv (dim_vector (1, ival.length ())); + + for (int i = 0; ! error_state && i < ival.length (); i++) + { + iv(i) = ival(i).index_vector (); + if (! error_state) + is_scalar = is_scalar && iv(i).is_scalar (); + } + + if (! error_state) + { + Array<cdef_object> rhs_mat; + + if (! rhs_obj.is_array ()) + { + rhs_mat = Array<cdef_object> (dim_vector (1, 1)); + rhs_mat(0) = rhs_obj; + } + else + rhs_mat = rhs_obj.array_value (); + + if (! error_state) + { + octave_idx_type n = array.numel (); + + array.assign (iv, rhs_mat, cdef_object ()); + + if (! error_state) + { + if (array.numel () > n) + fill_empty_values (); + + if (! error_state) + { + refcount++; + retval = to_ov (cdef_object (this)); + } + } + } + } + } + else + ::error ("can't assign %s object into array of %s objects.", + rhs_obj.class_name ().c_str (), + class_name ().c_str ()); + } + } + else + ::error ("can't perform indexing operation on array of %s objects", + class_name ().c_str ()); + break; + + default: + ::error ("can't perform indexing operation on array of %s objects", + class_name ().c_str ()); + break; + } + + return retval; +} + +void +cdef_object_array::fill_empty_values (void) +{ + cdef_class cls = get_class (); + + if (! error_state) + { + cdef_object obj; + + int n = array.numel (); + + for (int i = 0; ! error_state && i < n; i++) + { + if (! array.xelem (i).ok ()) + { + if (! obj.ok ()) + { + obj = cls.construct_object (octave_value_list ()); + + if (! error_state) + array.xelem (i) = obj; + } + else + array.xelem (i) = obj.copy (); + } + } + } +} + +bool cdef_object_scalar::is_constructed_for (const cdef_class& cls) const +{ + return (is_constructed () + || ctor_list.find (cls) == ctor_list.end ()); +} + +bool cdef_object_scalar::is_partially_constructed_for (const cdef_class& cls) const +{ + std::map< cdef_class, std::list<cdef_class> >::const_iterator it; + + if (is_constructed ()) + return true; + else if ((it = ctor_list.find (cls)) == ctor_list.end () + || it->second.empty ()) + return true; + + for (std::list<cdef_class>::const_iterator lit = it->second.begin (); + lit != it->second.end (); ++lit) + if (! is_constructed_for (*lit)) + return false; + + return true; +} + +handle_cdef_object::~handle_cdef_object (void) +{ + gnulib::printf ("deleting %s object (handle)\n", + get_class ().get_name ().c_str ()); +} + +value_cdef_object::~value_cdef_object (void) +{ + gnulib::printf ("deleting %s object (value)\n", + get_class ().get_name ().c_str ()); +} + +cdef_class::cdef_class_rep::cdef_class_rep (const std::list<cdef_class>& superclasses) + : cdef_meta_object_rep (), member_count (0), handle_class (false), + object_count (0), meta (false) +{ + put ("SuperClasses", to_ov (superclasses)); + implicit_ctor_list = superclasses; +} + +cdef_method +cdef_class::cdef_class_rep::find_method (const std::string& nm, bool local) +{ + method_iterator it = method_map.find (nm); + + if (it == method_map.end ()) + { + // FIXME: look into class directory + } + else + { + cdef_method& meth = it->second; + + // FIXME: check if method reload needed + + if (meth.ok ()) + return meth; + } + + if (! local) + { + // Look into superclasses + + Cell super_classes = get ("SuperClasses").cell_value (); + + for (int i = 0; i < super_classes.numel (); i++) + { + cdef_class cls = lookup_class (super_classes(i)); + + if (! error_state) + { + cdef_method meth = cls.find_method (nm); + + if (meth.ok ()) + return meth; + } + } + } + + return cdef_method (); +} + +class ctor_analyzer : public tree_walker +{ +public: + ctor_analyzer (const std::string& ctor, const std::string& obj) + : tree_walker (), who (ctor), obj_name (obj) { } + + void visit_statement_list (tree_statement_list& t) + { + for (tree_statement_list::const_iterator it = t.begin (); + ! error_state && it != t.end (); ++it) + (*it)->accept (*this); + } + + void visit_statement (tree_statement& t) + { + if (t.is_expression ()) + t.expression ()->accept (*this); + } + + void visit_simple_assignment (tree_simple_assignment& t) + { + t.right_hand_side ()->accept (*this); + } + + void visit_multi_assignment (tree_multi_assignment& t) + { + t.right_hand_side ()->accept (*this); + } + + void visit_index_expression (tree_index_expression& t) + { + t.expression ()->accept (*this); + } + + void visit_funcall (tree_funcall& t) + { + octave_value fcn = t.function (); + + if (fcn.is_function ()) + { + octave_function *of = fcn.function_value (true); + + if (of) + { + if (of->name () == "__superclass_reference__") + { + octave_value_list args = t.arguments (); + + if (args(0).string_value () == obj_name) + { + std::string package_name = args(1).string_value (); + std::string class_name = args(2).string_value (); + + std::string ctor_name = (package_name.empty () + ? class_name + : package_name + "." + class_name); + + cdef_class cls = lookup_class (ctor_name, false); + + if (cls.ok ()) + ctor_list.push_back (cls); + } + } + } + } + } + + std::list<cdef_class> get_constructor_list (void) const + { return ctor_list; } + + // NO-OP + void visit_anon_fcn_handle (tree_anon_fcn_handle&) { } + void visit_argument_list (tree_argument_list&) { } + void visit_binary_expression (tree_binary_expression&) { } + void visit_break_command (tree_break_command&) { } + void visit_colon_expression (tree_colon_expression&) { } + void visit_continue_command (tree_continue_command&) { } + void visit_global_command (tree_global_command&) { } + void visit_persistent_command (tree_persistent_command&) { } + void visit_decl_elt (tree_decl_elt&) { } + void visit_decl_init_list (tree_decl_init_list&) { } + void visit_simple_for_command (tree_simple_for_command&) { } + void visit_complex_for_command (tree_complex_for_command&) { } + void visit_octave_user_script (octave_user_script&) { } + void visit_octave_user_function (octave_user_function&) { } + void visit_function_def (tree_function_def&) { } + void visit_identifier (tree_identifier&) { } + void visit_if_clause (tree_if_clause&) { } + void visit_if_command (tree_if_command&) { } + void visit_if_command_list (tree_if_command_list&) { } + void visit_switch_case (tree_switch_case&) { } + void visit_switch_case_list (tree_switch_case_list&) { } + void visit_switch_command (tree_switch_command&) { } + void visit_matrix (tree_matrix&) { } + void visit_cell (tree_cell&) { } + void visit_no_op_command (tree_no_op_command&) { } + void visit_constant (tree_constant&) { } + void visit_fcn_handle (tree_fcn_handle&) { } + void visit_parameter_list (tree_parameter_list&) { } + void visit_postfix_expression (tree_postfix_expression&) { } + void visit_prefix_expression (tree_prefix_expression&) { } + void visit_return_command (tree_return_command&) { } + void visit_return_list (tree_return_list&) { } + void visit_try_catch_command (tree_try_catch_command&) { } + void visit_unwind_protect_command (tree_unwind_protect_command&) { } + void visit_while_command (tree_while_command&) { } + void visit_do_until_command (tree_do_until_command&) { } + +private: + /* The name of the constructor being analyzed */ + std::string who; + + /* The name of the first output argument of the constructor */ + std::string obj_name; + + /* The list of superclass constructors that are explicitly called */ + std::list<cdef_class> ctor_list; +}; + +void +cdef_class::cdef_class_rep::install_method (const cdef_method& meth) +{ + method_map[meth.get_name ()] = meth; + + member_count++; + + if (meth.is_constructor ()) + { + // Analyze the constructor code to determine what superclass + // constructors are called explicitly. + + octave_function *of = meth.get_function ().function_value (true); + + if (of) + { + octave_user_function *uf = of->user_function_value (true); + + if (uf) + { + tree_parameter_list *ret_list = uf->return_list (); + tree_statement_list *body = uf->body (); + + if (ret_list && ret_list->size () == 1) + { + std::string obj_name = ret_list->front ()->name (); + ctor_analyzer a (meth.get_name (), obj_name); + + body->accept (a); + if (! error_state) + { + std::list<cdef_class> explicit_ctor_list + = a.get_constructor_list (); + + for (std::list<cdef_class>::const_iterator it = explicit_ctor_list.begin (); + ! error_state && it != explicit_ctor_list.end (); ++it) + { + gnulib::printf ("explicit superclass constructor: %s\n", + it->get_name ().c_str ()); + implicit_ctor_list.remove (*it); + } + } + } + else + ::error ("%s: invalid constructor output arguments", + meth.get_name ().c_str ()); + } + } + } +} + +void +cdef_class::cdef_class_rep::load_all_methods (void) +{ + // FIXME: re-scan class directory +} + +Cell +cdef_class::cdef_class_rep::get_methods (void) +{ + std::map<std::string,cdef_method> meths; + + find_methods (meths, false); + + if (! error_state) + { + Cell c (meths.size (), 1); + + int idx = 0; + + for (std::map<std::string,cdef_method>::const_iterator it = meths.begin (); + it != meths.end (); ++it, ++idx) + c (idx, 0) = to_ov (it->second); + + return c; + } + + return Cell (); +} + +void +cdef_class::cdef_class_rep::find_methods (std::map<std::string, cdef_method>& meths, + bool only_inherited) +{ + load_all_methods (); + + method_const_iterator it; + + for (it = method_map.begin (); it != method_map.end (); ++it) + { + if (! it->second.is_constructor ()) + { + std::string nm = it->second.get_name (); + + if (meths.find (nm) == meths.end ()) + { + if (only_inherited) + { + octave_value acc = it->second.get ("Access"); + + if (! acc.is_string () + || acc.string_value () == "private") + continue; + } + + meths[nm] = it->second; + } + } + } + + // Look into superclasses + + Cell super_classes = get ("SuperClasses").cell_value (); + + for (int i = 0; i < super_classes.numel (); i++) + { + cdef_class cls = lookup_class (super_classes(i)); + + if (! error_state) + cls.get_rep ()->find_methods (meths, true); + else + break; + } +} + +cdef_property +cdef_class::cdef_class_rep::find_property (const std::string& nm) +{ + property_iterator it = property_map.find (nm); + + if (it != property_map.end ()) + { + cdef_property& prop = it->second; + + if (prop.ok ()) + return prop; + } + + // Look into superclasses + + Cell super_classes = get ("SuperClasses").cell_value (); + + for (int i = 0; i < super_classes.numel (); i++) + { + cdef_class cls = lookup_class (super_classes(i)); + + if (! error_state) + { + cdef_property prop = cls.find_property (nm); + + if (prop.ok ()) + return prop; + } + } + + return cdef_property (); +} + +void +cdef_class::cdef_class_rep::install_property (const cdef_property& prop) +{ + property_map[prop.get_name ()] = prop; + + member_count++; +} + +Cell +cdef_class::cdef_class_rep::get_properties (void) +{ + std::map<std::string,cdef_property> props; + + find_properties (props, false); + + if (! error_state) + { + Cell c (props.size (), 1); + + int idx = 0; + + for (std::map<std::string,cdef_property>::const_iterator it = props.begin (); + it != props.end (); ++it, ++idx) + c (idx, 0) = to_ov (it->second); + + return c; + } + + return Cell (); +} + +void +cdef_class::cdef_class_rep::find_properties (std::map<std::string,cdef_property>& props, + bool only_inherited) +{ + property_const_iterator it; + + for (it = property_map.begin (); ! error_state && it != property_map.end (); + ++it) + { + std::string nm = it->second.get_name (); + + if (props.find (nm) == props.end ()) + { + if (only_inherited) + { + octave_value acc = it->second.get ("GetAccess"); + + if (! acc.is_string () + || acc.string_value () == "private") + continue; + } + + props[nm] = it->second; + } + } + + // Look into superclasses + + Cell super_classes = get ("SuperClasses").cell_value (); + + for (int i = 0; ! error_state && i < super_classes.numel (); i++) + { + cdef_class cls = lookup_class (super_classes(i)); + + if (! error_state) + cls.get_rep ()->find_properties (props, true); + else + break; + } +} + +void +cdef_class::cdef_class_rep::find_names (std::set<std::string>& names, + bool all) +{ + load_all_methods (); + + for (method_const_iterator it = method_map.begin (); + ! error_state && it != method_map.end(); ++it) + { + if (! it->second.is_constructor ()) + { + std::string nm = it->second.get_name (); + + if (! all) + { + octave_value acc = it->second.get ("Access"); + + if (! acc.is_string() + || acc.string_value () != "public") + continue; + } + + names.insert (nm); + } + } + + for (property_const_iterator it = property_map.begin (); + ! error_state && it != property_map.end (); ++it) + { + std::string nm = it->second.get_name (); + + if (! all) + { + octave_value acc = it->second.get ("GetAccess"); + + if (! acc.is_string() + || acc.string_value () != "public") + continue; + } + + names.insert (nm); + } + + // Look into superclasses + + Cell super_classes = get ("SuperClasses").cell_value (); + + for (int i = 0; ! error_state && i < super_classes.numel (); i++) + { + cdef_class cls = lookup_class (super_classes(i)); + + if (! error_state) + cls.get_rep ()->find_names (names, all); + else + break; + } +} + +string_vector +cdef_class::cdef_class_rep::get_names (void) +{ + std::set<std::string> names; + + find_names (names, false); + + if (! error_state) + { + string_vector v (names.size ()); + + int idx = 0; + for (std::set<std::string>::const_iterator it = names.begin (); + it != names.end (); ++it, ++idx) + v[idx] = *it; + + return v.sort (true); + } + + return string_vector (); +} + +void +cdef_class::cdef_class_rep::delete_object (cdef_object obj) +{ + method_iterator it = method_map.find ("delete"); + + if (it != method_map.end ()) + { + cdef_class cls = obj.get_class (); + + obj.set_class (wrap ()); + + it->second.execute (obj, octave_value_list (), 0, false); + + obj.set_class (cls); + } + + // FIXME: should we destroy corresponding properties here? + + // Call "delete" in super classes + + Cell super_classes = get ("SuperClasses").cell_value (); + + for (int i = 0; i < super_classes.numel (); i++) + { + cdef_class cls = lookup_class (super_classes(i)); + + if (!error_state) + cls.delete_object (obj); + } +} + +octave_value_list +cdef_class::cdef_class_rep::meta_subsref (const std::string& type, + const std::list<octave_value_list>& idx, + int nargout) +{ + size_t skip = 1; + + octave_value_list retval; + + switch (type[0]) + { + case '(': + // Constructor call + gnulib::printf ("constructor\n"); + retval(0) = construct (idx.front ()); + break; + + case '.': + // Static method, constant (or property?) + gnulib::printf ("static method/property\n"); + if (idx.front ().length () == 1) + { + std::string nm = idx.front ()(0).string_value (); + + if (! error_state) + { + cdef_method meth = find_method (nm); + + if (meth.ok ()) + { + if (meth.is_static ()) + { + octave_value_list args; + + if (type.length () > 1 && idx.size () > 1 + && type[1] == '(') + { + args = *(++(idx.begin ())); + skip++; + } + + retval = meth.execute (args, (type.length () > skip + ? 1 : nargout), true, + "meta.class"); + } + else + ::error ("method `%s' is not static", nm.c_str ()); + } + else + { + cdef_property prop = find_property (nm); + + if (prop.ok ()) + { + if (prop.is_constant ()) + retval(0) = prop.get_value (true, "meta.class"); + else + ::error ("property `%s' is not constant", + nm.c_str ()); + } + else + ::error ("no such method or property `%s'", nm.c_str ()); + } + } + else + ::error ("invalid meta.class indexing, expected a method or property name"); + } + else + ::error ("invalid meta.class indexing"); + break; + + default: + ::error ("invalid meta.class indexing"); + break; + } + + if (! error_state) + { + if (type.length () > skip && idx.size () > skip && ! retval.empty ()) + retval = retval(0).next_subsref (nargout, type, idx, skip); + } + + return retval; +} + +void +cdef_class::cdef_class_rep::meta_release (void) +{ + cdef_manager::unregister_class (wrap ()); +} + +void +cdef_class::cdef_class_rep::initialize_object (cdef_object& obj) +{ + // Populate the object with default property values + + std::list<cdef_class> super_classes = lookup_classes (get ("SuperClasses").cell_value ()); + + if (! error_state) + { + for (std::list<cdef_class>::iterator it = super_classes.begin (); + ! error_state && it != super_classes.end (); ++it) + it->initialize_object (obj); + + if (! error_state) + { + for (property_const_iterator it = property_map.begin (); + ! error_state && it != property_map.end (); ++it) + { + if (! it->second.get ("Dependent").bool_value ()) + { + octave_value pvalue = it->second.get ("DefaultValue"); + + if (pvalue.is_defined ()) + obj.put (it->first, pvalue); + else + obj.put (it->first, octave_value (Matrix ())); + } + } + + if (! error_state) + { + refcount++; + obj.mark_for_construction (cdef_class (this)); + } + } + } +} + +void +cdef_class::cdef_class_rep::run_constructor (cdef_object& obj, + const octave_value_list& args) +{ + octave_value_list empty_args; + + for (std::list<cdef_class>::const_iterator it = implicit_ctor_list.begin (); + ! error_state && it != implicit_ctor_list.end (); ++it) + { + cdef_class supcls = lookup_class (*it); + + if (! error_state) + supcls.run_constructor (obj, empty_args); + } + + if (error_state) + return; + + std::string cls_name = get_name (); + std::string ctor_name = get_base_name (cls_name); + + cdef_method ctor = find_method (ctor_name); + + if (ctor.ok ()) + { + octave_value_list ctor_args (args); + octave_value_list ctor_retval; + + ctor_args.prepend (to_ov (obj)); + ctor_retval = ctor.execute (ctor_args, 1, true, "constructor"); + + if (! error_state) + { + if (ctor_retval.length () == 1) + obj = to_cdef (ctor_retval(0)); + else + { + ::error ("%s: invalid number of output arguments for classdef constructor", + ctor_name.c_str ()); + return; + } + } + } + + obj.mark_as_constructed (wrap ()); +} + +octave_value +cdef_class::cdef_class_rep::construct (const octave_value_list& args) +{ + cdef_object obj = construct_object (args); + + if (! error_state && obj.ok ()) + return to_ov (obj); + + return octave_value (); +} + +cdef_object +cdef_class::cdef_class_rep::construct_object (const octave_value_list& args) +{ + if (! is_abstract ()) + { + cdef_object obj; + + if (is_meta_class ()) + { + // This code path is only used to create empty meta objects + // as filler for the empty values within a meta object array. + + cdef_class this_cls = wrap (); + + static cdef_object empty_class; + + if (this_cls == cdef_class::meta_class ()) + { + if (! empty_class.ok ()) + empty_class = make_class ("", std::list<cdef_class> ()); + obj = empty_class; + } + else if (this_cls == cdef_class::meta_property ()) + { + static cdef_property empty_property; + + if (! empty_class.ok ()) + empty_class = make_class ("", std::list<cdef_class> ()); + if (! empty_property.ok ()) + empty_property = make_property (empty_class, ""); + obj = empty_property; + } + else if (this_cls == cdef_class::meta_method ()) + { + static cdef_method empty_method; + + if (! empty_class.ok ()) + empty_class = make_class ("", std::list<cdef_class> ()); + if (! empty_method.ok ()) + empty_method = make_method (empty_class, "", octave_value ()); + obj = empty_method; + } + else if (this_cls == cdef_class::meta_package ()) + { + static cdef_package empty_package; + + if (! empty_package.ok ()) + empty_package = make_package (""); + obj = empty_package; + } + else + panic_impossible (); + + return obj; + } + else + { + if (is_handle_class ()) + obj = cdef_object (new handle_cdef_object ()); + else + obj = cdef_object (new value_cdef_object ()); + obj.set_class (wrap ()); + + initialize_object (obj); + + if (! error_state) + { + run_constructor (obj, args); + + if (! error_state) + return obj; + } + } + } + else + error ("cannot instantiate object for abstract class `%s'", + get_name ().c_str ()); + + return cdef_object (); +} + +static octave_value +compute_attribute_value (tree_classdef_attribute* t) +{ + if (t->expression ()) + { + if (t->expression ()->is_identifier ()) + { + std::string s = t->expression ()->name (); + + if (s == "public") + return std::string ("public"); + else if (s == "protected") + return std::string ("protected"); + else if (s == "private") + return std::string ("private"); + } + + return t->expression ()->rvalue1 (); + } + else + return octave_value (true); +} + +template<class T> +static std::string +attribute_value_to_string (T* t, octave_value v) +{ + if (v.is_string ()) + return v.string_value (); + else if (t->expression ()) + return t->expression ()->original_text (); + else + return std::string ("true"); +} + +cdef_class +cdef_class::make_meta_class (tree_classdef* t) +{ + cdef_class retval; + std::string class_name; + + // Class creation + + class_name = t->ident ()->name (); + gnulib::printf ("class: %s\n", class_name.c_str ()); + + std::list<cdef_class> slist; + + if (t->superclass_list ()) + { + for (tree_classdef_superclass_list::iterator it = t->superclass_list ()->begin (); + ! error_state && it != t->superclass_list ()->end (); ++it) + { + std::string sclass_name = + ((*it)->package () ? (*it)->package ()->name () + "." : std::string ()) + + (*it)->ident ()->name (); + + gnulib::printf ("superclass: %s\n", sclass_name.c_str ()); + + cdef_class sclass = lookup_class (sclass_name); + + if (! error_state) + { + if (! sclass.get ("Sealed").bool_value ()) + slist.push_back (sclass); + else + { + ::error ("`%s' cannot inherit from `%s', because it is sealed", + class_name.c_str (), sclass_name.c_str ()); + return retval; + } + } + else + return retval; + + } + } + + retval = ::make_class (class_name, slist); + + if (error_state) + return cdef_class (); + + // Class attributes + + if (t->attribute_list ()) + { + for (tree_classdef_attribute_list::iterator it = t->attribute_list ()->begin (); + it != t->attribute_list ()->end (); ++it) + { + std::string aname = (*it)->ident ()->name (); + octave_value avalue = compute_attribute_value (*it); + + gnulib::printf ("class attribute: %s = %s\n", aname.c_str (), + attribute_value_to_string (*it, avalue).c_str ()); + retval.put (aname, avalue); + } + } + + tree_classdef_body* b = t->body (); + + if (b) + { + // Method blocks + + std::list<tree_classdef_methods_block *> mb_list = b->methods_list (); + + for (tree_classdef_body::methods_list_iterator it = mb_list.begin (); + it != mb_list.end (); ++it) + { + std::map<std::string, octave_value> amap; + gnulib::printf ("method block\n"); + + // Method attributes + + if ((*it)->attribute_list ()) + { + for (tree_classdef_attribute_list::iterator ait = (*it)->attribute_list ()->begin (); + ait != (*it)->attribute_list ()->end (); ++ait) + { + std::string aname = (*ait)->ident ()->name (); + octave_value avalue = compute_attribute_value (*ait); + + gnulib::printf ("method attribute: %s = %s\n", aname.c_str (), + attribute_value_to_string (*ait, avalue).c_str ()); + amap[aname] = avalue; + } + } + + // Methods + + if ((*it)->element_list ()) + { + for (tree_classdef_methods_list::iterator mit = (*it)->element_list ()->begin (); + mit != (*it)->element_list ()->end (); ++mit) + { + std::string mname = mit->function_value ()->name (); + cdef_method meth = make_method (retval, mname, *mit); + + gnulib::printf ("%s: %s\n", (mname == class_name ? "constructor" : "method"), + mname.c_str ()); + for (std::map<std::string, octave_value>::iterator ait = amap.begin (); + ait != amap.end (); ++ait) + meth.put (ait->first, ait->second); + + retval.install_method (meth); + } + } + } + + // Property blocks + + // FIXME: default property expression should be able to call static + // methods of the class being constructed. A restricted CLASSNAME + // symbol should be added to the scope before evaluating default + // value expressions. + + std::list<tree_classdef_properties_block *> pb_list = b->properties_list (); + + for (tree_classdef_body::properties_list_iterator it = pb_list.begin (); + it != pb_list.end (); ++it) + { + std::map<std::string, octave_value> amap; + gnulib::printf ("property block\n"); + + // Property attributes + + if ((*it)->attribute_list ()) + { + for (tree_classdef_attribute_list::iterator ait = (*it)->attribute_list ()->begin (); + ait != (*it)->attribute_list ()->end (); ++ait) + { + std::string aname = (*ait)->ident ()->name (); + octave_value avalue = compute_attribute_value (*ait); + + gnulib::printf ("property attribute: %s = %s\n", aname.c_str (), + attribute_value_to_string (*ait, avalue).c_str ()); + if (aname == "Access") + { + amap["GetAccess"] = avalue; + amap["SetAccess"] = avalue; + } + else + amap[aname] = avalue; + } + } + + // Properties + + if ((*it)->element_list ()) + { + for (tree_classdef_property_list::iterator pit = (*it)->element_list ()->begin (); + pit != (*it)->element_list ()->end (); ++pit) + { + cdef_property prop = ::make_property (retval, (*pit)->ident ()->name ()); + + gnulib::printf ("property: %s\n", (*pit)->ident ()->name ().c_str ()); + if ((*pit)->expression ()) + { + octave_value pvalue = (*pit)->expression ()->rvalue1 (); + + gnulib::printf ("property default: %s\n", + attribute_value_to_string (*pit, pvalue).c_str ()); + prop.put ("DefaultValue", pvalue); + } + + for (std::map<std::string, octave_value>::iterator ait = amap.begin (); + ait != amap.end (); ++ait) + prop.put (ait->first, ait->second); + + retval.install_property (prop); + } + } + } + } + + return retval; +} + +octave_function* +cdef_class::get_method_function (const std::string& /* nm */) +{ + octave_classdef_meta* p = new octave_classdef_meta (*this); + + return p; +} + +octave_value +cdef_property::cdef_property_rep::get_value (const cdef_object& obj, + bool do_check_access, + const std::string& who) +{ + octave_value retval; + + if (do_check_access && ! check_get_access ()) + { + gripe_property_access (who, wrap (), false); + + return retval; + } + + if (! obj.is_constructed ()) + { + cdef_class cls (to_cdef (get ("DefiningClass"))); + + if (! obj.is_partially_constructed_for (cls)) + { + ::error ("cannot reference properties of class `%s' for non-constructed object", + cls.get_name ().c_str ()); + return retval; + } + } + + octave_value get_fcn = get ("GetMethod"); + + // FIXME: should check whether we're already in get accessor method + + if (get_fcn.is_empty ()) + retval = obj.get (get ("Name").string_value ()); + else + { + octave_value_list args; + + args(0) = to_ov (obj); + + args = execute_ov (get_fcn, args, 1); + + if (! error_state) + retval = args(0); + } + + return retval; +} + +octave_value +cdef_property::cdef_property_rep::get_value (bool do_check_access, + const std::string& who) +{ + if (do_check_access && ! check_get_access ()) + { + gripe_property_access (who, wrap (), false); + + return octave_value (); + } + + return get ("DefaultValue"); +} + +bool +cdef_property::cdef_property_rep::is_recursive_set (const cdef_object& /* obj */) const +{ + // FIXME: implement + return false; +} + +void +cdef_property::cdef_property_rep::set_value (cdef_object& obj, + const octave_value& val, + bool do_check_access, + const std::string& who) +{ + if (do_check_access && ! check_set_access ()) + { + gripe_property_access (who, wrap (), true); + + return; + } + + if (! obj.is_constructed ()) + { + cdef_class cls (to_cdef (get ("DefiningClass"))); + + if (! obj.is_partially_constructed_for (cls)) + { + ::error ("cannot reference properties of class `%s' for non-constructed object", + cls.get_name ().c_str ()); + return; + } + } + + octave_value set_fcn = get ("SetMethod"); + + if (set_fcn.is_empty () || is_recursive_set (obj)) + { + obj.put (get ("Name").string_value (), val); + } + else + { + octave_value_list args; + + args(0) = to_ov (obj); + args(1) = val; + + args = execute_ov (set_fcn, args, 1); + + if (! error_state) + { + if (args.length() > 0) + { + cdef_object new_obj = to_cdef (args(0)); + + if (! error_state) + obj = new_obj; + } + } + } +} + +bool +cdef_property::cdef_property_rep::check_get_access (void) const +{ + cdef_class cls (to_cdef (get ("DefiningClass"))); + + if (! error_state) + return ::check_access (cls, get ("GetAccess")); + + return false; +} + +bool +cdef_property::cdef_property_rep::check_set_access (void) const +{ + cdef_class cls (to_cdef (get ("DefiningClass"))); + + if (! error_state) + return ::check_access (cls, get ("SetAccess")); + + return false; +} + +void +cdef_method::cdef_method_rep::check_method (void) +{ + // FIXME: check whether re-load is needed +} + +octave_value_list +cdef_method::cdef_method_rep::execute (const octave_value_list& args, + int nargout, bool do_check_access, + const std::string& who) +{ + octave_value_list retval; + + if (do_check_access && ! check_access ()) + { + gripe_method_access (who, wrap ()); + + return retval; + } + + if (! get ("Abstract").bool_value ()) + { + check_method (); + + if (function.is_defined ()) + { + retval = execute_ov (function, args, nargout); + } + } + else + error ("%s: cannot execute abstract method", + get ("Name").string_value ().c_str ()); + + return retval; +} + +octave_value_list +cdef_method::cdef_method_rep::execute (const cdef_object& obj, + const octave_value_list& args, + int nargout, bool do_check_access, + const std::string& who) +{ + octave_value_list retval; + + if (do_check_access && ! check_access ()) + { + gripe_method_access (who, wrap ()); + + return retval; + } + + if (! get ("Abstract").bool_value ()) + { + check_method (); + + octave_value_list new_args; + + if (function.is_defined ()) + { + new_args.resize (args.length () + 1); + + new_args(0) = to_ov (obj); + for (int i = 0; i < args.length (); i++) + new_args(i+1) = args(i); + + retval = execute_ov (function, new_args, nargout); + } + } + else + error ("%s: cannot execute abstract method", + get ("Name").string_value ().c_str ()); + + return retval; +} + +bool +cdef_method::cdef_method_rep::is_constructor (void) const +{ + if (function.is_function()) + return function.function_value ()->is_classdef_constructor (); + + return false; +} + +bool +cdef_method::cdef_method_rep::check_access (void) const +{ + cdef_class cls (to_cdef (get ("DefiningClass"))); + + if (! error_state) + return ::check_access (cls, get ("Access")); + + return false; +} + +octave_value_list +cdef_method::cdef_method_rep::meta_subsref + (const std::string& type, const std::list<octave_value_list>& idx, + int nargout) +{ + octave_value_list retval; + + switch (type[0]) + { + case '(': + retval = execute (idx.front (), type.length () > 1 ? 1 : nargout, true); + break; + + default: + error ("invalid meta.method indexing"); + break; + } + + if (! error_state) + { + if (type.length () > 1 && idx.size () > 1 && ! retval.empty ()) + retval = retval(0).next_subsref (nargout, type, idx, 1); + } + + return retval; +} + +static cdef_package +lookup_package (const std::string& name) +{ + return cdef_manager::find_package (name); +} + +static octave_value_list +package_fromName (const octave_value_list& args, int /* nargout */) +{ + octave_value_list retval; + + if (args.length () == 1) + { + std::string name = args(0).string_value (); + + if (! error_state) + retval(0) = to_ov (lookup_package (name)); + else + error ("fromName: invalid package name, expected a string value"); + } + else + error ("fromName: invalid number of parameters"); + + return retval; +} + +static octave_value_list +package_get_classes (const octave_value_list& args, int /* nargout */) +{ + octave_value_list retval (1, Matrix ()); + + if (args.length () == 1 && args(0).type_name () == "object" + && args(0).class_name () == "meta.package") + { + cdef_package pack (to_cdef (args(0))); + + retval(0) = pack.get_classes (); + } + + return retval; +} + +static octave_value_list +package_get_functions (const octave_value_list& args, int /* nargout */) +{ + octave_value_list retval (1, Matrix ()); + + if (args.length () == 0 && args(0).type_name () == "object" + && args(0).class_name () == "meta.package") + { + cdef_package pack (to_cdef (args(0))); + + retval(0) = pack.get_functions (); + } + + return retval; +} + +static octave_value_list +package_get_packages (const octave_value_list& args, int /* nargout */) +{ + octave_value_list retval (1, Matrix ()); + + if (args.length () == 0 && args(0).type_name () == "object" + && args(0).class_name () == "meta.package") + { + cdef_package pack (to_cdef (args(0))); + + retval(0) = pack.get_packages (); + } + + return retval; +} + +void +cdef_package::cdef_package_rep::install_class (const cdef_class& cls, + const std::string& nm) +{ + class_map[nm] = cls; + + member_count++; +} + +void +cdef_package::cdef_package_rep::install_function (const octave_value& fcn, + const std::string& nm) +{ + function_map[nm] = fcn; +} + +void +cdef_package::cdef_package_rep::install_package (const cdef_package& pack, + const std::string& nm) +{ + package_map[nm] = pack; + + member_count++; +} + +template<class T1, class T2> +Cell +map2Cell (const std::map<T1, T2>& m) +{ + Cell retval (1, m.size ()); + int i = 0; + + for (typename std::map<T1, T2>::const_iterator it = m.begin (); + it != m.end (); ++it, ++i) + { + retval(i) = to_ov (it->second); + } + + return retval; +} + +Cell +cdef_package::cdef_package_rep::get_classes (void) const +{ return map2Cell (class_map); } + +Cell +cdef_package::cdef_package_rep::get_functions (void) const +{ return map2Cell (function_map); } + +Cell +cdef_package::cdef_package_rep::get_packages (void) const +{ return map2Cell (package_map); } + +cdef_class cdef_class::_meta_class = cdef_class (); +cdef_class cdef_class::_meta_property = cdef_class (); +cdef_class cdef_class::_meta_method = cdef_class (); +cdef_class cdef_class::_meta_package = cdef_class (); + +cdef_package cdef_package::_meta = cdef_package (); + +void +install_classdef (void) +{ + octave_classdef::register_type (); + + /* bootstrap */ + cdef_class handle = make_class ("handle"); + cdef_class meta_class = cdef_class::_meta_class = make_meta_class ("meta.class", handle); + handle.set_class (meta_class); + meta_class.set_class (meta_class); + + /* meta classes */ + cdef_class meta_property = cdef_class::_meta_property = make_meta_class ("meta.property", handle); + cdef_class meta_method = cdef_class::_meta_method = make_meta_class ("meta.method", handle); + cdef_class meta_package = cdef_class::_meta_package = make_meta_class ("meta.package", handle); + + cdef_class meta_event = make_meta_class ("meta.event", handle); + cdef_class meta_dynproperty = make_meta_class ("meta.dynamicproperty", handle); + + /* meta.class properties */ + meta_class.install_property (make_attribute (meta_class, "Abstract")); + meta_class.install_property (make_attribute (meta_class, "ConstructOnLoad")); + meta_class.install_property (make_property (meta_class, "ContainingPackage")); + meta_class.install_property (make_property (meta_class, "Description")); + meta_class.install_property (make_property (meta_class, "DetailedDescription")); + meta_class.install_property (make_property (meta_class, "Events")); + meta_class.install_property (make_attribute (meta_class, "HandleCompatible")); + meta_class.install_property (make_attribute (meta_class, "Hidden")); + meta_class.install_property + (make_property (meta_class, "InferiorClasses", + make_fcn_handle (class_get_inferiorclasses, "meta.class>get.InferiorClasses"), + "public", Matrix (), "private")); + meta_class.install_property + (make_property (meta_class, "Methods", + make_fcn_handle (class_get_methods, "meta.class>get.Methods"), + "public", Matrix (), "private")); + meta_class.install_property + (make_property (meta_class, "MethodList", + make_fcn_handle (class_get_methods, "meta.class>get.MethodList"), + "public", Matrix (), "private")); + meta_class.install_property (make_attribute (meta_class, "Name")); + meta_class.install_property + (make_property (meta_class, "Properties", + make_fcn_handle (class_get_properties, "meta.class>get.Properties"), + "public", Matrix (), "private")); + meta_class.install_property + (make_property (meta_class, "PropertyList", + make_fcn_handle (class_get_properties, "meta.class>get.PropertyList"), + "public", Matrix (), "private")); + meta_class.install_property (make_attribute (meta_class, "Sealed")); + meta_class.install_property + (make_property (meta_class, "SuperClasses", + make_fcn_handle (class_get_superclasses, "meta.class>get.SuperClasses"), + "public", Matrix (), "private")); + meta_class.install_property + (make_property (meta_class, "SuperClassList", + make_fcn_handle (class_get_superclasses, "meta.class>get.SuperClassList"), + "public", Matrix (), "private")); + /* meta.class methods */ + meta_class.install_method (make_method (meta_class, "fromName", class_fromName, + "public", true)); + meta_class.install_method (make_method (meta_class, "fevalStatic", class_fevalStatic, + "public", false)); + meta_class.install_method (make_method (meta_class, "getConstant", class_getConstant, + "public", false)); + meta_class.install_method (make_method (meta_class, "eq", class_eq)); + meta_class.install_method (make_method (meta_class, "ne", class_ne)); + meta_class.install_method (make_method (meta_class, "lt", class_lt)); + meta_class.install_method (make_method (meta_class, "le", class_le)); + meta_class.install_method (make_method (meta_class, "gt", class_gt)); + meta_class.install_method (make_method (meta_class, "ge", class_ge)); + + /* meta.method properties */ + meta_method.install_property (make_attribute (meta_method, "Abstract")); + meta_method.install_property (make_attribute (meta_method, "Access")); + meta_method.install_property (make_attribute (meta_method, "DefiningClass")); + meta_method.install_property (make_attribute (meta_method, "Description")); + meta_method.install_property (make_attribute (meta_method, "DetailedDescription")); + meta_method.install_property (make_attribute (meta_method, "Hidden")); + meta_method.install_property (make_attribute (meta_method, "Name")); + meta_method.install_property (make_attribute (meta_method, "Sealed")); + meta_method.install_property (make_attribute (meta_method, "Static")); + + /* meta.property properties */ + meta_property.install_property (make_attribute (meta_property, "Name")); + meta_property.install_property (make_attribute (meta_property, "Description")); + meta_property.install_property (make_attribute (meta_property, "DetailedDescription")); + meta_property.install_property (make_attribute (meta_property, "Abstract")); + meta_property.install_property (make_attribute (meta_property, "Constant")); + meta_property.install_property (make_attribute (meta_property, "GetAccess")); + meta_property.install_property (make_attribute (meta_property, "SetAccess")); + meta_property.install_property (make_attribute (meta_property, "Dependent")); + meta_property.install_property (make_attribute (meta_property, "Transient")); + meta_property.install_property (make_attribute (meta_property, "Hidden")); + meta_property.install_property (make_attribute (meta_property, "GetObservable")); + meta_property.install_property (make_attribute (meta_property, "SetObservable")); + meta_property.install_property (make_attribute (meta_property, "GetMethod")); + meta_property.install_property (make_attribute (meta_property, "SetMethod")); + meta_property.install_property (make_attribute (meta_property, "DefiningClass")); + meta_property.install_property + (make_property (meta_property, "DefaultValue", + make_fcn_handle (property_get_defaultvalue, "meta.property>get.DefaultValue"), + "public", Matrix (), "private")); + meta_property.install_property (make_attribute (meta_property, "HasDefault")); + /* meta.property events */ + // FIXME: add events + + /* handle methods */ + handle.install_method (make_method (handle, "delete", handle_delete)); + + /* meta.package properties */ + meta_package.install_property (make_attribute (meta_package, "Name")); + meta_package.install_property (make_property (meta_package, "ContainingPackage")); + meta_package.install_property + (make_property (meta_package, "ClassList", + make_fcn_handle (package_get_classes, "meta.package>get.ClassList"), + "public", Matrix (), "private")); + meta_package.install_property + (make_property (meta_package, "Classes", + make_fcn_handle (package_get_classes, "meta.package>get.Classes"), + "public", Matrix (), "private")); + meta_package.install_property + (make_property (meta_package, "FunctionList", + make_fcn_handle (package_get_functions, "meta.package>get.FunctionList"), + "public", Matrix (), "private")); + meta_package.install_property + (make_property (meta_package, "Functions", + make_fcn_handle (package_get_functions, "meta.package>get.Functions"), + "public", Matrix (), "private")); + meta_package.install_property + (make_property (meta_package, "PackageList", + make_fcn_handle (package_get_packages, "meta.package>get.PackageList"), + "public", Matrix (), "private")); + meta_package.install_property + (make_property (meta_package, "Packages", + make_fcn_handle (package_get_packages, "meta.package>get.Packages"), + "public", Matrix (), "private")); + meta_package.install_method (make_method (meta_package, "fromName", package_fromName, + "public", true)); + + /* create "meta" package */ + cdef_package package_meta = cdef_package::_meta = make_package ("meta"); + package_meta.install_class (meta_class, "class"); + package_meta.install_class (meta_property, "property"); + package_meta.install_class (meta_method, "method"); + package_meta.install_class (meta_package, "package"); + package_meta.install_class (meta_event, "event"); + package_meta.install_class (meta_dynproperty, "dynproperty"); +} + +//---------------------------------------------------------------------------- + +cdef_manager* cdef_manager::instance = 0; + +void +cdef_manager::create_instance (void) +{ + instance = new cdef_manager (); + + if (instance) + singleton_cleanup_list::add (cleanup_instance); +} + +cdef_class +cdef_manager::do_find_class (const std::string& name, + bool error_if_not_found, bool load_if_not_found) +{ + std::map<std::string, cdef_class>::iterator it = all_classes.find (name); + + if (it == all_classes.end ()) + { + // FIXME: implement this properly, take package prefix into account + + if (load_if_not_found) + { + octave_value ov_cls = symbol_table::find (name); + + if (ov_cls.is_defined ()) + it = all_classes.find (name); + } + } + + if (it == all_classes.end ()) + { + if (error_if_not_found) + error ("class not found: %s", name.c_str ()); + } + else + { + cdef_class cls = it->second; + + if (! cls.is_builtin ()) + cls = lookup_class (cls); + + if (cls.ok ()) + return cls; + else + all_classes.erase (it); + } + + return cdef_class (); +} + +octave_function* +cdef_manager::do_find_method_symbol (const std::string& method_name, + const std::string& class_name) +{ + octave_function *retval = 0; + + cdef_class cls = find_class (class_name, false, false); + + if (cls.ok ()) + { + cdef_method meth = cls.find_method (method_name); + + if (meth.ok ()) + retval = new octave_classdef_meta (meth); + } + + return retval; +} + +cdef_package +cdef_manager::do_find_package (const std::string& name, + bool error_if_not_found) +{ + cdef_package retval; + + std::map<std::string, cdef_package>::const_iterator it + = all_packages.find (name); + + if (it != all_packages.end ()) + { + retval = it->second; + + if (! retval.ok ()) + error ("invalid package `%s'", name.c_str ()); + } + else if (error_if_not_found) + error ("unknown package `%s'", name.c_str ()); + + return retval; +} + +//---------------------------------------------------------------------------- + +DEFUN (__meta_get_package__, args, , "") +{ + octave_value retval; + + if (args.length () == 1) + { + std::string cname = args(0).string_value (); + + if (! error_state) + retval = to_ov (lookup_package (cname)); + else + error ("invalid package name, expected a string value"); + } + else + print_usage (); + + return retval; +} + +DEFUN (__superclass_reference__, args, /* nargout */, + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} __superclass_reference__ ()\n\ +Undocumented internal function.\n\ +@end deftypefn") +{ + return octave_value (new octave_classdef_superclass_ref (args)); +} + +DEFUN (__meta_class_query__, args, /* nargout */, + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} __meta_class_query__ ()\n\ +Undocumented internal function.\n\ +@end deftypefn") +{ + octave_value retval; + + std::cerr << "__meta_class_query__ (" + << args(0).string_value () << ", " + << args(1).string_value () << ")" + << std::endl; + + if (args.length () == 2) + { + std::string pkg = args(0).string_value (); + std::string cls = args(1).string_value (); + + if (! pkg.empty ()) + cls = pkg + "." + cls; + + if (! error_state) + retval = to_ov (lookup_class (cls)); + else + error ("invalid class name, expected a string value"); + } + else + print_usage (); + + return retval; +} + +DEFUN (metaclass, args, /* nargout */, + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} metaclass (obj)\n\ +Returns the meta.class object corresponding to the class of @var{obj}.\n\ +@end deftypefn") +{ + octave_value retval; + + if (args.length () == 1) + { + cdef_object obj = to_cdef (args(0)); + + if (! error_state) + retval = to_ov (obj.get_class ()); + else + print_usage (); + } + else + print_usage (); + + return retval; +} + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; End: *** +*/
new file mode 100644 --- /dev/null +++ b/libinterp/octave-value/ov-classdef.h @@ -0,0 +1,1604 @@ +/* + +Copyright (C) 2012 Michael Goffioul + +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 +<http://www.gnu.org/licenses/>. + +*/ + +#if !defined (octave_classdef_h) +#define octave_classdef_h 1 + +#include <map> +#include <set> +#include <string> + +#include "oct-map.h" +#include "oct-refcount.h" +#include "ov-base.h" + +class cdef_object; +class cdef_class; +class cdef_property; +class cdef_method; +class cdef_package; + +class tree_classdef; + +// This is mainly a boostrap class to declare the expected interface. +// The actual base class is cdef_class_base, which is declared after +// cdef_object, such that it can contain cdef_object objects. +class +cdef_object_rep +{ +public: + friend class cdef_object; + +public: + cdef_object_rep (void) : refcount (1) { } + + virtual ~cdef_object_rep (void) { } + + virtual cdef_class get_class (void) const; + + virtual void set_class (const cdef_class&) + { gripe_invalid_object ("set_class"); } + + virtual cdef_object_rep* clone (void) const + { + gripe_invalid_object ("clone"); + return new cdef_object_rep (); + } + + virtual cdef_object_rep* empty_clone (void) const + { + gripe_invalid_object ("empty_clone"); + return new cdef_object_rep (); + } + + virtual cdef_object_rep* copy (void) const + { + gripe_invalid_object ("copy"); + return new cdef_object_rep (); + } + + virtual cdef_object_rep* make_array (void) const + { + gripe_invalid_object ("make_array"); + return new cdef_object_rep (); + } + + virtual bool is_array (void) const { return false; } + + virtual bool is_value_object (void) const { return false; } + + virtual bool is_handle_object (void) const { return false; } + + virtual bool is_meta_object (void) const { return false; } + + virtual Array<cdef_object> array_value (void) const + { + gripe_invalid_object ("array_value"); + return Array<cdef_object> (); + } + + virtual void put (const std::string&, const octave_value&) + { gripe_invalid_object ("put"); } + + virtual octave_value get (const std::string&) const + { + gripe_invalid_object ("get"); + return octave_value (); + } + + virtual octave_value_list + subsref (const std::string&, const std::list<octave_value_list>&, + int, size_t&, const cdef_class&) + { + gripe_invalid_object ("subsref"); + return octave_value_list (); + } + + virtual octave_value + subsasgn (const std::string&, const std::list<octave_value_list>&, + const octave_value&) + { + gripe_invalid_object ("subsasgn"); + return octave_value (); + } + + virtual string_vector map_keys(void) const; + + virtual bool is_valid (void) const { return false; } + + std::string class_name (void) const; + + virtual void mark_for_construction (const cdef_class&) + { gripe_invalid_object ("mark_for_construction"); } + + virtual bool is_constructed_for (const cdef_class&) const + { + gripe_invalid_object ("is_constructed_for"); + return false; + } + + virtual bool is_partially_constructed_for (const cdef_class&) const + { + gripe_invalid_object ("is_partially_constructed_for"); + return false; + } + + virtual void mark_as_constructed (void) + { gripe_invalid_object ("mark_as_constructed"); } + + virtual void mark_as_constructed (const cdef_class&) + { gripe_invalid_object ("mark_as_constructed"); } + + virtual bool is_constructed (void) const + { + gripe_invalid_object ("is_constructed"); + return false; + } + + virtual octave_idx_type static_count (void) const { return 0; } + + virtual void destroy (void) { delete this; } + + void release (void) + { + if (--refcount == static_count ()) + destroy (); + } + + virtual dim_vector dims (void) const { return dim_vector (); } + +protected: + /* reference count */ + octave_refcount<octave_idx_type> refcount; + +protected: + /* Restricted copying */ + cdef_object_rep (const cdef_object_rep&) + : refcount (1) { } + +private: + /* No assignment */ + cdef_object_rep& operator = (const cdef_object_rep& ); + + void gripe_invalid_object (const char *who) const + { error ("%s: invalid object", who); } +}; + +class +cdef_object +{ +public: + /* FIXME: use a null object */ + cdef_object (void) + : rep (new cdef_object_rep ()) { } + + cdef_object (const cdef_object& obj) + : rep (obj.rep) + { + rep->refcount++; + } + + cdef_object (cdef_object_rep *r) + : rep (r) { } + + virtual ~cdef_object (void) + { rep->release (); } + + cdef_object& operator = (const cdef_object& obj) + { + if (rep != obj.rep) + { + rep->release (); + + rep = obj.rep; + rep->refcount++; + } + + return *this; + } + + cdef_class get_class (void) const; + + void set_class (const cdef_class& cls) { rep->set_class (cls); } + + std::string class_name (void) const + { return rep->class_name (); } + + cdef_object clone (void) const + { return cdef_object (rep->clone ()); } + + cdef_object empty_clone (void) const + { return cdef_object (rep->empty_clone ()); } + + dim_vector dims (void) const { return rep->dims (); } + + cdef_object make_array (void) const + { return cdef_object (rep->make_array ()); } + + cdef_object copy (void) const + { return cdef_object (rep->copy ()); } + + bool is_array (void) const { return rep->is_array (); } + + bool is_value_object (void) const { return rep->is_value_object (); } + + bool is_handle_object (void) const { return rep->is_handle_object (); } + + bool is_meta_object (void) const { return rep->is_meta_object (); } + + Array<cdef_object> array_value (void) const { return rep->array_value (); } + + void put (const std::string& pname, const octave_value& val) + { rep->put (pname, val); } + + octave_value get (const std::string& pname) const + { return rep->get (pname); } + + octave_value_list + subsref (const std::string& type, const std::list<octave_value_list>& idx, + int nargout, size_t& skip, const cdef_class& context) + { return rep->subsref (type, idx, nargout, skip, context); } + + octave_value + subsasgn (const std::string& type, const std::list<octave_value_list>& idx, + const octave_value& rhs) + { + make_unique (); + return rep->subsasgn (type, idx, rhs); + } + + string_vector map_keys (void) const { return rep->map_keys (); } + + const cdef_object_rep* get_rep (void) const { return rep; } + + bool ok (void) const { return rep->is_valid (); } + + void mark_for_construction (const cdef_class& cls) + { rep->mark_for_construction (cls); } + + bool is_constructed (void) const { return rep->is_constructed (); } + + bool is_constructed_for (const cdef_class& cls) const + { return rep->is_constructed_for (cls); } + + bool is_partially_constructed_for (const cdef_class& cls) const + { return rep->is_partially_constructed_for (cls); } + + void mark_as_constructed (void) { rep->mark_as_constructed (); } + + void mark_as_constructed (const cdef_class& cls) + { rep->mark_as_constructed (cls); } + +protected: + cdef_object_rep* get_rep (void) { return rep; } + + void make_unique (void) + { + if (rep->refcount > 1) + *this = clone (); + } + +private: + cdef_object_rep *rep; +}; + +class +cdef_object_base : public cdef_object_rep +{ +public: + cdef_object_base (void) + : cdef_object_rep (), klass () + { + register_object (); + } + + ~cdef_object_base (void) { unregister_object (); } + + cdef_class get_class (void) const; + + void set_class (const cdef_class& cls); + + cdef_object_rep* empty_clone (void) const + { return new cdef_object_base (*this); } + + cdef_object_rep* make_array (void) const; + +protected: + // Restricted copying! + cdef_object_base (const cdef_object_base& obj) + : cdef_object_rep (obj), klass (obj.klass) + { + register_object (); + } + +private: + void register_object (void); + + void unregister_object (void); + +private: + // The class of the object + cdef_object klass; + +private: + // No assignment! + cdef_object_base& operator = (const cdef_object_base&); +}; + +class +cdef_object_array : public cdef_object_base +{ +public: + cdef_object_array (void) : cdef_object_base () { } + + cdef_object_array (const Array<cdef_object>& a) + : cdef_object_base (), array (a) { } + + cdef_object_rep* clone (void) const + { return new cdef_object_array (*this); } + + dim_vector dims (void) const { return array.dims (); } + + bool is_valid (void) const { return true; } + + bool is_array (void) const { return true; } + + Array<cdef_object> array_value (void) const { return array; } + + octave_value_list + subsref (const std::string& type, const std::list<octave_value_list>& idx, + int nargout, size_t& skip, const cdef_class& context); + + octave_value + subsasgn (const std::string& type, const std::list<octave_value_list>& idx, + const octave_value& rhs); + +private: + Array<cdef_object> array; + +private: + void fill_empty_values (void); + + // Private copying! + cdef_object_array (const cdef_object_array& obj) + : cdef_object_base (obj), array (obj.array) { } + + // No assignment! + cdef_object_array& operator = (const cdef_object_array&); +}; + +class +cdef_object_scalar : public cdef_object_base +{ +public: + cdef_object_scalar (void) : cdef_object_base () { } + + ~cdef_object_scalar (void) { } + + dim_vector dims (void) const { return dim_vector (1, 1); } + + void put (const std::string& pname, const octave_value& val) + { map.assign (pname, val); } + + octave_value get (const std::string& pname) const + { + Cell val = map.contents (pname); + + if (val.numel () > 0) + return val(0, 0); + else + { + error ("get: unknown slot: %s", pname.c_str ()); + return octave_value (); + } + } + + octave_value_list + subsref (const std::string& type, const std::list<octave_value_list>& idx, + int nargout, size_t& skip, const cdef_class& context); + + octave_value + subsasgn (const std::string& type, const std::list<octave_value_list>& idx, + const octave_value& rhs); + + void mark_for_construction (const cdef_class&); + + bool is_constructed_for (const cdef_class& cls) const; + + bool is_partially_constructed_for (const cdef_class& cls) const; + + void mark_as_constructed (void) { ctor_list.clear (); } + + void mark_as_constructed (const cdef_class& cls) { ctor_list.erase (cls); } + + bool is_constructed (void) const { return ctor_list.empty (); } + +protected: + // Object property values + Octave_map map; + + // Internal/temporary structure used during object construction + std::map< cdef_class, std::list<cdef_class> > ctor_list; + +protected: + // Restricted object copying! + cdef_object_scalar (const cdef_object_scalar& obj) + : cdef_object_base (obj), map (obj.map), ctor_list (obj.ctor_list) { } + +private: + // No assignment! + cdef_object_scalar& operator = (const cdef_object_scalar&); +}; + +class +handle_cdef_object : public cdef_object_scalar +{ +public: + handle_cdef_object (void) + : cdef_object_scalar () { } + + ~handle_cdef_object (void); + + cdef_object_rep* clone (void) const + { + handle_cdef_object *obj = const_cast<handle_cdef_object *> (this); + obj->refcount++; + return obj; + } + + cdef_object_rep* copy (void) const + { return new handle_cdef_object (*this); } + + bool is_valid (void) const { return true; } + + bool is_handle_object (void) const { return true; } + +protected: + // Restricted copying! + handle_cdef_object (const handle_cdef_object& obj) + : cdef_object_scalar (obj) { } + +private: + // No assignment + handle_cdef_object& operator = (const handle_cdef_object&); +}; + +class +value_cdef_object : public cdef_object_scalar +{ +public: + value_cdef_object (void) + : cdef_object_scalar () { } + + ~value_cdef_object (void); + + cdef_object_rep* clone (void) const + { return new value_cdef_object (*this); } + + cdef_object_rep* copy (void) const { return clone (); } + + bool is_valid (void) const { return true; } + + bool is_value_object (void) const { return true; } + +private: + // Private copying! + value_cdef_object (const value_cdef_object& obj) + : cdef_object_scalar (obj) { } + + // No assignment! + value_cdef_object& operator = (const value_cdef_object&); +}; + +class +cdef_meta_object_rep : public handle_cdef_object +{ +public: + cdef_meta_object_rep (void) + : handle_cdef_object () { } + + ~cdef_meta_object_rep (void) { } + + cdef_object_rep* copy (void) const + { return new cdef_meta_object_rep (*this); } + + bool is_meta_object (void) const { return true; } + + virtual bool is_class (void) const { return false; } + + virtual bool is_property (void) const { return false; } + + virtual bool is_method (void) const { return false; } + + virtual bool is_package (void) const { return false; } + + virtual octave_value_list + meta_subsref (const std::string& /* type */, + const std::list<octave_value_list>& /* idx */, + int /* nargout */) + { + ::error ("subsref: invalid meta object"); + return octave_value_list (); + } + + virtual void meta_release (void) { } + + virtual bool meta_is_postfix_index_handled (char /* type */) const + { return false; } + +protected: + // Restricted copying! + cdef_meta_object_rep (const cdef_meta_object_rep& obj) + : handle_cdef_object (obj) { } + +private: + // No assignment! + cdef_meta_object_rep& operator = (const cdef_meta_object_rep&); +}; + +class +cdef_meta_object : public cdef_object +{ +public: + cdef_meta_object (void) + : cdef_object () { } + + cdef_meta_object (const cdef_meta_object& obj) + : cdef_object (obj) { } + + cdef_meta_object (cdef_meta_object_rep *r) + : cdef_object (r) { } + + // Object consistency is checked in sub-classes. + cdef_meta_object (const cdef_object& obj) + : cdef_object (obj) { } + + ~cdef_meta_object (void) { } + + bool is_class (void) const { return get_rep ()->is_class (); } + + bool is_property (void) const { return get_rep ()->is_property (); } + + bool is_method (void) const { return get_rep ()->is_method (); } + + bool is_package (void) const { return get_rep ()->is_package (); } + + octave_value_list + meta_subsref (const std::string& type, + const std::list<octave_value_list>& idx, int nargout) + { return get_rep ()->meta_subsref (type, idx, nargout); } + + void meta_release (void) { get_rep ()->meta_release (); } + + bool meta_is_postfix_index_handled (char type) const + { return get_rep ()->meta_is_postfix_index_handled (type); } + +private: + cdef_meta_object_rep* get_rep (void) + { return dynamic_cast<cdef_meta_object_rep *> (cdef_object::get_rep ()); } + + const cdef_meta_object_rep* get_rep (void) const + { return dynamic_cast<const cdef_meta_object_rep *> (cdef_object::get_rep ()); } +}; + +class +cdef_class : public cdef_meta_object +{ +private: + + class + cdef_class_rep : public cdef_meta_object_rep + { + public: + cdef_class_rep (void) + : cdef_meta_object_rep (), member_count (0), handle_class (false), + object_count (0), meta (false) { } + + cdef_class_rep (const std::list<cdef_class>& superclasses); + + cdef_object_rep* copy (void) const { return new cdef_class_rep (*this); } + + bool is_class (void) const { return true; } + + std::string get_name (void) const + { return get ("Name").string_value (); } + + void set_name (const std::string& nm) { put ("Name", nm); } + + bool is_abstract (void) const { return get ("Abstract").bool_value (); } + + bool is_sealed (void) const { return get ("Sealed").bool_value (); } + + cdef_method find_method (const std::string& nm, bool local = false); + + void install_method (const cdef_method& meth); + + Cell get_methods (void); + + cdef_property find_property (const std::string& nm); + + void install_property (const cdef_property& prop); + + Cell get_properties (void); + + string_vector get_names (void); + + void set_directory (const std::string& dir) { directory = dir; } + + std::string get_directory (void) const { return directory; } + + void delete_object (cdef_object obj); + + octave_value_list + meta_subsref (const std::string& type, + const std::list<octave_value_list>& idx, int nargout); + + void meta_release (void); + + bool meta_is_postfix_index_handled (char type) const + { return (type == '(' || type == '.'); } + + octave_value construct (const octave_value_list& args); + + cdef_object construct_object (const octave_value_list& args); + + void initialize_object (cdef_object& obj); + + void run_constructor (cdef_object& obj, const octave_value_list& args); + + void mark_as_handle_class (void) { handle_class = true; } + + bool is_handle_class (void) const { return handle_class; } + + void register_object (void) { object_count++; } + + void unregister_object (void) { object_count--; } + + octave_idx_type static_count (void) const { return member_count; } + + void destroy (void) + { + if (member_count) + { + refcount++; + cdef_class lock (this); + + member_count = 0; + method_map.clear (); + property_map.clear (); + } + else + delete this; + } + + void mark_as_meta_class (void) { meta = true; } + + bool is_meta_class (void) const { return meta; } + + private: + void load_all_methods (void); + + void find_names (std::set<std::string>& names, bool all); + + void find_properties (std::map<std::string,cdef_property>& props, + bool only_inherited); + + void find_methods (std::map<std::string, cdef_method>& meths, + bool only_inherited); + + cdef_class wrap (void) + { + refcount++; + return cdef_class (this); + } + + private: + // The @-directory were this class is loaded from. + // (not used yet) + std::string directory; + + // The methods defined by this class. + std::map<std::string,cdef_method> method_map; + + // The properties defined by this class. + std::map<std::string,cdef_property> property_map; + + // The number of members in this class (methods, properties...) + octave_idx_type member_count; + + // TRUE if this class is a handle class. A class is a handle + // class when the abstract "handle" class is one of its superclasses. + bool handle_class; + + // The list of super-class constructors that are called implicitly by the + // the classdef engine when creating an object. These constructors are not + // called explicitly by the class constructor. + std::list<cdef_class> implicit_ctor_list; + + // The number of objects of this class. + octave_refcount<octave_idx_type> object_count; + + // TRUE if this class is a built-in meta class. + bool meta; + + // Utility iterator typedef's. + typedef std::map<std::string,cdef_method>::iterator method_iterator; + typedef std::map<std::string,cdef_method>::const_iterator method_const_iterator; + typedef std::map<std::string,cdef_property>::iterator property_iterator; + typedef std::map<std::string,cdef_property>::const_iterator property_const_iterator; + + private: + cdef_class_rep (const cdef_class_rep& c) + : cdef_meta_object_rep (c), directory (c.directory), + method_map (c.method_map), property_map (c.property_map), + member_count (c.member_count), handle_class (c.handle_class), + implicit_ctor_list (c.implicit_ctor_list), + object_count (c.object_count), meta (c.meta) { } + }; + +public: + // Create and invalid class object + cdef_class (void) + : cdef_meta_object () { } + + cdef_class (const std::string& nm, + const std::list<cdef_class>& superclasses) + : cdef_meta_object (new cdef_class_rep (superclasses)) + { get_rep ()->set_name (nm); } + + cdef_class (const cdef_class& cls) + : cdef_meta_object (cls) { } + + cdef_class (const cdef_object& obj) + : cdef_meta_object (obj) + { + // This should never happen... + if (! is_class ()) + error ("internal error: invalid assignment from %s to meta.class object", + class_name ().c_str ()); + } + + cdef_class& operator = (const cdef_class& cls) + { + cdef_object::operator= (cls); + + return *this; + } + + cdef_method find_method (const std::string& nm, bool local = false); + + void install_method (const cdef_method& meth) + { get_rep ()->install_method (meth); } + + Cell get_methods (void) { return get_rep ()->get_methods (); } + + cdef_property find_property (const std::string& nm); + + void install_property (const cdef_property& prop) + { get_rep ()->install_property (prop); } + + Cell get_properties (void) { return get_rep ()->get_properties (); } + + string_vector get_names (void) { return get_rep ()->get_names (); } + + bool is_abstract (void) const { return get_rep ()->is_abstract (); } + + bool is_sealed (void) const { return get_rep ()->is_sealed (); } + + void set_directory (const std::string& dir) + { get_rep ()->set_directory (dir); } + + std::string get_directory (void) const + { return get_rep ()->get_directory (); } + + std::string get_name (void) const + { return get_rep ()->get_name (); } + + bool is_builtin (void) const + { return get_directory ().empty (); } + + void delete_object (cdef_object obj) + { get_rep ()->delete_object (obj); } + + static cdef_class make_meta_class (tree_classdef* t); + + octave_function* get_method_function (const std::string& nm); + + octave_function* get_constructor_function (void) + { return get_method_function (get_name ()); } + + octave_value construct (const octave_value_list& args) + { return get_rep ()->construct (args); } + + cdef_object construct_object (const octave_value_list& args) + { return get_rep ()->construct_object (args); } + + void initialize_object (cdef_object& obj) + { get_rep ()->initialize_object (obj); } + + void run_constructor (cdef_object& obj, const octave_value_list& args) + { get_rep ()->run_constructor (obj, args); } + + void mark_as_handle_class (void) + { get_rep ()->mark_as_handle_class (); } + + bool is_handle_class (void) const + { return get_rep ()->is_handle_class (); } + + void mark_as_meta_class (void) { get_rep ()->mark_as_meta_class (); } + + bool is_meta_class (void) const { return get_rep ()->is_meta_class (); } + + static const cdef_class& meta_class (void) { return _meta_class; } + static const cdef_class& meta_property (void) { return _meta_property; } + static const cdef_class& meta_method (void) { return _meta_method; } + static const cdef_class& meta_package (void) { return _meta_package; } + + void register_object (void) { get_rep ()->register_object (); } + + void unregister_object (void) { get_rep ()->unregister_object (); } + +private: + cdef_class_rep* get_rep (void) + { return dynamic_cast<cdef_class_rep *> (cdef_object::get_rep ()); } + + const cdef_class_rep* get_rep (void) const + { return dynamic_cast<const cdef_class_rep *> (cdef_object::get_rep ()); } + + friend bool operator == (const cdef_class&, const cdef_class&); + friend bool operator != (const cdef_class&, const cdef_class&); + friend bool operator < (const cdef_class&, const cdef_class&); + +private: + static cdef_class _meta_class; + static cdef_class _meta_property; + static cdef_class _meta_method; + static cdef_class _meta_package; + + friend void install_classdef (void); +}; + +inline bool +operator == (const cdef_class& clsa, const cdef_class& clsb) +// FIXME: is this really the right way to check class equality? +{ return (clsa.get_rep () == clsb.get_rep ()); } + +inline bool +operator != (const cdef_class& clsa, const cdef_class& clsb) +{ return ! (clsa == clsb); } + +// This is only to be able to use cdef_class as map keys. +inline bool +operator < (const cdef_class& clsa, const cdef_class& clsb) +{ return clsa.get_rep () < clsb.get_rep (); } + +class +cdef_property : public cdef_meta_object +{ + friend class cdef_class; + +private: + + class + cdef_property_rep : public cdef_meta_object_rep + { + public: + cdef_property_rep (void) + : cdef_meta_object_rep () { } + + cdef_object_rep* copy (void) const { return new cdef_property_rep (*this); } + + bool is_property (void) const { return true; } + + std::string get_name (void) const { return get("Name").string_value (); } + + void set_name (const std::string& nm) { put ("Name", nm); } + + bool is_constant (void) const { return get("Constant").bool_value (); } + + octave_value get_value (bool do_check_access = true, + const std::string& who = std::string ()); + + octave_value get_value (const cdef_object& obj, + bool do_check_access = true, + const std::string& who = std::string ()); + + void set_value (cdef_object& obj, const octave_value& val, + bool do_check_access = true, + const std::string& who = std::string ()); + + bool check_get_access (void) const; + + bool check_set_access (void) const; + + private: + cdef_property_rep (const cdef_property_rep& p) + : cdef_meta_object_rep (p) { } + + bool is_recursive_set (const cdef_object& obj) const; + + cdef_property wrap (void) + { + refcount++; + return cdef_property (this); + } + }; + +public: + cdef_property (void) : cdef_meta_object () { } + + cdef_property (const std::string& nm) + : cdef_meta_object (new cdef_property_rep ()) + { get_rep ()->set_name (nm); } + + cdef_property (const cdef_property& prop) + : cdef_meta_object (prop) { } + + cdef_property (const cdef_object& obj) + : cdef_meta_object (obj) + { + // This should never happen... + if (! is_property ()) + error ("internal error: invalid assignment from %s to meta.property object", + class_name ().c_str ()); + } + + cdef_property& operator = (const cdef_property& prop) + { + cdef_object::operator= (prop); + + return *this; + } + + octave_value get_value (const cdef_object& obj, bool do_check_access = true, + const std::string& who = std::string ()) + { return get_rep ()->get_value (obj, do_check_access, who); } + + octave_value get_value (bool do_check_access = true, + const std::string& who = std::string ()) + { return get_rep ()->get_value (do_check_access, who); } + + void set_value (cdef_object& obj, const octave_value& val, + bool do_check_access = true, + const std::string& who = std::string ()) + { get_rep ()->set_value (obj, val, do_check_access, who); } + + bool check_get_access (void) const + { return get_rep ()->check_get_access (); } + + bool check_set_access (void) const + { return get_rep ()->check_set_access (); } + + std::string get_name (void) const { return get_rep ()->get_name (); } + + bool is_constant (void) const { return get_rep ()->is_constant (); } + +private: + cdef_property_rep* get_rep (void) + { return dynamic_cast<cdef_property_rep *> (cdef_object::get_rep ()); } + + const cdef_property_rep* get_rep (void) const + { return dynamic_cast<const cdef_property_rep *> (cdef_object::get_rep ()); } +}; + +class +cdef_method : public cdef_meta_object +{ + friend class cdef_class; + +private: + + class + cdef_method_rep : public cdef_meta_object_rep + { + public: + cdef_method_rep (void) : cdef_meta_object_rep () { } + + cdef_object_rep* copy (void) const { return new cdef_method_rep(*this); } + + bool is_method (void) const { return true; } + + std::string get_name (void) const { return get("Name").string_value (); } + + void set_name (const std::string& nm) { put ("Name", nm); } + + bool is_static (void) const { return get("Static").bool_value (); } + + octave_value get_function (void) const { return function; } + + void set_function (const octave_value& fcn) { function = fcn; } + + bool check_access (void) const; + + octave_value_list execute (const octave_value_list& args, int nargout, + bool do_check_access = true, + const std::string& who = std::string ()); + + octave_value_list execute (const cdef_object& obj, + const octave_value_list& args, int nargout, + bool do_check_access = true, + const std::string& who = std::string ()); + + bool is_constructor (void) const; + + octave_value_list + meta_subsref (const std::string& type, + const std::list<octave_value_list>& idx, int nargout); + + bool meta_is_postfix_index_handled (char type) const + { return (type == '(' || type == '.'); } + + private: + cdef_method_rep (const cdef_method_rep& m) + : cdef_meta_object_rep (m), function (m.function) { } + + void check_method (void); + + cdef_method wrap (void) + { + refcount++; + return cdef_method (this); + } + + private: + octave_value function; + }; + +public: + cdef_method (void) : cdef_meta_object () { } + + cdef_method (const std::string& nm) + : cdef_meta_object (new cdef_method_rep ()) + { get_rep ()->set_name (nm); } + + cdef_method (const cdef_method& meth) + : cdef_meta_object (meth) { } + + cdef_method (const cdef_object& obj) + : cdef_meta_object (obj) + { + // This should never happen... + if (! is_method ()) + error ("internal error: invalid assignment from %s to meta.method object", + class_name ().c_str ()); + } + + cdef_method& operator = (const cdef_method& meth) + { + cdef_object::operator= (meth); + + return *this; + } + + /* normal invokation */ + octave_value_list execute (const octave_value_list& args, int nargout, + bool do_check_access = true, + const std::string& who = std::string ()) + { return get_rep ()->execute (args, nargout, do_check_access, who); } + + /* dot-invokation: object is pushed as 1st argument */ + octave_value_list execute (const cdef_object& obj, + const octave_value_list& args, int nargout, + bool do_check_access = true, + const std::string& who = std::string ()) + { return get_rep ()->execute (obj, args, nargout, do_check_access, who); } + + bool check_access (void) const { return get_rep ()->check_access (); } + + std::string get_name (void) const { return get_rep ()->get_name (); } + + bool is_static (void) const { return get_rep ()->is_static (); } + + void set_function (const octave_value& fcn) + { get_rep ()->set_function (fcn); } + + octave_value get_function (void) const + { return get_rep ()->get_function (); } + + bool is_constructor (void) const + { return get_rep ()->is_constructor (); } + +private: + cdef_method_rep* get_rep (void) + { return dynamic_cast<cdef_method_rep *> (cdef_object::get_rep ()); } + + const cdef_method_rep* get_rep (void) const + { return dynamic_cast<const cdef_method_rep *> (cdef_object::get_rep ()); } +}; + +inline cdef_class +cdef_object_rep::get_class (void) const +{ + gripe_invalid_object ("get_class"); + return cdef_class (); +} + +inline std::string +cdef_object_rep::class_name (void) const +{ return get_class ().get_name (); } + +inline cdef_class +cdef_object::get_class (void) const +{ return rep->get_class (); } + +inline cdef_class +cdef_object_base::get_class (void) const +{ return cdef_class (klass); } + +inline void +cdef_object_base::set_class (const cdef_class& cls) +{ + if ((klass.ok () && cls.ok () && cls != get_class ()) + || (klass.ok () && ! cls.ok ()) + || (! klass.ok () && cls.ok ())) + { + unregister_object (); + klass = cls; + register_object (); + } +} + +inline void +cdef_object_base::register_object (void) +{ + if (klass.ok ()) + { + cdef_class cls (get_class ()); + + if (! error_state && cls.ok ()) + cls.register_object (); + } +} + +inline void +cdef_object_base::unregister_object (void) +{ + if (klass.ok ()) + { + cdef_class cls (get_class ()); + + if (! error_state && cls.ok ()) + cls.unregister_object (); + } +} + +inline cdef_object_rep* +cdef_object_base::make_array (void) const +{ + cdef_object_rep* r = new cdef_object_array (); + + r->set_class (get_class ()); + + return r; +} + +inline cdef_method +cdef_class::find_method (const std::string& nm, bool local) +{ return get_rep ()->find_method (nm, local); } + +inline cdef_property +cdef_class::find_property (const std::string& nm) +{ return get_rep ()->find_property (nm); } + +class +cdef_package : public cdef_meta_object +{ + friend class cdef_class; + +private: + + class + cdef_package_rep : public cdef_meta_object_rep + { + public: + cdef_package_rep (void) : cdef_meta_object_rep (), member_count (0) { } + + cdef_object_rep* copy (void) const { return new cdef_package_rep (*this); } + + bool is_package (void) const { return true; } + + std::string get_name (void) const { return get("Name").string_value (); } + + void set_name (const std::string& nm) { put ("Name", nm); } + + void install_class (const cdef_class& cls, const std::string& nm); + + void install_function (const octave_value& fcn, const std::string& nm); + + void install_package (const cdef_package& pack, const std::string& nm); + + Cell get_classes (void) const; + + Cell get_functions (void) const; + + Cell get_packages (void) const; + + octave_idx_type static_count (void) const { return member_count; } + + void destroy (void) + { + if (member_count) + { + refcount++; + cdef_package lock (this); + + member_count = 0; + class_map.clear (); + package_map.clear (); + } + else + delete this; + } + + private: + std::map<std::string, cdef_class> class_map; + std::map<std::string, octave_value> function_map; + std::map<std::string, cdef_package> package_map; + + // The number of registered members in this package (classes, packages). + // This only accounts for the members that back-reference to this package. + octave_idx_type member_count; + + typedef std::map<std::string, cdef_class>::iterator class_iterator; + typedef std::map<std::string, cdef_class>::const_iterator class_const_iterator; + typedef std::map<std::string, octave_value>::iterator function_iterator; + typedef std::map<std::string, octave_value>::const_iterator function_const_iterator; + typedef std::map<std::string, cdef_package>::iterator package_iterator; + typedef std::map<std::string, cdef_package>::const_iterator package_const_iterator; + + private: + cdef_package_rep (const cdef_package_rep& p) + : cdef_meta_object_rep (p), class_map (p.class_map), + function_map (p.function_map), package_map (p.package_map), + member_count (p.member_count) { } + }; + +public: + cdef_package (void) : cdef_meta_object () { } + + cdef_package (const std::string& nm) + : cdef_meta_object (new cdef_package_rep ()) + { get_rep ()->set_name (nm); } + + cdef_package (const cdef_package& pack) + : cdef_meta_object (pack) { } + + cdef_package (const cdef_object& obj) + : cdef_meta_object (obj) + { + // This should never happen... + if (! is_package ()) + error ("internal error: invalid assignment from %s to meta.package object", + class_name ().c_str ()); + } + + cdef_package& operator = (const cdef_package& pack) + { + cdef_object::operator= (pack); + + return *this; + } + + void install_class (const cdef_class& cls, const std::string& nm) + { get_rep ()->install_class (cls, nm); } + + void install_function (const octave_value& fcn, const std::string& nm) + { get_rep ()->install_function (fcn, nm); } + + void install_package (const cdef_package& pack, const std::string& nm) + { get_rep ()->install_package (pack, nm); } + + Cell get_classes (void) const + { return get_rep ()->get_classes (); } + + Cell get_functions (void) const + { return get_rep ()->get_functions (); } + + Cell get_packages (void) const + { return get_rep ()->get_packages (); } + + std::string get_name (void) const { return get_rep ()->get_name (); } + + static const cdef_package& meta (void) { return _meta; } + +private: + cdef_package_rep* get_rep (void) + { return dynamic_cast<cdef_package_rep *> (cdef_object::get_rep ()); } + + const cdef_package_rep* get_rep (void) const + { return dynamic_cast<const cdef_package_rep *> (cdef_object::get_rep ()); } + +private: + static cdef_package _meta; + + friend void install_classdef (void); +}; + +class +octave_classdef : public octave_base_value +{ +public: + octave_classdef (void) + : octave_base_value (), object () { } + + octave_classdef (const cdef_object& obj) + : octave_base_value (), object (obj) { } + + octave_classdef (const octave_classdef& obj) + : octave_base_value (obj), object (obj.object) { } + + octave_base_value* clone (void) const + { return new octave_classdef (object.clone ()); } + + octave_base_value* empty_clone (void) const + { return new octave_classdef (object.empty_clone ()); } + + cdef_object get_object (void) const { return object; } + + cdef_object& get_object_ref (void) { return object; } + + bool is_defined (void) const { return true; } + + bool is_map (void) const { return true; } + + bool print_as_scalar (void) const { return true; } + + void print(std::ostream& os, bool pr_as_read_syntax = false) const + { + // FIXME: should call "display" method + print_raw(os, pr_as_read_syntax); + newline(os); + } + + void print_raw(std::ostream& os, bool /* pr_as_read_syntax */ = false) const + { + if (object.is_array ()) + os << "array (" << object.dims ().str () << ") of " + << object.class_name () << " objects"; + else + os << object.class_name () << " object"; + } + + octave_value_list subsref (const std::string& type, + const std::list<octave_value_list>& idx, int nargout); + + octave_value subsref (const std::string& type, + const std::list<octave_value_list>& idx) + { + octave_value_list retval = subsref (type, idx, 1); + return (retval.length () > 0 ? retval(0) : octave_value ()); + } + + octave_value subsasgn (const std::string& type, + const std::list<octave_value_list>& idx, + const octave_value& rhs); + + octave_value + undef_subsasgn (const std::string& type, + const std::list<octave_value_list>& idx, + const octave_value& rhs); + + string_vector map_keys (void) const { return object.map_keys (); } + + dim_vector dims (void) const { return object.dims (); } + +private: + cdef_object object; + +private: + DECLARE_OCTAVE_ALLOCATOR + +public: + int type_id (void) const { return t_id; } + std::string type_name (void) const { return t_name; } + std::string class_name (void) const { return object.class_name (); } + + static int static_type_id (void) { return t_id; } + static std::string static_type_name (void) { return t_name; } + static std::string static_class_name (void) { return "<unknown>"; } + static void register_type (void); + +private: + static int t_id; + + static const std::string t_name; +}; + +inline octave_value +to_ov (const cdef_object& obj) +{ + if (obj.ok ()) + return octave_value (new octave_classdef (obj)); + else + return octave_value (Matrix ()); +} + +inline octave_value +to_ov (const octave_value& ov) +{ return ov; } + +inline cdef_object +to_cdef (const octave_value& val) +{ + if (val.type_name () == "object") + return dynamic_cast<octave_classdef *> (val.internal_rep ())->get_object (); + else + { + warning ("trying to cast non-object into object"); + return cdef_object (); + } +} + +inline cdef_object& +to_cdef_ref (octave_value& val) +{ + static cdef_object empty; + + if (val.type_name () == "object") + return dynamic_cast<octave_classdef *> (val.internal_rep ())->get_object_ref (); + else + { + warning ("trying to cast non-object into object"); + return empty; + } +} + +inline cdef_object +to_cdef (const cdef_object& obj) +{ return obj; } + +OCTINTERP_API void install_classdef (void); + +class +cdef_manager +{ +public: + + static cdef_class find_class (const std::string& name, + bool error_if_not_found = true, + bool load_if_not_found = true) + { + if (instance_ok ()) + return instance->do_find_class (name, error_if_not_found, + load_if_not_found); + + return cdef_class (); + } + + static octave_function* find_method_symbol (const std::string& method_name, + const std::string& class_name) + { + if (instance_ok ()) + return instance->do_find_method_symbol (method_name, class_name); + + return 0; + } + + static cdef_package find_package (const std::string& name, + bool error_if_not_found = true) + { + if (instance_ok ()) + return instance->do_find_package (name, error_if_not_found); + + return cdef_package (); + } + + static void register_class (const cdef_class& cls) + { + if (instance_ok ()) + instance->do_register_class (cls); + } + + static void unregister_class (const cdef_class& cls) + { + if (instance_ok ()) + instance->do_unregister_class (cls); + } + + static void register_package (const cdef_package& pkg) + { + if (instance_ok ()) + instance->do_register_package (pkg); + } + + static void unregister_package (const cdef_package& pkg) + { + if (instance_ok ()) + instance->do_unregister_package (pkg); + } + +private: + + cdef_manager (void) { } + + cdef_manager (const cdef_manager&); + + cdef_manager& operator = (const cdef_manager&); + + ~cdef_manager (void) { } + + static void create_instance (void); + + static bool instance_ok (void) + { + bool retval = true; + + if (! instance) + create_instance (); + + if (! instance) + { + ::error ("unable to create cdef_manager!"); + + retval = false; + } + + return retval; + } + + static void cleanup_instance (void) + { + delete instance; + + instance = 0; + } + + cdef_class do_find_class (const std::string& name, bool error_if_not_found, + bool load_if_not_found); + + octave_function* do_find_method_symbol (const std::string& method_name, + const std::string& class_name); + + cdef_package do_find_package (const std::string& name, + bool error_if_not_found); + + void do_register_class (const cdef_class& cls) + { all_classes[cls.get_name ()] = cls; } + + void do_unregister_class (const cdef_class& cls) + { all_classes.erase(cls.get_name ()); } + + void do_register_package (const cdef_package& pkg) + { all_packages[pkg.get_name ()] = pkg; } + + void do_unregister_package (const cdef_package& pkg) + { all_packages.erase(pkg.get_name ()); } + +private: + + // The single cdef_manager instance + static cdef_manager *instance; + + // All registered/loaded classes + std::map<std::string, cdef_class> all_classes; + + // All registered/loaded packages + std::map<std::string, cdef_package> all_packages; +}; + +#endif + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; End: *** +*/
--- a/libinterp/octave-value/ov-fcn.h +++ b/libinterp/octave-value/ov-fcn.h @@ -85,6 +85,10 @@ virtual bool is_class_constructor (const std::string& = std::string ()) const { return false; } + virtual bool + is_classdef_constructor (const std::string& = std::string ()) const + { return false; } + virtual bool is_class_method (const std::string& = std::string ()) const { return false; } @@ -148,6 +152,9 @@ virtual void accept (tree_walker&) { } + virtual bool is_postfix_index_handled (char type) const + { return (type == '(' || type == '{'); } + protected: octave_function (const std::string& nm,
--- a/libinterp/octave-value/ov-usr-fcn.cc +++ b/libinterp/octave-value/ov-usr-fcn.cc @@ -191,7 +191,7 @@ num_named_args (param_list ? param_list->length () : 0), subfunction (false), inline_function (false), anonymous_function (false), nested_function (false), - class_constructor (false), class_method (false), + class_constructor (none), class_method (false), parent_scope (-1), local_scope (sid), curr_unwind_protect_frame (0) #ifdef HAVE_LLVM @@ -370,7 +370,7 @@ octave_value_list octave_user_function::do_multi_index_op (int nargout, - const octave_value_list& args, + const octave_value_list& _args, const std::list<octave_lvalue>* lvalue_list) { octave_value_list retval; @@ -381,6 +381,23 @@ if (! cmd_list) return retval; + // If this function is a classdef constructor, extract the first input + // argument, which must be the partially constructed object instance. + + octave_value_list args (_args); + octave_value_list ret_args; + + if (is_classdef_constructor ()) + { + if (args.length () > 0) + { + ret_args = args.slice (0, 1, true); + args = args.slice (1, args.length () - 1, true); + } + else + panic_impossible (); + } + #ifdef HAVE_LLVM if (is_special_expr () && tree_jit::execute (*this, args, retval)) @@ -424,6 +441,25 @@ return retval; } + // For classdef constructor, pre-populate the output arguments + // with the pre-initialized object instance, extracted above. + + if (is_classdef_constructor ()) + { + if (ret_list) + { + ret_list->define_from_arg_vector (ret_args); + if (error_state) + return retval; + } + else + { + ::error ("%s: invalid classdef constructor, no output argument defined", + dispatch_class ().c_str ()); + return retval; + } + } + // Force parameter list to be undefined when this function exits. // Doing so decrements the reference counts on the values of local // variables that are also named function parameters.
--- a/libinterp/octave-value/ov-usr-fcn.h +++ b/libinterp/octave-value/ov-usr-fcn.h @@ -300,11 +300,19 @@ void mark_as_nested_function (void) { nested_function = true; } - void mark_as_class_constructor (void) { class_constructor = true; } + void mark_as_class_constructor (void) { class_constructor = legacy; } + + void mark_as_classdef_constructor (void) { class_constructor = classdef; } bool is_class_constructor (const std::string& cname = std::string ()) const { - return class_constructor + return class_constructor == legacy + ? (cname.empty () ? true : cname == dispatch_class ()) : false; + } + + bool is_classdef_constructor (const std::string& cname = std::string ()) const + { + return class_constructor == classdef ? (cname.empty () ? true : cname == dispatch_class ()) : false; } @@ -380,6 +388,13 @@ private: + enum class_ctor_type + { + none, + legacy, + classdef + }; + // List of arguments for this function. These are local variables. tree_parameter_list *param_list; @@ -436,8 +451,8 @@ // TRUE means this is a nested function. (either a child or parent) bool nested_function; - // TRUE means this function is the constructor for class object. - bool class_constructor; + // Enum describing whether this function is the constructor for class object. + class_ctor_type class_constructor; // TRUE means this function is a method for a class. bool class_method;
--- a/libinterp/octave-value/ov.cc +++ b/libinterp/octave-value/ov.cc @@ -65,6 +65,7 @@ #include "ov-range.h" #include "ov-struct.h" #include "ov-class.h" +#include "ov-classdef.h" #include "ov-oncleanup.h" #include "ov-cs-list.h" #include "ov-colon.h"
--- a/libinterp/octave.cc +++ b/libinterp/octave.cc @@ -64,6 +64,7 @@ #include "oct-obj.h" #include "ops.h" #include "ov.h" +#include "ov-classdef.h" #include "ov-range.h" #include "toplev.h" #include "parse.h" @@ -878,6 +879,8 @@ install_builtins (); + install_classdef (); + for (std::list<std::string>::const_iterator it = command_line_path.begin (); it != command_line_path.end (); it++) load_path::set_command_line_path (*it);
--- a/libinterp/parse-tree/lex.h +++ b/libinterp/parse-tree/lex.h @@ -242,7 +242,9 @@ looking_at_matrix_or_assign_lhs (false), looking_for_object_index (false), looking_at_indirect_ref (false), parsing_class_method (false), - maybe_classdef_get_set_method (false), parsing_classdef (false), + parsing_classdef (false), maybe_classdef_get_set_method (false), + parsing_classdef_get_method (false), + parsing_classdef_set_method (false), quote_is_transpose (false), force_script (false), reading_fcn_file (false), reading_script_file (false), reading_classdef_file (false), @@ -316,13 +318,19 @@ // true means we are parsing a class method in function or classdef file. bool parsing_class_method; + // true means we are parsing a classdef file + bool parsing_classdef; + // true means we are parsing a class method declaration line in a // classdef file and can accept a property get or set method name. // for example, "get.propertyname" is recognized as a function name. bool maybe_classdef_get_set_method; - // true means we are parsing a classdef file - bool parsing_classdef; + // TRUE means we are parsing a classdef get.method. + bool parsing_classdef_get_method; + + // TRUE means we are parsing a classdef set.method. + bool parsing_classdef_set_method; // return transpose or start a string? bool quote_is_transpose;
--- a/libinterp/parse-tree/lex.ll +++ b/libinterp/parse-tree/lex.ll @@ -632,7 +632,7 @@ { curr_lexer->looking_for_object_index = true; - return curr_lexer->count_token_internal (SUPERCLASSREF); + return curr_lexer->count_token (id_tok); } } @@ -650,7 +650,7 @@ { curr_lexer->looking_for_object_index = true; - return curr_lexer->count_token_internal (METAQUERY); + return curr_lexer->count_token (id_tok); } } @@ -1674,8 +1674,10 @@ looking_for_object_index = false; looking_at_indirect_ref = false; parsing_class_method = false; + parsing_classdef = false; maybe_classdef_get_set_method = false; - parsing_classdef = false; + parsing_classdef_get_method = false; + parsing_classdef_set_method = false; force_script = false; reading_fcn_file = false; reading_script_file = false; @@ -2891,32 +2893,36 @@ int octave_lexer::handle_superclass_identifier (void) { - std::string pkg; char *yytxt = flex_yytext (); + int c = yytxt[flex_yyleng()-1]; + std::string meth = strip_trailing_whitespace (yytxt); + + int cont_is_spc = eat_continuation (); + + int spc_gobbled = (cont_is_spc || c == ' ' || c == '\t'); + size_t pos = meth.find ("@"); - std::string cls = meth.substr (pos).substr (1); - meth = meth.substr (0, pos - 1); - + std::string cls = meth.substr (pos + 1); + meth = meth.substr (0, pos); + + std::string pkg; pos = cls.find ("."); if (pos != std::string::npos) { - pkg = cls.substr (pos).substr (1); - cls = cls.substr (0, pos - 1); + pkg = cls.substr (0, pos); + cls = cls.substr (pos + 1); } int kw_token = (is_keyword_token (meth) || is_keyword_token (cls) || is_keyword_token (pkg)); if (kw_token) { - error ("method, class and package names may not be keywords"); + error ("method, class, and package names may not be keywords"); return LEXICAL_ERROR; } - push_token (new token (SUPERCLASSREF, - meth.empty () ? 0 : &(symbol_table::insert (meth)), - cls.empty () ? 0 : &(symbol_table::insert (cls)), - pkg.empty () ? 0 : &(symbol_table::insert (pkg)), + push_token (new token (SUPERCLASSREF, meth, pkg, cls, input_line_number, current_input_column)); current_input_column += flex_yyleng (); @@ -2927,28 +2933,32 @@ int octave_lexer::handle_meta_identifier (void) { - std::string pkg; char *yytxt = flex_yytext (); + int c = yytxt[flex_yyleng()-1]; + std::string cls = strip_trailing_whitespace (yytxt).substr (1); + + int cont_is_spc = eat_continuation (); + + int spc_gobbled = (cont_is_spc || c == ' ' || c == '\t'); + + std::string pkg; size_t pos = cls.find ("."); - if (pos != std::string::npos) { - pkg = cls.substr (pos).substr (1); - cls = cls.substr (0, pos - 1); + pkg = cls.substr (0, pos); + cls = cls.substr (pos + 1); } int kw_token = is_keyword_token (cls) || is_keyword_token (pkg); if (kw_token) { - error ("class and package names may not be keywords"); + error ("class and package names may not be keywords"); return LEXICAL_ERROR; } - push_token (new token (METAQUERY, - cls.empty () ? 0 : &(symbol_table::insert (cls)), - pkg.empty () ? 0 : &(symbol_table::insert (pkg)), - input_line_number, current_input_column)); + push_token (new token (METAQUERY, pkg, cls, input_line_number, + current_input_column)); current_input_column += flex_yyleng ();
--- a/libinterp/parse-tree/module.mk +++ b/libinterp/parse-tree/module.mk @@ -28,6 +28,7 @@ parse-tree/pt-cbinop.h \ parse-tree/pt-cell.h \ parse-tree/pt-check.h \ + parse-tree/pt-classdef.h \ parse-tree/pt-cmd.h \ parse-tree/pt-colon.h \ parse-tree/pt-const.h \ @@ -36,6 +37,7 @@ parse-tree/pt-except.h \ parse-tree/pt-exp.h \ parse-tree/pt-fcn-handle.h \ + parse-tree/pt-funcall.h \ parse-tree/pt-id.h \ parse-tree/pt-idx.h \ parse-tree/pt-jump.h \ @@ -60,6 +62,7 @@ parse-tree/pt-cbinop.cc \ parse-tree/pt-cell.cc \ parse-tree/pt-check.cc \ + parse-tree/pt-classdef.cc \ parse-tree/pt-cmd.cc \ parse-tree/pt-colon.cc \ parse-tree/pt-const.cc \ @@ -68,6 +71,7 @@ parse-tree/pt-except.cc \ parse-tree/pt-exp.cc \ parse-tree/pt-fcn-handle.cc \ + parse-tree/pt-funcall.cc \ parse-tree/pt-id.cc \ parse-tree/pt-idx.cc \ parse-tree/pt-jump.cc \
--- a/libinterp/parse-tree/oct-parse.in.yy +++ b/libinterp/parse-tree/oct-parse.in.yy @@ -62,6 +62,7 @@ #include "load-path.h" #include "oct-hist.h" #include "oct-map.h" +#include "ov-classdef.h" #include "ov-fcn-handle.h" #include "ov-usr-fcn.h" #include "ov-null-mat.h" @@ -71,6 +72,7 @@ #include "parse-private.h" #include "pt-all.h" #include "pt-eval.h" +#include "pt-funcall.h" #include "symtab.h" #include "token.h" #include "unwind-prot.h" @@ -169,12 +171,15 @@ // Types for the nonterminals we generate. char sep_type; + token *tok_type; tree *tree_type; tree_matrix *tree_matrix_type; tree_cell *tree_cell_type; tree_expression *tree_expression_type; tree_constant *tree_constant_type; tree_fcn_handle *tree_fcn_handle_type; + tree_funcall *tree_funcall_type; + tree_function_def *tree_function_def_type; tree_anon_fcn_handle *tree_anon_fcn_handle_type; tree_identifier *tree_identifier_type; tree_index_expression *tree_index_expression_type; @@ -194,7 +199,24 @@ tree_statement *tree_statement_type; tree_statement_list *tree_statement_list_type; octave_user_function *octave_user_function_type; - void *dummy_type; + + tree_classdef *tree_classdef_type; + tree_classdef_attribute* tree_classdef_attribute_type; + tree_classdef_attribute_list* tree_classdef_attribute_list_type; + tree_classdef_superclass* tree_classdef_superclass_type; + tree_classdef_superclass_list* tree_classdef_superclass_list_type; + tree_classdef_body* tree_classdef_body_type; + tree_classdef_property* tree_classdef_property_type; + tree_classdef_property_list* tree_classdef_property_list_type; + tree_classdef_properties_block* tree_classdef_properties_block_type; + tree_classdef_methods_list* tree_classdef_methods_list_type; + tree_classdef_methods_block* tree_classdef_methods_block_type; + tree_classdef_event* tree_classdef_event_type; + tree_classdef_events_list* tree_classdef_events_list_type; + tree_classdef_events_block* tree_classdef_events_block_type; + tree_classdef_enum* tree_classdef_enum_type; + tree_classdef_enum_list* tree_classdef_enum_list_type; + tree_classdef_enum_block* tree_classdef_enum_block_type; } // Tokens with line and column information. @@ -221,6 +243,7 @@ %token <tok_val> TRY CATCH %token <tok_val> GLOBAL PERSISTENT %token <tok_val> FCN_HANDLE +%token <tok_val> CLASSDEF %token <tok_val> PROPERTIES METHODS EVENTS ENUMERATION %token <tok_val> METAQUERY %token <tok_val> SUPERCLASSREF @@ -228,13 +251,13 @@ // Other tokens. %token END_OF_INPUT LEXICAL_ERROR -%token FCN INPUT_FILE CLASSDEF +%token FCN INPUT_FILE // %token VARARGIN VARARGOUT %token CHOOSE_ASSIGNMENT // Nonterminals we construct. -%type <comment_type> stash_comment function_beg classdef_beg -%type <comment_type> properties_beg methods_beg events_beg enum_beg +%type <comment_type> stash_comment function_beg +%type <tok_type> classdef_beg %type <sep_type> sep_no_nl opt_sep_no_nl nl opt_nl sep opt_sep %type <tree_type> input %type <tree_constant_type> string constant magic_colon @@ -246,18 +269,19 @@ %type <tree_expression_type> primary_expr oper_expr %type <tree_expression_type> simple_expr colon_expr assign_expr expression %type <tree_identifier_type> identifier fcn_name magic_tilde -%type <tree_identifier_type> superclass_identifier meta_identifier -%type <octave_user_function_type> function1 function2 classdef1 +%type <tree_funcall_type> superclass_identifier meta_identifier +%type <octave_user_function_type> function1 function2 %type <tree_index_expression_type> word_list_cmd %type <tree_colon_expression_type> colon_expr1 %type <tree_argument_list_type> arg_list word_list assign_lhs %type <tree_argument_list_type> cell_or_matrix_row %type <tree_parameter_list_type> param_list param_list1 param_list2 %type <tree_parameter_list_type> return_list return_list1 -%type <tree_parameter_list_type> superclasses opt_superclasses %type <tree_command_type> command select_command loop_command -%type <tree_command_type> jump_command except_command function -%type <tree_command_type> file classdef +%type <tree_command_type> jump_command except_command +%type <tree_function_def_type> function +%type <tree_classdef_type> classdef +%type <tree_command_type> file %type <tree_if_command_type> if_command %type <tree_if_clause_type> elseif_clause else_clause %type <tree_if_command_list_type> if_cmd_list1 if_cmd_list @@ -267,25 +291,26 @@ %type <tree_decl_elt_type> decl2 %type <tree_decl_init_list_type> decl1 %type <tree_decl_command_type> declaration -%type <tree_statement_type> statement function_end classdef_end +%type <tree_statement_type> statement function_end %type <tree_statement_list_type> simple_list simple_list1 list list1 %type <tree_statement_list_type> opt_list input1 -// These types need to be specified. -%type <dummy_type> attr -%type <dummy_type> class_event -%type <dummy_type> class_enum -%type <dummy_type> class_property -%type <dummy_type> properties_list -%type <dummy_type> properties_block -%type <dummy_type> methods_list -%type <dummy_type> methods_block -%type <dummy_type> opt_attr_list -%type <dummy_type> attr_list -%type <dummy_type> events_list -%type <dummy_type> events_block -%type <dummy_type> enum_list -%type <dummy_type> enum_block -%type <dummy_type> class_body + +%type <tree_classdef_attribute_type> attr +%type <tree_classdef_attribute_list_type> attr_list opt_attr_list +%type <tree_classdef_superclass_type> superclass +%type <tree_classdef_superclass_list_type> superclass_list opt_superclass_list +%type <tree_classdef_body_type> class_body +%type <tree_classdef_property_type> class_property +%type <tree_classdef_property_list_type> property_list +%type <tree_classdef_properties_block_type> properties_block +%type <tree_classdef_methods_list_type> methods_list +%type <tree_classdef_methods_block_type> methods_block +%type <tree_classdef_event_type> class_event +%type <tree_classdef_events_list_type> events_list +%type <tree_classdef_events_block_type> events_block +%type <tree_classdef_enum_type> class_enum +%type <tree_classdef_enum_list_type> enum_list +%type <tree_classdef_enum_block_type> enum_block // Precedence and associativity. %right '=' ADD_EQ SUB_EQ MUL_EQ DIV_EQ LEFTDIV_EQ POW_EQ EMUL_EQ EDIV_EQ ELEFTDIV_EQ EPOW_EQ OR_EQ AND_EQ LSHIFT_EQ RSHIFT_EQ @@ -406,11 +431,26 @@ superclass_identifier : SUPERCLASSREF - { $$ = new tree_identifier ($1->line (), $1->column ()); } + { + std::string method_nm = $1->superclass_method_name (); + std::string package_nm = $1->superclass_package_name (); + std::string class_nm = $1->superclass_class_name (); + + $$ = curr_parser.make_superclass_ref + (method_nm, package_nm, class_nm, + $1->line (), $1->column ()); + } ; meta_identifier : METAQUERY - { $$ = new tree_identifier ($1->line (), $1->column ()); } + { + std::string package_nm = $1->meta_package_name (); + std::string class_nm = $1->meta_class_name (); + + $$ = curr_parser.make_meta_class_query + (package_nm, class_nm, + $1->line (), $1->column ()); + } ; string : DQ_STRING @@ -746,8 +786,6 @@ { $$ = $1; } | file { $$ = $1; } - | classdef - { $$ = $1; } ; // ===================== @@ -1113,6 +1151,13 @@ $$ = 0; } + | INPUT_FILE opt_nl classdef opt_sep END_OF_INPUT + { + if (curr_lexer->reading_classdef_file) + curr_parser.classdef_object = $3; + + $$ = 0; + } ; // =================== @@ -1153,12 +1198,14 @@ { curr_lexer->parsed_function_name.top () = true; curr_lexer->maybe_classdef_get_set_method = false; + curr_lexer->parsing_classdef_get_method = true; $$ = $3; } | SET '.' identifier { curr_lexer->parsed_function_name.top () = true; curr_lexer->maybe_classdef_get_set_method = false; + curr_lexer->parsing_classdef_set_method = true; $$ = $3; } ; @@ -1228,157 +1275,201 @@ // Classdef // ======== -classdef_beg : CLASSDEF stash_comment +classdef_beg : CLASSDEF { - $$ = 0; + if (! curr_lexer->reading_classdef_file) + { + curr_parser.bison_error ("classdef must appear inside a file containing only a class definition"); + YYABORT; + } + curr_lexer->parsing_classdef = true; + $$ = $1; } ; -classdef_end : END +classdef : classdef_beg stash_comment opt_attr_list identifier opt_superclass_list opt_sep class_body opt_sep END { curr_lexer->parsing_classdef = false; - - if (curr_parser.end_token_ok ($1, token::classdef_end)) - $$ = curr_parser.make_end ("endclassdef", $1->line (), $1->column ()); - else + if (! ($$ = curr_parser.make_classdef ($1, $3, $4, $5, $7, $9, $2))) ABORT_PARSE; } ; -classdef1 : classdef_beg opt_attr_list identifier opt_superclasses - { $$ = 0; } - ; - -classdef : classdef1 opt_sep class_body opt_sep stash_comment classdef_end - { $$ = 0; } - ; - opt_attr_list : // empty { $$ = 0; } | '(' attr_list ')' - { $$ = 0; } + { $$ = $2; } ; attr_list : attr - { $$ = 0; } + { $$ = new tree_classdef_attribute_list ($1); } | attr_list ',' attr - { $$ = 0; } + { + $1->append ($3); + $$ = $1; + } ; attr : identifier - { $$ = 0; } + { $$ = new tree_classdef_attribute ($1); } | identifier '=' decl_param_init expression - { $$ = 0; } + { + curr_lexer->looking_at_initializer_expression = false; + $$ = new tree_classdef_attribute ($1, $4); + } | EXPR_NOT identifier - { $$ = 0; } + { $$ = new tree_classdef_attribute ($2, false); } ; -opt_superclasses +opt_superclass_list : // empty { $$ = 0; } - | superclasses - { $$ = 0; } + | superclass_list + { $$ = $1; } ; -superclasses : EXPR_LT identifier '.' identifier - { $$ = 0; } - | EXPR_LT identifier - { $$ = 0; } - | superclasses EXPR_AND identifier '.' identifier - { $$ = 0; } - | superclasses EXPR_AND identifier - { $$ = 0; } +superclass_list : EXPR_LT superclass + { $$ = new tree_classdef_superclass_list ($2); } + | superclass_list EXPR_AND superclass + { + $1->append ($3); + $$ = $1; + } + ; + +superclass : identifier + { $$ = new tree_classdef_superclass ($1); } + | identifier '.' identifier + { $$ = new tree_classdef_superclass ($3, $1); } ; class_body : properties_block - { $$ = 0; } + { $$ = new tree_classdef_body ($1); } | methods_block - { $$ = 0; } + { $$ = new tree_classdef_body ($1); } | events_block - { $$ = 0; } + { $$ = new tree_classdef_body ($1); } | enum_block - { $$ = 0; } + { $$ = new tree_classdef_body ($1); } | class_body opt_sep properties_block - { $$ = 0; } + { + $1->append ($3); + $$ = $1; + } | class_body opt_sep methods_block - { $$ = 0; } + { + $1->append ($3); + $$ = $1; + } | class_body opt_sep events_block - { $$ = 0; } + { + $1->append ($3); + $$ = $1; + } | class_body opt_sep enum_block - { $$ = 0; } - ; - -properties_beg : PROPERTIES stash_comment - { $$ = 0; } + { + $1->append ($3); + $$ = $1; + } ; properties_block - : properties_beg opt_attr_list opt_sep properties_list opt_sep END - { $$ = 0; } + : PROPERTIES stash_comment opt_attr_list opt_sep property_list opt_sep END + { + if (! ($$ = curr_parser.make_classdef_properties_block + ($1, $3, $5, $7, $2))) + ABORT_PARSE; + } ; -properties_list +property_list : class_property - { $$ = 0; } - | properties_list opt_sep class_property - { $$ = 0; } + { $$ = new tree_classdef_property_list ($1); } + | property_list opt_sep class_property + { + $1->append ($3); + $$ = $1; + } ; class_property : identifier - { $$ = 0; } + { $$ = new tree_classdef_property ($1); } | identifier '=' decl_param_init expression ';' - { $$ = 0; } + { + curr_lexer->looking_at_initializer_expression = false; + $$ = new tree_classdef_property ($1, $4); + } ; -methods_beg : METHODS stash_comment - { $$ = 0; } - ; - -methods_block : methods_beg opt_attr_list opt_sep methods_list opt_sep END - { $$ = 0; } +methods_block : METHODS stash_comment opt_attr_list opt_sep methods_list opt_sep END + { + if (! ($$ = curr_parser.make_classdef_methods_block + ($1, $3, $5, $7, $2))) + ABORT_PARSE; + } ; methods_list : function - { $$ = 0; } + { + octave_value fcn; + if ($1) + fcn = $1->function (); + delete $1; + $$ = new tree_classdef_methods_list (fcn); + } | methods_list opt_sep function - { $$ = 0; } + { + octave_value fcn; + if ($3) + fcn = $3->function (); + delete $3; + + $1->append (fcn); + $$ = $1; + } ; -events_beg : EVENTS stash_comment - { $$ = 0; } - ; - -events_block : events_beg opt_attr_list opt_sep events_list opt_sep END - { $$ = 0; } +events_block : EVENTS stash_comment opt_attr_list opt_sep events_list opt_sep END + { + if (! ($$ = curr_parser.make_classdef_events_block + ($1, $3, $5, $7, $2))) + ABORT_PARSE; + } ; events_list : class_event - { $$ = 0; } + { $$ = new tree_classdef_events_list ($1); } | events_list opt_sep class_event - { $$ = 0; } + { + $1->append ($3); + $$ = $1; + } ; class_event : identifier - { $$ = 0; } + { $$ = new tree_classdef_event ($1); } ; -enum_beg : ENUMERATION stash_comment - { $$ = 0; } - ; - -enum_block : enum_beg opt_attr_list opt_sep enum_list opt_sep END - { $$ = 0; } +enum_block : ENUMERATION stash_comment opt_attr_list opt_sep enum_list opt_sep END + { + if (! ($$ = curr_parser.make_classdef_enum_block + ($1, $3, $5, $7, $2))) + ABORT_PARSE; + } ; enum_list : class_enum - { $$ = 0; } + { $$ = new tree_classdef_enum_list ($1); } | enum_list opt_sep class_enum - { $$ = 0; } + { + $1->append ($3); + $$ = $1; + } ; class_enum : identifier '(' expression ')' - { $$ = 0; } + { $$ = new tree_classdef_enum ($1, $3); } ; // ============= @@ -2808,6 +2899,172 @@ curr_lexer->looking_at_parameter_list = false; } +tree_funcall * +octave_parser::make_superclass_ref (const std::string& method_nm, + const std::string& package_nm, + const std::string& class_nm, + int l, int c) +{ + octave_value_list args; + + args(2) = class_nm; + args(1) = package_nm; + args(0) = method_nm; + + octave_value fcn + = symbol_table::find_built_in_function ("__superclass_reference__"); + + return new tree_funcall (fcn, args); +} + +tree_funcall * +octave_parser::make_meta_class_query (const std::string& package_nm, + const std::string& class_nm, + int l, int c) +{ + octave_value_list args; + + args(1) = class_nm; + args(0) = package_nm; + + octave_value fcn + = symbol_table::find_built_in_function ("__meta_class_query__"); + + return new tree_funcall (fcn, args); +} + +// A CLASSDEF block defines a class that has a constructor and other +// methods, but it is not an executable command. Parsing the block +// makes some changes in the symbol table (inserting the constructor +// and methods, and adding to the list of known objects) and creates +// a parse tree containing meta information about the class. + +tree_classdef * +octave_parser::make_classdef (token *tok_val, + tree_classdef_attribute_list *a, + tree_identifier *id, + tree_classdef_superclass_list *sc, + tree_classdef_body *body, token *end_tok, + octave_comment_list *lc) +{ + tree_classdef *retval = 0; + + std::string cls_name = id->name (); + + std::string nm = curr_lexer->fcn_file_name; + + size_t pos = nm.find_last_of (file_ops::dir_sep_chars ()); + + if (pos != std::string::npos) + nm = curr_lexer->fcn_file_name.substr (pos+1); + + if (nm != cls_name) + { + bison_error ("invalid classdef definition, the class name must match the file name"); + return retval; + } + + if (end_token_ok (end_tok, token::classdef_end)) + { + octave_comment_list *tc = octave_comment_buffer::get_comment (); + + int l = tok_val->line (); + int c = tok_val->column (); + + retval = new tree_classdef (a, id, sc, body, lc, tc, l, c); + } + + return retval; +} + +tree_classdef_properties_block * +octave_parser::make_classdef_properties_block (token *tok_val, + tree_classdef_attribute_list *a, + tree_classdef_property_list *plist, + token *end_tok, + octave_comment_list *lc) +{ + tree_classdef_properties_block *retval = 0; + + if (end_token_ok (end_tok, token::properties_end)) + { + octave_comment_list *tc = octave_comment_buffer::get_comment (); + + int l = tok_val->line (); + int c = tok_val->column (); + + retval = new tree_classdef_properties_block (a, plist, lc, tc, l, c); + } + + return retval; +} + +tree_classdef_methods_block * +octave_parser::make_classdef_methods_block (token *tok_val, + tree_classdef_attribute_list *a, + tree_classdef_methods_list *mlist, + token *end_tok, + octave_comment_list *lc) +{ + tree_classdef_methods_block *retval = 0; + + if (end_token_ok (end_tok, token::methods_end)) + { + octave_comment_list *tc = octave_comment_buffer::get_comment (); + + int l = tok_val->line (); + int c = tok_val->column (); + + retval = new tree_classdef_methods_block (a, mlist, lc, tc, l, c); + } + + return retval; +} + +tree_classdef_events_block * +octave_parser::make_classdef_events_block (token *tok_val, + tree_classdef_attribute_list *a, + tree_classdef_events_list *elist, + token *end_tok, + octave_comment_list *lc) +{ + tree_classdef_events_block *retval = 0; + + if (end_token_ok (end_tok, token::events_end)) + { + octave_comment_list *tc = octave_comment_buffer::get_comment (); + + int l = tok_val->line (); + int c = tok_val->column (); + + retval = new tree_classdef_events_block (a, elist, lc, tc, l, c); + } + + return retval; +} + +tree_classdef_enum_block * +octave_parser::make_classdef_enum_block (token *tok_val, + tree_classdef_attribute_list *a, + tree_classdef_enum_list *elist, + token *end_tok, + octave_comment_list *lc) +{ + tree_classdef_enum_block *retval = 0; + + if (end_token_ok (end_tok, token::enumeration_end)) + { + octave_comment_list *tc = octave_comment_buffer::get_comment (); + + int l = tok_val->line (); + int c = tok_val->column (); + + retval = new tree_classdef_enum_block (a, elist, lc, tc, l, c); + } + + return retval; +} + // Make an index expression. tree_index_expression * @@ -2825,7 +3082,8 @@ int l = expr->line (); int c = expr->column (); - expr->mark_postfix_indexed (); + if (! expr->is_postfix_indexed ()) + expr->set_postfix_index (type); if (expr->is_index_expression ()) { @@ -2852,6 +3110,9 @@ int l = expr->line (); int c = expr->column (); + if (! expr->is_postfix_indexed ()) + expr->set_postfix_index ('.'); + if (expr->is_index_expression ()) { tree_index_expression *tmp = static_cast<tree_index_expression *> (expr); @@ -2878,6 +3139,9 @@ int l = expr->line (); int c = expr->column (); + if (! expr->is_postfix_indexed ()) + expr->set_postfix_index ('.'); + if (expr->is_index_expression ()) { tree_index_expression *tmp = static_cast<tree_index_expression *> (expr); @@ -3073,6 +3337,7 @@ void octave_parser::bison_error (const char *s) + { int err_col = curr_lexer->current_input_column - 1; @@ -3192,7 +3457,22 @@ fcn_ptr = curr_parser.primary_fcn_ptr; - if (status != 0) + if (status == 0) + { + if (curr_parser.curr_lexer->reading_classdef_file + && curr_parser.classdef_object) + { + // Convert parse tree for classdef object to + // meta.class info (and stash it in the symbol + // table?). Return pointer to constructor? + + if (fcn_ptr) + panic_impossible (); + + fcn_ptr = curr_parser.classdef_object->make_meta_class (); + } + } + else error ("parse error while reading file %s", full_file.c_str ()); } else if (require_file)
--- a/libinterp/parse-tree/parse.h +++ b/libinterp/parse-tree/parse.h @@ -43,6 +43,18 @@ class tree_argument_list; class tree_array_list; class tree_cell; +class tree_classdef; +class tree_classdef_attribute_list; +class tree_classdef_body; +class tree_classdef_enum_block; +class tree_classdef_enum_list; +class tree_classdef_events_block; +class tree_classdef_events_list; +class tree_classdef_methods_block; +class tree_classdef_methods_list; +class tree_classdef_properties_block; +class tree_classdef_property_list; +class tree_classdef_superclass_list; class tree_colon_expression; class tree_command; class tree_constant; @@ -50,6 +62,7 @@ class tree_decl_init_list; class tree_expression; class tree_fcn_handle; +class tree_funcall; class tree_function_def; class tree_identifier; class tree_if_clause; @@ -136,7 +149,7 @@ parsing_subfunctions (false), max_fcn_depth (0), curr_fcn_depth (0), primary_fcn_scope (-1), curr_class_name (), function_scopes (), primary_fcn_ptr (0), - stmt_list (0), + classdef_object (0), stmt_list (0), curr_lexer (new octave_lexer ()), parser_state (0) { init (); @@ -311,6 +324,47 @@ void recover_from_parsing_function (void); + tree_funcall * + make_superclass_ref (const std::string& method_nm, + const std::string& package_nm, + const std::string& class_nm, + int l, int c); + + tree_funcall * + make_meta_class_query (const std::string& package_nm, + const std::string& class_nm, + int l, int c); + + tree_classdef * + make_classdef (token *tok_val, tree_classdef_attribute_list *a, + tree_identifier *id, tree_classdef_superclass_list *sc, + tree_classdef_body *body, token *end_tok, + octave_comment_list *lc); + + tree_classdef_properties_block * + make_classdef_properties_block (token *tok_val, + tree_classdef_attribute_list *a, + tree_classdef_property_list *plist, + token *end_tok, octave_comment_list *lc); + + tree_classdef_methods_block * + make_classdef_methods_block (token *tok_val, + tree_classdef_attribute_list *a, + tree_classdef_methods_list *mlist, + token *end_tok, octave_comment_list *lc); + + tree_classdef_events_block * + make_classdef_events_block (token *tok_val, + tree_classdef_attribute_list *a, + tree_classdef_events_list *elist, + token *end_tok, octave_comment_list *lc); + + tree_classdef_enum_block * + make_classdef_enum_block (token *tok_val, + tree_classdef_attribute_list *a, + tree_classdef_enum_list *elist, + token *end_tok, octave_comment_list *lc); + // Make an index expression. tree_index_expression * make_index_expression (tree_expression *expr, @@ -400,6 +454,9 @@ // Pointer to the primary user function or user script function. octave_function *primary_fcn_ptr; + // Pointer to the classdef object we just parsed, if any. + tree_classdef *classdef_object; + // Result of parsing input. tree_statement_list *stmt_list;
--- a/libinterp/parse-tree/pt-all.h +++ b/libinterp/parse-tree/pt-all.h @@ -30,6 +30,7 @@ #include "pt-binop.h" #include "pt-cbinop.h" #include "pt-check.h" +#include "pt-classdef.h" #include "pt-cmd.h" #include "pt-colon.h" #include "pt-const.h" @@ -37,6 +38,7 @@ #include "pt-except.h" #include "pt-exp.h" #include "pt-fcn-handle.h" +#include "pt-funcall.h" #include "pt-id.h" #include "pt-idx.h" #include "pt-jump.h"
--- a/libinterp/parse-tree/pt-bp.cc +++ b/libinterp/parse-tree/pt-bp.cc @@ -299,6 +299,12 @@ } void +tree_breakpoint::visit_funcall (tree_funcall&) +{ + panic_impossible (); +} + +void tree_breakpoint::visit_parameter_list (tree_parameter_list&) { panic_impossible ();
--- a/libinterp/parse-tree/pt-bp.h +++ b/libinterp/parse-tree/pt-bp.h @@ -106,6 +106,8 @@ void visit_fcn_handle (tree_fcn_handle&); + void visit_funcall (tree_funcall&); + void visit_parameter_list (tree_parameter_list&); void visit_postfix_expression (tree_postfix_expression&);
--- a/libinterp/parse-tree/pt-check.cc +++ b/libinterp/parse-tree/pt-check.cc @@ -357,6 +357,11 @@ } void +tree_checker::visit_funcall (tree_funcall& /* fc */) +{ +} + +void tree_checker::visit_parameter_list (tree_parameter_list& lst) { tree_parameter_list::iterator p = lst.begin ();
--- a/libinterp/parse-tree/pt-check.h +++ b/libinterp/parse-tree/pt-check.h @@ -91,6 +91,8 @@ void visit_fcn_handle (tree_fcn_handle&); + void visit_funcall (tree_funcall&); + void visit_parameter_list (tree_parameter_list&); void visit_postfix_expression (tree_postfix_expression&);
new file mode 100644 --- /dev/null +++ b/libinterp/parse-tree/pt-classdef.cc @@ -0,0 +1,259 @@ +/* + +Copyright (C) 2012 John W. Eaton + +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 +<http://www.gnu.org/licenses/>. + +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ov-classdef.h" +#include "pt-classdef.h" + +// Classdef attribute + +void +tree_classdef_attribute::accept (tree_walker& tw) +{ + tw.visit_classdef_attribute (*this); +} + +// Classdef attribute_list + +tree_classdef_attribute_list::~tree_classdef_attribute_list (void) +{ + while (! empty ()) + { + iterator p = begin (); + delete *p; + erase (p); + } +} + +void +tree_classdef_attribute_list::accept (tree_walker& tw) +{ + tw.visit_classdef_attribute_list (*this); +} + +// Classdef superclass + +void +tree_classdef_superclass::accept (tree_walker& tw) +{ + tw.visit_classdef_superclass (*this); +} + +// Classdef superclass_list + +tree_classdef_superclass_list::~tree_classdef_superclass_list (void) +{ + while (! empty ()) + { + iterator p = begin (); + delete *p; + erase (p); + } +} + +void +tree_classdef_superclass_list::accept (tree_walker& tw) +{ + tw.visit_classdef_superclass_list (*this); +} + +// Classdef property + +void +tree_classdef_property::accept (tree_walker& tw) +{ + tw.visit_classdef_property (*this); +} + +// Classdef property_list + +tree_classdef_property_list::~tree_classdef_property_list (void) +{ + while (! empty ()) + { + iterator p = begin (); + delete *p; + erase (p); + } +} + +void +tree_classdef_property_list::accept (tree_walker& tw) +{ + tw.visit_classdef_property_list (*this); +} + +// Classdef properties_block + +void +tree_classdef_properties_block::accept (tree_walker& tw) +{ + tw.visit_classdef_properties_block (*this); +} + +// Classdef methods_list + +void +tree_classdef_methods_list::accept (tree_walker& tw) +{ + tw.visit_classdef_methods_list (*this); +} + +// Classdef methods_block + +void +tree_classdef_methods_block::accept (tree_walker& tw) +{ + tw.visit_classdef_methods_block (*this); +} + +// Classdef event + +void +tree_classdef_event::accept (tree_walker& tw) +{ + tw.visit_classdef_event (*this); +} + +// Classdef events_list + +tree_classdef_events_list::~tree_classdef_events_list (void) +{ + while (! empty ()) + { + iterator p = begin (); + delete *p; + erase (p); + } +} + +void +tree_classdef_events_list::accept (tree_walker& tw) +{ + tw.visit_classdef_events_list (*this); +} + +// Classdef events_block + +void +tree_classdef_events_block::accept (tree_walker& tw) +{ + tw.visit_classdef_events_block (*this); +} + +// Classdef enum + +void +tree_classdef_enum::accept (tree_walker& tw) +{ + tw.visit_classdef_enum (*this); +} + +// Classdef enum_list + +tree_classdef_enum_list::~tree_classdef_enum_list (void) +{ + while (! empty ()) + { + iterator p = begin (); + delete *p; + erase (p); + } +} + +void +tree_classdef_enum_list::accept (tree_walker& tw) +{ + tw.visit_classdef_enum_list (*this); +} + +// Classdef enum_block + +void +tree_classdef_enum_block::accept (tree_walker& tw) +{ + tw.visit_classdef_enum_block (*this); +} + +// Classdef body + +tree_classdef_body::~tree_classdef_body (void) +{ + while (! properties_lst.empty ()) + { + properties_list_iterator p = properties_lst.begin (); + delete *p; + properties_lst.erase (p); + } + + while (! methods_lst.empty ()) + { + methods_list_iterator p = methods_lst.begin (); + delete *p; + methods_lst.erase (p); + } + + while (! events_lst.empty ()) + { + events_list_iterator p = events_lst.begin (); + delete *p; + events_lst.erase (p); + } + + while (! enum_lst.empty ()) + { + enum_list_iterator p = enum_lst.begin (); + delete *p; + enum_lst.erase (p); + } +} + +// Classdef + +octave_function* +tree_classdef::make_meta_class (void) +{ + octave_value retval; + cdef_class cls = cdef_class::make_meta_class (this); + + if (cls.ok ()) + return cls.get_constructor_function (); + + return 0; +} + +tree_classdef * +tree_classdef::dup (symbol_table::scope_id, + symbol_table::context_id) const +{ + // FIXME + return 0; +} + +void +tree_classdef::accept (tree_walker& tw) +{ + tw.visit_classdef (*this); +}
new file mode 100644 --- /dev/null +++ b/libinterp/parse-tree/pt-classdef.h @@ -0,0 +1,653 @@ +/* + +Copyright (C) 2012 John W. Eaton + +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 +<http://www.gnu.org/licenses/>. + +*/ + +#if !defined (octave_tree_classdef_h) +#define octave_tree_classdef_h 1 + +class octave_value; + +class tree_walker; + +#include "pt-cmd.h" +#include "pt-exp.h" +#include "pt-id.h" + +#include "base-list.h" + +#include <list> + +class tree_classdef_attribute +{ +public: + + tree_classdef_attribute (tree_identifier *i = 0, tree_expression *e = 0) + : id (i), expr (e), neg (false) { } + + tree_classdef_attribute (tree_identifier *i, bool b) + : id (i), expr (0), neg (b) { } + + ~tree_classdef_attribute (void) + { + delete id; + delete expr; + } + + tree_identifier *ident (void) { return id; } + + tree_expression *expression (void) { return expr; } + + bool negate (void) { return neg; } + + void accept (tree_walker&); + +private: + + tree_identifier *id; + tree_expression *expr; + bool neg; + + // No copying! + + tree_classdef_attribute (const tree_classdef_attribute&); + + tree_classdef_attribute& operator = (const tree_classdef_attribute&); +}; + +class tree_classdef_attribute_list : public octave_base_list<tree_classdef_attribute *> +{ +public: + + tree_classdef_attribute_list (void) { } + + tree_classdef_attribute_list (tree_classdef_attribute *a) { append (a); } + + tree_classdef_attribute_list (const octave_base_list<tree_classdef_attribute *>& a) + : octave_base_list<tree_classdef_attribute *> (a) { } + + ~tree_classdef_attribute_list (void); + + void accept (tree_walker&); + +private: + + // No copying! + + tree_classdef_attribute_list (const tree_classdef_attribute_list&); + + tree_classdef_attribute_list& operator = (const tree_classdef_attribute_list&); +}; + +class tree_classdef_superclass +{ +public: + + tree_classdef_superclass (tree_identifier *i = 0, tree_identifier *p = 0) + : id (i), pkg (p) { } + + ~tree_classdef_superclass (void) + { + delete id; + delete pkg; + } + + tree_identifier *ident (void) { return id; } + + tree_identifier * package (void) { return pkg; } + + void accept (tree_walker&); + +private: + + tree_identifier *id; + tree_identifier *pkg; + + // No copying! + + tree_classdef_superclass (const tree_classdef_superclass&); + + tree_classdef_superclass& operator = (const tree_classdef_superclass&); +}; + +class tree_classdef_superclass_list : public octave_base_list<tree_classdef_superclass *> +{ +public: + + tree_classdef_superclass_list (void) { } + + tree_classdef_superclass_list (tree_classdef_superclass *sc) { append (sc); } + + tree_classdef_superclass_list (const octave_base_list<tree_classdef_superclass *>& a) + : octave_base_list<tree_classdef_superclass *> (a) { } + + ~tree_classdef_superclass_list (void); + + void accept (tree_walker&); + +private: + + // No copying! + + tree_classdef_superclass_list (const tree_classdef_superclass_list&); + + tree_classdef_superclass_list& operator = (const tree_classdef_superclass_list&); +}; + +template <typename T> +class tree_classdef_element : public tree +{ +public: + + tree_classdef_element (tree_classdef_attribute_list *a, + octave_base_list<T> *elist, + octave_comment_list *lc, octave_comment_list *tc, + int l = -1, int c = -1) + : tree (l, c), attr_list (a), elt_list (elist), + lead_comm (lc), trail_comm (tc) + { } + + ~tree_classdef_element (void) + { + delete attr_list; + delete elt_list; + delete lead_comm; + delete trail_comm; + } + + tree_classdef_attribute_list *attribute_list (void) { return attr_list; } + + octave_base_list<T> *element_list (void) { return elt_list; } + + octave_comment_list *leading_comment (void) { return lead_comm; } + + octave_comment_list *trailing_comment (void) { return trail_comm; } + + void accept (tree_walker&) { } + +private: + + // List of attributes that apply to this class. + tree_classdef_attribute_list *attr_list; + + // The list of objects contained in this block. + octave_base_list<T> *elt_list; + + // Comment preceding the token marking the beginning of the block. + octave_comment_list *lead_comm; + + // Comment preceding END token. + octave_comment_list *trail_comm; + + // No copying! + + tree_classdef_element (const tree_classdef_element&); + + tree_classdef_element& operator = (const tree_classdef_element&); +}; + +class tree_classdef_property +{ +public: + + tree_classdef_property (tree_identifier *i = 0, tree_expression *e = 0) + : id (i), expr (e) { } + + ~tree_classdef_property (void) + { + delete id; + delete expr; + } + + tree_identifier *ident (void) { return id; } + + tree_expression *expression (void) { return expr; } + + void accept (tree_walker&); + +private: + + tree_identifier *id; + tree_expression *expr; + + // No copying! + + tree_classdef_property (const tree_classdef_property&); + + tree_classdef_property& operator = (const tree_classdef_property&); +}; + +class tree_classdef_property_list : public octave_base_list<tree_classdef_property *> +{ +public: + + tree_classdef_property_list (void) { } + + tree_classdef_property_list (tree_classdef_property* p) { append (p); } + + tree_classdef_property_list (const octave_base_list<tree_classdef_property *>& a) + : octave_base_list<tree_classdef_property *> (a) { } + + ~tree_classdef_property_list (void); + + void accept (tree_walker&); + +private: + + // No copying! + + tree_classdef_property_list (const tree_classdef_property_list&); + + tree_classdef_property_list& operator = (const tree_classdef_property_list&); +}; + +class tree_classdef_properties_block + : public tree_classdef_element<tree_classdef_property *> +{ +public: + + tree_classdef_properties_block (tree_classdef_attribute_list *a, + tree_classdef_property_list *plist, + octave_comment_list *lc, + octave_comment_list *tc, + int l = -1, int c = -1) + : tree_classdef_element<tree_classdef_property *> (a, plist, lc, tc, l, c) { } + + ~tree_classdef_properties_block (void) { } + + void accept (tree_walker&); + +private: + + // No copying! + + tree_classdef_properties_block (const tree_classdef_properties_block&); + + tree_classdef_properties_block& operator = (const tree_classdef_properties_block&); +}; + +class tree_classdef_methods_list : public octave_base_list<octave_value> +{ +public: + + tree_classdef_methods_list (void) { } + + tree_classdef_methods_list (const octave_value& f) { append (f); } + + tree_classdef_methods_list (const octave_base_list<octave_value>& a) + : octave_base_list<octave_value> (a) { } + + ~tree_classdef_methods_list (void) { } + + void accept (tree_walker&); + +private: + + // No copying! + + tree_classdef_methods_list (const tree_classdef_methods_list&); + + tree_classdef_methods_list& operator = (const tree_classdef_methods_list&); +}; + +class tree_classdef_methods_block : public tree_classdef_element<octave_value> +{ +public: + + tree_classdef_methods_block (tree_classdef_attribute_list *a, + tree_classdef_methods_list *mlist, + octave_comment_list *lc, + octave_comment_list *tc, int l = -1, int c = -1) + : tree_classdef_element<octave_value> (a, mlist, lc, tc, l, c) { } + + ~tree_classdef_methods_block (void) { } + + void accept (tree_walker&); + +private: + + // No copying! + + tree_classdef_methods_block (const tree_classdef_methods_block&); + + tree_classdef_methods_block& operator = (const tree_classdef_methods_block&); +}; + +class tree_classdef_event +{ +public: + + tree_classdef_event (tree_identifier *i = 0) : id (i) { } + + ~tree_classdef_event (void) + { + delete id; + } + + tree_identifier *ident (void) { return id; } + + void accept (tree_walker&); + +private: + + tree_identifier *id; + + // No copying! + + tree_classdef_event (const tree_classdef_event&); + + tree_classdef_event& operator = (const tree_classdef_event&); +}; + +class tree_classdef_events_list : public octave_base_list<tree_classdef_event *> +{ +public: + + tree_classdef_events_list (void) { } + + tree_classdef_events_list (tree_classdef_event *e) { append (e); } + + tree_classdef_events_list (const octave_base_list<tree_classdef_event *>& a) + : octave_base_list<tree_classdef_event *> (a) { } + + ~tree_classdef_events_list (void); + + void accept (tree_walker&); + +private: + + // No copying! + + tree_classdef_events_list (const tree_classdef_events_list&); + + tree_classdef_events_list& operator = (const tree_classdef_events_list&); +}; + +class tree_classdef_events_block + : public tree_classdef_element<tree_classdef_event *> +{ +public: + + tree_classdef_events_block (tree_classdef_attribute_list *a, + tree_classdef_events_list *elist, + octave_comment_list *lc, + octave_comment_list *tc, int l = -1, int c = -1) + : tree_classdef_element<tree_classdef_event *> (a, elist, lc, tc, l, c) { } + + ~tree_classdef_events_block (void) { } + + void accept (tree_walker&); + +private: + + // No copying! + + tree_classdef_events_block (const tree_classdef_events_block&); + + tree_classdef_events_block& operator = (const tree_classdef_events_block&); +}; + +class tree_classdef_enum +{ +public: + + tree_classdef_enum (void) : id (0), expr (0) { } + + tree_classdef_enum (tree_identifier *i, tree_expression *e) + : id (i), expr (e) { } + + ~tree_classdef_enum (void) + { + delete id; + delete expr; + } + + tree_identifier *ident (void) { return id; } + + tree_expression *expression (void) { return expr; } + + void accept (tree_walker&); + +private: + + tree_identifier *id; + tree_expression *expr; + + // No copying! + + tree_classdef_enum (const tree_classdef_enum&); + + tree_classdef_enum& operator = (const tree_classdef_enum&); +}; + +class tree_classdef_enum_list : public octave_base_list<tree_classdef_enum *> +{ +public: + + tree_classdef_enum_list (void) { } + + tree_classdef_enum_list (tree_classdef_enum *e) { append (e); } + + tree_classdef_enum_list (const octave_base_list<tree_classdef_enum *>& a) + : octave_base_list<tree_classdef_enum *> (a) { } + + ~tree_classdef_enum_list (void); + + void accept (tree_walker&); + +private: + + // No copying! + + tree_classdef_enum_list (const tree_classdef_enum_list&); + + tree_classdef_enum_list& operator = (const tree_classdef_enum_list&); +}; + +class tree_classdef_enum_block + : public tree_classdef_element<tree_classdef_enum *> +{ +public: + + tree_classdef_enum_block (tree_classdef_attribute_list *a, + tree_classdef_enum_list *elist, + octave_comment_list *lc, + octave_comment_list *tc, int l = -1, int c = -1) + : tree_classdef_element<tree_classdef_enum *> (a, elist, lc, tc, l, c) { } + + ~tree_classdef_enum_block (void) { } + + void accept (tree_walker&); + +private: + + // No copying! + + tree_classdef_enum_block (const tree_classdef_enum_block&); + + tree_classdef_enum_block& operator = (const tree_classdef_enum_block&); +}; + +class tree_classdef_body +{ +public: + + typedef std::list<tree_classdef_properties_block *>::iterator properties_list_iterator; + typedef std::list<tree_classdef_properties_block *>::const_iterator properties_list_const_iterator; + + typedef std::list<tree_classdef_methods_block *>::iterator methods_list_iterator; + typedef std::list<tree_classdef_methods_block *>::const_iterator methods_list_const_iterator; + + typedef std::list<tree_classdef_events_block *>::iterator events_list_iterator; + typedef std::list<tree_classdef_events_block *>::const_iterator events_list_const_iterator; + + typedef std::list<tree_classdef_enum_block *>::iterator enum_list_iterator; + typedef std::list<tree_classdef_enum_block *>::const_iterator enum_list_const_iterator; + + tree_classdef_body (void) + : properties_lst (), methods_lst (), events_lst (), enum_lst () { } + + tree_classdef_body (tree_classdef_properties_block *pb) + : properties_lst (), methods_lst (), events_lst (), enum_lst () + { + append (pb); + } + + tree_classdef_body (tree_classdef_methods_block *mb) + : properties_lst (), methods_lst (), events_lst (), enum_lst () + { + append (mb); + } + + tree_classdef_body (tree_classdef_events_block *evb) + : properties_lst (), methods_lst (), events_lst (), enum_lst () + { + append (evb); + } + + tree_classdef_body (tree_classdef_enum_block *enb) + : properties_lst (), methods_lst (), events_lst (), enum_lst () + { + append (enb); + } + + ~tree_classdef_body (void); + + void append (tree_classdef_properties_block *pb) + { + properties_lst.push_back (pb); + } + + void append (tree_classdef_methods_block *mb) + { + methods_lst.push_back (mb); + } + + void append (tree_classdef_events_block *evb) + { + events_lst.push_back (evb); + } + + void append (tree_classdef_enum_block *enb) + { + enum_lst.push_back (enb); + } + + std::list<tree_classdef_properties_block *> properties_list (void) + { + return properties_lst; + } + + std::list<tree_classdef_methods_block *> methods_list (void) + { + return methods_lst; + } + + std::list<tree_classdef_events_block *> events_list (void) + { + return events_lst; + } + + std::list<tree_classdef_enum_block *> enum_list (void) + { + return enum_lst; + } + + void accept (tree_walker&); + +private: + + std::list<tree_classdef_properties_block *> properties_lst; + + std::list<tree_classdef_methods_block *> methods_lst; + + std::list<tree_classdef_events_block *> events_lst; + + std::list<tree_classdef_enum_block *> enum_lst; + + // No copying! + + tree_classdef_body (const tree_classdef_body&); + + tree_classdef_body& operator = (const tree_classdef_body&); +}; + +// Classdef definition. + +class tree_classdef : public tree_command +{ +public: + + tree_classdef (tree_classdef_attribute_list *a, tree_identifier *i, + tree_classdef_superclass_list *sc, + tree_classdef_body *b, octave_comment_list *lc, + octave_comment_list *tc, int l = -1, int c = -1) + : tree_command (l, c), attr_list (a), id (i), + supclass_list (sc), element_list (b), lead_comm (lc), trail_comm (tc) { } + + ~tree_classdef (void) + { + delete attr_list; + delete id; + delete supclass_list; + delete element_list; + delete lead_comm; + delete trail_comm; + } + + tree_classdef_attribute_list *attribute_list (void) { return attr_list; } + + tree_identifier *ident (void) { return id; } + + tree_classdef_superclass_list *superclass_list (void) { return supclass_list; } + + tree_classdef_body *body (void) { return element_list; } + + octave_comment_list *leading_comment (void) { return lead_comm; } + octave_comment_list *trailing_comment (void) { return trail_comm; } + + octave_function* make_meta_class (void); + + tree_classdef *dup (symbol_table::scope_id scope, + symbol_table::context_id context) const; + + void accept (tree_walker& tw); + +private: + + tree_classdef_attribute_list *attr_list; + + tree_identifier *id; + + tree_classdef_superclass_list *supclass_list; + + tree_classdef_body *element_list; + + octave_comment_list *lead_comm; + octave_comment_list *trail_comm; + + // No copying! + + tree_classdef (const tree_classdef&); + + tree_classdef& operator = (const tree_classdef&); +}; + +#endif
--- a/libinterp/parse-tree/pt-eval.cc +++ b/libinterp/parse-tree/pt-eval.cc @@ -637,6 +637,12 @@ } void +tree_evaluator::visit_funcall (tree_funcall&) +{ + panic_impossible (); +} + +void tree_evaluator::visit_parameter_list (tree_parameter_list&) { panic_impossible ();
--- a/libinterp/parse-tree/pt-eval.h +++ b/libinterp/parse-tree/pt-eval.h @@ -102,6 +102,8 @@ void visit_fcn_handle (tree_fcn_handle&); + void visit_funcall (tree_funcall&); + void visit_parameter_list (tree_parameter_list&); void visit_postfix_expression (tree_postfix_expression&);
--- a/libinterp/parse-tree/pt-exp.h +++ b/libinterp/parse-tree/pt-exp.h @@ -40,7 +40,7 @@ public: tree_expression (int l = -1, int c = -1) - : tree (l, c), num_parens (0), postfix_indexed (false), + : tree (l, c), num_parens (0), postfix_index_type ('\0'), print_flag (false) { } virtual ~tree_expression (void) { } @@ -85,7 +85,9 @@ int paren_count (void) const { return num_parens; } - bool is_postfix_indexed (void) const { return postfix_indexed; } + bool is_postfix_indexed (void) const { return (postfix_index_type != '\0'); } + + char postfix_index (void) const { return postfix_index_type; } // Check if the result of the expression should be printed. // Should normally be used in conjunction with @@ -106,9 +108,9 @@ return this; } - tree_expression *mark_postfix_indexed (void) + tree_expression *set_postfix_index (char type) { - postfix_indexed = true; + postfix_index_type = type; return this; } @@ -121,7 +123,7 @@ virtual void copy_base (const tree_expression& e) { num_parens = e.num_parens; - postfix_indexed = e.postfix_indexed; + postfix_index_type = e.postfix_index_type; print_flag = e.print_flag; } @@ -135,9 +137,10 @@ // ==> 0 for expression e2 int num_parens; - // A flag that says whether this expression has an index associated - // with it. See the code in tree_identifier::rvalue for the rationale. - bool postfix_indexed; + // The first index type associated with this expression. This field + // is 0 (character '\0') if the expression has no associated index. + // See the code in tree_identifier::rvalue for the rationale. + char postfix_index_type; // Print result of rvalue for this expression? bool print_flag;
new file mode 100644 --- /dev/null +++ b/libinterp/parse-tree/pt-funcall.cc @@ -0,0 +1,83 @@ +/* + +Copyright (C) 2012 John W. Eaton + +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 +<http://www.gnu.org/licenses/>. + +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ov-fcn.h" +#include "pt-funcall.h" +#include "pt-walk.h" + +// Function call objects. + +void +tree_funcall::print (std::ostream& os, bool pr_as_read_syntax, + bool pr_orig_text) +{ + print_raw (os, pr_as_read_syntax, pr_orig_text); +} + +void +tree_funcall::print_raw (std::ostream& os, bool pr_as_read_syntax, + bool pr_orig_text) +{ + if (pr_orig_text) + { + os << original_text (); + } + else + { + octave_function *fp = fcn.function_value (); + std::string nm = fp ? fp->name () : std::string ("<invalid-function>"); + + os << nm << " ("; + + octave_idx_type len = args.length (); + for (octave_idx_type i = 0; i < len; i++) + { + args(i).print_raw (os, pr_as_read_syntax); + + if (i < len - 1) + os << ", "; + } + + os << ")"; + } +} + +tree_funcall * +tree_funcall::dup (symbol_table::scope_id, + symbol_table::context_id context) const +{ + tree_funcall *new_fc = new tree_funcall (fcn, args, line (), column ()); + + new_fc->copy_base (*new_fc); + + return new_fc; +} + +void +tree_funcall::accept (tree_walker& tw) +{ + tw.visit_funcall (*this); +}
new file mode 100644 --- /dev/null +++ b/libinterp/parse-tree/pt-funcall.h @@ -0,0 +1,101 @@ +/* + +Copyright (C) 2012 John W. Eaton + +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 +<http://www.gnu.org/licenses/>. + +*/ + +#if !defined (octave_tree_funcall_h) +#define octave_tree_funcall_h 1 + +#include "ov.h" +#include "oct-obj.h" +#include "parse.h" +#include "pt-exp.h" + +// Function call. This class only represents function calls that have +// known functions (most useful for calls to built-in functions that +// are generated by the parser) and fixed argument lists, known at +// compile time. + +class +tree_funcall : public tree_expression +{ +public: + + tree_funcall (const octave_value& f, const octave_value_list& a, + int l = -1, int c = -1) + : tree_expression (l, c), fcn (f), args (a) + { + if (! fcn.is_function ()) + error ("tree_funcall: invalid function"); + } + + ~tree_funcall (void) { } + + bool has_magic_end (void) const { return false; } + + void print (std::ostream& os, bool pr_as_read_syntax = false, + bool pr_orig_txt = true); + + void print_raw (std::ostream& os, bool pr_as_read_syntax = false, + bool pr_orig_txt = true); + + tree_funcall *dup (symbol_table::scope_id, + symbol_table::context_id context) const; + + octave_value rvalue1 (int nargout) + { + octave_value retval; + + const octave_value_list tmp = rvalue (nargout); + + if (! tmp.empty ()) + retval = tmp(0); + + return retval; + } + + octave_value_list rvalue (int nargout) + { + return feval (fcn.function_value (), args, nargout); + } + + octave_value function (void) const { return fcn; } + + octave_value_list arguments (void) const { return args; } + + void accept (tree_walker& tw); + +private: + + // Function to call. Error if not a valid function at time of + // construction. + octave_value fcn; + + // Argument list. + octave_value_list args; + + // No copying! + + tree_funcall (const tree_funcall&); + + tree_funcall& operator = (const tree_funcall&); +}; + +#endif
--- a/libinterp/parse-tree/pt-id.cc +++ b/libinterp/parse-tree/pt-id.cc @@ -76,13 +76,19 @@ // // If this identifier refers to a function, we need to know // whether it is indexed so that we can do the same thing - // for 'f' and 'f()'. If the index is present, return the - // function object and let tree_index_expression::rvalue - // handle indexing. Otherwise, arrange to call the function - // here, so that we don't return the function definition as - // a value. + // for 'f' and 'f()'. If the index is present and the function + // object declares it can handle it, return the function object + // and let tree_index_expression::rvalue handle indexing. + // Otherwise, arrange to call the function here, so that we don't + // return the function definition as a value. - if (val.is_function () && ! is_postfix_indexed ()) + octave_function *fcn = 0; + + if (val.is_function ()) + fcn = val.function_value (true); + + if (fcn && ! (is_postfix_indexed () + && fcn->is_postfix_index_handled (postfix_index ()))) { octave_value_list tmp_args;
--- a/libinterp/parse-tree/pt-id.h +++ b/libinterp/parse-tree/pt-id.h @@ -32,6 +32,7 @@ class tree_walker; +#include "oct-lvalue.h" #include "pt-bp.h" #include "pt-exp.h" #include "symtab.h"
--- a/libinterp/parse-tree/pt-pr-code.cc +++ b/libinterp/parse-tree/pt-pr-code.cc @@ -749,6 +749,18 @@ } void +tree_print_code::visit_funcall (tree_funcall& fc) +{ + indent (); + + print_parens (fc, "("); + + fc.print_raw (os, true, print_original_text); + + print_parens (fc, ")"); +} + +void tree_print_code::visit_parameter_list (tree_parameter_list& lst) { tree_parameter_list::iterator p = lst.begin ();
--- a/libinterp/parse-tree/pt-pr-code.h +++ b/libinterp/parse-tree/pt-pr-code.h @@ -109,6 +109,8 @@ void visit_fcn_handle (tree_fcn_handle&); + void visit_funcall (tree_funcall&); + void visit_parameter_list (tree_parameter_list&); void visit_postfix_expression (tree_postfix_expression&);
--- a/libinterp/parse-tree/pt-walk.h +++ b/libinterp/parse-tree/pt-walk.h @@ -52,6 +52,7 @@ class tree_no_op_command; class tree_constant; class tree_fcn_handle; +class tree_funcall; class tree_parameter_list; class tree_postfix_expression; class tree_prefix_expression; @@ -65,6 +66,24 @@ class tree_while_command; class tree_do_until_command; +class tree_classdef_attribute; +class tree_classdef_attribute_list; +class tree_classdef_superclass; +class tree_classdef_superclass_list; +class tree_classdef_property; +class tree_classdef_property_list; +class tree_classdef_properties_block; +class tree_classdef_methods_list; +class tree_classdef_methods_block; +class tree_classdef_event; +class tree_classdef_events_list; +class tree_classdef_events_block; +class tree_classdef_enum; +class tree_classdef_enum_list; +class tree_classdef_enum_block; +class tree_classdef_body; +class tree_classdef; + class tree_walker { @@ -158,6 +177,9 @@ visit_fcn_handle (tree_fcn_handle&) = 0; virtual void + visit_funcall (tree_funcall&) = 0; + + virtual void visit_parameter_list (tree_parameter_list&) = 0; virtual void @@ -193,6 +215,57 @@ virtual void visit_do_until_command (tree_do_until_command&) = 0; + virtual void + visit_classdef_attribute (tree_classdef_attribute&) { } /* = 0; */ + + virtual void + visit_classdef_attribute_list (tree_classdef_attribute_list&) { } /* = 0; */ + + virtual void + visit_classdef_superclass (tree_classdef_superclass&) { } /* = 0; */ + + virtual void + visit_classdef_superclass_list (tree_classdef_superclass_list&) { } /* = 0; */ + + virtual void + visit_classdef_property (tree_classdef_property&) { } /* = 0; */ + + virtual void + visit_classdef_property_list (tree_classdef_property_list&) { } /* = 0; */ + + virtual void + visit_classdef_properties_block (tree_classdef_properties_block&) { } /* = 0; */ + + virtual void + visit_classdef_methods_list (tree_classdef_methods_list&) { } /* = 0; */ + + virtual void + visit_classdef_methods_block (tree_classdef_methods_block&) { } /* = 0; */ + + virtual void + visit_classdef_event (tree_classdef_event&) { } /* = 0; */ + + virtual void + visit_classdef_events_list (tree_classdef_events_list&) { } /* = 0; */ + + virtual void + visit_classdef_events_block (tree_classdef_events_block&) { } /* = 0; */ + + virtual void + visit_classdef_enum (tree_classdef_enum&) { } /* = 0; */ + + virtual void + visit_classdef_enum_list (tree_classdef_enum_list&) { } /* = 0; */ + + virtual void + visit_classdef_enum_block (tree_classdef_enum_block&) { } /* = 0; */ + + virtual void + visit_classdef_body (tree_classdef_body&) { } /* = 0; */ + + virtual void + visit_classdef (tree_classdef&) { } /* = 0; */ + protected: tree_walker (void) { }
--- a/libinterp/parse-tree/token.cc +++ b/libinterp/parse-tree/token.cc @@ -97,38 +97,50 @@ sr = s; } -token::token (int tv, symbol_table::symbol_record *cls, - symbol_table::symbol_record *pkg, int l, int c) +token::token (int tv, const std::string& pkg, const std::string& cls, + int l, int c) +{ + maybe_cmd = false; + tspc = false; + line_num = l; + column_num = c; + tok_val = tv; + type_tag = meta_name_token; + mc.package_nm = new std::string (pkg); + mc.class_nm = new std::string (cls); +} + +token::token (int tv, const std::string& mth, const std::string& pkg, + const std::string& cls, int l, int c) { maybe_cmd = false; tspc = false; line_num = l; column_num = c; tok_val = tv; - type_tag = meta_rec_token; - mc.cr = cls; - mc.pr = pkg; -} - -token::token (int tv, symbol_table::symbol_record *mth, - symbol_table::symbol_record *cls, - symbol_table::symbol_record *pkg, int l, int c) -{ - maybe_cmd = false; - tspc = false; - line_num = l; - column_num = c; - tok_val = tv; - type_tag = scls_rec_token; - sc.mr = mth; - sc.cr = cls; - sc.pr = pkg; + type_tag = scls_name_token; + sc.method_nm = new std::string (mth); + sc.package_nm = new std::string (pkg); + sc.class_nm = new std::string (cls); } token::~token (void) { if (type_tag == string_token) delete str; + + if (type_tag == scls_name_token) + { + delete sc.method_nm; + delete sc.package_nm; + delete sc.class_nm; + } + + if (type_tag == meta_name_token) + { + delete mc.package_nm; + delete mc.class_nm; + } } std::string @@ -165,39 +177,39 @@ return sr; } -symbol_table::symbol_record * -token::method_rec (void) +std::string +token::superclass_method_name (void) { - assert (type_tag == scls_rec_token); - return sc.mr; + assert (type_tag == scls_name_token); + return *sc.method_nm; } -symbol_table::symbol_record * -token::class_rec (void) +std::string +token::superclass_package_name (void) { - assert (type_tag == scls_rec_token); - return sc.cr; + assert (type_tag == scls_name_token); + return *sc.package_nm; } -symbol_table::symbol_record * -token::package_rec (void) +std::string +token::superclass_class_name (void) { - assert (type_tag == scls_rec_token); - return sc.pr; + assert (type_tag == scls_name_token); + return *sc.class_nm; } -symbol_table::symbol_record * -token::meta_class_rec (void) +std::string +token::meta_package_name (void) { - assert (type_tag == meta_rec_token); - return mc.cr; + assert (type_tag == meta_name_token); + return *mc.package_nm; } -symbol_table::symbol_record * -token::meta_package_rec (void) +std::string +token::meta_class_name (void) { - assert (type_tag == meta_rec_token); - return mc.pr; + assert (type_tag == meta_name_token); + return *mc.class_nm; } std::string
--- a/libinterp/parse-tree/token.h +++ b/libinterp/parse-tree/token.h @@ -40,8 +40,8 @@ double_token, ettype_token, sym_rec_token, - scls_rec_token, - meta_rec_token + scls_name_token, + meta_name_token }; enum end_tok_type @@ -69,11 +69,10 @@ int l = -1, int c = -1); token (int tv, end_tok_type t, int l = -1, int c = -1); token (int tv, symbol_table::symbol_record *s, int l = -1, int c = -1); - token (int tv, symbol_table::symbol_record *cls, - symbol_table::symbol_record *pkg, int l = -1, int c = -1); - token (int tv, symbol_table::symbol_record *mth, - symbol_table::symbol_record *cls, - symbol_table::symbol_record *pkg, int l = -1, int c = -1); + token (int tv, const std::string& pkg, const std::string& cls, + int l = -1, int c = -1); + token (int tv, const std::string& mth, const std::string& pkg, + const std::string& cls, int l = -1, int c = -1); ~token (void); @@ -100,12 +99,12 @@ end_tok_type ettype (void) const; symbol_table::symbol_record *sym_rec (void); - symbol_table::symbol_record *method_rec (void); - symbol_table::symbol_record *class_rec (void); - symbol_table::symbol_record *package_rec (void); + std::string superclass_method_name (void); + std::string superclass_package_name (void); + std::string superclass_class_name (void); - symbol_table::symbol_record *meta_class_rec (void); - symbol_table::symbol_record *meta_package_rec (void); + std::string meta_package_name (void); + std::string meta_class_name (void); std::string text_rep (void); @@ -131,14 +130,14 @@ symbol_table::symbol_record *sr; struct { - symbol_table::symbol_record *mr; - symbol_table::symbol_record *cr; - symbol_table::symbol_record *pr; + std::string *method_nm; + std::string *package_nm; + std::string *class_nm; } sc; struct { - symbol_table::symbol_record *cr; - symbol_table::symbol_record *pr; + std::string *package_nm; + std::string *class_nm; } mc; }; std::string orig_text;
--- a/liboctave/util/base-list.h +++ b/liboctave/util/base-list.h @@ -89,8 +89,6 @@ // For backward compatibility. void append (const elt_type& s) { lst.push_back (s); } -protected: - octave_base_list (void) : lst () { } octave_base_list (const std::list<elt_type>& l) : lst (l) { }
--- a/test/Makefile.am +++ b/test/Makefile.am @@ -53,6 +53,7 @@ include bug-35448/module.mk include bug-36025/module.mk include bug-38236/module.mk +include classdef/module.mk include classes/module.mk include class-concat/module.mk include ctor-vs-method/module.mk
new file mode 100644 --- /dev/null +++ b/test/classdef/classdef.tst @@ -0,0 +1,73 @@ +## Copyright (C) 2013 Ben Abbott +## +## 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 +## <http://www.gnu.org/licenses/>. + +%% Test script for classdef OOP. +%% Requires the path to contain the test classes. +%% +%% Note: This script and all classes are also intended to run +%% in MATLAB to test compatibility. Don't break that! +%% +%% To Do: This script tests to ensure that things done correctly work +%% corrrectly. It should also check that things done incorrectly +%% error properly. +%% +%% The classes used for the tests reside in the test/classdef with others +%% in the test directory. +%% +%% The classes provide the ability to test most of the major features +%% of the classdef OOP facilities. There are a number of classes, mostly +%% kind of the same, that create a hierarchy. + +%% Basic classdef tests for value class +%!shared p, q, i, amt +%! q = foo_value_class (); +%! p = foo_value_class (4, 4*12, 50e3); +%! i = p.rate / (12 * 100); +%! amt = (p.principle * i) / (1 - (1 + i)^(-p.term)); +%!assert (isempty (q.rate)); +%!assert (isempty (q.principle)); +%!assert (isempty (q.term)); +%!assert (class (p), "foo_value_class"); +%!assert (p.term, 48); +%!assert (p.rate, 4.0); +%!assert (p.principle, 50e3); +%!assert (p.amount, amt, eps ()) +%!assert (amount (p), amt, eps ()) +%!xtest +%! assert (properties (p), {'rate'; 'term'; 'principle'}) +%!xtest +%! assert (methods (p), {'amount'; 'foo_value_class'}) +%!assert (isempty (foo_value_class().rate)) +%!error <property `rate' is not constant> foo_value_class.rate + +%% Static method and Constant Property +%!assert (foo_static_method_constant_property.radians_per_cycle, 2*pi); +%!assert (foo_static_method_constant_property().radians_per_cycle, 2*pi); +%!assert (foo_static_method_constant_property().pie, pi); +%!error <property `frequency' is not constant> foo_static_method_constant_property.frequency +%!error <method `cosine' is not static> foo_static_method_constant_property.cosine +%!test +%! obj = foo_static_method_constant_property; +%! obj.frequency = 10; +%! assert (obj.cosine (0.1), cos (2 * pi * 10 * 0.1), eps ()) +%! assert (obj.sine (0.1), sin (2 * pi * 10 * 0.1), eps ()) + +%!xtest +%! obj = foo_method_changes_property_size (3); +%! obj = obj.move_element_to_end (2); +%! assert (obj.element, [1 3 2])
new file mode 100644 --- /dev/null +++ b/test/classdef/foo_method_changes_property_size.m @@ -0,0 +1,14 @@ +classdef foo_method_changes_property_size + properties + element; + end + methods + function obj = foo_method_changes_property_size (n) + obj.element = 1:n; + end + function obj = move_element_to_end (obj, n) + obj.element(end+1) = obj.element(n); + obj.element(n) = []; + end + end +end
new file mode 100644 --- /dev/null +++ b/test/classdef/foo_static_method_constant_property.m @@ -0,0 +1,30 @@ +classdef foo_static_method_constant_property + properties + frequency; + end + properties (Constant = true) + pie = pi; + end + methods + function obj = foo_static_method_constant_property (f) + if (nargin == 1) + obj.frequency = f; + elseif (nargin ~= 0) + error ('foo_static_method_constant_property:SyntaxError', ... + 'foo_static_method_constant_property: Invalid syntax') + end + end + function res = cosine (obj, t) + res = cos (obj.radians_per_cycle () * obj.frequency * t); + end + function res = sine (obj, t) + res = sin (obj.radians_per_cycle () * obj.frequency * t); + end + end + methods (Static) + function res = radians_per_cycle () + res = 2 * foo_static_method_constant_property.pie; + end + end +end +
new file mode 100644 --- /dev/null +++ b/test/classdef/foo_value_class.m @@ -0,0 +1,28 @@ +classdef foo_value_class + properties + rate; + term; + principle; + end + methods + function obj = foo_value_class (r, t, p) + if (nargin == 3) + obj.rate = r; + obj.term = t; + obj.principle = p; + elseif (nargin ~= 0) + error ('foo_value_class:SyntaxError', ... + 'foo_value_class: Invalid syntax') + end + end + function amt = amount (obj) + i = obj.rate / (12 * 100); + if (i == 0 && obj.term == 0) + amt = obj.principle; + else + amt = (obj.principle * i) / (1 - (1 + i)^(-obj.term)); + end + end + end +end +