diff options
author | John Wiegley <johnw@newartisans.com> | 2010-06-22 21:56:19 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2010-06-22 21:56:19 -0400 |
commit | e8e28c794bfb12fe1c9562c5bd124688ce45fc8e (patch) | |
tree | 3a6dfcaba3fae13dce2286c917aa74969a7d5be9 | |
parent | 0648c2f6dccb5c56e66d50a255177c92e46d5fea (diff) | |
download | fork-ledger-e8e28c794bfb12fe1c9562c5bd124688ce45fc8e.tar.gz fork-ledger-e8e28c794bfb12fe1c9562c5bd124688ce45fc8e.tar.bz2 fork-ledger-e8e28c794bfb12fe1c9562c5bd124688ce45fc8e.zip |
Added report query modifiers: for, since, until
Now instead of ledger reg expense -p "this month", you can say:
ledger reg expense for this month
And as a shorthand for "for until this month", you can just say "until
this month" or "since this month".
-rw-r--r-- | src/precmd.cc | 10 | ||||
-rw-r--r-- | src/query.cc | 73 | ||||
-rw-r--r-- | src/query.h | 24 | ||||
-rw-r--r-- | src/report.cc | 70 | ||||
-rw-r--r-- | src/report.h | 1 |
5 files changed, 131 insertions, 47 deletions
diff --git a/src/precmd.cc b/src/precmd.cc index d9977ab6..8ca27fd8 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -202,21 +202,19 @@ value_t query_command(call_scope_t& args) query_t query(args.value(), report.what_to_keep(), ! report.HANDLED(collapse)); - if (query.has_predicate(query_t::QUERY_LIMIT)) { + if (query.has_query(query_t::QUERY_LIMIT)) { call_scope_t sub_args(static_cast<scope_t&>(args)); - sub_args.push_back - (string_value(query.get_predicate(query_t::QUERY_LIMIT).print_to_str())); + sub_args.push_back(string_value(query.get_query(query_t::QUERY_LIMIT))); parse_command(sub_args); } - if (query.has_predicate(query_t::QUERY_SHOW)) { + if (query.has_query(query_t::QUERY_SHOW)) { out << std::endl << _("====== Display predicate ======") << std::endl << std::endl; call_scope_t disp_sub_args(static_cast<scope_t&>(args)); - disp_sub_args.push_back - (string_value(query.get_predicate(query_t::QUERY_SHOW).print_to_str())); + disp_sub_args.push_back(string_value(query.get_query(query_t::QUERY_SHOW))); parse_command(disp_sub_args); } diff --git a/src/query.cc b/src/query.cc index 43e3ffed..bed6afae 100644 --- a/src/query.cc +++ b/src/query.cc @@ -55,6 +55,10 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token() resume: switch (*arg_i) { + case '\0': + assert(false); + break; + case '\'': case '"': case '/': { @@ -85,12 +89,17 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token() if (multiple_args && consume_next_arg) { consume_next_arg = false; token_t tok(token_t::TERM, string(arg_i, arg_end)); + prev_arg_i = arg_i; arg_i = arg_end; return tok; } bool consume_next = false; switch (*arg_i) { + case '\0': + assert(false); + break; + case ' ': case '\t': case '\r': @@ -121,6 +130,10 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token() string::const_iterator beg = arg_i; for (; arg_i != arg_end; ++arg_i) { switch (*arg_i) { + case '\0': + assert(false); + break; + case ' ': case '\t': case '\n': @@ -130,6 +143,7 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token() else ident.push_back(*arg_i); break; + case '(': case ')': case '&': @@ -174,6 +188,12 @@ test_ident: return token_t(token_t::TOK_SHOW); else if (ident == "bold") return token_t(token_t::TOK_BOLD); + else if (ident == "for") + return token_t(token_t::TOK_FOR); + else if (ident == "since") + return token_t(token_t::TOK_SINCE); + else if (ident == "until") + return token_t(token_t::TOK_UNTIL); else if (ident == "expr") { // The expr keyword takes the whole of the next string as its argument. consume_next_arg = true; @@ -230,6 +250,9 @@ query_t::parser_t::parse_query_term(query_t::lexer_t::token_t::kind_t tok_contex switch (tok.kind) { case lexer_t::token_t::TOK_SHOW: case lexer_t::token_t::TOK_BOLD: + case lexer_t::token_t::TOK_FOR: + case lexer_t::token_t::TOK_SINCE: + case lexer_t::token_t::TOK_UNTIL: case lexer_t::token_t::END_REACHED: lexer.push_token(tok); break; @@ -423,7 +446,8 @@ query_t::parser_t::parse_query_expr(lexer_t::token_t::kind_t tok_context, if (! subexpression) { if (limiter) query_map.insert - (query_map_t::value_type(QUERY_LIMIT, predicate_t(limiter, what_to_keep))); + (query_map_t::value_type + (QUERY_LIMIT, predicate_t(limiter, what_to_keep).print_to_str())); lexer_t::token_t tok = lexer.peek_token(); while (tok.kind != lexer_t::token_t::END_REACHED) { @@ -445,7 +469,8 @@ query_t::parser_t::parse_query_expr(lexer_t::token_t::kind_t tok_context, if (node) query_map.insert - (query_map_t::value_type(QUERY_SHOW, predicate_t(node, what_to_keep))); + (query_map_t::value_type + (QUERY_SHOW, predicate_t(node, what_to_keep).print_to_str())); break; } @@ -462,7 +487,49 @@ query_t::parser_t::parse_query_expr(lexer_t::token_t::kind_t tok_context, if (node) query_map.insert - (query_map_t::value_type(QUERY_BOLD, predicate_t(node, what_to_keep))); + (query_map_t::value_type + (QUERY_BOLD, predicate_t(node, what_to_keep).print_to_str())); + break; + } + + case lexer_t::token_t::TOK_FOR: + case lexer_t::token_t::TOK_SINCE: + case lexer_t::token_t::TOK_UNTIL: { + tok = lexer.next_token(); + + string for_string; + + if (tok.kind == lexer_t::token_t::TOK_SINCE) + for_string = "since"; + else if (tok.kind == lexer_t::token_t::TOK_UNTIL) + for_string = "until"; + + lexer.consume_next_arg = true; + tok = lexer.peek_token(); + + while (tok.kind != lexer_t::token_t::END_REACHED) { + tok = lexer.next_token(); + assert(tok.kind == lexer_t::token_t::TERM); + + if (*tok.value == "show" || *tok.value == "bold" || + *tok.value == "for" || *tok.value == "since" || + *tok.value == "until") { + lexer.token_cache = lexer_t::token_t(); + lexer.arg_i = lexer.prev_arg_i; + lexer.consume_next_arg = false; + break; + } + + if (! for_string.empty()) + for_string += " "; + for_string += *tok.value; + + lexer.consume_next_arg = true; + tok = lexer.peek_token(); + } + + if (! for_string.empty()) + query_map.insert(query_map_t::value_type(QUERY_FOR, for_string)); break; } diff --git a/src/query.h b/src/query.h index e953206a..5a4651a0 100644 --- a/src/query.h +++ b/src/query.h @@ -60,6 +60,7 @@ public: value_t::sequence_t::const_iterator begin; value_t::sequence_t::const_iterator end; + string::const_iterator prev_arg_i; string::const_iterator arg_i; string::const_iterator arg_end; @@ -90,6 +91,9 @@ public: TOK_SHOW, TOK_BOLD, + TOK_FOR, + TOK_SINCE, + TOK_UNTIL, TERM, @@ -141,6 +145,9 @@ public: case TOK_EXPR: return "TOK_EXPR"; case TOK_SHOW: return "TOK_SHOW"; case TOK_BOLD: return "TOK_BOLD"; + case TOK_FOR: return "TOK_FOR"; + case TOK_SINCE: return "TOK_SINCE"; + case TOK_UNTIL: return "TOK_UNTIL"; case TERM: return string("TERM(") + *value + ")"; case END_REACHED: return "END_REACHED"; } @@ -164,6 +171,9 @@ public: case TOK_EXPR: return "expr"; case TOK_SHOW: return "show"; case TOK_BOLD: return "bold"; + case TOK_FOR: return "for"; + case TOK_SINCE: return "since"; + case TOK_UNTIL: return "until"; case END_REACHED: return "<EOF>"; @@ -201,8 +211,7 @@ public: arg_i(lexer.arg_i), arg_end(lexer.arg_end), consume_whitespace(lexer.consume_whitespace), consume_next_arg(lexer.consume_next_arg), - multiple_args(lexer.multiple_args), - token_cache(lexer.token_cache) + multiple_args(lexer.multiple_args), token_cache(lexer.token_cache) { TRACE_CTOR(query_t::lexer_t, "copy"); } @@ -225,10 +234,11 @@ public: enum kind_t { QUERY_LIMIT, QUERY_SHOW, - QUERY_BOLD + QUERY_BOLD, + QUERY_FOR }; - typedef std::map<kind_t, predicate_t> query_map_t; + typedef std::map<kind_t, string> query_map_t; protected: class parser_t @@ -315,16 +325,16 @@ public: return parser->parse(subexpression); } - bool has_predicate(const kind_t& id) const { + bool has_query(const kind_t& id) const { return parser && parser->query_map.find(id) != parser->query_map.end(); } - predicate_t get_predicate(const kind_t& id) const { + string get_query(const kind_t& id) const { if (parser) { query_map_t::const_iterator i = parser->query_map.find(id); if (i != parser->query_map.end()) return (*i).second; } - return predicate_t(); + return empty_string; } bool tokens_remaining() { diff --git a/src/report.cc b/src/report.cc index 4a7ed2e5..9d733674 100644 --- a/src/report.cc +++ b/src/report.cc @@ -145,26 +145,8 @@ void report_t::normalize_options(const string& verb) // using -b or -e). Then, if no _duration_ was specified (such as monthly), // then ignore the period since the begin/end are the only interesting // details. - if (HANDLED(period_)) { - date_interval_t interval(HANDLER(period_).str()); - - optional<date_t> begin = interval.begin(); - optional<date_t> end = interval.end(); - - if (! HANDLED(begin_) && begin) { - string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; - HANDLER(limit_).on(string("?normalize"), predicate); - } - if (! HANDLED(end_) && end) { - string predicate = "date<[" + to_iso_extended_string(*end) + "]"; - HANDLER(limit_).on(string("?normalize"), predicate); - } - - if (! interval.duration) - HANDLER(period_).off(); - else if (! HANDLED(sort_all_)) - HANDLER(sort_xacts_).on_only(string("?normalize")); - } + if (HANDLED(period_)) + normalize_period(); // If -j or -J were specified, set the appropriate format string now so as // to avoid option ordering issues were we to have done it during the @@ -254,26 +236,52 @@ void report_t::normalize_options(const string& verb) } } +void report_t::normalize_period() +{ + date_interval_t interval(HANDLER(period_).str()); + + optional<date_t> begin = interval.begin(); + optional<date_t> end = interval.end(); + + if (! HANDLED(begin_) && begin) { + string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; + HANDLER(limit_).on(string("?normalize"), predicate); + } + if (! HANDLED(end_) && end) { + string predicate = "date<[" + to_iso_extended_string(*end) + "]"; + HANDLER(limit_).on(string("?normalize"), predicate); + } + + if (! interval.duration) + HANDLER(period_).off(); + else if (! HANDLED(sort_all_)) + HANDLER(sort_xacts_).on_only(string("?normalize")); +} + void report_t::parse_query_args(const value_t& args, const string& whence) { query_t query(args, what_to_keep()); - if (query.has_predicate(query_t::QUERY_LIMIT)) { - HANDLER(limit_) - .on(whence, query.get_predicate(query_t::QUERY_LIMIT).print_to_str()); - DEBUG("report.predicate", "Predicate = " << HANDLER(limit_).str()); + if (query.has_query(query_t::QUERY_LIMIT)) { + HANDLER(limit_).on(whence, query.get_query(query_t::QUERY_LIMIT)); + DEBUG("report.predicate", "Limit predicate = " << HANDLER(limit_).str()); } - if (query.has_predicate(query_t::QUERY_SHOW)) { - HANDLER(display_) - .on(whence, query.get_predicate(query_t::QUERY_SHOW).print_to_str()); + if (query.has_query(query_t::QUERY_SHOW)) { + HANDLER(display_).on(whence, query.get_query(query_t::QUERY_SHOW)); DEBUG("report.predicate", "Display predicate = " << HANDLER(display_).str()); } - if (query.has_predicate(query_t::QUERY_BOLD)) { - HANDLER(bold_if_) - .set_expr(whence, query.get_predicate(query_t::QUERY_BOLD).print_to_str()); - DEBUG("report.predicate", "Bolding predicate = " << HANDLER(display_).str()); + if (query.has_query(query_t::QUERY_BOLD)) { + HANDLER(bold_if_).set_expr(whence, query.get_query(query_t::QUERY_BOLD)); + DEBUG("report.predicate", "Bolding predicate = " << HANDLER(bold_if_).str()); + } + + if (query.has_query(query_t::QUERY_FOR)) { + HANDLER(period_).on(whence, query.get_query(query_t::QUERY_FOR)); + DEBUG("report.predicate", "Report period = " << HANDLER(period_).str()); + + normalize_period(); // it needs normalization } } diff --git a/src/report.h b/src/report.h index fc2fc1a2..59a632e6 100644 --- a/src/report.h +++ b/src/report.h @@ -126,6 +126,7 @@ public: } void normalize_options(const string& verb); + void normalize_period(); void parse_query_args(const value_t& args, const string& whence); void posts_report(post_handler_ptr handler); |