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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [host/] [libcdl/] [parse.cxx] - Blame information for rev 857

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

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

powered by: WebSVN 2.1.0

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