3 //============================================================================
7 // Miscellaneous parsing routines
9 //============================================================================
10 //####COPYRIGHTBEGIN####
12 // ----------------------------------------------------------------------------
13 // Copyright (C) 2002 Bart Veer
14 // Copyright (C) 1999, 2000 Red Hat, Inc.
16 // This file is part of the eCos host tools.
18 // This program is free software; you can redistribute it and/or modify it
19 // under the terms of the GNU General Public License as published by the Free
20 // Software Foundation; either version 2 of the License, or (at your option)
23 // This program is distributed in the hope that it will be useful, but WITHOUT
24 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
25 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
28 // You should have received a copy of the GNU General Public License along with
29 // this program; if not, write to the Free Software Foundation, Inc.,
30 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 // ----------------------------------------------------------------------------
34 //####COPYRIGHTEND####
35 //============================================================================
36 //#####DESCRIPTIONBEGIN####
43 //####DESCRIPTIONEND####
44 //============================================================================
49 // ----------------------------------------------------------------------------
51 #include "cdlconfig.h"
53 // Get the infrastructure types, assertions, tracing and similar
55 #include <cyg/infra/cyg_ass.h>
56 #include <cyg/infra/cyg_trac.h>
58 // <cdlcore.hxx> defines everything implemented in this module.
59 // It implicitly supplies <string>, <vector> and <map> because
60 // the class definitions rely on these headers.
61 #include <cdlcore.hxx>
67 // ----------------------------------------------------------------------------
68 // All CDL data is read via a Tcl interpreter, so the parsing is done by
69 // procedures that appear as Tcl commands. This has obvious advantages in
70 // terms of expressive power, but does result in a little bit of confusion
71 // when producing diagnostics.
73 // Errors should not bypass the Tcl interpreter, to ensure that that
74 // stays in a consistent state. In particular it is not possible to let
75 // arbitrary C++ exceptions to go straight through the Tcl interpreter,
76 // this is likely to result in a corrupted interpreter.
78 // Also, it is not a good idea to abort parsing as soon as anything
79 // goes wrong. Instead there should be an error handling callback associated
80 // with the interpreter, which can be used to report errors. This
81 // callback may choose to raise an exception.
83 // Consider the case of parsing a property (which accounts for most of
84 // the code in this module). A property does not exist in isolation,
85 // only within the context of a suitable CDL entity such as an option.
86 // If parsing succeeds and a property object is created then it must
87 // be added to the current CDL entity.
89 // Therefore a typical parse routine looks like this:
91 // 1) get the current CDL node from the interpreter
92 // 2) do basic parsing of the data. Any errors should be reported
94 // 3) create a suitable CdlProperty object
95 // 4) add the property to the current entity
97 // std::bad_alloc and CdlStringException exceptions can be thrown, they
98 // will be intercepted by the CdlInterpreter class.
103 // ----------------------------------------------------------------------------
104 // The string "property " is needed in various places. Provide it as a static
105 // to cut down the number of times the string constructor has to run.
106 static std::string property_string = "property ";
109 //{{{ Generic parsing-related utilities
111 //{{{ argv manipulation
113 // ----------------------------------------------------------------------------
114 // Some of the properties have aliases in the CDL data, so argv[0] has to be
115 // used to work out what is actually being parsed. However the Tcl interpreter
116 // may prefix the command name with :: to indicate the global namespace.
118 CdlParse::get_tcl_cmd_name(std::string name)
122 if ((name[0] == ':') && (name[1] == ':')) {
123 result = std::string(name, 2, name.size() - 2);
130 // Given a list of arguments, concatenate them together into a C++ string.
131 // This makes expression parsing easier. The final argument should be an
132 // index into the array, typically the result of a call to skip_argv_options().
134 CdlParse::concatenate_argv(int argc, const char* argv[], int index)
136 CYG_REPORT_FUNCNAME("CdlParse::concatenate_argv");
138 std::string result = "";
139 for ( int i = index ; i < argc; i++) {
143 result += std::string(argv[i]);
150 // ----------------------------------------------------------------------------
153 // Some properties accept modifiers in their argument list. For example,
154 // by default a "compile" property is just a list of source files that
155 // should be compiled and added to the current library. It is possible
156 // to specify an alternative library via a modifier:
159 // compile -library=libextras.a dummy.cxx
162 // The rules applied for such options are intended to follow common Tcl
165 // 1) any initial arguments beginning with a - are assumed to be
168 // 2) the first argument which does not begin with a - stops option
171 // 3) option processing can also be terminated with a -- argument.
172 // This may occasionally be required, e.g. to have -ve constants
175 // 4) the parsing code does not distinguish between single and double
176 // hyphens. If there happens to be a second - in an option then this
179 // 5) it is not necessary to supply the whole option name, as long as
180 // what is provided is unambiguous. For example -lib=libextras.a is
181 // acceptable (for now anyway, conceivably it would break in future).
182 // Option processing is case sensitive.
184 // 6) name/value pairs can take the form -name=value or the form
185 // -name value, whichever is appropriate.
187 // The parse_options() function takes the current interpreter (so that
188 // it can generate diagnostics), a prefix such as `property
189 // "requires"' or `package CYGPKG_HAL', details of the options to be
190 // parsed, an argc/argv list, an index, and a reference to a vector.
191 // The parsed options get placed in the vector, and the index argument
192 // gets updated to point at the first non-option argument. It will
193 // report problems via report_warning().
195 // The options description consists of a pointer to an array of
196 // C strings (allowing static initialization). Passing a zero
197 // argument indicates that the property takes no options. Otherwise
198 // the array should be zero terminated.
200 // Each entry in the array takes the form "name:flags". The optional
201 // flags consist of zero or more characters indicating how the option
202 // should be interpreted. Valid flags are:
204 // f - this option takes no data, it is just a boolean flag. The
205 // default behaviour assumes name/value pairs.
207 // m - this option can occur multiple times. The default behaviour
208 // is that each option can only occur once.
210 // Utility function to get hold of an option name without the colon
211 // or terminating flags.
214 get_option_string(char* name)
216 std::string result = "";
217 while ((*name != ':') && (*name != '\0')) {
224 CdlParse::parse_options(CdlInterpreter interp, std::string diag_prefix, char** options,
225 int argc, const char* argv[], int index,
226 std::vector<std::pair<std::string,std::string> >& result)
228 CYG_REPORT_FUNCNAMETYPE("CdlParse::parse_options", "final index %d");
229 CYG_REPORT_FUNCARG4XV(interp, options, argc, argv);
230 CYG_PRECONDITION_CLASSC(interp);
231 CYG_PRECONDITIONC(argc > 0); // The property name must be present.
232 // It is possible for some of the arguments to have been processed already.
233 CYG_PRECONDITIONC((index > 0) && (index <= argc));
235 while((index < argc) && ('-' == argv[index][0])) {
237 std::string name = "";
238 std::string value = "";
240 // The sequence -- should always terminate option processing.
241 if (0 == strcmp(argv[index], "--")) {
246 const char* arg_ptr = argv[index];
247 // Skip the initial -, and the second one as well if it is present.
248 if ('-' == *++arg_ptr) {
252 // Construct the option name. This is the current argument up
253 // to but not including the '=' character or EOD.
254 while (('=' != *arg_ptr) && ('\0' != *arg_ptr)) {
259 // One of "-", "-=xxx", or "--=x"
260 CdlParse::report_warning(interp, diag_prefix, std::string("Invalid option string `") + argv[index] + "'.");
263 // Do not try to extract the value unless we are sure there
264 // should be one. Instead try to match the option name. The
265 // current value of name should be a unique substring of
266 // one of the known option strings.
268 // Note that the supplied options descriptor can be NULL,
269 // since most properties do not yet take any options.
270 // In that case opt_index will remain at -1, and we should
271 // get an "invalid option" diagnostic.
275 for (i = 0; 0 != options[i]; i++) {
276 if (0 == strncmp(name.c_str(), options[i], name.size())) {
277 if (-1 != opt_index) {
278 CdlParse::report_warning(interp, diag_prefix,
279 std::string("Ambiguous option name `") + name + "'.\n" +
280 "It can match `" + get_option_string(options[opt_index]) + "'\n" +
281 "or `" + get_option_string(options[i]) + "'.");
290 if (-1 == opt_index) {
291 CdlParse::report_warning(interp, diag_prefix, std::string("Invalid option `") + name + "'.");
296 // The option has been identified successfully. Extract the flags.
297 bool flag_flag = false;
298 bool multiple_flag = false;
299 char* tmp = options[opt_index];
300 while (('\0' != *tmp) && (':' != *tmp)) {
308 } else if ('m' == *tmp) {
309 multiple_flag = true;
310 } else if ('\0' != *tmp) {
311 CYG_FAIL("Invalid property option");
313 } while ('\0' != *tmp);
316 // We now know the full option name. Use it for future diagnostics.
317 name = get_option_string(options[opt_index]);
319 // Take care of the value.
321 // There should not be a value. If the current argument is of the
322 // form x=y then this is an error.
323 if ('=' == *arg_ptr) {
324 CdlParse::report_warning(interp, diag_prefix, std::string("Option `") + name + "' does not take any data.");
326 // Leave index pointing at the next argument to be processed.
329 if ('=' == *arg_ptr) {
330 value = std::string(++arg_ptr);
331 } else if (++index == argc) {
332 CdlParse::report_warning(interp, diag_prefix, std::string("Missing data for option `") + name + "'.");
338 // At this stage index points at the next argument to be processed, and should not
341 // Unless the option can occur multiple times, make sure that it is not already
342 // present in the options vector.
343 if (!multiple_flag) {
344 for (i = 0; i < result.size(); i++) {
345 if (name == result[i].first) {
346 CdlParse::report_warning(interp, diag_prefix, std::string("Option `") + name + "' can only be used once.");
352 // The name/value pair is valid, so add it to the result vector.
353 result.push_back(std::make_pair(name, value));
356 CYG_REPORT_RETVAL(index);
361 //{{{ Diagnostic construction
363 // Construct a suitable diagnostic for a parsing error. This may occur
364 // when reading in a CDL script, a savefile, a database, or anything
367 // A diagnostic should take the following form:
369 // <context> <linenumber> [, <node identifier>] [, <extra identifier>] : [<classification>, ] <message>
371 // The context should be set in the Tcl interpreter. Typically it
372 // will be a filename.
374 // In practice generating the line number is not really feasible at
375 // present, the Tcl interpreter does not keep track of sufficient
376 // information. At least, not in the public data structures, there is
377 // a termOffset field in the internal data structures which might
378 // be used to do the right thing. I do not want to start relying
379 // on Tcl internals just yet, or add support to the Tcl core for
380 // keeping track of line numbers.
382 // For many data files there will the concept of a current node,
383 // e.g. an option whose properties or savefile information are
384 // being processed. The CdlInterpreter class keeps track of the
385 // current node, so if it is defined then the node's class and
386 // name can be part of the message. This happens automatically,
387 // no effort is required on the part of calling code.
389 // There may also be additional information, for example
390 // identifying the specific property where the error was detected.
391 // This is handled by an extra argument.
393 // The classification is likely to be something like "warning",
394 // "error", or "internal error". It is controlled by the calling
395 // code, but typically it is provided by calling via report_warning()
398 // The message should identify the actual error. It should be
399 // a proper sentence, i.e. begin with a capital error and end with
400 // a full stop, unless the last word is an identifier or filename
401 // or something similarly special in which case the trailing
402 // dot will be discarded. The message should not end with a
403 // newline character, and the result string will not end with one
404 // either. That is left to higher level code.
407 CdlParse::construct_diagnostic(CdlInterpreter interp, std::string classification, std::string sub_id, std::string message)
409 CYG_REPORT_FUNCNAME("CdlParse::construct_diagnostic");
410 CYG_PRECONDITION_CLASSC(interp);
412 std::string context = interp->get_context();
413 CdlNode current_node = interp->get_node();
417 result = "<unknown context>";
421 if (0 != current_node) {
422 result += ", " + current_node->get_class_name() + " " + current_node->get_name();
425 result += ", " + sub_id;
427 result += ": " + classification;
429 // Now it is time to start worrying about layout, indenting
430 // subsequent lines, and so on.
431 int index = result.length();
432 int message_len = message.length();
434 bool indent_needed = false;
436 // Find out how many characters there are in the message up to the first newline
437 for (message_index = 0; (message_index < message_len) && ('\n' != message[message_index]); message_index++) {
441 // Should the message start on the next line, suitably indented?
442 // This depends in part on whether or not there was a classification.
443 if ("" == classification) {
444 // The current result ends with a colon and a space.
445 if ((index + message_index) <= 72) {
446 // The first line of the message can still fit. No need to do anything.
448 // Start indenting immediately, do not add anything else to the current line.
449 indent_needed = true;
452 // We may want a comma and a space after the classification
453 if ((index + 2 + message_index) <= 72) {
456 indent_needed = true;
460 // Now we can process the message one character at a time, adding
461 // newlines and indentation just in time.
462 for (message_index = 0; message_index < message_len; message_index++) {
465 indent_needed = false;
468 if ('\n' == message[message_index]) {
469 indent_needed = true;
471 result += message[message_index];
480 //{{{ Error count tracking
482 // Keep track of the number of errors that have occurred while doing some
483 // parsing. This functionality is not provided directly by the CdlInterpreter
484 // class, instead it is implemented using assoc data.
486 static const char error_count_key[] = "CdlErrorCount";
489 error_count_delproc(ClientData data, Tcl_Interp* interp)
491 CYG_REPORT_FUNCNAME("CdlParse::error_count_delproc");
492 int* newed_ptr = static_cast<int*>(data);
498 CdlParse::clear_error_count(CdlInterpreter interp)
500 CYG_REPORT_FUNCNAME("CdlParse::clear_error_count");
501 CYG_REPORT_FUNCARG1("interp %p", interp);
502 CYG_PRECONDITION_CLASSC(interp);
504 int* newed_ptr = static_cast<int*>(interp->get_assoc_data(error_count_key));
505 if (0 != newed_ptr) {
513 CdlParse::incr_error_count(CdlInterpreter interp, int how_much)
515 CYG_REPORT_FUNCNAME("CdlParse::incr_error_counter");
516 CYG_REPORT_FUNCARG2("interp %p, how_much %d", interp, how_much);
517 CYG_PRECONDITION_CLASSC(interp);
518 CYG_PRECONDITION(how_much > 0, "previous errors cannot be undone");
520 int* newed_ptr = static_cast<int*>(interp->get_assoc_data(error_count_key));
521 if (0 == newed_ptr) {
522 newed_ptr = new int(how_much);
523 interp->set_assoc_data(error_count_key, static_cast<void*>(newed_ptr), &error_count_delproc);
525 CYG_ASSERT((*newed_ptr + how_much) > *newed_ptr, "number of parsing errors should not overflow");
526 *newed_ptr += how_much;
533 CdlParse::get_error_count(CdlInterpreter interp)
535 CYG_REPORT_FUNCNAMETYPE("CdlParse::get_error_count", "count %d");
536 CYG_REPORT_FUNCARG1("interp %p", interp);
537 CYG_PRECONDITION_CLASSC(interp);
540 int* newed_ptr = static_cast<int*>(interp->get_assoc_data(error_count_key));
541 if (0 != newed_ptr) {
545 CYG_REPORT_RETVAL(result);
550 //{{{ Error and warning reporting
552 // Report errors and warnings. These will be called during parsing
553 // operations, both of CDL and similar data scripts and for savefiles.
554 // The parsing involves running a Tcl interpreter extended with the
555 // appropriate set of commands. Typically the call graph will look
556 // something like this:
558 // libcdl C++ code such as load_package()
559 // libcdl CdlInterpreter::eval()
561 // libcdl parsing code
564 // If the Tcl script is invalid then parsing errors may get reported
565 // at the higher level code as well.
567 // There are two classes of diagnostic: errors and warnings.
568 // Additional levels may be added in future, but there does not seem
569 // to be an urgent need for them. Client code should provide callback
570 // functions so that the messages can be displayed to the user, and
571 // these callbacks will be registered with the current CdlInterpreter.
573 // If no error callback is defined then a ParseException will be
574 // raised instead, and the rest of the current script will not be
575 // processed. Alternatively the error callback itself can raise a
576 // ParseException. Care is taken to ensure that the exception does not
577 // go straight through the Tcl interpreter, since that would prevent
578 // the Tcl code from cleaning up appropriately. If no exception is
579 // raised then the library keeps track of the number of errors, and
580 // this information is accessible once the script has been fully
581 // processed. This allows multiple errors to be reported in a single
584 // If no warning callback is provided then warnings are ignored.
587 CdlParse::report_error(CdlInterpreter interp, std::string sub_id, std::string message)
589 CYG_REPORT_FUNCNAME("CdlParse::report_error");
590 CYG_REPORT_FUNCARG1("interp %p", interp);
591 CYG_PRECONDITION_CLASSC(interp);
593 incr_error_count(interp);
595 std::string full_message = construct_diagnostic(interp, "error", sub_id, message);
597 // Now, either invoke the callback if it is provided, or throw the exception.
598 CdlDiagnosticFnPtr fn = interp->get_error_fn_ptr();
600 throw CdlParseException(full_message);
609 CdlParse::report_warning(CdlInterpreter interp, std::string sub_id, std::string message)
611 CYG_REPORT_FUNCNAME("CdlParse::report_warning");
612 CYG_REPORT_FUNCARG1("interp %p", interp);
613 CYG_PRECONDITION_CLASSC(interp);
615 // If there is no warning callback, do nothing. This is really a
616 // bug in the calling application.
617 CdlDiagnosticFnPtr fn = interp->get_warning_fn_ptr();
619 std::string full_message = construct_diagnostic(interp, "warning", sub_id, message);
627 //{{{ The "unknown" command
629 // ----------------------------------------------------------------------------
630 // This routine should be installed in interpreters that get used for
631 // parsing CDL scripts. It gets invoked when the CDL script contains
632 // an unrecognised command, e.g. because of a typo, and makes sure that
633 // the usual diagnostics process is observed.
635 // This routine should be uninstalled after the parsing is complete,
636 // to avoid e.g. a ParseException when it is not expected.
638 CdlParse::unknown_command(CdlInterpreter interp, int argc, const char* argv[])
640 CYG_REPORT_FUNCNAME("CdlParse::unknown_command");
641 CYG_REPORT_FUNCARG3XV(interp, argc, argv);
642 CYG_PRECONDITIONC(2 <= argc);
643 CYG_PRECONDITION_CLASSC(interp);
645 report_error(interp, "", std::string("Unknown command `") + argv[1] + "'.");
646 CYG_UNUSED_PARAM(int, argc);
654 //{{{ Property-related parser utilities
656 // ----------------------------------------------------------------------------
657 // Utilities related to parsing properties, rather than more general parsing.
659 // A variant of report_parse_error() which also adds the property prefix.
661 CdlParse::report_property_parse_error(CdlInterpreter interp, std::string argv0, std::string msg)
663 CYG_REPORT_FUNCNAME("CdlPase::report_property_parse_error");
665 incr_error_count(interp);
667 std::string diag = construct_diagnostic(interp, "error",
668 std::string("property ") + CdlParse::get_tcl_cmd_name(argv0),
671 // Now, either invoke the callback if it is provided, or throw the exception.
672 CdlDiagnosticFnPtr fn = interp->get_error_fn_ptr();
674 throw CdlParseException(diag);
683 CdlParse::report_property_parse_error(CdlInterpreter interp, CdlProperty prop, std::string msg)
685 CYG_REPORT_FUNCNAME("CdlParse::report_property_parse_error");
686 report_property_parse_error(interp, (prop->get_argv())[0], msg);
690 // Repeat for warnings
692 CdlParse::report_property_parse_warning(CdlInterpreter interp, std::string argv0, std::string msg)
694 CYG_REPORT_FUNCNAME("CdlPase::report_property_parse_warning");
696 CdlDiagnosticFnPtr fn = interp->get_error_fn_ptr();
698 std::string diag = construct_diagnostic(interp, "error",
699 std::string("property ") + CdlParse::get_tcl_cmd_name(argv0),
708 CdlParse::report_property_parse_warning(CdlInterpreter interp, CdlProperty prop, std::string msg)
710 CYG_REPORT_FUNCNAME("CdlParse::report_property_parse_warning");
711 report_property_parse_warning(interp, (prop->get_argv())[0], msg);
716 //{{{ Generic property parsers
718 // ----------------------------------------------------------------------------
721 // These routines provide some more generic property parsing routines. argv[0]
722 // generally provides sufficient information to allow for sensible error messages.
723 // The command-specific parsers have to provide a property name. In addition it is
724 // possible to provide a function to handle per-command options, and another
725 // function that performs a final sanity check before the property gets added
726 // to the current entity.
728 //{{{ parse_minimal_property()
730 // ----------------------------------------------------------------------------
731 // A minimal property takes no arguments.
734 CdlParse::parse_minimal_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
735 char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_Minimal))
737 CYG_REPORT_FUNCNAME("parse_minimal_property");
738 CYG_PRECONDITION_CLASSC(interp);
740 CdlProperty_Minimal new_property = 0;
742 std::vector<std::pair<std::string,std::string> > options;
743 int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
745 if (data_index < argc) {
746 CdlParse::report_property_parse_error(interp, argv[0], std::string("Unexpected data `") + argv[data_index] + "'.");
749 // The command is valid, turn it into a property.
750 // The property has been parsed successfully. Add it to the current node
751 CdlNode current_node = interp->get_node();
752 CYG_ASSERTC(0 != current_node);
753 new_property = CdlProperty_MinimalBody::make(current_node, name, argc, argv, options);
754 if (0 != final_parser) {
755 (*final_parser)(interp, new_property);
760 if (0 != new_property) {
770 //{{{ parse_string_property()
772 // ----------------------------------------------------------------------------
775 CdlParse::parse_string_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
776 char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_String))
778 CYG_REPORT_FUNCNAME("parse_string_property");
779 CYG_PRECONDITION_CLASSC(interp);
781 CdlProperty_String new_property = 0;
784 std::vector<std::pair<std::string,std::string> > options;
785 int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
787 if (data_index == argc) {
788 CdlParse::report_property_parse_error(interp, argv[0], "Missing argument.");
789 } else if ((data_index + 1) < argc) {
790 CdlParse::report_property_parse_error(interp, argv[0], std::string("Too many arguments, expecting just one."));
793 CdlNode current_node = interp->get_node();
794 CYG_ASSERTC(0 != current_node);
795 new_property = CdlProperty_StringBody::make(current_node, name, argv[data_index], argc, argv, options);
796 if (0 != final_parser) {
797 (*final_parser)(interp, new_property);
801 if (0 != new_property) {
812 //{{{ parse_tclcode_property()
814 // ----------------------------------------------------------------------------
817 CdlParse::parse_tclcode_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
818 char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_TclCode))
820 CYG_REPORT_FUNCNAME("parse_tclcode_property");
821 CYG_PRECONDITION_CLASSC(interp);
823 CdlProperty_TclCode new_property = 0;
825 std::vector<std::pair<std::string,std::string> > options;
826 int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
828 if (data_index == argc) {
829 CdlParse::report_property_parse_error(interp, argv[0], "Missing Tcl code.");
830 } else if ((data_index + 1) < argc) {
831 CdlParse::report_property_parse_error(interp, argv[0], std::string("Invalid number of arguments.\n") +
832 "Expecting one argument, a Tcl code fragment.");
833 } else if (!Tcl_CommandComplete(CDL_TCL_CONST_CAST(char*, argv[data_index]))) {
834 CdlParse::report_property_parse_error(interp, argv[0], "Incomplete Tcl code fragment.");
837 CdlNode current_node = interp->get_node();
838 CYG_ASSERTC(0 != current_node);
839 new_property = CdlProperty_TclCodeBody::make(current_node, name, argv[data_index], argc, argv, options);
840 if (0 != final_parser) {
841 (*final_parser)(interp, new_property);
845 if (0 != new_property) {
855 //{{{ parse_stringvector_property()
857 // ----------------------------------------------------------------------------
860 CdlParse::parse_stringvector_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
861 char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_StringVector),
864 CYG_REPORT_FUNCNAME("parse_tclcode_property");
865 CYG_PRECONDITION_CLASSC(interp);
867 CdlProperty_StringVector new_property = 0;
869 std::vector<std::pair<std::string,std::string> > options;
870 int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
872 if (!allow_empty && (data_index == argc)) {
873 CdlParse::report_property_parse_error(interp, argv[0], "Missing arguments.");
876 // Creating the property requires a vector of strings.
877 std::vector<std::string> strings;
878 for ( ; data_index < argc; data_index++) {
879 strings.push_back(argv[data_index]);
881 CdlNode current_node = interp->get_node();
882 CYG_ASSERTC(0 != current_node);
883 new_property = CdlProperty_StringVectorBody::make(current_node, name, strings, argc, argv, options);
884 if (0 != final_parser) {
885 (*final_parser)(interp, new_property);
890 if (0 != new_property) {
900 //{{{ parse_reference_property()
902 // ----------------------------------------------------------------------------
905 CdlParse::parse_reference_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
906 char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_Reference),
907 bool allow_empty, CdlUpdateHandler update_handler)
909 CYG_REPORT_FUNCNAME("parse_reference_property");
910 CYG_PRECONDITION_CLASSC(interp);
912 CdlProperty_Reference new_property = 0;
914 std::vector<std::pair<std::string,std::string> > options;
915 int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
917 if (data_index == argc) {
918 CdlParse::report_property_parse_error(interp, argv[0], "Missing argument.");
919 } else if ((data_index + 1) < argc) {
920 CdlParse::report_property_parse_error(interp, argv[0], "Too many arguments, expecting just one.");
922 std::string refname = argv[data_index];
923 if (!(Cdl::is_valid_cdl_name(refname) || (allow_empty && ("" == refname)))) {
924 CdlParse::report_property_parse_error(interp, argv[0], "`" + refname + "' is not a valid CDL name");
926 CdlNode current_node = interp->get_node();
927 CYG_ASSERTC(0 != current_node);
928 new_property = CdlProperty_ReferenceBody::make(current_node, name, refname,
929 update_handler, argc, argv, options);
930 if (0 != final_parser) {
931 (*final_parser)(interp, new_property);
936 if (0 != new_property) {
946 //{{{ parse_expression_property()
948 // ----------------------------------------------------------------------------
951 CdlParse::parse_expression_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
952 char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_Expression),
953 CdlUpdateHandler update_handler)
955 CYG_REPORT_FUNCNAME("parse_expression_property");
956 CYG_PRECONDITION_CLASSC(interp);
958 CdlProperty_Expression new_property = 0;
959 CdlExpression expr = 0;
961 std::vector<std::pair<std::string,std::string> > options;
962 int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
964 std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index);
965 if ("" == all_args) {
966 CdlParse::report_property_parse_error(interp, argv[0], "Missing expression data.");
969 // The CdlExpression class has its own parsing routine. This
970 // will raise an exception if there are any problems. It is
971 // desirable to catch the exception and report the error via
972 // the normal reporting mechanisms, which may allow parsing to
975 expr = CdlExpressionBody::parse(all_args);
976 } catch(CdlParseException e) {
977 CdlParse::report_property_parse_error(interp, argv[0], e.get_message());
980 CdlNode current_node = interp->get_node();
981 CYG_ASSERTC(0 != current_node);
982 new_property = CdlProperty_ExpressionBody::make(current_node, name, expr, update_handler, argc, argv, options);
983 if (0 != final_parser) {
984 (*final_parser)(interp, new_property);
992 if (0 != new_property) {
1005 //{{{ parse_list_expression_property()
1007 // ----------------------------------------------------------------------------
1010 CdlParse::parse_listexpression_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
1011 char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_ListExpression),
1012 CdlUpdateHandler update_handler)
1014 CYG_REPORT_FUNCNAME("parse_list_expression_property");
1015 CYG_PRECONDITION_CLASSC(interp);
1017 CdlProperty_ListExpression new_property = 0;
1018 CdlListExpression expr = 0;
1020 std::vector<std::pair<std::string,std::string> > options;
1021 int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
1023 std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index);
1024 if ("" == all_args) {
1025 CdlParse::report_property_parse_error(interp, argv[0], "Missing list expression data.");
1029 expr = CdlListExpressionBody::parse(all_args);
1030 } catch(CdlParseException e) {
1031 CdlParse::report_property_parse_error(interp, argv[0], e.get_message());
1034 CdlNode current_node = interp->get_node();
1035 CYG_ASSERTC(0 != current_node);
1036 new_property = CdlProperty_ListExpressionBody::make(current_node, name, expr, update_handler,
1037 argc, argv, options);
1038 if (0 != final_parser) {
1039 (*final_parser)(interp, new_property);
1047 if (0 != new_property) {
1048 delete new_property;
1059 //{{{ parse_goalexpression_property()
1061 // ----------------------------------------------------------------------------
1064 CdlParse::parse_goalexpression_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
1065 char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_GoalExpression),
1066 CdlUpdateHandler update_handler)
1068 CYG_REPORT_FUNCNAMETYPE("parse_goal_expression_property", "result %d");
1069 CYG_PRECONDITION_CLASSC(interp);
1071 CdlProperty_GoalExpression new_property = 0;
1072 CdlGoalExpression expr = 0;
1074 std::vector<std::pair<std::string,std::string> > options;
1075 int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
1077 std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index);
1078 if ("" == all_args) {
1079 CdlParse::report_property_parse_error(interp, argv[0], "Missing goal expression data.");
1083 expr = CdlGoalExpressionBody::parse(all_args);
1084 } catch(CdlParseException e) {
1085 CdlParse::report_property_parse_error(interp, argv[0], e.get_message());
1088 CdlNode current_node = interp->get_node();
1089 CYG_ASSERTC(0 != current_node);
1090 new_property = CdlProperty_GoalExpressionBody::make(current_node, name, expr, update_handler,
1091 argc, argv, options);
1092 if (0 != final_parser) {
1093 (*final_parser)(interp, new_property);
1102 if (0 != new_property) {
1103 delete new_property;