Mercurial > flash_v2
diff host/libcdl/parse.cxx @ 82:6736c52df507 ecos-sw-2000-04-14
Merge from eCos master repository on 2000-04-14-13:35:46-BST
| author | jlarmour |
|---|---|
| date | Tue, 18 Apr 2000 21:51:55 +0000 |
| parents | 435cced73e2f |
| children | 6ed91473a1cd |
line wrap: on
line diff
--- a/host/libcdl/parse.cxx +++ b/host/libcdl/parse.cxx @@ -112,14 +112,17 @@ static std::string property_string = "pr // Some of the properties have aliases in the CDL data, so argv[0] has to be // used to work out what is actually being parsed. However the Tcl interpreter // may prefix the command name with :: to indicate the global namespace. -const char* -CdlParse::get_tcl_cmd_name(const char* name) +std::string +CdlParse::get_tcl_cmd_name(std::string name) { + std::string result; + if ((name[0] == ':') && (name[1] == ':')) { - return &(name[2]); + result = std::string(name, 2, name.size() - 2); } else { - return name; + result = name; } + return result; } // Given a list of arguments, concatenate them together into a C++ string. @@ -252,7 +255,7 @@ CdlParse::parse_options(CdlInterpreter i if ("" == name) { // One of "-", "-=xxx", or "--=x" - CdlParse::report_warning(interp, diag_prefix + ", invalid option string " + argv[index]); + CdlParse::report_warning(interp, diag_prefix, std::string("Invalid option string `") + argv[index] + "'."); } // Do not try to extract the value unless we are sure there @@ -270,9 +273,10 @@ CdlParse::parse_options(CdlInterpreter i for (i = 0; 0 != options[i]; i++) { if (0 == strncmp(name.c_str(), options[i], name.size())) { if (-1 != opt_index) { - CdlParse::report_warning(interp, diag_prefix + ", ambiguous option name " + name + - ", it can match " + get_option_string(options[opt_index]) + - " or " + get_option_string(options[i])); + CdlParse::report_warning(interp, diag_prefix, + std::string("Ambiguous option name `") + name + "'.\n" + + "It can match `" + get_option_string(options[opt_index]) + "'\n" + + "or `" + get_option_string(options[i]) + "'."); index++; break; } else { @@ -282,7 +286,7 @@ CdlParse::parse_options(CdlInterpreter i } } if (-1 == opt_index) { - CdlParse::report_warning(interp, diag_prefix + ", invalid option " + name); + CdlParse::report_warning(interp, diag_prefix, std::string("Invalid option `") + name + "'."); index++; break; } @@ -315,7 +319,7 @@ CdlParse::parse_options(CdlInterpreter i // There should not be a value. If the current argument is of the // form x=y then this is an error. if ('=' == *arg_ptr) { - CdlParse::report_warning(interp, diag_prefix + ", option " + name + " does not take any data"); + CdlParse::report_warning(interp, diag_prefix, std::string("Option `") + name + "' does not take any data."); } // Leave index pointing at the next argument to be processed. index++; @@ -323,7 +327,7 @@ CdlParse::parse_options(CdlInterpreter i if ('=' == *arg_ptr) { value = std::string(++arg_ptr); } else if (++index == argc) { - CdlParse::report_warning(interp, diag_prefix + ", missing data for option " + name); + CdlParse::report_warning(interp, diag_prefix, std::string("Missing data for option `") + name + "'."); } else { value = argv[index]; } @@ -337,8 +341,7 @@ CdlParse::parse_options(CdlInterpreter i if (!multiple_flag) { for (i = 0; i < result.size(); i++) { if (name == result[i].first) { - CdlParse::report_warning(interp, diag_prefix + ", option " + name + - " can only be used once."); + CdlParse::report_warning(interp, diag_prefix, std::string("Option `") + name + "' can only be used once."); break; } } @@ -353,25 +356,121 @@ CdlParse::parse_options(CdlInterpreter i } //}}} -//{{{ Diagnostic prefix +//{{{ Diagnostic construction -// Construct a suitable prefix for any warning or error message. This -// should include the filename and the entity name. +// Construct a suitable diagnostic for a parsing error. This may occur +// when reading in a CDL script, a savefile, a database, or anything +// similar. +// +// A diagnostic should take the following form: +// +// <context> <linenumber> [, <node identifier>] [, <extra identifier>] : [<classification>, ] <message> +// +// The context should be set in the Tcl interpreter. Typically it +// will be a filename. +// +// In practice generating the line number is not really feasible at +// present, the Tcl interpreter does not keep track of sufficient +// information. At least, not in the public data structures, there is +// a termOffset field in the internal data structures which might +// be used to do the right thing. I do not want to start relying +// on Tcl internals just yet, or add support to the Tcl core for +// keeping track of line numbers. // -// Obviously a line number would be rather useful as well, but this is not -// very easy because of the way Tcl interpreters work. +// For many data files there will the concept of a current node, +// e.g. an option whose properties or savefile information are +// being processed. The CdlInterpreter class keeps track of the +// current node, so if it is defined then the node's class and +// name can be part of the message. This happens automatically, +// no effort is required on the part of calling code. +// +// There may also be additional information, for example +// identifying the specific property where the error was detected. +// This is handled by an extra argument. +// +// The classification is likely to be something like "warning", +// "error", or "internal error". It is controlled by the calling +// code, but typically it is provided by calling via report_warning() +// etc. +// +// The message should identify the actual error. It should be +// a proper sentence, i.e. begin with a capital error and end with +// a full stop, unless the last word is an identifier or filename +// or something similarly special in which case the trailing +// dot will be discarded. The message should not end with a +// newline character, and the result string will not end with one +// either. That is left to higher level code. std::string -CdlParse::get_diagnostic_prefix(CdlInterpreter interp) +CdlParse::construct_diagnostic(CdlInterpreter interp, std::string classification, std::string sub_id, std::string message) { - std::string filename = interp->get_filename(); - CdlNode current_node = interp->get_node(); + CYG_REPORT_FUNCNAME("CdlParse::construct_diagnostic"); + CYG_PRECONDITION_CLASSC(interp); + + std::string context = interp->get_context(); + CdlNode current_node = interp->get_node(); - std::string result = ("" != filename) ? filename : "<unknown data source>"; + std::string result; + if ("" == context) { + result = "<unknown context>"; + } else { + result = context; + } if (0 != current_node) { result += ", " + current_node->get_class_name() + " " + current_node->get_name(); } - result += "\n "; + if ("" != sub_id) { + result += ", " + sub_id; + } + result += ": " + classification; + + // Now it is time to start worrying about layout, indenting + // subsequent lines, and so on. + int index = result.length(); + int message_len = message.length(); + int message_index; + bool indent_needed = false; + + // Find out how many characters there are in the message up to the first newline + for (message_index = 0; (message_index < message_len) && ('\n' != message[message_index]); message_index++) { + ; + } + + // Should the message start on the next line, suitably indented? + // This depends in part on whether or not there was a classification. + if ("" == classification) { + // The current result ends with a colon and a space. + if ((index + message_index) <= 72) { + // The first line of the message can still fit. No need to do anything. + } else { + // Start indenting immediately, do not add anything else to the current line. + indent_needed = true; + } + } else { + // We may want a comma and a space after the classification + if ((index + 2 + message_index) <= 72) { + result += ", "; + } else { + indent_needed = true; + } + } + + // Now we can process the message one character at a time, adding + // newlines and indentation just in time. + for (message_index = 0; message_index < message_len; message_index++) { + if (indent_needed) { + result += "\n "; + indent_needed = false; + } + + if ('\n' == message[message_index]) { + indent_needed = true; + } else { + result += message[message_index]; + } + } + + CYG_REPORT_RETURN(); return result; } @@ -448,47 +547,76 @@ CdlParse::get_error_count(CdlInterpreter //}}} //{{{ Error and warning reporting -// Report an error or warning. This involves adding a suitable prefix -// and invoking the reporting callback currently associated with the -// interpreter. For errors it is also necessary to increment the error -// counter so that later count can detect the number of errors that -// have occurred. +// Report errors and warnings. These will be called during parsing +// operations, both of CDL and similar data scripts and for savefiles. +// The parsing involves running a Tcl interpreter extended with the +// appropriate set of commands. Typically the call graph will look +// something like this: +// +// libcdl C++ code such as load_package() +// libcdl CdlInterpreter::eval() +// Tcl interpreter +// libcdl parsing code +// report_error() +// +// If the Tcl script is invalid then parsing errors may get reported +// at the higher level code as well. // -// The error callback is allowed to raise a CdlParseException. This should -// not be caught here. Instead this exception is caught in every parse -// routine, before it can go back through the Tcl interpreter. +// There are two classes of diagnostic: errors and warnings. +// Additional levels may be added in future, but there does not seem +// to be an urgent need for them. Client code should provide callback +// functions so that the messages can be displayed to the user, and +// these callbacks will be registered with the current CdlInterpreter. // -// FIXME: cope with prefixing multiline error messages. +// If no error callback is defined then a ParseException will be +// raised instead, and the rest of the current script will not be +// processed. Alternatively the error callback itself can raise a +// ParseException. Care is taken to ensure that the exception does not +// go straight through the Tcl interpreter, since that would prevent +// the Tcl code from cleaning up appropriately. If no exception is +// raised then the library keeps track of the number of errors, and +// this information is accessible once the script has been fully +// processed. This allows multiple errors to be reported in a single +// run. +// +// If no warning callback is provided then warnings are ignored. void -CdlParse::report_error(CdlInterpreter interp, std::string message) +CdlParse::report_error(CdlInterpreter interp, std::string sub_id, std::string message) + throw(CdlParseException, std::bad_alloc) { CYG_REPORT_FUNCNAME("CdlParse::report_error"); CYG_REPORT_FUNCARG1("interp %p", interp); CYG_PRECONDITION_CLASSC(interp); incr_error_count(interp); - - message = get_diagnostic_prefix(interp) + message; + std::string full_message = construct_diagnostic(interp, "error", sub_id, message); + + // Now, either invoke the callback if it is provided, or throw the exception. CdlDiagnosticFnPtr fn = interp->get_error_fn_ptr(); - CYG_ASSERT(0 != fn, "during parsing an interpreter should have an associated error reporting function"); - (*fn)(message); - + if (0 == fn) { + throw CdlParseException(full_message); + } else { + (*fn)(full_message); + } + CYG_REPORT_RETURN(); } void -CdlParse::report_warning(CdlInterpreter interp, std::string message) +CdlParse::report_warning(CdlInterpreter interp, std::string sub_id, std::string message) { CYG_REPORT_FUNCNAME("CdlParse::report_warning"); CYG_REPORT_FUNCARG1("interp %p", interp); CYG_PRECONDITION_CLASSC(interp); - - message = get_diagnostic_prefix(interp) + message; + + // If there is no warning callback, do nothing. This is really a + // bug in the calling application. CdlDiagnosticFnPtr fn = interp->get_warning_fn_ptr(); if (0 != fn) { - (*fn)(message); + std::string full_message = construct_diagnostic(interp, "warning", sub_id, message); + (*fn)(full_message); } CYG_REPORT_RETURN(); @@ -512,8 +640,8 @@ CdlParse::unknown_command(CdlInterpreter CYG_REPORT_FUNCARG3XV(interp, argc, argv); CYG_PRECONDITIONC(2 <= argc); CYG_PRECONDITION_CLASSC(interp); - - report_error(interp, std::string("Unknown command `") + argv[1] + "'."); + + report_error(interp, "", std::string("Unknown command `") + argv[1] + "'."); CYG_UNUSED_PARAM(int, argc); return TCL_OK; @@ -527,49 +655,61 @@ CdlParse::unknown_command(CdlInterpreter // ---------------------------------------------------------------------------- // Utilities related to parsing properties, rather than more general parsing. -// Provide a prefix that matches the current property. -std::string -CdlParse::get_property_prefix(char* argv0) -{ - CYG_REPORT_FUNCNAME("CdlParse::get_property_prefix"); - - std::string result = std::string("Property " ) + CdlParse::get_tcl_cmd_name(argv0) + ", "; - - CYG_REPORT_RETURN(); - return result; -} - -std::string -CdlParse::get_property_prefix(CdlProperty prop) -{ - CYG_REPORT_FUNCNAME("CdlParse::get_property_prefix"); - - std::string result = std::string("Property "); - const std::vector<std::string>& argv = prop->get_argv(); - result = result + argv[0] + ", "; - - CYG_REPORT_RETURN(); - return result; -} - // A variant of report_parse_error() which also adds the property prefix. void -CdlParse::report_property_parse_error(CdlInterpreter interp, char* argv0, std::string msg) +CdlParse::report_property_parse_error(CdlInterpreter interp, std::string argv0, std::string msg) + throw(CdlParseException, std::bad_alloc) { CYG_REPORT_FUNCNAME("CdlPase::report_property_parse_error"); - report_error(interp, get_property_prefix(argv0) + msg); + incr_error_count(interp); + + std::string diag = construct_diagnostic(interp, "error", + std::string("property ") + CdlParse::get_tcl_cmd_name(argv0), + msg); + + // Now, either invoke the callback if it is provided, or throw the exception. + CdlDiagnosticFnPtr fn = interp->get_error_fn_ptr(); + if (0 == fn) { + throw CdlParseException(diag); + } else { + (*fn)(diag); + } CYG_REPORT_RETURN(); } void CdlParse::report_property_parse_error(CdlInterpreter interp, CdlProperty prop, std::string msg) + throw(CdlParseException, std::bad_alloc) { CYG_REPORT_FUNCNAME("CdlParse::report_property_parse_error"); + report_property_parse_error(interp, (prop->get_argv())[0], msg); + CYG_REPORT_RETURN(); +} - report_error(interp, get_property_prefix(prop) + msg); +// Repeat for warnings +void +CdlParse::report_property_parse_warning(CdlInterpreter interp, std::string argv0, std::string msg) +{ + CYG_REPORT_FUNCNAME("CdlPase::report_property_parse_warning"); + CdlDiagnosticFnPtr fn = interp->get_error_fn_ptr(); + if (0 != fn) { + std::string diag = construct_diagnostic(interp, "error", + std::string("property ") + CdlParse::get_tcl_cmd_name(argv0), + msg); + (*fn)(diag); + } + + CYG_REPORT_RETURN(); +} + +void +CdlParse::report_property_parse_warning(CdlInterpreter interp, CdlProperty prop, std::string msg) +{ + CYG_REPORT_FUNCNAME("CdlParse::report_property_parse_warning"); + report_property_parse_warning(interp, (prop->get_argv())[0], msg); CYG_REPORT_RETURN(); } @@ -604,7 +744,7 @@ CdlParse::parse_minimal_property(CdlInte int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options); if (data_index < argc) { - CdlParse::report_property_parse_error(interp, argv[0], std::string("Unexpected data ") + argv[data_index]); + CdlParse::report_property_parse_error(interp, argv[0], std::string("Unexpected data `") + argv[data_index] + "'."); } else { // The command is valid, turn it into a property. @@ -646,7 +786,7 @@ CdlParse::parse_string_property(CdlInter int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options); if (data_index == argc) { - CdlParse::report_property_parse_error(interp, argv[0], "missing argument."); + CdlParse::report_property_parse_error(interp, argv[0], "Missing argument."); } else if ((data_index + 1) < argc) { CdlParse::report_property_parse_error(interp, argv[0], std::string("Too many arguments, expecting just one.")); } else { @@ -686,12 +826,12 @@ CdlParse::parse_tclcode_property(CdlInte int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options); if (data_index == argc) { - CdlParse::report_property_parse_error(interp, argv[0], "missing Tcl code."); + CdlParse::report_property_parse_error(interp, argv[0], "Missing Tcl code."); } else if ((data_index + 1) < argc) { CdlParse::report_property_parse_error(interp, argv[0], std::string("Invalid number of arguments.\n") + - " Expecting one argument, a Tcl code fragment."); + "Expecting one argument, a Tcl code fragment."); } else if (!Tcl_CommandComplete(argv[data_index])) { - CdlParse::report_property_parse_error(interp, argv[0], "incomplete Tcl code fragment."); + CdlParse::report_property_parse_error(interp, argv[0], "Incomplete Tcl code fragment."); } else { CdlNode current_node = interp->get_node(); @@ -718,7 +858,8 @@ CdlParse::parse_tclcode_property(CdlInte int CdlParse::parse_stringvector_property(CdlInterpreter interp, int argc, char** argv, std::string name, - char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_StringVector)) + char** options_desc, void (*final_parser)(CdlInterpreter, CdlProperty_StringVector), + bool allow_empty) { CYG_REPORT_FUNCNAME("parse_tclcode_property"); CYG_PRECONDITION_CLASSC(interp); @@ -728,8 +869,8 @@ CdlParse::parse_stringvector_property(Cd std::vector<std::pair<std::string,std::string> > options; int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options); - if (data_index == argc) { - CdlParse::report_property_parse_error(interp, argv[0], "missing arguments."); + if (!allow_empty && (data_index == argc)) { + CdlParse::report_property_parse_error(interp, argv[0], "Missing arguments."); } else { // Creating the property requires a vector of strings. @@ -774,9 +915,9 @@ CdlParse::parse_reference_property(CdlIn int data_index = CdlParse::parse_options(interp, property_string + argv[0], options_desc, argc, argv, 1, options); if (data_index == argc) { - CdlParse::report_property_parse_error(interp, argv[0], "missing argument."); + CdlParse::report_property_parse_error(interp, argv[0], "Missing argument."); } else if ((data_index + 1) < argc) { - CdlParse::report_property_parse_error(interp, argv[0], "too many arguments, expecting just one."); + CdlParse::report_property_parse_error(interp, argv[0], "Too many arguments, expecting just one."); } else { std::string refname = argv[data_index]; if (!Cdl::is_valid_cdl_name(refname)) { @@ -822,7 +963,7 @@ CdlParse::parse_expression_property(CdlI std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index); if ("" == all_args) { - CdlParse::report_property_parse_error(interp, argv[0], "missing expression data."); + CdlParse::report_property_parse_error(interp, argv[0], "Missing expression data."); } else { // The CdlExpression class has its own parsing routine. This @@ -881,7 +1022,7 @@ CdlParse::parse_listexpression_property( std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index); if ("" == all_args) { - CdlParse::report_property_parse_error(interp, argv[0], "missing list expression data."); + CdlParse::report_property_parse_error(interp, argv[0], "Missing list expression data."); } else { try { @@ -935,7 +1076,7 @@ CdlParse::parse_goalexpression_property( std::string all_args = CdlParse::concatenate_argv(argc, argv, data_index); if ("" == all_args) { - CdlParse::report_property_parse_error(interp, argv[0], "missing goal expression data."); + CdlParse::report_property_parse_error(interp, argv[0], "Missing goal expression data."); } else { try {
