summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2012-02-27 04:59:04 -0600
committerJohn Wiegley <johnw@newartisans.com>2012-02-27 05:02:25 -0600
commit2ec35ea6e98dcfb03fb8c2e90b85991aaa7be2fa (patch)
tree762118fc9fec617672c84980ba7efc9bd1f980f9 /src
parent0e7b4fb1821a80ee43fafd55447a01255564eb3d (diff)
downloadfork-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.h9
-rw-r--r--src/journal.cc66
-rw-r--r--src/journal.h28
-rw-r--r--src/textual.cc58
-rw-r--r--src/xact.cc6
-rw-r--r--src/xact.h16
6 files changed, 137 insertions, 46 deletions
diff --git a/src/expr.h b/src/expr.h
index c4cd5dc5..79ae2864 100644
--- a/src/expr.h
+++ b/src/expr.h
@@ -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
diff --git a/src/xact.h b/src/xact.h
index 3e628817..ff4b7bc2 100644
--- a/src/xact.h
+++ b/src/xact.h
@@ -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;