diff options
author | John Wiegley <johnw@newartisans.com> | 2012-02-27 04:59:04 -0600 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2012-02-27 05:02:25 -0600 |
commit | 2ec35ea6e98dcfb03fb8c2e90b85991aaa7be2fa (patch) | |
tree | 762118fc9fec617672c84980ba7efc9bd1f980f9 /src | |
parent | 0e7b4fb1821a80ee43fafd55447a01255564eb3d (diff) | |
download | fork-ledger-2ec35ea6e98dcfb03fb8c2e90b85991aaa7be2fa.tar.gz fork-ledger-2ec35ea6e98dcfb03fb8c2e90b85991aaa7be2fa.tar.bz2 fork-ledger-2ec35ea6e98dcfb03fb8c2e90b85991aaa7be2fa.zip |
Implement the "tag" metadata directive
Diffstat (limited to 'src')
-rw-r--r-- | src/expr.h | 9 | ||||
-rw-r--r-- | src/journal.cc | 66 | ||||
-rw-r--r-- | src/journal.h | 28 | ||||
-rw-r--r-- | src/textual.cc | 58 | ||||
-rw-r--r-- | src/xact.cc | 6 | ||||
-rw-r--r-- | src/xact.h | 16 |
6 files changed, 137 insertions, 46 deletions
@@ -58,6 +58,15 @@ public: typedef intrusive_ptr<op_t> ptr_op_t; typedef intrusive_ptr<const op_t> const_ptr_op_t; + enum check_expr_kind_t { + EXPR_GENERAL, + EXPR_ASSERTION, + EXPR_CHECK + }; + + typedef std::pair<expr_t, check_expr_kind_t> check_expr_pair; + typedef std::list<check_expr_pair> check_expr_list; + protected: ptr_op_t ptr; diff --git a/src/journal.cc b/src/journal.cc index 17fcb687..8f382125 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -217,8 +217,38 @@ void journal_t::register_commodity(commodity_t& comm, } } -#if 0 -void journal_t::register_metadata(const string& key, const string& value, +namespace { + void check_metadata(journal_t& journal, + const string& key, const value_t& value, + variant<int, xact_t *, post_t *> context, + const string& location) + { + std::pair<tag_check_exprs_map::iterator, + tag_check_exprs_map::iterator> range = + journal.tag_check_exprs.equal_range(key); + + for (tag_check_exprs_map::iterator i = range.first; + i != range.second; + ++i) { + value_scope_t val_scope + (context.which() == 1 ? + static_cast<scope_t&>(*boost::get<xact_t *>(context)) : + static_cast<scope_t&>(*boost::get<post_t *>(context)), value); + + if (! (*i).second.first.calc(val_scope).to_boolean()) { + if ((*i).second.second == expr_t::EXPR_ASSERTION) + throw_(parse_error, + _("Metadata assertion failed for (%1: %2): %3") + << key << value << (*i).second.first); + else + warning_(_("%1Metadata check failed for (%2: %3): %4") + << location << key << value << (*i).second.first); + } + } + } +} + +void journal_t::register_metadata(const string& key, const value_t& value, variant<int, xact_t *, post_t *> context, const string& location) { @@ -244,8 +274,34 @@ void journal_t::register_metadata(const string& key, const string& value, throw_(parse_error, _("Unknown metadata tag '%1'") << key); } } + + if (! value.is_null()) + check_metadata(*this, key, value, context, location); +} + +namespace { + void check_all_metadata(journal_t& journal, + variant<int, xact_t *, post_t *> context) + { + xact_t * xact = context.which() == 1 ? boost::get<xact_t *>(context) : NULL; + post_t * post = context.which() == 2 ? boost::get<post_t *>(context) : NULL; + + if ((xact || post) && xact ? xact->metadata : post->metadata) { + foreach (const item_t::string_map::value_type& pair, + xact ? *xact->metadata : *post->metadata) { + const string& key(pair.first); + + // jww (2012-02-27): We really need to know the parsing context, + // both here and for the call to warning_ in + // xact_t::extend_xact. + if (optional<value_t> value = pair.second.first) + journal.register_metadata(key, *value, context, ""); + else + journal.register_metadata(key, NULL_VALUE, context, ""); + } + } + } } -#endif bool journal_t::add_xact(xact_t * xact) { @@ -258,6 +314,10 @@ bool journal_t::add_xact(xact_t * xact) extend_xact(xact); + check_all_metadata(*this, xact); + foreach (post_t * post, xact->posts) + check_all_metadata(*this, post); + // If a transaction with this UUID has already been seen, simply do // not add this one to the journal. However, all automated checks // will have been performed by extend_xact, so asserts can still be diff --git a/src/journal.h b/src/journal.h index 7411aaf6..9c42ec6a 100644 --- a/src/journal.h +++ b/src/journal.h @@ -45,10 +45,10 @@ #include "utils.h" #include "times.h" #include "mask.h" +#include "expr.h" namespace ledger { -class commodity_t; class xact_base_t; class xact_t; class auto_xact_t; @@ -57,16 +57,17 @@ class post_t; class account_t; class scope_t; -typedef std::list<xact_t *> xacts_list; -typedef std::list<auto_xact_t *> auto_xacts_list; -typedef std::list<period_xact_t *> period_xacts_list; - -typedef std::pair<mask_t, string> payee_mapping_t; -typedef std::list<payee_mapping_t> payee_mappings_t; -typedef std::pair<mask_t, account_t *> account_mapping_t; -typedef std::list<account_mapping_t> account_mappings_t; -typedef std::map<const string, account_t *> accounts_map; -typedef std::map<string, xact_t *> checksum_map_t; +typedef std::list<xact_t *> xacts_list; +typedef std::list<auto_xact_t *> auto_xacts_list; +typedef std::list<period_xact_t *> period_xacts_list; +typedef std::pair<mask_t, string> payee_mapping_t; +typedef std::list<payee_mapping_t> payee_mappings_t; +typedef std::pair<mask_t, account_t *> account_mapping_t; +typedef std::list<account_mapping_t> account_mappings_t; +typedef std::map<const string, account_t *> accounts_map; +typedef std::map<string, xact_t *> checksum_map_t; +typedef std::multimap<string, + expr_t::check_expr_pair> tag_check_exprs_map; class journal_t : public noncopyable { @@ -130,6 +131,7 @@ public: accounts_map account_aliases; account_mappings_t payees_for_unknown_accounts; checksum_map_t checksum_map; + tag_check_exprs_map tag_check_exprs; bool was_loaded; bool force_checking; @@ -168,11 +170,9 @@ public: void register_commodity(commodity_t& comm, variant<int, xact_t *, post_t *> context, const string& location); -#if 0 - void register_metadata(const string& key, const string& value, + void register_metadata(const string& key, const value_t& value, variant<int, xact_t *, post_t *> context, const string& location); -#endif bool add_xact(xact_t * xact); void extend_xact(xact_base_t * xact); diff --git a/src/textual.cc b/src/textual.cc index de28325f..5b0193cb 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -158,6 +158,8 @@ namespace { void default_commodity_directive(char * line); + void tag_directive(char * line); + void apply_directive(char * line); void apply_account_directive(char * line); void apply_tag_directive(char * line); @@ -605,14 +607,14 @@ void instance_t::automated_xact_directive(char * line) const char c = *p; p = skip_ws(&p[*p == 'a' ? 6 : (*p == 'c' ? 5 : 4)]); if (! ae->check_exprs) - ae->check_exprs = auto_xact_t::check_expr_list(); + ae->check_exprs = expr_t::check_expr_list(); ae->check_exprs->push_back - (auto_xact_t::check_expr_pair(expr_t(p), - c == 'a' ? - auto_xact_t::EXPR_ASSERTION : - (c == 'c' ? - auto_xact_t::EXPR_CHECK : - auto_xact_t::EXPR_GENERAL))); + (expr_t::check_expr_pair(expr_t(p), + c == 'a' ? + expr_t::EXPR_ASSERTION : + (c == 'c' ? + expr_t::EXPR_CHECK : + expr_t::EXPR_GENERAL))); } else { reveal_context = false; @@ -903,14 +905,14 @@ void instance_t::account_directive(char * line) ae->pos->beg_pos = beg_pos; ae->pos->beg_line = beg_linenum; ae->pos->sequence = context.sequence++; - ae->check_exprs = auto_xact_t::check_expr_list(); + ae->check_exprs = expr_t::check_expr_list(); } ae->check_exprs->push_back - (auto_xact_t::check_expr_pair(expr_t(b), - keyword == "assert" ? - auto_xact_t::EXPR_ASSERTION : - auto_xact_t::EXPR_CHECK)); + (expr_t::check_expr_pair(expr_t(b), + keyword == "assert" ? + expr_t::EXPR_ASSERTION : + expr_t::EXPR_CHECK)); } else if (keyword == "eval" || keyword == "expr") { bind_scope_t bound_scope(context.scope, *account); @@ -1057,6 +1059,32 @@ void instance_t::commodity_default_directive(commodity_t& comm) commodity_pool_t::current_pool->default_commodity = &comm; } +void instance_t::tag_directive(char * line) +{ + char * p = skip_ws(line); + context.journal.register_metadata(p, NULL_VALUE, 0, + file_context(pathname, linenum)); + + while (peek_whitespace_line()) { + read_line(line); + char * q = skip_ws(line); + if (! *q) + break; + + char * b = next_element(q); + string keyword(q); + if (keyword == "assert" || keyword == "check") { + context.journal.tag_check_exprs.insert + (tag_check_exprs_map::value_type + (string(p), + expr_t::check_expr_pair(expr_t(b), + keyword == "assert" ? + expr_t::EXPR_ASSERTION : + expr_t::EXPR_CHECK))); + } + } +} + void instance_t::eval_directive(char * line) { expr_t expr(line); @@ -1176,7 +1204,11 @@ bool instance_t::general_directive(char * line) break; case 't': - if (std::strcmp(p, "test") == 0) { + if (std::strcmp(p, "tag") == 0) { + tag_directive(arg); + return true; + } + else if (std::strcmp(p, "test") == 0) { comment_directive(arg); return true; } diff --git a/src/xact.cc b/src/xact.cc index 5da61b7b..ae571b62 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -664,12 +664,12 @@ void auto_xact_t::extend_xact(xact_base_t& xact) } if (check_exprs) { - foreach (check_expr_pair& pair, *check_exprs) { - if (pair.second == auto_xact_t::EXPR_GENERAL) { + foreach (expr_t::check_expr_pair& pair, *check_exprs) { + if (pair.second == expr_t::EXPR_GENERAL) { pair.first.calc(bound_scope); } else if (! pair.first.calc(bound_scope).to_boolean()) { - if (pair.second == auto_xact_t::EXPR_ASSERTION) + if (pair.second == expr_t::EXPR_ASSERTION) throw_(parse_error, _("Transaction assertion failed: %1") << pair.first); else @@ -152,21 +152,11 @@ private: class auto_xact_t : public xact_base_t { public: - predicate_t predicate; - bool try_quick_match; - + predicate_t predicate; + bool try_quick_match; std::map<string, bool> memoized_results; - enum xact_expr_kind_t { - EXPR_GENERAL, - EXPR_ASSERTION, - EXPR_CHECK - }; - - typedef std::pair<expr_t, xact_expr_kind_t> check_expr_pair; - typedef std::list<check_expr_pair> check_expr_list; - - optional<check_expr_list> check_exprs; + optional<expr_t::check_expr_list> check_exprs; struct deferred_tag_data_t { string tag_data; |