From f9c1820740bc6438795242249208d47f7916d726 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 4 Jun 2011 18:42:33 -0400 Subject: [PATCH] Argument Parsing - Added proper handling for sequences, in that they must be contiguous. - Added placeholder code for default command, and automatic info report. - Eliminated Context::parse. - Eliminated Context::run Timer, because when it goes out of scope, it adds timing messages to the deubg output, which at the end of Context::run has already been displayed. In addition, the Context::dispatch timer is about 0.2 milliseconds shorter, so the two are redundant. --- src/Arguments.cpp | 255 +++++++++++++++++++++++++++---------- src/Arguments.h | 7 +- src/Context.cpp | 251 +----------------------------------- src/commands/CmdModify.cpp | 2 +- src/main.cpp | 4 +- 5 files changed, 198 insertions(+), 321 deletions(-) diff --git a/src/Arguments.cpp b/src/Arguments.cpp index 02cc07432..90501d20f 100644 --- a/src/Arguments.cpp +++ b/src/Arguments.cpp @@ -124,7 +124,11 @@ void Arguments::append_stdin () // Scan all the arguments, and assign a category for each one. void Arguments::categorize () { - bool terminated = false; + bool terminated = false; + bool found_command = false; + bool found_sequence = false; + bool found_something_after_sequence = false; + bool found_non_sequence = false; // Generate a vector of command keywords against which autoComplete can run. std::vector keywords; @@ -132,96 +136,201 @@ void Arguments::categorize () for (k = context.commands.begin (); k != context.commands.end (); ++k) keywords.push_back (k->first); - // First scan for a command. + // Now categorize every argument. + std::string ignored; std::vector >::iterator arg; for (arg = this->begin (); arg != this->end (); ++arg) { - if (arg->first == "--") - break; +/* + std::cout << "# " << leftJustify (arg->first, 36) + << " found_command=" << (found_command ? "true " : "false") + << " found_sequence=" << (found_sequence ? "true " : "false") + << " found_something_after_sequence=" << (found_something_after_sequence ? "true " : "false") + << " found_non_sequence=" << (found_non_sequence ? "true " : "false") + << "\n"; +*/ - std::vector matches; - if (autoComplete (arg->first, keywords, matches) == 1) - { - if (arg->first != matches[0]) - context.debug ("Arguments::categorize keyword '" + arg->first + "' --> '" + matches[0] + "'"); - else - context.debug ("Arguments::categorize keyword '" + arg->first + "'"); - - // Not only categorize the command, but overwrite the original command - // with the full command name. - arg->first = matches[0]; - arg->second = "command"; - - // Only the first match is a command. - break; - } - } - - // Now categorize every uncategorized argument. - std::string ignored; - for (arg = this->begin (); arg != this->end (); ++arg) - { if (!terminated) { // Nothing after -- is to be interpreted in any way. if (arg->first == "--") { terminated = true; + found_non_sequence = true; + if (found_sequence) + found_something_after_sequence = true; + arg->second = "terminator"; } - else if (arg->second != "command") + // program + else if (arg == this->begin ()) { - // program - if (arg == this->begin ()) - arg->second = "program"; + arg->second = "program"; + } - // rc: - else if (arg->first.substr (0, 3) == "rc:") - arg->second = "rc"; + // command + else if (!found_command && + is_command (keywords, arg->first)) + { + found_command = true; + found_non_sequence = true; + if (found_sequence) + found_something_after_sequence = true; - // rc.[:=] - else if (arg->first.substr (0, 3) == "rc.") - arg->second = "override"; + arg->second = "command"; + } - // [+-]tag - else if (is_tag (arg->first)) - arg->second = "tag"; + // rc: + else if (arg->first.substr (0, 3) == "rc:") + { + found_non_sequence = true; + if (found_sequence) + found_something_after_sequence = true; - // /pattern/ - else if (is_pattern (arg->first)) - arg->second = "pattern"; + arg->second = "rc"; + } - // - // .[:=] - else if (is_attmod (arg->first)) - arg->second = "attmod"; + // rc.[:=] + else if (arg->first.substr (0, 3) == "rc.") + { + found_non_sequence = true; + if (found_sequence) + found_something_after_sequence = true; - // [:=] - else if (is_attr (arg->first)) - arg->second = "attribute"; + arg->second = "override"; + } - // [-][,...] - else if (is_id (arg->first)) - arg->second = "id"; + // [-][,...] + else if (!found_something_after_sequence && + is_id (arg->first)) + { + found_sequence = true; + arg->second = "id"; + } - // ///[g] - else if (is_subst (arg->first)) - arg->second = "substitution"; + // [,...] + else if (!found_something_after_sequence && + is_uuid (arg->first)) + { + found_sequence = true; + arg->second = "uuid"; + } - // [,...] - else if (is_uuid (arg->first)) - arg->second = "uuid"; + // [+-]tag + else if (is_tag (arg->first)) + { + found_non_sequence = true; + if (found_sequence) + found_something_after_sequence = true; - // If the type is not known, it is treated as a generic word. - else if (arg->second == "") - arg->second = "word"; + arg->second = "tag"; + } + + // + // .[:=] + else if (is_attmod (arg->first)) + { + found_non_sequence = true; + if (found_sequence) + found_something_after_sequence = true; + + arg->second = "attmod"; + } + + // [:=] + else if (is_attr (arg->first)) + { + found_non_sequence = true; + if (found_sequence) + found_something_after_sequence = true; + + arg->second = "attribute"; + } + + // ///[g] + else if (is_subst (arg->first)) + { + found_non_sequence = true; + if (found_sequence) + found_something_after_sequence = true; + + arg->second = "substitution"; + } + + // /pattern/ + else if (is_pattern (arg->first)) + { + found_non_sequence = true; + if (found_sequence) + found_something_after_sequence = true; + + arg->second = "pattern"; + } + + // If the type is not known, it is treated as a generic word. + else + { + found_non_sequence = true; + if (found_sequence) + found_something_after_sequence = true; + + arg->second = "word"; } } // All post-termination arguments are simply words. else + { + found_non_sequence = true; + if (found_sequence) + found_something_after_sequence = true; + arg->second = "word"; + } + } + + // If no command was specified, and there were no command line arguments + // then invoke the default command. + if (!found_command) + { + if (found_non_sequence) + { + // TODO Invoke the default command. +/* + // Apply overrides, if any. + std::string defaultCommand = config.get ("default.command"); + if (defaultCommand != "") + { + // Add on the overrides. + defaultCommand += " " + file_override + " " + var_overrides; + + // Stuff the command line. + args.clear (); + split (args, defaultCommand, ' '); + header ("[task " + trim (defaultCommand) + "]"); + + // Reinitialize the context and recurse. + file_override = ""; + var_overrides = ""; + footnotes.clear (); + //initialize (); + parse (args, cmd, task, sequence, subst, filter); + } + else + throw std::string (STRING_TRIVIAL_INPUT); +*/ + } + + // If the command "task 123" is entered, but with no modifier arguments, + // then the actual command is assumed to be "info". + else if (!found_non_sequence && + found_sequence) + { + // TODO Invoke the info command. +// std::cout << STRING_ASSUME_INFO << "\n"; +// parseCmd.command = "info"; + } } } @@ -398,6 +507,21 @@ bool Arguments::find_command (std::string& command) return false; } +//////////////////////////////////////////////////////////////////////////////// +bool Arguments::is_command ( + const std::vector & keywords, + std::string& command) +{ + std::vector matches; + if (autoComplete (command, keywords, matches) == 1) + { + command = matches[0]; + return true; + } + + return false; +} + //////////////////////////////////////////////////////////////////////////////// // ______________ // | | @@ -495,9 +619,12 @@ bool Arguments::is_subst (const std::string& input) // // bool Arguments::is_pattern (const std::string& input) { - if (input[0] == '/' && - input.length () > 2 && - input[input.length () - 1] == '/') + unsigned int length = input.length (); + + if (input[0] == '/' && + length > 2 && + input[length - 1] == '/' && + input.find ('/', 1) == length - 1) return true; return false; diff --git a/src/Arguments.h b/src/Arguments.h index 037ba740e..4cb246a2f 100644 --- a/src/Arguments.h +++ b/src/Arguments.h @@ -54,11 +54,8 @@ public: std::string combine (); bool find_command (std::string&); -/* - void extract_filter (); - void extract_modifications (); -*/ + bool is_command (const std::vector &, std::string&); bool is_attr (const std::string&); bool is_attmod (const std::string&); bool is_subst (const std::string&); @@ -76,6 +73,8 @@ public: bool extract_tag (const std::string&, char&, std::string&); /* + void extract_filter (); + void extract_modifications (); void extract_words (); */ diff --git a/src/Context.cpp b/src/Context.cpp index 2ad8d2e81..c2e7a8b4a 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -141,10 +141,9 @@ void Context::initialize (int argc, const char** argv) //////////////////////////////////////////////////////////////////////////////// int Context::run () { - Timer t ("Context::run"); - int rc; std::string output; + try { rc = dispatch (output); @@ -407,254 +406,6 @@ void Context::loadAliases () aliases[var->substr (6)] = config.get (*var); } -//////////////////////////////////////////////////////////////////////////////// -/* -void Context::parse ( - std::vector & parseArgs, - Cmd& parseCmd, - Task& parseTask, - Sequence& parseSequence, - Subst& parseSubst, - Filter& parseFilter) -{ - Timer t ("Context::parse"); - - Att attribute; - tagAdditions.clear (); - tagRemovals.clear (); - std::string descCandidate = ""; - bool terminated = false; - bool foundSequence = false; - bool foundSomethingAfterSequence = false; - bool foundNonSequence = false; - - foreach (arg, parseArgs) - { - if (!terminated) - { - // The '--' argument shuts off all parsing - everything is an argument. - if (*arg == "--") - { - debug ("parse terminator '" + *arg + "'"); - terminated = true; - } - - // Sequence - // Note: "add" doesn't require an ID - else if (parseCmd.command != "add" && - ! foundSomethingAfterSequence && - parseSequence.valid (*arg)) - { - debug ("parse sequence '" + *arg + "'"); - parseSequence.parse (*arg); - foundSequence = true; - } - - // Tags to include begin with '+'. - else if (arg->length () > 1 && - (*arg)[0] == '+' && - noSpaces (*arg)) - { - debug ("parse tag addition '" + *arg + "'"); - if (foundSequence) - foundSomethingAfterSequence = true; - - foundNonSequence = true; - - if (arg->find (',') != std::string::npos) - throw std::string (STRING_TAGS_NO_COMMAS); - - tagAdditions.push_back (arg->substr (1)); - parseTask.addTag (arg->substr (1)); - } - - // Tags to remove begin with '-'. - else if (arg->length () > 1 && - (*arg)[0] == '-' && - noSpaces (*arg)) - { - debug ("parse tag removal '" + *arg + "'"); - if (foundSequence) - foundSomethingAfterSequence = true; - - foundNonSequence = true; - - if (arg->find (',') != std::string::npos) - throw std::string (STRING_TAGS_NO_COMMAS); - - tagRemovals.push_back (arg->substr (1)); - } - - // Substitution of description and/or annotation text. - else if (parseSubst.valid (*arg)) - { - if (foundSequence) - foundSomethingAfterSequence = true; - - foundNonSequence = true; - - debug ("parse subst '" + *arg + "'"); - parseSubst.parse (*arg); - } - - // Atributes - name[.mod]:[value] - else if (attribute.valid (*arg)) - { - debug ("parse attribute '" + *arg + "'"); - if (foundSequence) - foundSomethingAfterSequence = true; - - foundNonSequence = true; - - attribute.parse (*arg); - - // There has to be a better way. And it starts with a fresh coffee. - std::string name = attribute.name (); - std::string mod = attribute.mod (); - std::string value = attribute.value (); - if (attribute.validNameValue (name, mod, value)) - { - attribute.name (name); - attribute.mod (mod); - attribute.value (value); - - // Preserve modifier in the key, to allow multiple modifiers on the - // same attribute. Bug #252. - if (name != "" && mod != "") - parseTask[name + "." + mod] = attribute; - else - parseTask[name] = attribute; - - autoFilter (attribute, parseFilter); - } - - // *arg has the appearance of an attribute (foo:bar), but isn't - // recognized, so downgrade it to part of the description. - else - { - if (foundSequence) - foundSomethingAfterSequence = true; - - foundNonSequence = true; - - if (descCandidate.length ()) - descCandidate += " "; - descCandidate += *arg; - } - } - - // It might be a command if one has not already been found. - else if (parseCmd.command == "" && - parseCmd.valid (*arg)) - { - debug ("parse cmd '" + *arg + "'"); - parseCmd.parse (*arg); - - if (foundSequence) - foundSomethingAfterSequence = true; - - foundNonSequence = true; - } - - // Anything else is just considered description. - else - { - if (foundSequence) - foundSomethingAfterSequence = true; - - foundNonSequence = true; - - if (descCandidate.length ()) - descCandidate += " "; - descCandidate += *arg; - } - } - - // Command is terminated, therefore everything subsequently is a description. - else - { - debug ("parse post-termination description '" + *arg + "'"); - if (foundSequence) - foundSomethingAfterSequence = true; - - if (descCandidate.length ()) - descCandidate += " "; - descCandidate += *arg; - } - } - - if (descCandidate != "" && noVerticalSpace (descCandidate)) - { - debug ("parse description '" + descCandidate + "'"); - parseTask.set ("description", descCandidate); - - foundNonSequence = true; - - // Now convert the description to a filter on each word, if necessary. - if (parseCmd.isReadOnlyCommand ()) - { - std::vector words; - split (words, descCandidate, ' '); - std::vector ::iterator it; - for (it = words.begin (); it != words.end (); ++it) - { - Att a ("description", "contains", *it); - autoFilter (a, parseFilter); - } - } - } - - // At this point, either a sequence or a command should have been found. - if (parseSequence.size () == 0 && parseCmd.command == "") - parseCmd.parse (descCandidate); - - // Read-only command (reports, status, info ...) use filters. Write commands - // (add, done ...) do not. The filter was constructed iteratively above, but - // tags were omitted, so they are added now. - if (parseCmd.isReadOnlyCommand ()) - autoFilter (parseFilter); - - // If no command was specified, and there were no command line arguments - // then invoke the default command. - if (parseCmd.command == "") - { - if (parseArgs.size () == 0) - { - // Apply overrides, if any. - std::string defaultCommand = config.get ("default.command"); - if (defaultCommand != "") - { - // Add on the overrides. - defaultCommand += " " + file_override + " " + var_overrides; - - // Stuff the command line. - args.clear (); - split (args, defaultCommand, ' '); - header ("[task " + trim (defaultCommand) + "]"); - - // Reinitialize the context and recurse. - file_override = ""; - var_overrides = ""; - footnotes.clear (); - //initialize (); - parse (args, cmd, task, sequence, subst, filter); - } - else - throw std::string (STRING_TRIVIAL_INPUT); - } - - // If the command "task 123" is entered, but with no modifier arguments, - // then the actual command is assumed to be "info". - else if (!foundNonSequence && - (parseTask.id != 0 || parseSequence.size () != 0)) - { - std::cout << STRING_ASSUME_INFO << "\n"; - parseCmd.command = "info"; - } - } -} -*/ - //////////////////////////////////////////////////////////////////////////////// void Context::decomposeSortField ( const std::string& field, diff --git a/src/commands/CmdModify.cpp b/src/commands/CmdModify.cpp index 11c11512e..06692440e 100644 --- a/src/commands/CmdModify.cpp +++ b/src/commands/CmdModify.cpp @@ -50,8 +50,8 @@ CmdModify::CmdModify () //////////////////////////////////////////////////////////////////////////////// int CmdModify::execute (std::string& output) { - int count = 0; /* + int count = 0; std::stringstream out; std::vector tasks; diff --git a/src/main.cpp b/src/main.cpp index 1bc6c5586..1db9dbb00 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -70,13 +70,13 @@ int main (int argc, const char** argv) catch (std::string& error) { std::cout << error << "\n"; - return -1; + status = -1; } catch (...) { std::cerr << STRING_UNKNOWN_ERROR << "\n"; - return -2; + status = -2; } return status;