diff host/libcdl/parse.cxx @ 76:435cced73e2f ecos-v1_3_1-release

eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
author jlarmour
date Tue, 28 Mar 2000 14:10:45 +0000
parents
children 6736c52df507
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/host/libcdl/parse.cxx
@@ -0,0 +1,975 @@
+//{{{  Banner                                   
+
+//============================================================================
+//
+//     parse.cxx
+//
+//     Miscellaneous parsing routines
+//
+//============================================================================
+//####COPYRIGHTBEGIN####
+//                                                                          
+// ----------------------------------------------------------------------------
+// Copyright (C) 1999, 2000 Red Hat, Inc.
+//
+// This file is part of the eCos host tools.
+//
+// This program 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 2 of the License, or (at your option) 
+// any later version.
+// 
+// This program 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
+// this program; if not, write to the Free Software Foundation, Inc., 
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// ----------------------------------------------------------------------------
+//                                                                          
+//####COPYRIGHTEND####
+//============================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):   bartv
+// Contact(s):  bartv
+// Date:        1999/02/23
+// Version:     0.02
+//
+//####DESCRIPTIONEND####
+//============================================================================
+
+//}}}
+//{{{  #include's                               
+
+// ----------------------------------------------------------------------------
+#include "cdlconfig.h"
+
+// Get the infrastructure types, assertions, tracing and similar
+// facilities.
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/cyg_trac.h>
+
+// <cdlcore.hxx> defines everything implemented in this module.
+// It implicitly supplies <string>, <vector> and <map> because
+// the class definitions rely on these headers.
+#include <cdlcore.hxx>
+
+//}}}
+
+//{{{  Description                              
+
+// ----------------------------------------------------------------------------
+// All CDL data is read via a Tcl interpreter, so the parsing is done by
+// procedures that appear as Tcl commands. This has obvious advantages in
+// terms of expressive power, but does result in a little bit of confusion
+// when producing diagnostics.
+//
+// Errors should not bypass the Tcl interpreter, to ensure that that
+// stays in a consistent state. In particular it is not possible to let
+// arbitrary C++ exceptions to go straight through the Tcl interpreter,
+// this is likely to result in a corrupted interpreter.
+// 
+// Also, it is not a good idea to abort parsing as soon as anything
+// goes wrong. Instead there should be an error handling callback associated
+// with the interpreter, which can be used to report errors. This
+// callback may choose to raise an exception.
+//
+// Consider the case of parsing a property (which accounts for most of
+// the code in this module). A property does not exist in isolation,
+// only within the context of a suitable CDL entity such as an option.
+// If parsing succeeds and a property object is created then it must
+// be added to the current CDL entity.
+//
+// Therefore a typical parse routine looks like this:
+//
+//  1) get the current CDL node from the interpreter
+//  2) do basic parsing of the data. Any errors should be reported
+//     via the callback.
+//  3) create a suitable CdlProperty object
+//  4) add the property to the current entity
+//
+// std::bad_alloc and CdlStringException exceptions can be thrown, they
+// will be intercepted by the CdlInterpreter class.
+
+//}}}
+//{{{  Statics                                  
+
+// ----------------------------------------------------------------------------
+// The string "property " is needed in various places. Provide it as a static
+// to cut down the number of times the string constructor has to run.
+static std::string property_string = "property ";
+
+//}}}
+//{{{  Generic parsing-related utilities        
+
+//{{{  argv manipulation                        
+
+// ----------------------------------------------------------------------------
+// Some of the properties have aliases in the CDL data, so argv[0] has to be
+// used to work out what is actually being parsed. However the Tcl interpreter
+// may prefix the command name with :: to indicate the global namespace.
+const char*
+CdlParse::get_tcl_cmd_name(const char* name)
+{
+    if ((name[0] == ':') && (name[1] == ':')) {
+        return &(name[2]);
+    } else {
+        return name;
+    }
+}
+
+// Given a list of arguments, concatenate them together into a C++ string.
+// This makes expression parsing easier. The final argument should be an
+// index into the array, typically the result of a call to skip_argv_options().
+std::string
+CdlParse::concatenate_argv(int argc, char** argv, int index)
+{
+    CYG_REPORT_FUNCNAME("CdlParse::concatenate_argv");
+
+    std::string result = "";
+    for ( int i = index ; i < argc; i++) {
+        if (i > index) {
+            result += ' ';
+        }
+        result += std::string(argv[i]);
+    }
+    
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+// Option parsing.
+//
+// Some properties accept modifiers in their argument list. For example,
+// by default a "compile" property is just a list of source files that
+// should be compiled and added to the current library. It is possible
+// to specify an alternative library via a modifier:
+//
+//    ...
+//    compile -library=libextras.a dummy.cxx
+//    ...
+//
+// The rules applied for such options are intended to follow common Tcl
+// practice:
+//
+// 1) any initial arguments beginning with a - are assumed to be
+//    options.
+//
+// 2) the first argument which does not begin with a - stops option
+//    processing.
+//
+// 3) option processing can also be terminated with a -- argument.
+//    This may occasionally be required, e.g. to have -ve constants
+//    in an expression.
+//
+// 4) the parsing code does not distinguish between single and double
+//    hyphens. If there happens to be a second - in an option then this
+//    is just ignored.
+//
+// 5) it is not necessary to supply the whole option name, as long as
+//    what is provided is unambiguous. For example -lib=libextras.a is
+//    acceptable (for now anyway, conceivably it would break in future).
+//    Option processing is case sensitive.
+//
+// 6) name/value pairs can take the form -name=value or the form
+//    -name value, whichever is appropriate.
+//
+// The parse_options() function takes the current interpreter (so that
+// it can generate diagnostics), a prefix such as `property
+// "requires"' or `package CYGPKG_HAL', details of the options to be
+// parsed, an argc/argv list, an index, and a reference to a vector.
+// The parsed options get placed in the vector, and the index argument
+// gets updated to point at the first non-option argument. It will
+// report problems via report_warning().
+//
+// The options description consists of a pointer to an array of
+// C strings (allowing static initialization). Passing a zero
+// argument indicates that the property takes no options. Otherwise
+// the array should be zero terminated.
+//
+// Each entry in the array takes the form "name:flags". The optional
+// flags consist of zero or more characters indicating how the option
+// should be interpreted. Valid flags are:
+//
+// f    - this option takes no data, it is just a boolean flag. The
+//        default behaviour assumes name/value pairs.
+//
+// m    - this option can occur multiple times. The default behaviour
+//        is that each option can only occur once.
+
+// Utility function to get hold of an option name without the colon
+// or terminating flags.
+
+static std::string
+get_option_string(char* name)
+{
+    std::string result = "";
+    while ((*name != ':') && (*name != '\0')) {
+        result += *name++;
+    }
+    return result;
+}
+
+int
+CdlParse::parse_options(CdlInterpreter interp, std::string diag_prefix, char** options,
+                                 int argc, char** argv, int index,
+                                 std::vector<std::pair<std::string,std::string> >& result)
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlParse::parse_options", "final index %d");
+    CYG_REPORT_FUNCARG4XV(interp, options, argc, argv);
+    CYG_PRECONDITION_CLASSC(interp);
+    CYG_PRECONDITIONC(argc > 0);        // The property name must be present.
+    // It is possible for some of the arguments to have been processed already.
+    CYG_PRECONDITIONC((index > 0) && (index <= argc));
+
+    while((index < argc) && ('-' == argv[index][0])) {
+
+        std::string name  = "";
+        std::string value = "";
+
+        // The sequence -- should always terminate option processing.
+        if (0 == strcmp(argv[index], "--")) {
+            index++;
+            break;
+        }
+
+        char* arg_ptr       = argv[index];
+        // Skip the initial -, and the second one as well if it is present.
+        if ('-' == *++arg_ptr) {
+            arg_ptr++;
+        }
+
+        // Construct the option name. This is the current argument up
+        // to but not including the '=' character or EOD.
+        while (('=' != *arg_ptr) && ('\0' != *arg_ptr)) {
+            name += *arg_ptr++;
+        }
+
+        if ("" == name) {
+            // One of "-", "-=xxx", or "--=x"
+            CdlParse::report_warning(interp, diag_prefix + ", invalid option string " + argv[index]);
+        }
+
+        // Do not try to extract the value unless we are sure there
+        // should be one. Instead try to match the option name. The
+        // current value of name should be a unique substring of
+        // one of the known option strings.
+        //
+        // Note that the supplied options descriptor can be NULL,
+        // since most properties do not yet take any options.
+        // In that case opt_index will remain at -1, and we should
+        // get an "invalid option" diagnostic.
+        unsigned int i;
+        int opt_index = -1;
+        if (0 != options) {
+            for (i = 0; 0 != options[i]; i++) {
+                if (0 == strncmp(name.c_str(), options[i], name.size())) {
+                    if (-1 != opt_index) {
+                        CdlParse::report_warning(interp, diag_prefix + ", ambiguous option name " + name +
+                                                 ", it can match " + get_option_string(options[opt_index]) +
+                                                 " or " + get_option_string(options[i]));
+                        index++;
+                        break;
+                    } else {
+                        opt_index = i;
+                    }
+                }
+            }
+        }
+        if (-1 == opt_index) {
+            CdlParse::report_warning(interp, diag_prefix + ", invalid option " + name);
+            index++;
+            break;
+        }
+
+        // The option has been identified successfully. Extract the flags.
+        bool    flag_flag       = false;
+        bool    multiple_flag   = false;
+        char*   tmp = options[opt_index];
+        while (('\0' != *tmp) && (':' != *tmp)) {
+            tmp++;
+        }
+        if (':' == *tmp) {
+            do {
+                tmp++;
+                if ('f' == *tmp) {
+                    flag_flag = true;
+                } else if ('m' == *tmp) {
+                    multiple_flag = true;
+                } else if ('\0' != *tmp) {
+                    CYG_FAIL("Invalid property option");
+                }
+            } while ('\0' != *tmp);
+        }
+
+        // We now know the full option name. Use it for future diagnostics.
+        name = get_option_string(options[opt_index]);
+
+        // Take care of the value.
+        if (flag_flag) {
+            // There should not be a value. If the current argument is of the
+            // form x=y then this is an error.
+            if ('=' == *arg_ptr) {
+                CdlParse::report_warning(interp, diag_prefix + ", option " + name + " does not take any data");
+            }
+            // Leave index pointing at the next argument to be processed.
+            index++;
+        } else {
+            if ('=' == *arg_ptr) {
+                value = std::string(++arg_ptr);
+            } else if (++index == argc) {
+                CdlParse::report_warning(interp, diag_prefix + ", missing data for option " + name);
+            } else {
+                value = argv[index];
+            }
+            index++;
+        }
+        // At this stage index points at the next argument to be processed, and should not
+        // be updated again.
+        
+        // Unless the option can occur multiple times, make sure that it is not already
+        // present in the options vector.
+        if (!multiple_flag) {
+            for (i = 0; i < result.size(); i++) {
+                if (name == result[i].first) {
+                    CdlParse::report_warning(interp, diag_prefix + ", option " + name +
+                                             " can only be used once.");
+                    break;
+                }
+            }
+        }
+
+        // The name/value pair is valid, so add it to the result vector.
+        result.push_back(std::make_pair(name, value));
+    }
+    
+    CYG_REPORT_RETVAL(index);
+    return index;
+}
+
+//}}}
+//{{{  Diagnostic prefix                        
+
+// Construct a suitable prefix for any warning or error message. This
+// should include the filename and the entity name.
+//
+// Obviously a line number would be rather useful as well, but this is not
+// very easy because of the way Tcl interpreters work.
+
+std::string
+CdlParse::get_diagnostic_prefix(CdlInterpreter interp)
+{
+    std::string filename        = interp->get_filename();
+    CdlNode     current_node    = interp->get_node();
+
+    std::string result = ("" != filename) ? filename : "<unknown data source>";
+    if (0 != current_node) {
+        result += ", " + current_node->get_class_name() + " " + current_node->get_name();
+    }
+    result += "\n    ";
+    return result;
+}
+
+//}}}
+//{{{  Error count tracking                     
+
+// Keep track of the number of errors that have occurred while doing some
+// parsing. This functionality is not provided directly by the CdlInterpreter
+// class, instead it is implemented using assoc data.
+
+static const char       error_count_key[]       = "CdlErrorCount";
+
+static void
+error_count_delproc(ClientData data, Tcl_Interp* interp)
+{
+    CYG_REPORT_FUNCNAME("CdlParse::error_count_delproc");
+    int* newed_ptr = static_cast<int*>(data);
+    delete newed_ptr;
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlParse::clear_error_count(CdlInterpreter interp)
+{
+    CYG_REPORT_FUNCNAME("CdlParse::clear_error_count");
+    CYG_REPORT_FUNCARG1("interp %p", interp);
+    CYG_PRECONDITION_CLASSC(interp);
+
+    int*        newed_ptr = static_cast<int*>(interp->get_assoc_data(error_count_key));
+    if (0 != newed_ptr) {
+        *newed_ptr = 0;
+    }
+
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlParse::incr_error_count(CdlInterpreter interp, int how_much)
+{
+    CYG_REPORT_FUNCNAME("CdlParse::incr_error_counter");
+    CYG_REPORT_FUNCARG2("interp %p, how_much %d", interp, how_much);
+    CYG_PRECONDITION_CLASSC(interp);
+    CYG_PRECONDITION(how_much > 0, "previous errors cannot be undone");
+    
+    int* newed_ptr = static_cast<int*>(interp->get_assoc_data(error_count_key));
+    if (0 == newed_ptr) {
+        newed_ptr = new int(how_much);
+        interp->set_assoc_data(error_count_key, static_cast<void*>(newed_ptr), &error_count_delproc);
+    } else {
+        CYG_ASSERT((*newed_ptr + how_much) > *newed_ptr, "number of parsing errors should not overflow");
+        *newed_ptr += how_much;
+    }
+
+    CYG_REPORT_RETURN();
+}
+
+int
+CdlParse::get_error_count(CdlInterpreter interp)
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlParse::get_error_count", "count %d");
+    CYG_REPORT_FUNCARG1("interp %p", interp);
+    CYG_PRECONDITION_CLASSC(interp);
+
+    int result = 0;
+    int* newed_ptr = static_cast<int*>(interp->get_assoc_data(error_count_key));
+    if (0 != newed_ptr) {
+        result = *newed_ptr;
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  Error and warning reporting              
+
+// Report an error or warning. This involves adding a suitable prefix
+// and invoking the reporting callback currently associated with the
+// interpreter. For errors it is also necessary to increment the error
+// counter so that later count can detect the number of errors that
+// have occurred.
+//
+// The error callback is allowed to raise a CdlParseException. This should
+// not be caught here. Instead this exception is caught in every parse
+// routine, before it can go back through the Tcl interpreter.
+//
+// FIXME: cope with prefixing multiline error messages.
+
+void
+CdlParse::report_error(CdlInterpreter interp, std::string message)
+{
+    CYG_REPORT_FUNCNAME("CdlParse::report_error");
+    CYG_REPORT_FUNCARG1("interp %p", interp);
+    CYG_PRECONDITION_CLASSC(interp);
+
+    incr_error_count(interp);
+    
+    message = get_diagnostic_prefix(interp) + message;
+
+    CdlDiagnosticFnPtr fn = interp->get_error_fn_ptr();
+    CYG_ASSERT(0 != fn, "during parsing an interpreter should have an associated error reporting function");
+    (*fn)(message);
+
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlParse::report_warning(CdlInterpreter interp, std::string message)
+{
+    CYG_REPORT_FUNCNAME("CdlParse::report_warning");
+    CYG_REPORT_FUNCARG1("interp %p", interp);
+    CYG_PRECONDITION_CLASSC(interp);
+    
+    message = get_diagnostic_prefix(interp) + message;
+    CdlDiagnosticFnPtr fn = interp->get_warning_fn_ptr();
+    if (0 != fn) {
+        (*fn)(message);
+    }
+
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  The "unknown" command                    
+
+// ----------------------------------------------------------------------------
+// This routine should be installed in interpreters that get used for
+// parsing CDL scripts. It gets invoked when the CDL script contains
+// an unrecognised command, e.g. because of a typo, and makes sure that
+// the usual diagnostics process is observed.
+//
+// This routine should be uninstalled after the parsing is complete,
+// to avoid e.g. a ParseException when it is not expected.
+int
+CdlParse::unknown_command(CdlInterpreter interp, int argc, char** argv)
+{
+    CYG_REPORT_FUNCNAME("CdlParse::unknown_command");
+    CYG_REPORT_FUNCARG3XV(interp, argc, argv);
+    CYG_PRECONDITIONC(2 <= argc);
+    CYG_PRECONDITION_CLASSC(interp);
+    
+    report_error(interp, std::string("Unknown command `") + argv[1] + "'.");
+    CYG_UNUSED_PARAM(int, argc);
+    
+    return TCL_OK;
+}
+
+//}}}
+
+//}}}
+//{{{  Property-related parser utilities        
+
+// ----------------------------------------------------------------------------
+// Utilities related to parsing properties, rather than more general parsing.
+
+// Provide a prefix that matches the current property.
+std::string
+CdlParse::get_property_prefix(char* argv0)
+{
+    CYG_REPORT_FUNCNAME("CdlParse::get_property_prefix");
+
+    std::string result = std::string("Property " ) + CdlParse::get_tcl_cmd_name(argv0) + ", ";
+
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+std::string
+CdlParse::get_property_prefix(CdlProperty prop)
+{
+    CYG_REPORT_FUNCNAME("CdlParse::get_property_prefix");
+
+    std::string result = std::string("Property ");
+    const std::vector<std::string>& argv = prop->get_argv();
+    result = result + argv[0] + ", ";
+    
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+// A variant of report_parse_error() which also adds the property prefix.
+void
+CdlParse::report_property_parse_error(CdlInterpreter interp, char* argv0, std::string msg)
+{
+    CYG_REPORT_FUNCNAME("CdlPase::report_property_parse_error");
+
+    report_error(interp, get_property_prefix(argv0) + msg);
+    
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlParse::report_property_parse_error(CdlInterpreter interp, CdlProperty prop, std::string msg)
+{
+    CYG_REPORT_FUNCNAME("CdlParse::report_property_parse_error");
+
+    report_error(interp, get_property_prefix(prop) + msg);
+
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  Generic property parsers                 
+
+// ----------------------------------------------------------------------------
+// Generic parsers
+//
+// These routines provide some more generic property parsing routines. argv[0]
+// generally provides sufficient information to allow for sensible error messages.
+// The command-specific parsers have to provide a property name. In addition it is
+// possible to provide a function to handle per-command options, and another
+// function that performs a final sanity check before the property gets added
+// to the current entity.
+
+//{{{  parse_minimal_property()         
+
+// ----------------------------------------------------------------------------
+// A minimal property takes no arguments.
+
+int
+CdlParse::parse_minimal_property(CdlInterpreter interp, int argc, char** argv, std::string name,
+                                 char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_Minimal))
+{
+    CYG_REPORT_FUNCNAME("parse_minimal_property");
+    CYG_PRECONDITION_CLASSC(interp);
+    
+    CdlProperty_Minimal new_property = 0;
+    try {
+        std::vector<std::pair<std::string,std::string> > options;
+        int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
+        
+        if (data_index < argc) {
+            CdlParse::report_property_parse_error(interp, argv[0],  std::string("Unexpected data ") + argv[data_index]);
+        } else {
+        
+            // The command is valid, turn it into a property.
+            // The property has been parsed successfully. Add it to the current node
+            CdlNode current_node = interp->get_node();
+            CYG_ASSERTC(0 != current_node);
+            new_property = CdlProperty_MinimalBody::make(current_node, name, argc, argv, options);
+            if (0 != final_parser) {
+                (*final_parser)(interp, new_property);
+            }
+        }
+    } catch(...) {
+        
+        if (0 != new_property) {
+            delete new_property;
+        }
+        throw;
+    }
+    
+    return TCL_OK;
+}
+
+//}}}
+//{{{  parse_string_property()          
+
+// ----------------------------------------------------------------------------
+
+int
+CdlParse::parse_string_property(CdlInterpreter interp, int argc, char** argv, std::string name,
+                                char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_String))
+{
+    CYG_REPORT_FUNCNAME("parse_string_property");
+    CYG_PRECONDITION_CLASSC(interp);
+    
+    CdlProperty_String new_property = 0;
+    
+    try {
+        std::vector<std::pair<std::string,std::string> > options;
+        int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
+
+        if (data_index == argc) {
+            CdlParse::report_property_parse_error(interp, argv[0], "missing argument.");
+        } else if ((data_index + 1) < argc) {
+            CdlParse::report_property_parse_error(interp, argv[0], std::string("Too many arguments, expecting just one."));
+        } else {
+        
+            CdlNode current_node = interp->get_node();
+            CYG_ASSERTC(0 != current_node);
+            new_property = CdlProperty_StringBody::make(current_node, name, argv[data_index], argc, argv, options);
+            if (0 != final_parser) {
+                (*final_parser)(interp, new_property);
+            }
+        }
+    } catch(...) {
+        if (0 != new_property) {
+            delete new_property;
+        }
+        throw;
+    }
+    
+    return TCL_OK;
+}
+
+//}}}
+//{{{  parse_tclcode_property()         
+
+// ----------------------------------------------------------------------------
+
+int
+CdlParse::parse_tclcode_property(CdlInterpreter interp, int argc, char** argv, std::string name,
+                                 char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_TclCode))
+{
+    CYG_REPORT_FUNCNAME("parse_tclcode_property");
+    CYG_PRECONDITION_CLASSC(interp);
+    
+    CdlProperty_TclCode new_property = 0;
+    try {
+        std::vector<std::pair<std::string,std::string> > options;
+        int data_index      = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
+        
+        if (data_index == argc) {
+            CdlParse::report_property_parse_error(interp, argv[0], "missing Tcl code.");
+        } else if ((data_index + 1) < argc) {
+            CdlParse::report_property_parse_error(interp, argv[0], std::string("Invalid number of arguments.\n") +
+                                         "    Expecting one argument, a Tcl code fragment.");
+        } else if (!Tcl_CommandComplete(argv[data_index])) {
+            CdlParse::report_property_parse_error(interp, argv[0], "incomplete Tcl code fragment.");
+        } else {
+        
+            CdlNode current_node = interp->get_node();
+            CYG_ASSERTC(0 != current_node);
+            new_property = CdlProperty_TclCodeBody::make(current_node, name, argv[data_index], argc, argv, options);
+            if (0 != final_parser) {
+                (*final_parser)(interp, new_property);
+            }
+        }
+     } catch(...) {
+        if (0 != new_property) {
+            delete new_property;
+        }
+        throw;
+    }
+    
+    return TCL_OK;
+}
+
+//}}}
+//{{{  parse_stringvector_property()    
+
+// ----------------------------------------------------------------------------
+
+int
+CdlParse::parse_stringvector_property(CdlInterpreter interp, int argc, char** argv, std::string name,
+                                      char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_StringVector))
+{
+    CYG_REPORT_FUNCNAME("parse_tclcode_property");
+    CYG_PRECONDITION_CLASSC(interp);
+    
+    CdlProperty_StringVector new_property = 0;
+    try {
+        std::vector<std::pair<std::string,std::string> > options;
+        int data_index      = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
+        
+        if (data_index == argc) {
+            CdlParse::report_property_parse_error(interp, argv[0], "missing arguments.");
+        } else {
+
+            // Creating the property requires a vector of strings.
+            std::vector<std::string>  strings;
+            for ( ; data_index < argc; data_index++) {
+                strings.push_back(argv[data_index]);
+            }
+            CdlNode current_node = interp->get_node();
+            CYG_ASSERTC(0 != current_node);
+            new_property = CdlProperty_StringVectorBody::make(current_node, name, strings, argc, argv, options);
+            if (0 != final_parser) {
+                (*final_parser)(interp, new_property);
+            }
+        }
+    } catch(...) {
+
+        if (0 != new_property) {
+            delete new_property;
+        }
+        throw;
+    }
+    
+    return TCL_OK;
+}
+
+//}}}
+//{{{  parse_reference_property()       
+
+// ----------------------------------------------------------------------------
+
+int
+CdlParse::parse_reference_property(CdlInterpreter interp, int argc, char** argv, std::string name,
+                                   char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_Reference),
+                                   CdlUpdateHandler update_handler)
+{
+    CYG_REPORT_FUNCNAME("parse_reference_property");
+    CYG_PRECONDITION_CLASSC(interp);
+    
+    CdlProperty_Reference new_property = 0;
+    try {
+        std::vector<std::pair<std::string,std::string> > options;
+        int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
+        
+        if (data_index == argc) {
+            CdlParse::report_property_parse_error(interp, argv[0], "missing argument.");
+        } else if ((data_index + 1) < argc) {
+            CdlParse::report_property_parse_error(interp, argv[0], "too many arguments, expecting just one.");
+        } else {
+            std::string refname = argv[data_index];
+            if (!Cdl::is_valid_cdl_name(refname)) {
+                CdlParse::report_property_parse_error(interp, argv[0], "`" + refname + "' is not a valid CDL name");
+            } else {
+                CdlNode current_node = interp->get_node();
+                CYG_ASSERTC(0 != current_node);
+                new_property = CdlProperty_ReferenceBody::make(current_node, name, refname,
+                                                               update_handler, argc, argv, options);
+                if (0 != final_parser) {
+                    (*final_parser)(interp, new_property);
+                }
+            }
+        }
+    } catch(...) {
+        if (0 != new_property) {
+            delete new_property;
+        }
+        throw;
+    }
+    
+    return TCL_OK;
+}
+
+//}}}
+//{{{  parse_expression_property()      
+
+// ----------------------------------------------------------------------------
+
+int
+CdlParse::parse_expression_property(CdlInterpreter interp, int argc, char** argv, std::string name,
+                                    char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_Expression),
+                                    CdlUpdateHandler update_handler)
+{
+    CYG_REPORT_FUNCNAME("parse_expression_property");
+    CYG_PRECONDITION_CLASSC(interp);
+    
+    CdlProperty_Expression new_property = 0;
+    CdlExpression expr = 0;
+    try {
+        std::vector<std::pair<std::string,std::string> > options;
+        int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
+        
+        std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index);
+        if ("" == all_args) {
+            CdlParse::report_property_parse_error(interp, argv[0], "missing expression data.");
+        } else {
+        
+            // The CdlExpression class has its own parsing routine. This
+            // will raise an exception if there are any problems. It is
+            // desirable to catch the exception and report the error via
+            // the normal reporting mechanisms, which may allow parsing to
+            // continue.
+            try {
+                expr = CdlExpressionBody::parse(all_args);
+            } catch(CdlParseException e) {
+                CdlParse::report_property_parse_error(interp, argv[0], e.get_message());
+            }
+            if (0 != expr) {
+                CdlNode current_node = interp->get_node();
+                CYG_ASSERTC(0 != current_node);
+                new_property = CdlProperty_ExpressionBody::make(current_node, name, expr, update_handler, argc, argv, options);
+                if (0 != final_parser) {
+                    (*final_parser)(interp, new_property);
+                }
+            }
+        }
+    } catch(...) {
+        if (0 != expr) {
+            delete expr;
+        }
+        if (0 != new_property) {
+            delete new_property;
+        }
+        throw;
+    }
+    
+    if (0 != expr) {
+        delete expr;
+    }
+    return TCL_OK;
+}
+
+//}}}
+//{{{  parse_list_expression_property() 
+
+// ----------------------------------------------------------------------------
+
+int
+CdlParse::parse_listexpression_property(CdlInterpreter interp, int argc, char** argv, std::string name,
+                                        char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_ListExpression),
+                                        CdlUpdateHandler update_handler)
+{
+    CYG_REPORT_FUNCNAME("parse_list_expression_property");
+    CYG_PRECONDITION_CLASSC(interp);
+    
+    CdlProperty_ListExpression new_property = 0;
+    CdlListExpression expr = 0;
+    try {
+        std::vector<std::pair<std::string,std::string> > options;
+        int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
+
+        std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index);
+        if ("" == all_args) {
+            CdlParse::report_property_parse_error(interp, argv[0], "missing list expression data.");
+        } else {
+        
+            try {
+                expr = CdlListExpressionBody::parse(all_args);
+            } catch(CdlParseException e) {
+                CdlParse::report_property_parse_error(interp, argv[0], e.get_message());
+            }
+            if (0 != expr) {
+                CdlNode current_node = interp->get_node();
+                CYG_ASSERTC(0 != current_node);
+                new_property = CdlProperty_ListExpressionBody::make(current_node, name, expr, update_handler,
+                                                                    argc, argv, options);
+                if (0 != final_parser) {
+                    (*final_parser)(interp, new_property);
+                }
+            }
+        }
+    } catch(...) {
+        if (0 != expr) {
+            delete expr;
+        }
+        if (0 != new_property) {
+            delete new_property;
+        }
+        throw;
+    }
+    if (0 != expr) {
+        delete expr;
+    }
+    return TCL_OK;
+}
+
+//}}}
+//{{{  parse_goalexpression_property()  
+
+// ----------------------------------------------------------------------------
+
+int
+CdlParse::parse_goalexpression_property(CdlInterpreter interp, int argc, char** argv, std::string name,
+                                        char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_GoalExpression),
+                                        CdlUpdateHandler update_handler)
+{
+    CYG_REPORT_FUNCNAMETYPE("parse_goal_expression_property", "result %d");
+    CYG_PRECONDITION_CLASSC(interp);
+    
+    CdlProperty_GoalExpression new_property = 0;
+    CdlGoalExpression expr = 0;
+    try {
+        std::vector<std::pair<std::string,std::string> > options;
+        int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
+
+        std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index);
+        if ("" == all_args) {
+            CdlParse::report_property_parse_error(interp, argv[0], "missing goal expression data.");
+        } else {
+
+            try {
+                expr = CdlGoalExpressionBody::parse(all_args);
+            } catch(CdlParseException e) {
+                CdlParse::report_property_parse_error(interp, argv[0], e.get_message());
+            }
+            if (0 != expr) {
+                CdlNode current_node = interp->get_node();
+                CYG_ASSERTC(0 != current_node);
+                new_property = CdlProperty_GoalExpressionBody::make(current_node, name, expr, update_handler,
+                                                                    argc, argv, options);
+                if (0 != final_parser) {
+                    (*final_parser)(interp, new_property);
+                }
+            }
+        }
+    } catch(...) {
+
+        if (0 != expr) {
+            delete expr;
+        }
+        if (0 != new_property) {
+            delete new_property;
+        }
+        throw;
+    }
+    if (0 != expr) {
+        delete expr;
+    }
+
+    return TCL_OK;
+}
+
+//}}}
+
+//}}}