db9ed0895b77e0748a643ba499bca2510f53ca40
[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 "cdlconfig.h"
51
52 // Get the infrastructure types, assertions, tracing and similar
53 // facilities.
54 #include <cyg/infra/cyg_ass.h>
55 #include <cyg/infra/cyg_trac.h>
56
57 // <cdlcore.hxx> defines everything implemented in this module.
58 // It implicitly supplies <string>, <vector> and <map> because
59 // the class definitions rely on these headers.
60 #include <cdlcore.hxx>
61
62 //}}}
63
64 //{{{  Description                              
65
66 // ----------------------------------------------------------------------------
67 // All CDL data is read via a Tcl interpreter, so the parsing is done by
68 // procedures that appear as Tcl commands. This has obvious advantages in
69 // terms of expressive power, but does result in a little bit of confusion
70 // when producing diagnostics.
71 //
72 // Errors should not bypass the Tcl interpreter, to ensure that that
73 // stays in a consistent state. In particular it is not possible to let
74 // arbitrary C++ exceptions to go straight through the Tcl interpreter,
75 // this is likely to result in a corrupted interpreter.
76 // 
77 // Also, it is not a good idea to abort parsing as soon as anything
78 // goes wrong. Instead there should be an error handling callback associated
79 // with the interpreter, which can be used to report errors. This
80 // callback may choose to raise an exception.
81 //
82 // Consider the case of parsing a property (which accounts for most of
83 // the code in this module). A property does not exist in isolation,
84 // only within the context of a suitable CDL entity such as an option.
85 // If parsing succeeds and a property object is created then it must
86 // be added to the current CDL entity.
87 //
88 // Therefore a typical parse routine looks like this:
89 //
90 //  1) get the current CDL node from the interpreter
91 //  2) do basic parsing of the data. Any errors should be reported
92 //     via the callback.
93 //  3) create a suitable CdlProperty object
94 //  4) add the property to the current entity
95 //
96 // std::bad_alloc and CdlStringException exceptions can be thrown, they
97 // will be intercepted by the CdlInterpreter class.
98
99 //}}}
100 //{{{  Statics                                  
101
102 // ----------------------------------------------------------------------------
103 // The string "property " is needed in various places. Provide it as a static
104 // to cut down the number of times the string constructor has to run.
105 static std::string property_string = "property ";
106
107 //}}}
108 //{{{  Generic parsing-related utilities        
109
110 //{{{  argv manipulation                        
111
112 // ----------------------------------------------------------------------------
113 // Some of the properties have aliases in the CDL data, so argv[0] has to be
114 // used to work out what is actually being parsed. However the Tcl interpreter
115 // may prefix the command name with :: to indicate the global namespace.
116 std::string
117 CdlParse::get_tcl_cmd_name(std::string name)
118 {
119     std::string result;
120     
121     if ((name[0] == ':') && (name[1] == ':')) {
122         result = std::string(name, 2, name.size() - 2);
123     } else {
124         result = name;
125     }
126     return result;
127 }
128
129 // Given a list of arguments, concatenate them together into a C++ string.
130 // This makes expression parsing easier. The final argument should be an
131 // index into the array, typically the result of a call to skip_argv_options().
132 std::string
133 CdlParse::concatenate_argv(int argc, const char* argv[], int index)
134 {
135     CYG_REPORT_FUNCNAME("CdlParse::concatenate_argv");
136
137     std::string result = "";
138     for ( int i = index ; i < argc; i++) {
139         if (i > index) {
140             result += ' ';
141         }
142         result += std::string(argv[i]);
143     }
144     
145     CYG_REPORT_RETURN();
146     return result;
147 }
148
149 // ----------------------------------------------------------------------------
150 // Option parsing.
151 //
152 // Some properties accept modifiers in their argument list. For example,
153 // by default a "compile" property is just a list of source files that
154 // should be compiled and added to the current library. It is possible
155 // to specify an alternative library via a modifier:
156 //
157 //    ...
158 //    compile -library=libextras.a dummy.cxx
159 //    ...
160 //
161 // The rules applied for such options are intended to follow common Tcl
162 // practice:
163 //
164 // 1) any initial arguments beginning with a - are assumed to be
165 //    options.
166 //
167 // 2) the first argument which does not begin with a - stops option
168 //    processing.
169 //
170 // 3) option processing can also be terminated with a -- argument.
171 //    This may occasionally be required, e.g. to have -ve constants
172 //    in an expression.
173 //
174 // 4) the parsing code does not distinguish between single and double
175 //    hyphens. If there happens to be a second - in an option then this
176 //    is just ignored.
177 //
178 // 5) it is not necessary to supply the whole option name, as long as
179 //    what is provided is unambiguous. For example -lib=libextras.a is
180 //    acceptable (for now anyway, conceivably it would break in future).
181 //    Option processing is case sensitive.
182 //
183 // 6) name/value pairs can take the form -name=value or the form
184 //    -name value, whichever is appropriate.
185 //
186 // The parse_options() function takes the current interpreter (so that
187 // it can generate diagnostics), a prefix such as `property
188 // "requires"' or `package CYGPKG_HAL', details of the options to be
189 // parsed, an argc/argv list, an index, and a reference to a vector.
190 // The parsed options get placed in the vector, and the index argument
191 // gets updated to point at the first non-option argument. It will
192 // report problems via report_warning().
193 //
194 // The options description consists of a pointer to an array of
195 // C strings (allowing static initialization). Passing a zero
196 // argument indicates that the property takes no options. Otherwise
197 // the array should be zero terminated.
198 //
199 // Each entry in the array takes the form "name:flags". The optional
200 // flags consist of zero or more characters indicating how the option
201 // should be interpreted. Valid flags are:
202 //
203 // f    - this option takes no data, it is just a boolean flag. The
204 //        default behaviour assumes name/value pairs.
205 //
206 // m    - this option can occur multiple times. The default behaviour
207 //        is that each option can only occur once.
208
209 // Utility function to get hold of an option name without the colon
210 // or terminating flags.
211
212 static std::string
213 get_option_string(char* name)
214 {
215     std::string result = "";
216     while ((*name != ':') && (*name != '\0')) {
217         result += *name++;
218     }
219     return result;
220 }
221
222 int
223 CdlParse::parse_options(CdlInterpreter interp, std::string diag_prefix, char** options,
224                                  int argc, const char* argv[], int index,
225                                  std::vector<std::pair<std::string,std::string> >& result)
226 {
227     CYG_REPORT_FUNCNAMETYPE("CdlParse::parse_options", "final index %d");
228     CYG_REPORT_FUNCARG4XV(interp, options, argc, argv);
229     CYG_PRECONDITION_CLASSC(interp);
230     CYG_PRECONDITIONC(argc > 0);        // The property name must be present.
231     // It is possible for some of the arguments to have been processed already.
232     CYG_PRECONDITIONC((index > 0) && (index <= argc));
233
234     while((index < argc) && ('-' == argv[index][0])) {
235
236         std::string name  = "";
237         std::string value = "";
238
239         // The sequence -- should always terminate option processing.
240         if (0 == strcmp(argv[index], "--")) {
241             index++;
242             break;
243         }
244
245         const char* arg_ptr       = argv[index];
246         // Skip the initial -, and the second one as well if it is present.
247         if ('-' == *++arg_ptr) {
248             arg_ptr++;
249         }
250
251         // Construct the option name. This is the current argument up
252         // to but not including the '=' character or EOD.
253         while (('=' != *arg_ptr) && ('\0' != *arg_ptr)) {
254             name += *arg_ptr++;
255         }
256
257         if ("" == name) {
258             // One of "-", "-=xxx", or "--=x"
259             CdlParse::report_warning(interp, diag_prefix, std::string("Invalid option string `") + argv[index] + "'.");
260         }
261
262         // Do not try to extract the value unless we are sure there
263         // should be one. Instead try to match the option name. The
264         // current value of name should be a unique substring of
265         // one of the known option strings.
266         //
267         // Note that the supplied options descriptor can be NULL,
268         // since most properties do not yet take any options.
269         // In that case opt_index will remain at -1, and we should
270         // get an "invalid option" diagnostic.
271         unsigned int i;
272         int opt_index = -1;
273         if (0 != options) {
274             for (i = 0; 0 != options[i]; i++) {
275                 if (0 == strncmp(name.c_str(), options[i], name.size())) {
276                     if (-1 != opt_index) {
277                         CdlParse::report_warning(interp, diag_prefix,
278                                                  std::string("Ambiguous option name `") + name + "'.\n" +
279                                                  "It can match `" + get_option_string(options[opt_index]) + "'\n" +
280                                                  "or `" + get_option_string(options[i]) + "'.");
281                         index++;
282                         break;
283                     } else {
284                         opt_index = i;
285                     }
286                 }
287             }
288         }
289         if (-1 == opt_index) {
290             CdlParse::report_warning(interp, diag_prefix, std::string("Invalid option `") + name + "'.");
291             index++;
292             break;
293         }
294
295         // The option has been identified successfully. Extract the flags.
296         bool    flag_flag       = false;
297         bool    multiple_flag   = false;
298         char*   tmp = options[opt_index];
299         while (('\0' != *tmp) && (':' != *tmp)) {
300             tmp++;
301         }
302         if (':' == *tmp) {
303             do {
304                 tmp++;
305                 if ('f' == *tmp) {
306                     flag_flag = true;
307                 } else if ('m' == *tmp) {
308                     multiple_flag = true;
309                 } else if ('\0' != *tmp) {
310                     CYG_FAIL("Invalid property option");
311                 }
312             } while ('\0' != *tmp);
313         }
314
315         // We now know the full option name. Use it for future diagnostics.
316         name = get_option_string(options[opt_index]);
317
318         // Take care of the value.
319         if (flag_flag) {
320             // There should not be a value. If the current argument is of the
321             // form x=y then this is an error.
322             if ('=' == *arg_ptr) {
323                 CdlParse::report_warning(interp, diag_prefix,  std::string("Option `") + name + "' does not take any data.");
324             }
325             // Leave index pointing at the next argument to be processed.
326             index++;
327         } else {
328             if ('=' == *arg_ptr) {
329                 value = std::string(++arg_ptr);
330             } else if (++index == argc) {
331                 CdlParse::report_warning(interp, diag_prefix,  std::string("Missing data for option `") + name + "'.");
332             } else {
333                 value = argv[index];
334             }
335             index++;
336         }
337         // At this stage index points at the next argument to be processed, and should not
338         // be updated again.
339         
340         // Unless the option can occur multiple times, make sure that it is not already
341         // present in the options vector.
342         if (!multiple_flag) {
343             for (i = 0; i < result.size(); i++) {
344                 if (name == result[i].first) {
345                     CdlParse::report_warning(interp, diag_prefix, std::string("Option `") + name + "' can only be used once.");
346                     break;
347                 }
348             }
349         }
350
351         // The name/value pair is valid, so add it to the result vector.
352         result.push_back(std::make_pair(name, value));
353     }
354     
355     CYG_REPORT_RETVAL(index);
356     return index;
357 }
358
359 //}}}
360 //{{{  Diagnostic construction                  
361
362 // Construct a suitable diagnostic for a parsing error. This may occur
363 // when reading in a CDL script, a savefile, a database, or anything
364 // similar. 
365 //
366 // A diagnostic should take the following form:
367 //
368 //     <context> <linenumber> [, <node identifier>] [, <extra identifier>] : [<classification>, ] <message>
369 //
370 // The context should be set in the Tcl interpreter. Typically it
371 // will be a filename.
372 //
373 // In practice generating the line number is not really feasible at
374 // present, the Tcl interpreter does not keep track of sufficient
375 // information. At least, not in the public data structures, there is
376 // a termOffset field in the internal data structures which might
377 // be used to do the right thing. I do not want to start relying
378 // on Tcl internals just yet, or add support to the Tcl core for
379 // keeping track of line numbers.
380 //
381 // For many data files there will the concept of a current node,
382 // e.g. an option whose properties or savefile information are
383 // being processed. The CdlInterpreter class keeps track of the
384 // current node, so if it is defined then the node's class and
385 // name can be part of the message. This happens automatically,
386 // no effort is required on the part of calling code.
387 //
388 // There may also be additional information, for example
389 // identifying the specific property where the error was detected.
390 // This is handled by an extra argument.
391 //
392 // The classification is likely to be something like "warning",
393 // "error", or "internal error". It is controlled by the calling
394 // code, but typically it is provided by calling via report_warning()
395 // etc.
396 //
397 // The message should identify the actual error. It should be
398 // a proper sentence, i.e. begin with a capital error and end with
399 // a full stop, unless the last word is an identifier or filename
400 // or something similarly special in which case the trailing
401 // dot will be discarded. The message should not end with a
402 // newline character, and the result string will not end with one
403 // either. That is left to higher level code.
404
405 std::string
406 CdlParse::construct_diagnostic(CdlInterpreter interp, std::string classification, std::string sub_id, std::string message)
407 {
408     CYG_REPORT_FUNCNAME("CdlParse::construct_diagnostic");
409     CYG_PRECONDITION_CLASSC(interp);
410
411     std::string context      = interp->get_context();
412     CdlNode     current_node = interp->get_node();
413
414     std::string result;
415     if ("" == context) {
416         result = "<unknown context>";
417     } else {
418         result = context;
419     }
420     if (0 != current_node) {
421         result += ", " + current_node->get_class_name() + " " + current_node->get_name();
422     }
423     if ("" != sub_id) {
424         result += ", " + sub_id;
425     }
426     result += ": " + classification;
427     
428     // Now it is time to start worrying about layout, indenting
429     // subsequent lines, and so on.
430     int index        = result.length();
431     int message_len  = message.length();
432     int message_index;
433     bool indent_needed = false;
434
435     // Find out how many characters there are in the message up to the first newline
436     for (message_index = 0; (message_index < message_len) && ('\n' != message[message_index]); message_index++) {
437         ;
438     }
439
440     // Should the message start on the next line, suitably indented?
441     // This depends in part on whether or not there was a classification.
442     if ("" == classification) {
443         // The current result ends with a colon and a space.
444         if ((index + message_index) <= 72) {
445             // The first line of the message can still fit. No need to do anything.
446         } else {
447             // Start indenting immediately, do not add anything else to the current line.
448             indent_needed = true;
449         }
450     } else {
451         // We may want a comma and a space after the classification
452         if ((index + 2 + message_index) <= 72) {
453             result += ", ";
454         } else {
455             indent_needed = true;
456         }
457     }
458
459     // Now we can process the message one character at a time, adding
460     // newlines and indentation just in time.
461     for (message_index = 0; message_index < message_len; message_index++) {
462         if (indent_needed) {
463             result += "\n    ";
464             indent_needed = false;
465         }
466
467         if ('\n' == message[message_index]) {
468             indent_needed = true;
469         } else {
470             result += message[message_index];
471         }
472     }
473
474     CYG_REPORT_RETURN();
475     return result;
476 }
477
478 //}}}
479 //{{{  Error count tracking                     
480
481 // Keep track of the number of errors that have occurred while doing some
482 // parsing. This functionality is not provided directly by the CdlInterpreter
483 // class, instead it is implemented using assoc data.
484
485 static const char       error_count_key[]       = "CdlErrorCount";
486
487 static void
488 error_count_delproc(ClientData data, Tcl_Interp* interp)
489 {
490     CYG_REPORT_FUNCNAME("CdlParse::error_count_delproc");
491     int* newed_ptr = static_cast<int*>(data);
492     delete newed_ptr;
493     CYG_REPORT_RETURN();
494 }
495
496 void
497 CdlParse::clear_error_count(CdlInterpreter interp)
498 {
499     CYG_REPORT_FUNCNAME("CdlParse::clear_error_count");
500     CYG_REPORT_FUNCARG1("interp %p", interp);
501     CYG_PRECONDITION_CLASSC(interp);
502
503     int*        newed_ptr = static_cast<int*>(interp->get_assoc_data(error_count_key));
504     if (0 != newed_ptr) {
505         *newed_ptr = 0;
506     }
507
508     CYG_REPORT_RETURN();
509 }
510
511 void
512 CdlParse::incr_error_count(CdlInterpreter interp, int how_much)
513 {
514     CYG_REPORT_FUNCNAME("CdlParse::incr_error_counter");
515     CYG_REPORT_FUNCARG2("interp %p, how_much %d", interp, how_much);
516     CYG_PRECONDITION_CLASSC(interp);
517     CYG_PRECONDITION(how_much > 0, "previous errors cannot be undone");
518     
519     int* newed_ptr = static_cast<int*>(interp->get_assoc_data(error_count_key));
520     if (0 == newed_ptr) {
521         newed_ptr = new int(how_much);
522         interp->set_assoc_data(error_count_key, static_cast<void*>(newed_ptr), &error_count_delproc);
523     } else {
524         CYG_ASSERT((*newed_ptr + how_much) > *newed_ptr, "number of parsing errors should not overflow");
525         *newed_ptr += how_much;
526     }
527
528     CYG_REPORT_RETURN();
529 }
530
531 int
532 CdlParse::get_error_count(CdlInterpreter interp)
533 {
534     CYG_REPORT_FUNCNAMETYPE("CdlParse::get_error_count", "count %d");
535     CYG_REPORT_FUNCARG1("interp %p", interp);
536     CYG_PRECONDITION_CLASSC(interp);
537
538     int result = 0;
539     int* newed_ptr = static_cast<int*>(interp->get_assoc_data(error_count_key));
540     if (0 != newed_ptr) {
541         result = *newed_ptr;
542     }
543
544     CYG_REPORT_RETVAL(result);
545     return result;
546 }
547
548 //}}}
549 //{{{  Error and warning reporting              
550
551 // Report errors and warnings. These will be called during parsing
552 // operations, both of CDL and similar data scripts and for savefiles.
553 // The parsing involves running a Tcl interpreter extended with the
554 // appropriate set of commands. Typically the call graph will look
555 // something like this:
556 //
557 //     libcdl C++ code such as load_package()
558 //     libcdl CdlInterpreter::eval()
559 //     Tcl interpreter
560 //     libcdl parsing code
561 //     report_error()
562 //     
563 // If the Tcl script is invalid then parsing errors may get reported
564 // at the higher level code as well.
565 //
566 // There are two classes of diagnostic: errors and warnings.
567 // Additional levels may be added in future, but there does not seem
568 // to be an urgent need for them. Client code should provide callback
569 // functions so that the messages can be displayed to the user, and
570 // these callbacks will be registered with the current CdlInterpreter.
571 //
572 // If no error callback is defined then a ParseException will be
573 // raised instead, and the rest of the current script will not be
574 // processed. Alternatively the error callback itself can raise a
575 // ParseException. Care is taken to ensure that the exception does not
576 // go straight through the Tcl interpreter, since that would prevent
577 // the Tcl code from cleaning up appropriately. If no exception is
578 // raised then the library keeps track of the number of errors, and
579 // this information is accessible once the script has been fully
580 // processed. This allows multiple errors to be reported in a single
581 // run.
582 //
583 // If no warning callback is provided then warnings are ignored.
584
585 void
586 CdlParse::report_error(CdlInterpreter interp, std::string sub_id, std::string message)
587 {
588     CYG_REPORT_FUNCNAME("CdlParse::report_error");
589     CYG_REPORT_FUNCARG1("interp %p", interp);
590     CYG_PRECONDITION_CLASSC(interp);
591
592     incr_error_count(interp);
593
594     std::string full_message = construct_diagnostic(interp, "error", sub_id, message);
595
596     // Now, either invoke the callback if it is provided, or throw the exception.
597     CdlDiagnosticFnPtr fn = interp->get_error_fn_ptr();
598     if (0 == fn) {
599         throw CdlParseException(full_message);
600     } else {
601         (*fn)(full_message);
602     }
603     
604     CYG_REPORT_RETURN();
605 }
606
607 void
608 CdlParse::report_warning(CdlInterpreter interp, std::string sub_id, std::string message)
609 {
610     CYG_REPORT_FUNCNAME("CdlParse::report_warning");
611     CYG_REPORT_FUNCARG1("interp %p", interp);
612     CYG_PRECONDITION_CLASSC(interp);
613
614     // If there is no warning callback, do nothing. This is really a
615     // bug in the calling application.
616     CdlDiagnosticFnPtr fn = interp->get_warning_fn_ptr();
617     if (0 != fn) {
618         std::string full_message = construct_diagnostic(interp, "warning", sub_id, message);
619         (*fn)(full_message);
620     }
621
622     CYG_REPORT_RETURN();
623 }
624
625 //}}}
626 //{{{  The "unknown" command                    
627
628 // ----------------------------------------------------------------------------
629 // This routine should be installed in interpreters that get used for
630 // parsing CDL scripts. It gets invoked when the CDL script contains
631 // an unrecognised command, e.g. because of a typo, and makes sure that
632 // the usual diagnostics process is observed.
633 //
634 // This routine should be uninstalled after the parsing is complete,
635 // to avoid e.g. a ParseException when it is not expected.
636 int
637 CdlParse::unknown_command(CdlInterpreter interp, int argc, const char* argv[])
638 {
639     CYG_REPORT_FUNCNAME("CdlParse::unknown_command");
640     CYG_REPORT_FUNCARG3XV(interp, argc, argv);
641     CYG_PRECONDITIONC(2 <= argc);
642     CYG_PRECONDITION_CLASSC(interp);
643
644     report_error(interp, "", std::string("Unknown command `") + argv[1] + "'.");
645     CYG_UNUSED_PARAM(int, argc);
646     
647     return TCL_OK;
648 }
649
650 //}}}
651
652 //}}}
653 //{{{  Property-related parser utilities        
654
655 // ----------------------------------------------------------------------------
656 // Utilities related to parsing properties, rather than more general parsing.
657
658 // A variant of report_parse_error() which also adds the property prefix.
659 void
660 CdlParse::report_property_parse_error(CdlInterpreter interp, std::string argv0, std::string msg)
661 {
662     CYG_REPORT_FUNCNAME("CdlPase::report_property_parse_error");
663
664     incr_error_count(interp);
665     
666     std::string diag = construct_diagnostic(interp, "error",
667                                             std::string("property ") + CdlParse::get_tcl_cmd_name(argv0),
668                                             msg);
669
670     // Now, either invoke the callback if it is provided, or throw the exception.
671     CdlDiagnosticFnPtr fn = interp->get_error_fn_ptr();
672     if (0 == fn) {
673         throw CdlParseException(diag);
674     } else {
675         (*fn)(diag);
676     }
677     
678     CYG_REPORT_RETURN();
679 }
680
681 void
682 CdlParse::report_property_parse_error(CdlInterpreter interp, CdlProperty prop, std::string msg)
683 {
684     CYG_REPORT_FUNCNAME("CdlParse::report_property_parse_error");
685     report_property_parse_error(interp, (prop->get_argv())[0], msg);
686     CYG_REPORT_RETURN();
687 }
688
689 // Repeat for warnings
690 void
691 CdlParse::report_property_parse_warning(CdlInterpreter interp, std::string argv0, std::string msg)
692 {
693     CYG_REPORT_FUNCNAME("CdlPase::report_property_parse_warning");
694
695     CdlDiagnosticFnPtr fn = interp->get_error_fn_ptr();
696     if (0 != fn) {
697         std::string diag = construct_diagnostic(interp, "error",
698                                                 std::string("property ") + CdlParse::get_tcl_cmd_name(argv0),
699                                                 msg);
700         (*fn)(diag);
701     }
702     
703     CYG_REPORT_RETURN();
704 }
705
706 void
707 CdlParse::report_property_parse_warning(CdlInterpreter interp, CdlProperty prop, std::string msg)
708 {
709     CYG_REPORT_FUNCNAME("CdlParse::report_property_parse_warning");
710     report_property_parse_warning(interp, (prop->get_argv())[0], msg);
711     CYG_REPORT_RETURN();
712 }
713
714 //}}}
715 //{{{  Generic property parsers                 
716
717 // ----------------------------------------------------------------------------
718 // Generic parsers
719 //
720 // These routines provide some more generic property parsing routines. argv[0]
721 // generally provides sufficient information to allow for sensible error messages.
722 // The command-specific parsers have to provide a property name. In addition it is
723 // possible to provide a function to handle per-command options, and another
724 // function that performs a final sanity check before the property gets added
725 // to the current entity.
726
727 //{{{  parse_minimal_property()         
728
729 // ----------------------------------------------------------------------------
730 // A minimal property takes no arguments.
731
732 int
733 CdlParse::parse_minimal_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
734                                  char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_Minimal))
735 {
736     CYG_REPORT_FUNCNAME("parse_minimal_property");
737     CYG_PRECONDITION_CLASSC(interp);
738     
739     CdlProperty_Minimal new_property = 0;
740     try {
741         std::vector<std::pair<std::string,std::string> > options;
742         int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
743         
744         if (data_index < argc) {
745             CdlParse::report_property_parse_error(interp, argv[0], std::string("Unexpected data `") + argv[data_index] + "'.");
746         } else {
747         
748             // The command is valid, turn it into a property.
749             // The property has been parsed successfully. Add it to the current node
750             CdlNode current_node = interp->get_node();
751             CYG_ASSERTC(0 != current_node);
752             new_property = CdlProperty_MinimalBody::make(current_node, name, argc, argv, options);
753             if (0 != final_parser) {
754                 (*final_parser)(interp, new_property);
755             }
756         }
757     } catch(...) {
758         
759         if (0 != new_property) {
760             delete new_property;
761         }
762         throw;
763     }
764     
765     return TCL_OK;
766 }
767
768 //}}}
769 //{{{  parse_string_property()          
770
771 // ----------------------------------------------------------------------------
772
773 int
774 CdlParse::parse_string_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
775                                 char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_String))
776 {
777     CYG_REPORT_FUNCNAME("parse_string_property");
778     CYG_PRECONDITION_CLASSC(interp);
779     
780     CdlProperty_String new_property = 0;
781     
782     try {
783         std::vector<std::pair<std::string,std::string> > options;
784         int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
785
786         if (data_index == argc) {
787             CdlParse::report_property_parse_error(interp, argv[0], "Missing argument.");
788         } else if ((data_index + 1) < argc) {
789             CdlParse::report_property_parse_error(interp, argv[0], std::string("Too many arguments, expecting just one."));
790         } else {
791         
792             CdlNode current_node = interp->get_node();
793             CYG_ASSERTC(0 != current_node);
794             new_property = CdlProperty_StringBody::make(current_node, name, argv[data_index], argc, argv, options);
795             if (0 != final_parser) {
796                 (*final_parser)(interp, new_property);
797             }
798         }
799     } catch(...) {
800         if (0 != new_property) {
801             delete new_property;
802         }
803         throw;
804     }
805
806     CYG_REPORT_RETURN();
807     return TCL_OK;
808 }
809
810 //}}}
811 //{{{  parse_tclcode_property()         
812
813 // ----------------------------------------------------------------------------
814
815 int
816 CdlParse::parse_tclcode_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
817                                  char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_TclCode))
818 {
819     CYG_REPORT_FUNCNAME("parse_tclcode_property");
820     CYG_PRECONDITION_CLASSC(interp);
821     
822     CdlProperty_TclCode new_property = 0;
823     try {
824         std::vector<std::pair<std::string,std::string> > options;
825         int data_index      = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
826         
827         if (data_index == argc) {
828             CdlParse::report_property_parse_error(interp, argv[0], "Missing Tcl code.");
829         } else if ((data_index + 1) < argc) {
830             CdlParse::report_property_parse_error(interp, argv[0], std::string("Invalid number of arguments.\n") +
831                                          "Expecting one argument, a Tcl code fragment.");
832         } else if (!Tcl_CommandComplete(CDL_TCL_CONST_CAST(char*, argv[data_index]))) {
833             CdlParse::report_property_parse_error(interp, argv[0], "Incomplete Tcl code fragment.");
834         } else {
835         
836             CdlNode current_node = interp->get_node();
837             CYG_ASSERTC(0 != current_node);
838             new_property = CdlProperty_TclCodeBody::make(current_node, name, argv[data_index], argc, argv, options);
839             if (0 != final_parser) {
840                 (*final_parser)(interp, new_property);
841             }
842         }
843      } catch(...) {
844         if (0 != new_property) {
845             delete new_property;
846         }
847         throw;
848     }
849     
850     return TCL_OK;
851 }
852
853 //}}}
854 //{{{  parse_stringvector_property()    
855
856 // ----------------------------------------------------------------------------
857
858 int
859 CdlParse::parse_stringvector_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
860                                       char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_StringVector),
861                                       bool allow_empty)
862 {
863     CYG_REPORT_FUNCNAME("parse_tclcode_property");
864     CYG_PRECONDITION_CLASSC(interp);
865     
866     CdlProperty_StringVector new_property = 0;
867     try {
868         std::vector<std::pair<std::string,std::string> > options;
869         int data_index      = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
870         
871         if (!allow_empty && (data_index == argc)) {
872             CdlParse::report_property_parse_error(interp, argv[0], "Missing arguments.");
873         } else {
874
875             // Creating the property requires a vector of strings.
876             std::vector<std::string>  strings;
877             for ( ; data_index < argc; data_index++) {
878                 strings.push_back(argv[data_index]);
879             }
880             CdlNode current_node = interp->get_node();
881             CYG_ASSERTC(0 != current_node);
882             new_property = CdlProperty_StringVectorBody::make(current_node, name, strings, argc, argv, options);
883             if (0 != final_parser) {
884                 (*final_parser)(interp, new_property);
885             }
886         }
887     } catch(...) {
888
889         if (0 != new_property) {
890             delete new_property;
891         }
892         throw;
893     }
894     
895     return TCL_OK;
896 }
897
898 //}}}
899 //{{{  parse_reference_property()       
900
901 // ----------------------------------------------------------------------------
902
903 int
904 CdlParse::parse_reference_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
905                                    char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_Reference),
906                                    bool allow_empty, CdlUpdateHandler update_handler)
907 {
908     CYG_REPORT_FUNCNAME("parse_reference_property");
909     CYG_PRECONDITION_CLASSC(interp);
910     
911     CdlProperty_Reference new_property = 0;
912     try {
913         std::vector<std::pair<std::string,std::string> > options;
914         int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
915         
916         if (data_index == argc) {
917             CdlParse::report_property_parse_error(interp, argv[0], "Missing argument.");
918         } else if ((data_index + 1) < argc) {
919             CdlParse::report_property_parse_error(interp, argv[0], "Too many arguments, expecting just one.");
920         } else {
921             std::string refname = argv[data_index];
922             if (!(Cdl::is_valid_cdl_name(refname) || (allow_empty && ("" == refname)))) {
923                 CdlParse::report_property_parse_error(interp, argv[0], "`" + refname + "' is not a valid CDL name");
924             } else {
925                 CdlNode current_node = interp->get_node();
926                 CYG_ASSERTC(0 != current_node);
927                 new_property = CdlProperty_ReferenceBody::make(current_node, name, refname,
928                                                                update_handler, argc, argv, options);
929                 if (0 != final_parser) {
930                     (*final_parser)(interp, new_property);
931                 }
932             }
933         }
934     } catch(...) {
935         if (0 != new_property) {
936             delete new_property;
937         }
938         throw;
939     }
940     
941     return TCL_OK;
942 }
943
944 //}}}
945 //{{{  parse_expression_property()      
946
947 // ----------------------------------------------------------------------------
948
949 int
950 CdlParse::parse_expression_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
951                                     char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_Expression),
952                                     CdlUpdateHandler update_handler)
953 {
954     CYG_REPORT_FUNCNAME("parse_expression_property");
955     CYG_PRECONDITION_CLASSC(interp);
956     
957     CdlProperty_Expression new_property = 0;
958     CdlExpression expr = 0;
959     try {
960         std::vector<std::pair<std::string,std::string> > options;
961         int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
962         
963         std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index);
964         if ("" == all_args) {
965             CdlParse::report_property_parse_error(interp, argv[0], "Missing expression data.");
966         } else {
967         
968             // The CdlExpression class has its own parsing routine. This
969             // will raise an exception if there are any problems. It is
970             // desirable to catch the exception and report the error via
971             // the normal reporting mechanisms, which may allow parsing to
972             // continue.
973             try {
974                 expr = CdlExpressionBody::parse(all_args);
975             } catch(CdlParseException e) {
976                 CdlParse::report_property_parse_error(interp, argv[0], e.get_message());
977             }
978             if (0 != expr) {
979                 CdlNode current_node = interp->get_node();
980                 CYG_ASSERTC(0 != current_node);
981                 new_property = CdlProperty_ExpressionBody::make(current_node, name, expr, update_handler, argc, argv, options);
982                 if (0 != final_parser) {
983                     (*final_parser)(interp, new_property);
984                 }
985             }
986         }
987     } catch(...) {
988         if (0 != expr) {
989             delete expr;
990         }
991         if (0 != new_property) {
992             delete new_property;
993         }
994         throw;
995     }
996     
997     if (0 != expr) {
998         delete expr;
999     }
1000     return TCL_OK;
1001 }
1002
1003 //}}}
1004 //{{{  parse_list_expression_property() 
1005
1006 // ----------------------------------------------------------------------------
1007
1008 int
1009 CdlParse::parse_listexpression_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
1010                                         char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_ListExpression),
1011                                         CdlUpdateHandler update_handler)
1012 {
1013     CYG_REPORT_FUNCNAME("parse_list_expression_property");
1014     CYG_PRECONDITION_CLASSC(interp);
1015     
1016     CdlProperty_ListExpression new_property = 0;
1017     CdlListExpression expr = 0;
1018     try {
1019         std::vector<std::pair<std::string,std::string> > options;
1020         int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
1021
1022         std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index);
1023         if ("" == all_args) {
1024             CdlParse::report_property_parse_error(interp, argv[0], "Missing list expression data.");
1025         } else {
1026         
1027             try {
1028                 expr = CdlListExpressionBody::parse(all_args);
1029             } catch(CdlParseException e) {
1030                 CdlParse::report_property_parse_error(interp, argv[0], e.get_message());
1031             }
1032             if (0 != expr) {
1033                 CdlNode current_node = interp->get_node();
1034                 CYG_ASSERTC(0 != current_node);
1035                 new_property = CdlProperty_ListExpressionBody::make(current_node, name, expr, update_handler,
1036                                                                     argc, argv, options);
1037                 if (0 != final_parser) {
1038                     (*final_parser)(interp, new_property);
1039                 }
1040             }
1041         }
1042     } catch(...) {
1043         if (0 != expr) {
1044             delete expr;
1045         }
1046         if (0 != new_property) {
1047             delete new_property;
1048         }
1049         throw;
1050     }
1051     if (0 != expr) {
1052         delete expr;
1053     }
1054     return TCL_OK;
1055 }
1056
1057 //}}}
1058 //{{{  parse_goalexpression_property()  
1059
1060 // ----------------------------------------------------------------------------
1061
1062 int
1063 CdlParse::parse_goalexpression_property(CdlInterpreter interp, int argc, const char* argv[], std::string name,
1064                                         char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_GoalExpression),
1065                                         CdlUpdateHandler update_handler)
1066 {
1067     CYG_REPORT_FUNCNAMETYPE("parse_goal_expression_property", "result %d");
1068     CYG_PRECONDITION_CLASSC(interp);
1069     
1070     CdlProperty_GoalExpression new_property = 0;
1071     CdlGoalExpression expr = 0;
1072     try {
1073         std::vector<std::pair<std::string,std::string> > options;
1074         int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options);
1075
1076         std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index);
1077         if ("" == all_args) {
1078             CdlParse::report_property_parse_error(interp, argv[0], "Missing goal expression data.");
1079         } else {
1080
1081             try {
1082                 expr = CdlGoalExpressionBody::parse(all_args);
1083             } catch(CdlParseException e) {
1084                 CdlParse::report_property_parse_error(interp, argv[0], e.get_message());
1085             }
1086             if (0 != expr) {
1087                 CdlNode current_node = interp->get_node();
1088                 CYG_ASSERTC(0 != current_node);
1089                 new_property = CdlProperty_GoalExpressionBody::make(current_node, name, expr, update_handler,
1090                                                                     argc, argv, options);
1091                 if (0 != final_parser) {
1092                     (*final_parser)(interp, new_property);
1093                 }
1094             }
1095         }
1096     } catch(...) {
1097
1098         if (0 != expr) {
1099             delete expr;
1100         }
1101         if (0 != new_property) {
1102             delete new_property;
1103         }
1104         throw;
1105     }
1106     if (0 != expr) {
1107         delete expr;
1108     }
1109
1110     return TCL_OK;
1111 }
1112
1113 //}}}
1114
1115 //}}}