OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [tools/] [src/] [libcdl/] [parse.cxx] - Blame information for rev 609

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 26 unneback
//{{{  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
//}}}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.