]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - tools/src/libcdl/parse.cxx
RedBoot Release TX53-v3 2012-02-08
[karo-tx-redboot.git] / tools / src / libcdl / parse.cxx
1 //{{{  Banner
2
3 //============================================================================
4 //
5 //     parse.cxx
6 //
7 //     Miscellaneous parsing routines
8 //
9 //============================================================================
10 //####COPYRIGHTBEGIN####
11 //
12 // ----------------------------------------------------------------------------
13 // Copyright (C) 2002 Bart Veer
14 // Copyright (C) 1999, 2000 Red Hat, Inc.
15 //
16 // This file is part of the eCos host tools.
17 //
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)
21 // any later version.
22 //
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
26 // more details.
27 //
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.
31 //
32 // ----------------------------------------------------------------------------
33 //
34 //####COPYRIGHTEND####
35 //============================================================================
36 //#####DESCRIPTIONBEGIN####
37 //
38 // Author(s):   bartv
39 // Contact(s):  bartv
40 // Date:        1999/02/23
41 // Version:     0.02
42 //
43 //####DESCRIPTIONEND####
44 //============================================================================
45
46 //}}}
47 //{{{  #include's
48
49 // ----------------------------------------------------------------------------
50 #include <string.h>
51 #include "cdlconfig.h"
52
53 // Get the infrastructure types, assertions, tracing and similar
54 // facilities.
55 #include <cyg/infra/cyg_ass.h>
56 #include <cyg/infra/cyg_trac.h>
57
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>
62
63 //}}}
64
65 //{{{  Description
66
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.
72 //
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.
77 //
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.
82 //
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.
88 //
89 // Therefore a typical parse routine looks like this:
90 //
91 //  1) get the current CDL node from the interpreter
92 //  2) do basic parsing of the data. Any errors should be reported
93 //     via the callback.
94 //  3) create a suitable CdlProperty object
95 //  4) add the property to the current entity
96 //
97 // std::bad_alloc and CdlStringException exceptions can be thrown, they
98 // will be intercepted by the CdlInterpreter class.
99
100 //}}}
101 //{{{  Statics
102
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 ";
107
108 //}}}
109 //{{{  Generic parsing-related utilities
110
111 //{{{  argv manipulation
112
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.
117 std::string
118 CdlParse::get_tcl_cmd_name(std::string name)
119 {
120     std::string result;
121
122     if ((name[0] == ':') && (name[1] == ':')) {
123         result = std::string(name, 2, name.size() - 2);
124     } else {
125         result = name;
126     }
127     return result;
128 }
129
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().
133 std::string
134 CdlParse::concatenate_argv(int argc, const char* argv[], int index)
135 {
136     CYG_REPORT_FUNCNAME("CdlParse::concatenate_argv");
137
138     std::string result = "";
139     for ( int i = index ; i < argc; i++) {
140         if (i > index) {
141             result += ' ';
142         }
143         result += std::string(argv[i]);
144     }
145
146     CYG_REPORT_RETURN();
147     return result;
148 }
149
150 // ----------------------------------------------------------------------------
151 // Option parsing.
152 //
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:
157 //
158 //    ...
159 //    compile -library=libextras.a dummy.cxx
160 //    ...
161 //
162 // The rules applied for such options are intended to follow common Tcl
163 // practice:
164 //
165 // 1) any initial arguments beginning with a - are assumed to be
166 //    options.
167 //
168 // 2) the first argument which does not begin with a - stops option
169 //    processing.
170 //
171 // 3) option processing can also be terminated with a -- argument.
172 //    This may occasionally be required, e.g. to have -ve constants
173 //    in an expression.
174 //
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
177 //    is just ignored.
178 //
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.
183 //
184 // 6) name/value pairs can take the form -name=value or the form
185 //    -name value, whichever is appropriate.
186 //
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().
194 //
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.
199 //
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:
203 //
204 // f    - this option takes no data, it is just a boolean flag. The
205 //        default behaviour assumes name/value pairs.
206 //
207 // m    - this option can occur multiple times. The default behaviour
208 //        is that each option can only occur once.
209
210 // Utility function to get hold of an option name without the colon
211 // or terminating flags.
212
213 static std::string
214 get_option_string(const char* name)
215 {
216     std::string result = "";
217     while ((*name != ':') && (*name != '\0')) {
218         result += *name++;
219     }
220     return result;
221 }
222
223 int
224 CdlParse::parse_options(CdlInterpreter interp, std::string diag_prefix, const char** options,
225                                  int argc, const char* argv[], int index,
226                                  std::vector<std::pair<std::string,std::string> >& result)
227 {
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));
234
235     while((index < argc) && ('-' == argv[index][0])) {
236
237         std::string name  = "";
238         std::string value = "";
239
240         // The sequence -- should always terminate option processing.
241         if (0 == strcmp(argv[index], "--")) {
242             index++;
243             break;
244         }
245
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) {
249             arg_ptr++;
250         }
251
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)) {
255             name += *arg_ptr++;
256         }
257
258         if ("" == name) {
259             // One of "-", "-=xxx", or "--=x"
260             CdlParse::report_warning(interp, diag_prefix, std::string("Invalid option string `") + argv[index] + "'.");
261         }
262
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.
267         //
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.
272         unsigned int i;
273         int opt_index = -1;
274         if (options) {
275             for (i = 0; options[i] != NULL; i++) {
276                 if (strncmp(name.c_str(), options[i], name.size()) == 0) {
277                     if (opt_index != -1) {
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]) + "'.");
282                         index++;
283                         break;
284                     } else {
285                         opt_index = i;
286                     }
287                 }
288             }
289         }
290         if (opt_index == -1) {
291             CdlParse::report_warning(interp, diag_prefix, std::string("Invalid option `") + name + "'.");
292             index++;
293             break;
294         }
295
296         // The option has been identified successfully. Extract the flags.
297         bool    flag_flag       = false;
298         bool    multiple_flag   = false;
299         const char*     tmp = options[opt_index];
300         while (('\0' != *tmp) && (':' != *tmp)) {
301             tmp++;
302         }
303         if (':' == *tmp) {
304             do {
305                 tmp++;
306                 if ('f' == *tmp) {
307                     flag_flag = true;
308                 } else if ('m' == *tmp) {
309                     multiple_flag = true;
310                 } else if ('\0' != *tmp) {
311                     CYG_FAIL("Invalid property option");
312                 }
313             } while ('\0' != *tmp);
314         }
315
316         // We now know the full option name. Use it for future diagnostics.
317         name = get_option_string(options[opt_index]);
318
319         // Take care of the value.
320         if (flag_flag) {
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.");
325             }
326             // Leave index pointing at the next argument to be processed.
327             index++;
328         } else {
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 + "'.");
333             } else {
334                 value = argv[index];
335             }
336             index++;
337         }
338         // At this stage index points at the next argument to be processed, and should not
339         // be updated again.
340
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.");
347                     break;
348                 }
349             }
350         }
351
352         // The name/value pair is valid, so add it to the result vector.
353         result.push_back(std::make_pair(name, value));
354     }
355
356     CYG_REPORT_RETVAL(index);
357     return index;
358 }
359
360 //}}}
361 //{{{  Diagnostic construction
362
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
365 // similar.
366 //
367 // A diagnostic should take the following form:
368 //
369 //     <context> <linenumber> [, <node identifier>] [, <extra identifier>] : [<classification>, ] <message>
370 //
371 // The context should be set in the Tcl interpreter. Typically it
372 // will be a filename.
373 //
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.
381 //
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.
388 //
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.
392 //
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()
396 // etc.
397 //
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.
405
406 std::string
407 CdlParse::construct_diagnostic(CdlInterpreter interp, std::string classification, std::string sub_id, std::string message)
408 {
409     CYG_REPORT_FUNCNAME("CdlParse::construct_diagnostic");
410     CYG_PRECONDITION_CLASSC(interp);
411
412     std::string context      = interp->get_context();
413     CdlNode     current_node = interp->get_node();
414
415     std::string result;
416     if ("" == context) {
417         result = "<unknown context>";
418     } else {
419         result = context;
420     }
421     if (current_node) {
422         result += ", " + current_node->get_class_name() + " " + current_node->get_name();
423     }
424     if ("" != sub_id) {
425         result += ", " + sub_id;
426     }
427     result += ": " + classification;
428
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();
433     int message_index;
434     bool indent_needed = false;
435
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++) {
438         ;
439     }
440
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.
447         } else {
448             // Start indenting immediately, do not add anything else to the current line.
449             indent_needed = true;
450         }
451     } else {
452         // We may want a comma and a space after the classification
453         if ((index + 2 + message_index) <= 72) {
454             result += ", ";
455         } else {
456             indent_needed = true;
457         }
458     }
459
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++) {
463         if (indent_needed) {
464             result += "\n    ";
465             indent_needed = false;
466         }
467
468         if ('\n' == message[message_index]) {
469             indent_needed = true;
470         } else {
471             result += message[message_index];
472         }
473     }
474
475     CYG_REPORT_RETURN();
476     return result;
477 }
478
479 //}}}
480 //{{{  Error count tracking
481
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.
485
486 static const char       error_count_key[]       = "CdlErrorCount";
487
488 static void
489 error_count_delproc(ClientData data, Tcl_Interp* interp)
490 {
491     CYG_REPORT_FUNCNAME("CdlParse::error_count_delproc");
492     int* newed_ptr = static_cast<int*>(data);
493     delete newed_ptr;
494     CYG_REPORT_RETURN();
495 }
496
497 void
498 CdlParse::clear_error_count(CdlInterpreter interp)
499 {
500     CYG_REPORT_FUNCNAME("CdlParse::clear_error_count");
501     CYG_REPORT_FUNCARG1("interp %p", interp);
502     CYG_PRECONDITION_CLASSC(interp);
503
504     int*        newed_ptr = static_cast<int*>(interp->get_assoc_data(error_count_key));
505     if (newed_ptr) {
506         *newed_ptr = 0;
507     }
508
509     CYG_REPORT_RETURN();
510 }
511
512 void
513 CdlParse::incr_error_count(CdlInterpreter interp, int how_much)
514 {
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");
519
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);
524     } else {
525         CYG_ASSERT((*newed_ptr + how_much) > *newed_ptr, "number of parsing errors should not overflow");
526         *newed_ptr += how_much;
527     }
528
529     CYG_REPORT_RETURN();
530 }
531
532 int
533 CdlParse::get_error_count(CdlInterpreter interp)
534 {
535     CYG_REPORT_FUNCNAMETYPE("CdlParse::get_error_count", "count %d");
536     CYG_REPORT_FUNCARG1("interp %p", interp);
537     CYG_PRECONDITION_CLASSC(interp);
538
539     int result = 0;
540     int* newed_ptr = static_cast<int*>(interp->get_assoc_data(error_count_key));
541     if (newed_ptr) {
542         result = *newed_ptr;
543     }
544
545     CYG_REPORT_RETVAL(result);
546     return result;
547 }
548
549 //}}}
550 //{{{  Error and warning reporting
551
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:
557 //
558 //     libcdl C++ code such as load_package()
559 //     libcdl CdlInterpreter::eval()
560 //     Tcl interpreter
561 //     libcdl parsing code
562 //     report_error()
563 //
564 // If the Tcl script is invalid then parsing errors may get reported
565 // at the higher level code as well.
566 //
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.
572 //
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
582 // run.
583 //
584 // If no warning callback is provided then warnings are ignored.
585
586 void
587 CdlParse::report_error(CdlInterpreter interp, std::string sub_id, std::string message)
588 {
589     CYG_REPORT_FUNCNAME("CdlParse::report_error");
590     CYG_REPORT_FUNCARG1("interp %p", interp);
591     CYG_PRECONDITION_CLASSC(interp);
592
593     incr_error_count(interp);
594
595     std::string full_message = construct_diagnostic(interp, "error", sub_id, message);
596
597     // Now, either invoke the callback if it is provided, or throw the exception.
598     CdlDiagnosticFnPtr fn = interp->get_error_fn_ptr();
599     if (0 == fn) {
600         throw CdlParseException(full_message);
601     } else {
602         (*fn)(full_message);
603     }
604
605     CYG_REPORT_RETURN();
606 }
607
608 void
609 CdlParse::report_warning(CdlInterpreter interp, std::string sub_id, std::string message)
610 {
611     CYG_REPORT_FUNCNAME("CdlParse::report_warning");
612     CYG_REPORT_FUNCARG1("interp %p", interp);
613     CYG_PRECONDITION_CLASSC(interp);
614
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();
618     if (fn) {
619         std::string full_message = construct_diagnostic(interp, "warning", sub_id, message);
620         fn(full_message);
621     }
622
623     CYG_REPORT_RETURN();
624 }
625
626 //}}}
627 //{{{  The "unknown" command
628
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.
634 //
635 // This routine should be uninstalled after the parsing is complete,
636 // to avoid e.g. a ParseException when it is not expected.
637 int
638 CdlParse::unknown_command(CdlInterpreter interp, int argc, const char* argv[])
639 {
640     CYG_REPORT_FUNCNAME("CdlParse::unknown_command");
641     CYG_REPORT_FUNCARG3XV(interp, argc, argv);
642     CYG_PRECONDITIONC(2 <= argc);
643     CYG_PRECONDITION_CLASSC(interp);
644
645     report_error(interp, "", std::string("Unknown command `") + argv[1] + "'.");
646     CYG_UNUSED_PARAM(int, argc);
647
648     return TCL_OK;
649 }
650
651 //}}}
652
653 //}}}
654 //{{{  Property-related parser utilities
655
656 // ----------------------------------------------------------------------------
657 // Utilities related to parsing properties, rather than more general parsing.
658
659 // A variant of report_parse_error() which also adds the property prefix.
660 void
661 CdlParse::report_property_parse_error(CdlInterpreter interp, std::string argv0, std::string msg)
662 {
663     CYG_REPORT_FUNCNAME("CdlPase::report_property_parse_error");
664
665     incr_error_count(interp);
666
667     std::string diag = construct_diagnostic(interp, "error",
668                                             std::string("property ") + CdlParse::get_tcl_cmd_name(argv0),
669                                             msg);
670
671     // Now, either invoke the callback if it is provided, or throw the exception.
672     CdlDiagnosticFnPtr fn = interp->get_error_fn_ptr();
673     if (0 == fn) {
674         throw CdlParseException(diag);
675     } else {
676         (*fn)(diag);
677     }
678
679     CYG_REPORT_RETURN();
680 }
681
682 void
683 CdlParse::report_property_parse_error(CdlInterpreter interp, CdlProperty prop, std::string msg)
684 {
685     CYG_REPORT_FUNCNAME("CdlParse::report_property_parse_error");
686     report_property_parse_error(interp, (prop->get_argv())[0], msg);
687     CYG_REPORT_RETURN();
688 }
689
690 // Repeat for warnings
691 void
692 CdlParse::report_property_parse_warning(CdlInterpreter interp, std::string argv0, std::string msg)
693 {
694     CYG_REPORT_FUNCNAME("CdlPase::report_property_parse_warning");
695
696     CdlDiagnosticFnPtr fn = interp->get_error_fn_ptr();
697     if (fn) {
698         std::string diag = construct_diagnostic(interp, "error",
699                                                 std::string("property ") + CdlParse::get_tcl_cmd_name(argv0),
700                                                 msg);
701         fn(diag);
702     }
703
704     CYG_REPORT_RETURN();
705 }
706
707 void
708 CdlParse::report_property_parse_warning(CdlInterpreter interp, CdlProperty prop, std::string msg)
709 {
710     CYG_REPORT_FUNCNAME("CdlParse::report_property_parse_warning");
711     report_property_parse_warning(interp, (prop->get_argv())[0], msg);
712     CYG_REPORT_RETURN();
713 }
714
715 //}}}
716 //{{{  Generic property parsers
717
718 // ----------------------------------------------------------------------------
719 // Generic parsers
720 //
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.
727
728 //{{{  parse_minimal_property()
729
730 // ----------------------------------------------------------------------------
731 // A minimal property takes no arguments.
732
733 int
734 CdlParse::parse_minimal_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
735                                  const char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_Minimal))
736 {
737     CYG_REPORT_FUNCNAME("parse_minimal_property");
738     CYG_PRECONDITION_CLASSC(interp);
739
740     CdlProperty_Minimal new_property = 0;
741     try {
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);
744
745         if (data_index < argc) {
746             CdlParse::report_property_parse_error(interp, argv[0], std::string("Unexpected data `") + argv[data_index] + "'.");
747         } else {
748
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(current_node != NULL);
753             new_property = CdlProperty_MinimalBody::make(current_node, name, argc, argv, options);
754             if (final_parser) {
755                 final_parser(interp, new_property);
756             }
757         }
758     } catch(...) {
759         delete new_property;
760         throw;
761     }
762
763     return TCL_OK;
764 }
765
766 //}}}
767 //{{{  parse_string_property()
768
769 // ----------------------------------------------------------------------------
770
771 int
772 CdlParse::parse_string_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
773                                 const char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_String))
774 {
775     CYG_REPORT_FUNCNAME("parse_string_property");
776     CYG_PRECONDITION_CLASSC(interp);
777
778     CdlProperty_String new_property = 0;
779
780     try {
781         std::vector<std::pair<std::string,std::string> > options;
782         int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
783
784         if (data_index == argc) {
785             CdlParse::report_property_parse_error(interp, argv[0], "Missing argument.");
786         } else if ((data_index + 1) < argc) {
787             CdlParse::report_property_parse_error(interp, argv[0], std::string("Too many arguments, expecting just one."));
788         } else {
789
790             CdlNode current_node = interp->get_node();
791             CYG_ASSERTC(current_node != NULL);
792             new_property = CdlProperty_StringBody::make(current_node, name, argv[data_index], argc, argv, options);
793             if (final_parser) {
794                 final_parser(interp, new_property);
795             }
796         }
797     } catch(...) {
798         delete new_property;
799         throw;
800     }
801
802     CYG_REPORT_RETURN();
803     return TCL_OK;
804 }
805
806 //}}}
807 //{{{  parse_tclcode_property()
808
809 // ----------------------------------------------------------------------------
810
811 int
812 CdlParse::parse_tclcode_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
813                                  const char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_TclCode))
814 {
815     CYG_REPORT_FUNCNAME("parse_tclcode_property");
816     CYG_PRECONDITION_CLASSC(interp);
817
818     CdlProperty_TclCode new_property = 0;
819     try {
820         std::vector<std::pair<std::string,std::string> > options;
821         int data_index      = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
822
823         if (data_index == argc) {
824             CdlParse::report_property_parse_error(interp, argv[0], "Missing Tcl code.");
825         } else if ((data_index + 1) < argc) {
826             CdlParse::report_property_parse_error(interp, argv[0], std::string("Invalid number of arguments.\n") +
827                                          "Expecting one argument, a Tcl code fragment.");
828         } else if (!Tcl_CommandComplete(CDL_TCL_CONST_CAST(char*, argv[data_index]))) {
829             CdlParse::report_property_parse_error(interp, argv[0], "Incomplete Tcl code fragment.");
830         } else {
831
832             CdlNode current_node = interp->get_node();
833             CYG_ASSERTC(current_node != NULL);
834             new_property = CdlProperty_TclCodeBody::make(current_node, name, argv[data_index], argc, argv, options);
835             if (final_parser) {
836                 final_parser(interp, new_property);
837             }
838         }
839      } catch(...) {
840         delete new_property;
841         throw;
842     }
843
844     return TCL_OK;
845 }
846
847 //}}}
848 //{{{  parse_stringvector_property()
849
850 // ----------------------------------------------------------------------------
851
852 int
853 CdlParse::parse_stringvector_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
854                                       const char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_StringVector),
855                                       bool allow_empty)
856 {
857     CYG_REPORT_FUNCNAME("parse_tclcode_property");
858     CYG_PRECONDITION_CLASSC(interp);
859
860     CdlProperty_StringVector new_property = 0;
861     try {
862         std::vector<std::pair<std::string,std::string> > options;
863         int data_index      = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
864
865         if (!allow_empty && (data_index == argc)) {
866             CdlParse::report_property_parse_error(interp, argv[0], "Missing arguments.");
867         } else {
868
869             // Creating the property requires a vector of strings.
870             std::vector<std::string>  strings;
871             for ( ; data_index < argc; data_index++) {
872                 strings.push_back(argv[data_index]);
873             }
874             CdlNode current_node = interp->get_node();
875             CYG_ASSERTC(current_node != NULL);
876             new_property = CdlProperty_StringVectorBody::make(current_node, name, strings, argc, argv, options);
877             if (final_parser) {
878                 final_parser(interp, new_property);
879             }
880         }
881     } catch(...) {
882         delete new_property;
883         throw;
884     }
885
886     return TCL_OK;
887 }
888
889 //}}}
890 //{{{  parse_reference_property()
891
892 // ----------------------------------------------------------------------------
893
894 int
895 CdlParse::parse_reference_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
896                                    const char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_Reference),
897                                    bool allow_empty, CdlUpdateHandler update_handler)
898 {
899     CYG_REPORT_FUNCNAME("parse_reference_property");
900     CYG_PRECONDITION_CLASSC(interp);
901
902     CdlProperty_Reference new_property = 0;
903     try {
904         std::vector<std::pair<std::string,std::string> > options;
905         int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
906
907         if (data_index == argc) {
908             CdlParse::report_property_parse_error(interp, argv[0], "Missing argument.");
909         } else if ((data_index + 1) < argc) {
910             CdlParse::report_property_parse_error(interp, argv[0], "Too many arguments, expecting just one.");
911         } else {
912             std::string refname = argv[data_index];
913             if (!(Cdl::is_valid_cdl_name(refname) || (allow_empty && ("" == refname)))) {
914                 CdlParse::report_property_parse_error(interp, argv[0], "`" + refname + "' is not a valid CDL name");
915             } else {
916                 CdlNode current_node = interp->get_node();
917                 CYG_ASSERTC(current_node != NULL);
918                 new_property = CdlProperty_ReferenceBody::make(current_node, name, refname,
919                                                                update_handler, argc, argv, options);
920                 if (final_parser) {
921                     final_parser(interp, new_property);
922                 }
923             }
924         }
925     } catch(...) {
926         delete new_property;
927         throw;
928     }
929
930     return TCL_OK;
931 }
932
933 //}}}
934 //{{{  parse_expression_property()
935
936 // ----------------------------------------------------------------------------
937
938 int
939 CdlParse::parse_expression_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
940                                     const char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_Expression),
941                                     CdlUpdateHandler update_handler)
942 {
943     CYG_REPORT_FUNCNAME("parse_expression_property");
944     CYG_PRECONDITION_CLASSC(interp);
945
946     CdlProperty_Expression new_property = 0;
947     CdlExpression expr = 0;
948     try {
949         std::vector<std::pair<std::string,std::string> > options;
950         int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
951         std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index);
952         if ("" == all_args) {
953             CdlParse::report_property_parse_error(interp, argv[0], "Missing expression data.");
954         } else {
955
956             // The CdlExpression class has its own parsing routine. This
957             // will raise an exception if there are any problems. It is
958             // desirable to catch the exception and report the error via
959             // the normal reporting mechanisms, which may allow parsing to
960             // continue.
961             try {
962                 expr = CdlExpressionBody::parse(all_args);
963             } catch(CdlParseException e) {
964                 CdlParse::report_property_parse_error(interp, argv[0], e.get_message());
965             }
966             if (expr) {
967                 CdlNode current_node = interp->get_node();
968                 CYG_ASSERTC(current_node != NULL);
969                 new_property = CdlProperty_ExpressionBody::make(current_node, name, expr, update_handler, argc, argv, options);
970                 if (final_parser) {
971                     final_parser(interp, new_property);
972                 }
973             }
974         }
975     } catch(...) {
976         delete expr;
977         delete new_property;
978         throw;
979     }
980
981     delete expr;
982     return TCL_OK;
983 }
984
985 //}}}
986 //{{{  parse_list_expression_property()
987
988 // ----------------------------------------------------------------------------
989
990 int
991 CdlParse::parse_listexpression_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
992                                         const char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_ListExpression),
993                                         CdlUpdateHandler update_handler)
994 {
995     CYG_REPORT_FUNCNAME("parse_list_expression_property");
996     CYG_PRECONDITION_CLASSC(interp);
997
998     CdlProperty_ListExpression new_property = 0;
999     CdlListExpression expr = 0;
1000     try {
1001         std::vector<std::pair<std::string,std::string> > options;
1002         int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
1003
1004         std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index);
1005         if ("" == all_args) {
1006             CdlParse::report_property_parse_error(interp, argv[0], "Missing list expression data.");
1007         } else {
1008             try {
1009                 expr = CdlListExpressionBody::parse(all_args);
1010             } catch(CdlParseException e) {
1011                 CdlParse::report_property_parse_error(interp, argv[0], e.get_message());
1012             }
1013             if (expr) {
1014                 CdlNode current_node = interp->get_node();
1015                 CYG_ASSERTC(current_node != NULL);
1016                 new_property = CdlProperty_ListExpressionBody::make(current_node, name, expr, update_handler,
1017                                                                     argc, argv, options);
1018                 if (final_parser) {
1019                     final_parser(interp, new_property);
1020                 }
1021             }
1022         }
1023     } catch(...) {
1024         delete expr;
1025         delete new_property;
1026         throw;
1027     }
1028     delete expr;
1029     return TCL_OK;
1030 }
1031
1032 //}}}
1033 //{{{  parse_goalexpression_property()
1034
1035 // ----------------------------------------------------------------------------
1036
1037 int
1038 CdlParse::parse_goalexpression_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
1039                                         const char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_GoalExpression),
1040                                         CdlUpdateHandler update_handler)
1041 {
1042     CYG_REPORT_FUNCNAMETYPE("parse_goal_expression_property", "result %d");
1043     CYG_PRECONDITION_CLASSC(interp);
1044
1045     CdlProperty_GoalExpression new_property = 0;
1046     CdlGoalExpression expr = 0;
1047     try {
1048         std::vector<std::pair<std::string,std::string> > options;
1049         int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
1050
1051         std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index);
1052         if ("" == all_args) {
1053             CdlParse::report_property_parse_error(interp, argv[0], "Missing goal expression data.");
1054         } else {
1055             try {
1056                 expr = CdlGoalExpressionBody::parse(all_args);
1057             } catch(CdlParseException e) {
1058                 CdlParse::report_property_parse_error(interp, argv[0], e.get_message());
1059             }
1060             if (expr) {
1061                 CdlNode current_node = interp->get_node();
1062                 CYG_ASSERTC(current_node != NULL);
1063                 new_property = CdlProperty_GoalExpressionBody::make(current_node, name, expr, update_handler,
1064                                                                     argc, argv, options);
1065                 if (final_parser) {
1066                     final_parser(interp, new_property);
1067                 }
1068             }
1069         }
1070     } catch(...) {
1071         delete expr;
1072         delete new_property;
1073         throw;
1074     }
1075     delete expr;
1076
1077     return TCL_OK;
1078 }
1079
1080 //}}}
1081
1082 //}}}