source: SVN/cambria/redboot/host/libcdl/parse.cxx @ 1

Last change on this file since 1 was 1, checked in by Tim Harvey, 2 years ago

restored latest version of files from server backup

Signed-off-by: Tim Harvey <tharvey@…>

File size: 40.6 KB
Line 
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#include <string.h>
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.
105static 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.
116std::string
117CdlParse::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().
132std::string
133CdlParse::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
212static std::string
213get_option_string(char* name)
214{
215    std::string result = "";
216    while ((*name != ':') && (*name != '\0')) {
217        result += *name++;
218    }
219    return result;
220}
221
222int
223CdlParse::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
405std::string
406CdlParse::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
485static const char       error_count_key[]       = "CdlErrorCount";
486
487static void
488error_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
496void
497CdlParse::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
511void
512CdlParse::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
531int
532CdlParse::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
585void
586CdlParse::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
607void
608CdlParse::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.
636int
637CdlParse::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.
659void
660CdlParse::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
681void
682CdlParse::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
690void
691CdlParse::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
706void
707CdlParse::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
732int
733CdlParse::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
773int
774CdlParse::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
815int
816CdlParse::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
858int
859CdlParse::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
903int
904CdlParse::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
949int
950CdlParse::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
1008int
1009CdlParse::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
1062int
1063CdlParse::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//}}}
Note: See TracBrowser for help on using the repository browser.