summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/item.cc150
-rw-r--r--src/item.h18
-rw-r--r--src/mask.h8
-rw-r--r--src/op.cc97
-rw-r--r--src/op.h25
-rw-r--r--src/parser.cc36
-rw-r--r--src/parser.h2
-rw-r--r--src/predicate.cc47
-rw-r--r--src/report.cc2
-rw-r--r--src/session.cc2
-rw-r--r--src/textual.cc26
-rw-r--r--src/token.cc12
-rw-r--r--src/token.h1
-rw-r--r--src/value.cc61
-rw-r--r--src/value.h35
-rw-r--r--src/xact.cc16
16 files changed, 415 insertions, 123 deletions
diff --git a/src/item.cc b/src/item.cc
index 185ee033..3758e02d 100644
--- a/src/item.cc
+++ b/src/item.cc
@@ -37,6 +37,68 @@ namespace ledger {
bool item_t::use_effective_date = false;
+bool item_t::has_tag(const string& tag) const
+{
+ if (! metadata)
+ return false;
+ string_map::const_iterator i = metadata->find(tag);
+ return i != metadata->end();
+}
+
+optional<string> item_t::get_tag(const string& tag) const
+{
+ if (metadata) {
+ string_map::const_iterator i = metadata->find(tag);
+ if (i != metadata->end())
+ return (*i).second;
+ }
+ return none;
+}
+
+void item_t::set_tag(const string& tag,
+ const optional<string>& value)
+{
+ if (! metadata)
+ metadata = string_map();
+
+ DEBUG("item.meta", "Setting tag '" << tag << "' to value '"
+ << (value ? *value : string("<none>")) << "'");
+
+ std::pair<string_map::iterator, bool> result
+ = metadata->insert(string_map::value_type(tag, value));
+ assert(result.second);
+}
+
+void item_t::parse_tags(const char * p)
+{
+ if (! std::strchr(p, ':'))
+ return;
+
+ scoped_array<char> buf(new char[std::strlen(p) + 1]);
+
+ std::strcpy(buf.get(), p);
+
+ string tag;
+ for (char * q = std::strtok(buf.get(), " \t");
+ q;
+ q = std::strtok(NULL, " \t")) {
+ const std::size_t len = std::strlen(q);
+ if (! tag.empty()) {
+ set_tag(tag, string(p + (q - buf.get())));
+ break;
+ }
+ else if (q[0] == ':' && q[len - 1] == ':') { // a series of tags
+ for (char * r = std::strtok(q + 1, ":");
+ r;
+ r = std::strtok(NULL, ":"))
+ set_tag(r);
+ }
+ else if (q[len - 1] == ':') { // a metadata setting
+ tag = string(q, len - 1);
+ }
+ }
+}
+
namespace {
value_t get_status(item_t& item) {
return long(item.state());
@@ -59,7 +121,51 @@ namespace {
}
value_t get_note(item_t& item) {
- return string_value(item.note ? *item.note : empty_string);
+ return item.note ? string_value(*item.note) : value_t(false);
+ }
+
+ value_t has_tag(call_scope_t& args) {
+ item_t& item(find_scope<item_t>(args));
+ if (! item.metadata)
+ return false;
+
+ IF_DEBUG("item.meta") {
+ foreach (const item_t::string_map::value_type& data, *item.metadata) {
+ *_log_stream << " Tag: " << data.first << "\n";
+ *_log_stream << "Value: ";
+ if (data.second)
+ *_log_stream << *data.second << "\n";
+ else
+ *_log_stream << "<none>\n";
+ }
+ }
+
+ value_t& arg(args[0]);
+
+ if (arg.is_string()) {
+ if (args.size() == 1)
+ return item.has_tag(args[0].as_string());
+ else if (optional<string> tag = item.get_tag(args[0].as_string()))
+ return args[1] == string_value(*tag);
+ }
+ else if (arg.is_mask()) {
+ foreach (const item_t::string_map::value_type& data, *item.metadata) {
+ if (arg.as_mask().match(data.first)) {
+ if (args.size() == 1)
+ return true;
+ else if (data.second && args[1] == string_value(*data.second))
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ value_t get_tag(call_scope_t& args) {
+ item_t& item(find_scope<item_t>(args));
+ if (optional<string> value = item.get_tag(args[0].as_string()))
+ return string_value(*value);
+ return false;
}
value_t get_beg_pos(item_t& item) {
@@ -84,12 +190,37 @@ namespace {
}
}
+value_t get_comment(item_t& item)
+{
+ if (! item.note) {
+ return false;
+ } else {
+ std::ostringstream buf;
+ buf << "\n ;";
+ bool need_separator = false;
+ for (const char * p = item.note->c_str(); *p; p++) {
+ if (*p == '\n')
+ need_separator = true;
+ else {
+ if (need_separator) {
+ buf << "\n ;";
+ need_separator = false;
+ }
+ buf << *p;
+ }
+ }
+ return string_value(buf.str());
+ }
+}
+
expr_t::ptr_op_t item_t::lookup(const string& name)
{
switch (name[0]) {
case 'c':
if (name == "cleared")
return WRAP_FUNCTOR(get_wrapper<&get_cleared>);
+ else if (name == "comment")
+ return WRAP_FUNCTOR(get_wrapper<&get_comment>);
break;
case 'd':
@@ -97,6 +228,18 @@ expr_t::ptr_op_t item_t::lookup(const string& name)
return WRAP_FUNCTOR(get_wrapper<&get_date>);
break;
+ case 'h':
+ if (name == "has_tag")
+ return WRAP_FUNCTOR(ledger::has_tag);
+ else if (name == "has_meta")
+ return WRAP_FUNCTOR(ledger::has_tag);
+ break;
+
+ case 'm':
+ if (name == "meta")
+ return WRAP_FUNCTOR(ledger::get_tag);
+ break;
+
case 'n':
if (name == "note")
return WRAP_FUNCTOR(get_wrapper<&get_note>);
@@ -112,6 +255,11 @@ expr_t::ptr_op_t item_t::lookup(const string& name)
return WRAP_FUNCTOR(get_wrapper<&get_status>);
break;
+ case 't':
+ if (name == "tag")
+ return WRAP_FUNCTOR(ledger::get_tag);
+ break;
+
case 'u':
if (name == "uncleared")
return WRAP_FUNCTOR(get_wrapper<&get_uncleared>);
diff --git a/src/item.h b/src/item.h
index fab4458f..ce86ec6d 100644
--- a/src/item.h
+++ b/src/item.h
@@ -72,6 +72,9 @@ public:
optional<date_t> _date_eff;
optional<string> note;
+ typedef std::map<string, optional<string> > string_map;
+ optional<string_map> metadata;
+
unsigned short src_idx;
istream_pos_type full_beg_pos;
std::size_t full_beg_line;
@@ -122,6 +125,19 @@ public:
return ! (*this == entry);
}
+ virtual bool has_tag(const string& tag) const;
+ virtual optional<string> get_tag(const string& tag) const;
+ virtual void set_tag(const string& tag,
+ const optional<string>& value = none);
+ virtual void parse_tags(const char * p);
+
+ virtual void append_note(const char * p) {
+ if (note)
+ *note += p;
+ else
+ note = p;
+ }
+
virtual optional<date_t> actual_date() const {
return _date;
}
@@ -147,6 +163,8 @@ public:
bool valid() const;
};
+value_t get_comment(item_t& item);
+
} // namespace ledger
#endif // _ITEM_H
diff --git a/src/mask.h b/src/mask.h
index 23c5cedf..670c1807 100644
--- a/src/mask.h
+++ b/src/mask.h
@@ -72,10 +72,18 @@ public:
mask_t& operator=(const string& other);
+ bool operator==(const mask_t& other) const {
+ return expr == other.expr;
+ }
+
bool match(const string& str) const {
return boost::regex_search(str, expr);
}
+ bool empty() const {
+ return expr.empty();
+ }
+
void read(const char *& data);
void write(std::ostream& out) const;
};
diff --git a/src/op.cc b/src/op.cc
index fac4ce18..caf5af11 100644
--- a/src/op.cc
+++ b/src/op.cc
@@ -57,7 +57,9 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope)
return this;
ptr_op_t lhs(left()->compile(scope));
- ptr_op_t rhs(has_right() ? right()->compile(scope) : ptr_op_t());
+ ptr_op_t rhs(has_right() ? (kind == O_LOOKUP ?
+ right() : right()->compile(scope)) :
+ ptr_op_t());
if (lhs == left() && (! rhs || rhs == right()))
return this;
@@ -102,11 +104,6 @@ value_t expr_t::op_t::opcalc(scope_t& scope)
throw_(calc_error, "Unknown identifier '" << as_ident() << "'");
return left()->opcalc(scope);
- case MASK:
- throw_(calc_error,
- "Regexs can only be used in a match; did you mean: account =~ /"
- << as_mask() << '/');
-
case FUNCTION: {
// Evaluating a FUNCTION is the same as calling it directly; this happens
// when certain functions-that-look-like-variables (such as "amount") are
@@ -115,6 +112,27 @@ value_t expr_t::op_t::opcalc(scope_t& scope)
return as_function()(call_args);
}
+ case O_LOOKUP:
+ if (left()->kind == IDENT &&
+ left()->left() && left()->left()->kind == FUNCTION) {
+ call_scope_t call_args(scope);
+ if (value_t obj = left()->left()->as_function()(call_args)) {
+ if (obj.is_pointer()) {
+ scope_t& objscope(obj.as_ref_lval<scope_t>());
+ if (ptr_op_t member = objscope.lookup(right()->as_ident()))
+ return member->calc(objscope);
+ }
+ }
+ }
+ if (right()->kind != IDENT) {
+ throw_(calc_error,
+ "Right operand of . operator must be an identifier");
+ } else {
+ throw_(calc_error,
+ "Failed to lookup member '" << right()->as_ident() << "'");
+ }
+ break;
+
case O_CALL: {
call_scope_t call_args(scope);
@@ -122,20 +140,19 @@ value_t expr_t::op_t::opcalc(scope_t& scope)
call_args.set_args(right()->opcalc(scope));
ptr_op_t func = left();
+ const string& name(func->as_ident());
- assert(func->kind == IDENT);
func = func->left();
-
if (! func || func->kind != FUNCTION)
- throw_(calc_error, "Calling non-function");
+ throw_(calc_error, "Calling non-function '" << name << "'");
return func->as_function()(call_args);
}
case O_MATCH:
- if (! right()->is_mask())
+ if (! right()->is_value() || ! right()->as_value().is_mask())
throw_(calc_error, "Right-hand argument to match operator must be a regex");
- return right()->as_mask().match(left()->opcalc(scope).to_string());
+ return right()->as_value().as_mask().match(left()->opcalc(scope).to_string());
case INDEX: {
const call_scope_t& args(downcast<const call_scope_t>(scope));
@@ -237,10 +254,6 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const
out << as_ident();
break;
- case MASK:
- out << '/' << as_mask() << '/';
- break;
-
case FUNCTION:
out << "<FUNCTION>";
break;
@@ -370,6 +383,14 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const
found = true;
break;
+ case O_LOOKUP:
+ if (left() && left()->print(out, context))
+ found = true;
+ out << ".";
+ if (has_right() && right()->print(out, context))
+ found = true;
+ break;
+
case O_CALL:
if (left() && left()->print(out, context))
found = true;
@@ -425,10 +446,6 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const
out << "IDENT: " << as_ident();
break;
- case MASK:
- out << "MASK: " << as_mask();
- break;
-
case INDEX:
out << "INDEX: " << as_index();
break;
@@ -437,27 +454,28 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const
out << "FUNCTION";
break;
- case O_CALL: out << "O_CALL"; break;
- case O_MATCH: out << "O_MATCH"; break;
+ case O_LOOKUP: out << "O_LOOKUP"; break;
+ case O_CALL: out << "O_CALL"; break;
+ case O_MATCH: out << "O_MATCH"; break;
- case O_NOT: out << "O_NOT"; break;
- case O_NEG: out << "O_NEG"; break;
+ case O_NOT: out << "O_NOT"; break;
+ case O_NEG: out << "O_NEG"; break;
- case O_ADD: out << "O_ADD"; break;
- case O_SUB: out << "O_SUB"; break;
- case O_MUL: out << "O_MUL"; break;
- case O_DIV: out << "O_DIV"; break;
+ case O_ADD: out << "O_ADD"; break;
+ case O_SUB: out << "O_SUB"; break;
+ case O_MUL: out << "O_MUL"; break;
+ case O_DIV: out << "O_DIV"; break;
- case O_EQ: out << "O_EQ"; break;
- case O_LT: out << "O_LT"; break;
- case O_LTE: out << "O_LTE"; break;
- case O_GT: out << "O_GT"; break;
- case O_GTE: out << "O_GTE"; break;
+ case O_EQ: out << "O_EQ"; break;
+ case O_LT: out << "O_LT"; break;
+ case O_LTE: out << "O_LTE"; break;
+ case O_GT: out << "O_GT"; break;
+ case O_GTE: out << "O_GTE"; break;
- case O_AND: out << "O_AND"; break;
- case O_OR: out << "O_OR"; break;
+ case O_AND: out << "O_AND"; break;
+ case O_OR: out << "O_OR"; break;
- case O_COMMA: out << "O_COMMA"; break;
+ case O_COMMA: out << "O_COMMA"; break;
case LAST:
default:
@@ -508,12 +526,6 @@ void expr_t::op_t::read(const char *& data)
set_ident(temp);
break;
}
- case MASK: {
- mask_t temp;
- temp.read(data);
- set_mask(temp);
- break;
- }
case INDEX: {
long temp;
binary::read_long(data, temp);
@@ -550,9 +562,6 @@ void expr_t::op_t::write(std::ostream& out) const
case IDENT:
binary::write_string(out, as_ident());
break;
- case MASK:
- as_mask().write(out);
- break;
case INDEX:
binary::write_long(out, as_index());
break;
diff --git a/src/op.h b/src/op.h
index c643aeff..1b6e5e54 100644
--- a/src/op.h
+++ b/src/op.h
@@ -47,7 +47,6 @@
#define _OP_H
#include "expr.h"
-#include "mask.h"
namespace ledger {
@@ -71,7 +70,6 @@ private:
variant<std::size_t, // used by constant INDEX
value_t, // used by constant VALUE
string, // used by constant IDENT
- mask_t, // used by constant MASK
function_t, // used by terminal FUNCTION
ptr_op_t> // used by all binary operators
data;
@@ -81,7 +79,6 @@ public:
// Constants
VALUE,
IDENT,
- MASK,
INDEX,
CONSTANTS,
@@ -115,6 +112,7 @@ public:
O_COMMA,
+ O_LOOKUP,
O_CALL,
O_MATCH,
@@ -193,27 +191,6 @@ public:
data = val;
}
- bool is_mask() const {
- if (kind == MASK) {
- assert(data.type() == typeid(mask_t));
- return true;
- }
- return false;
- }
- mask_t& as_mask_lval() {
- assert(is_mask());
- return boost::get<mask_t>(data);
- }
- const mask_t& as_mask() const {
- return const_cast<op_t *>(this)->as_mask_lval();
- }
- void set_mask(const mask_t& val) {
- data = val;
- }
- void set_mask(const string& expr) {
- data = mask_t(expr);
- }
-
bool is_function() const {
return kind == FUNCTION;
}
diff --git a/src/parser.cc b/src/parser.cc
index 40871116..0819e3d8 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -47,11 +47,6 @@ expr_t::parser_t::parse_value_term(std::istream& in,
node->set_value(tok.value);
break;
- case token_t::MASK:
- node = new op_t(op_t::MASK);
- node->set_mask(tok.value.as_string());
- break;
-
case token_t::IDENT: {
string ident = tok.value.as_string();
@@ -89,6 +84,31 @@ expr_t::parser_t::parse_value_term(std::istream& in,
}
expr_t::ptr_op_t
+expr_t::parser_t::parse_dot_expr(std::istream& in,
+ const parse_flags_t& tflags) const
+{
+ ptr_op_t node(parse_value_term(in, tflags));
+
+ if (node && ! tflags.has_flags(PARSE_SINGLE)) {
+ token_t& tok = next_token(in, tflags);
+
+ if (tok.kind == token_t::DOT) {
+ ptr_op_t prev(node);
+ node = new op_t(op_t::O_LOOKUP);
+ node->set_left(prev);
+ node->set_right(parse_dot_expr(in, tflags));
+ if (! node->right())
+ throw_(parse_error,
+ tok.symbol << " operator not followed by argument");
+ } else {
+ push_token(tok);
+ }
+ }
+
+ return node;
+}
+
+expr_t::ptr_op_t
expr_t::parser_t::parse_unary_expr(std::istream& in,
const parse_flags_t& tflags) const
{
@@ -98,7 +118,7 @@ expr_t::parser_t::parse_unary_expr(std::istream& in,
switch (tok.kind) {
case token_t::EXCLAM: {
- ptr_op_t term(parse_value_term(in, tflags));
+ ptr_op_t term(parse_dot_expr(in, tflags));
if (! term)
throw_(parse_error,
tok.symbol << " operator not followed by argument");
@@ -115,7 +135,7 @@ expr_t::parser_t::parse_unary_expr(std::istream& in,
}
case token_t::MINUS: {
- ptr_op_t term(parse_value_term(in, tflags));
+ ptr_op_t term(parse_dot_expr(in, tflags));
if (! term)
throw_(parse_error,
tok.symbol << " operator not followed by argument");
@@ -133,7 +153,7 @@ expr_t::parser_t::parse_unary_expr(std::istream& in,
default:
push_token(tok);
- node = parse_value_term(in, tflags);
+ node = parse_dot_expr(in, tflags);
break;
}
diff --git a/src/parser.h b/src/parser.h
index ea165ebd..9c80cf38 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -83,6 +83,8 @@ private:
ptr_op_t parse_value_term(std::istream& in,
const parse_flags_t& flags) const;
+ ptr_op_t parse_dot_expr(std::istream& in,
+ const parse_flags_t& flags) const;
ptr_op_t parse_unary_expr(std::istream& in,
const parse_flags_t& flags) const;
ptr_op_t parse_mul_expr(std::istream& in,
diff --git a/src/predicate.cc b/src/predicate.cc
index 19019ec7..72b22f97 100644
--- a/src/predicate.cc
+++ b/src/predicate.cc
@@ -67,27 +67,29 @@ string args_to_predicate_expr(value_t::sequence_t::const_iterator begin,
append_and = true;
}
- if (arg == "desc" || arg == "DESC" ||
- arg == "payee" || arg == "PAYEE") {
- arg = string("@") + (*++begin).as_string();
- }
- else if (arg == "note" || arg == "NOTE") {
- arg = string("&") + (*++begin).as_string();
- }
- else if (arg == "tag" || arg == "TAG" ||
- arg == "meta" || arg == "META" ||
- arg == "data" || arg == "DATA") {
- arg = string("%") + (*++begin).as_string();
- }
- else if (arg == "expr" || arg == "EXPR") {
- arg = string("=") + (*++begin).as_string();
+ value_t::sequence_t::const_iterator next = begin;
+ if (++next != end) {
+ if (arg == "desc" || arg == "DESC" ||
+ arg == "payee" || arg == "PAYEE") {
+ arg = string("@") + (*++begin).as_string();
+ }
+ else if (arg == "note" || arg == "NOTE") {
+ arg = string("&") + (*++begin).as_string();
+ }
+ else if (arg == "tag" || arg == "TAG" ||
+ arg == "meta" || arg == "META" ||
+ arg == "data" || arg == "DATA") {
+ arg = string("%") + (*++begin).as_string();
+ }
+ else if (arg == "expr" || arg == "EXPR") {
+ arg = string("=") + (*++begin).as_string();
+ }
}
if (parse_argument) {
bool in_prefix = true;
bool in_suffix = false;
bool found_specifier = false;
- bool saw_tag_char = false;
bool no_final_slash = false;
only_parenthesis = true;
@@ -122,15 +124,14 @@ string args_to_predicate_expr(value_t::sequence_t::const_iterator begin,
bool found_metadata = false;
for (const char *q = c; *q != '\0'; q++)
if (*q == '=') {
- expr << "(metadata(\""
- << string(c + 1, q - c - 1) << "\") =~ /";
+ expr << "has_tag(/"
+ << string(c + 1, q - c - 1) << "/, /";
found_metadata = true;
c = q;
break;
}
if (! found_metadata) {
- expr << "(tag =~ /:";
- saw_tag_char = true;
+ expr << "has_tag(/";
}
found_specifier = true;
consumed = true;
@@ -151,9 +152,9 @@ string args_to_predicate_expr(value_t::sequence_t::const_iterator begin,
case ')':
if (! in_suffix) {
if (found_specifier) {
- if (saw_tag_char)
- expr << ':';
- expr << "/)";
+ if (! no_final_slash)
+ expr << "/";
+ expr << ")";
}
in_suffix = true;
}
@@ -171,8 +172,6 @@ string args_to_predicate_expr(value_t::sequence_t::const_iterator begin,
if (! in_suffix) {
if (found_specifier) {
- if (saw_tag_char)
- expr << ':';
if (! no_final_slash)
expr << "/";
expr << ")";
diff --git a/src/report.cc b/src/report.cc
index 747af341..efcb9c1b 100644
--- a/src/report.cc
+++ b/src/report.cc
@@ -157,6 +157,8 @@ namespace {
(args_to_predicate_expr(args.value().as_sequence().begin(),
args.value().as_sequence().end()));
+ DEBUG("report.predicate", "Predicate = " << report.predicate);
+
(report.*report_method)(handler_ptr(handler));
return true;
diff --git a/src/session.cc b/src/session.cc
index e45cfbc6..fc0601d5 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -84,7 +84,7 @@ session_t::session_t()
("%-.9D %-.35P %-.39A %22.108t %!22.132T\n%/"
"%48|%-.38A %22.108t %!22.132T\n"),
print_format
- ("%(date)%(cleared ? \" *\" : (uncleared ? \"\" : \" !\"))%(code ? \" (\" + code + \")\" : \"\") %(payee)\n %-34(account) %12(amount)\n%/ %-34(account) %12(amount)%(note ? \" ; \" + note : \"\")\n%/\n"),
+ ("%(date)%(cleared ? \" *\" : (uncleared ? \"\" : \" !\"))%(code ? \" (\" + code + \")\" : \"\") %(payee)%(entry.comment | \"\")\n %-34(account) %12(amount)%(comment | \"\")\n%/ %-34(account) %12(amount)%(comment | \"\")\n%/\n"),
balance_format
("%20(display_total) %(depth_spacer)%-(partial_account)\n"),
equity_format
diff --git a/src/textual.cc b/src/textual.cc
index 2e252830..94b2f4f0 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -1068,7 +1068,8 @@ entry_t * textual_parser_t::instance_t::parse_entry(std::istream& in,
TRACE_START(entry_details, 1, "Time spent parsing entry details:");
istream_pos_type end_pos;
- std::size_t beg_line = linenum;
+ std::size_t beg_line = linenum;
+ xact_t * last_xact = NULL;
while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) {
istream_pos_type beg_pos = in.tellg();
@@ -1086,13 +1087,23 @@ entry_t * textual_parser_t::instance_t::parse_entry(std::istream& in,
end_pos += len + 1;
linenum++;
- if (line[0] == ' ' || line[0] == '\t') {
- char * p = skip_ws(line);
- if (! *p)
- break;
- }
+ const char * p = skip_ws(line);
+ if (! *p)
+ break;
- if (xact_t * xact = parse_xact(line, master, curr.get())) {
+ if (*p == ';') {
+ // This is an entry note, and possibly a metadata info tag
+ if (last_xact) {
+ last_xact->append_note(p + 1);
+ last_xact->append_note("\n");
+ last_xact->parse_tags(p + 1);
+ } else {
+ curr->append_note(p + 1);
+ curr->append_note("\n");
+ curr->parse_tags(p + 1);
+ }
+ }
+ else if (xact_t * xact = parse_xact(line, master, curr.get())) {
if ((state == item_t::CLEARED && xact->state() != item_t::CLEARED) ||
(state == item_t::PENDING && xact->state() == item_t::UNCLEARED))
xact->set_state(state);
@@ -1105,6 +1116,7 @@ entry_t * textual_parser_t::instance_t::parse_entry(std::istream& in,
pos = end_pos;
curr->add_xact(xact);
+ last_xact = xact;
}
if (in.eof())
diff --git a/src/token.cc b/src/token.cc
index 0fd2c25a..a2046924 100644
--- a/src/token.cc
+++ b/src/token.cc
@@ -110,8 +110,7 @@ void expr_t::token_t::parse_ident(std::istream& in)
length = 0;
char c, buf[256];
- READ_INTO_(in, buf, 255, c, length,
- std::isalnum(c) || c == '_' || c == '.' || c == '-');
+ READ_INTO_(in, buf, 255, c, length, std::isalnum(c) || c == '_' || c == '-');
value.set_string(buf);
}
@@ -280,8 +279,8 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
in.get(c);
length++;
- kind = MASK;
- value.set_string(buf);
+ kind = VALUE;
+ value.set_mask(buf);
}
break;
}
@@ -334,6 +333,11 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
kind = GREATER;
break;
+ case '.':
+ in.get(c);
+ kind = DOT;
+ break;
+
case ',':
in.get(c);
kind = COMMA;
diff --git a/src/token.h b/src/token.h
index 30d2c0f4..47658749 100644
--- a/src/token.h
+++ b/src/token.h
@@ -89,6 +89,7 @@ struct expr_t::token_t : public noncopyable
QUERY, // ?
COLON, // :
+ DOT, // .
COMMA, // ,
TOK_EOF,
diff --git a/src/value.cc b/src/value.cc
index 7d6f9409..a34ceb98 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -76,6 +76,11 @@ value_t::storage_t& value_t::storage_t::operator=(const value_t::storage_t& rhs)
string(*reinterpret_cast<string *>(const_cast<char *>(rhs.data)));
break;
+ case MASK:
+ new(reinterpret_cast<mask_t *>(data))
+ mask_t(*reinterpret_cast<mask_t *>(const_cast<char *>(rhs.data)));
+ break;
+
case SEQUENCE:
*reinterpret_cast<sequence_t **>(data) =
new sequence_t(**reinterpret_cast<sequence_t **>
@@ -107,6 +112,9 @@ void value_t::storage_t::destroy()
case STRING:
reinterpret_cast<string *>(data)->~string();
break;
+ case MASK:
+ reinterpret_cast<mask_t *>(data)->~mask_t();
+ break;
case SEQUENCE:
checked_delete(*reinterpret_cast<sequence_t **>(data));
break;
@@ -134,7 +142,6 @@ void value_t::initialize()
false_value->type = BOOLEAN;
*reinterpret_cast<bool *>(false_value->data) = false;
-#if 0
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(bool));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(datetime_t));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(date_t));
@@ -143,9 +150,9 @@ void value_t::initialize()
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_t *));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_pair_t *));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(string));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(mask_t));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(sequence_t *));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(boost::any));
-#endif
DEBUG_(std::setw(3) << std::right << sizeof(bool)
<< " sizeof(bool)");
@@ -163,6 +170,8 @@ void value_t::initialize()
<< " sizeof(balance_pair_t *)");
DEBUG_(std::setw(3) << std::right << sizeof(string)
<< " sizeof(string)");
+ DEBUG_(std::setw(3) << std::right << sizeof(mask_t)
+ << " sizeof(mask_t)");
DEBUG_(std::setw(3) << std::right << sizeof(sequence_t *)
<< " sizeof(sequence_t *)");
DEBUG_(std::setw(3) << std::right << sizeof(boost::any)
@@ -203,6 +212,8 @@ value_t::operator bool() const
return as_balance_pair();
case STRING:
return ! as_string().empty();
+ case MASK:
+ return ! as_mask().empty();
case SEQUENCE:
return ! as_sequence().empty();
case POINTER:
@@ -303,6 +314,17 @@ string value_t::to_string() const
}
}
+mask_t value_t::to_mask() const
+{
+ if (is_mask()) {
+ return as_mask();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(MASK);
+ return temp.as_mask();
+ }
+}
+
value_t::sequence_t value_t::to_sequence() const
{
if (is_sequence()) {
@@ -922,6 +944,15 @@ bool value_t::is_equal_to(const value_t& val) const
case STRING:
if (val.is_string())
return as_string() == val.as_string();
+ else if (val.is_mask())
+ return val.as_mask().match(as_string());
+ break;
+
+ case MASK:
+ if (val.is_mask())
+ return as_mask() == val.as_mask();
+ else if (val.is_string())
+ return as_mask().match(val.as_string());
break;
case SEQUENCE:
@@ -1257,6 +1288,12 @@ void value_t::in_place_not()
case BALANCE_PAIR:
set_boolean(! as_balance_pair());
return;
+ case STRING:
+ set_boolean(as_string().empty());
+ return;
+ case MASK:
+ set_boolean(as_mask().empty());
+ return;
default:
break;
}
@@ -1283,6 +1320,8 @@ bool value_t::is_realzero() const
return as_balance_pair().is_realzero();
case STRING:
return as_string().empty();
+ case MASK:
+ return as_mask().empty();
case SEQUENCE:
return as_sequence().empty();
@@ -1314,6 +1353,8 @@ bool value_t::is_zero() const
return as_balance_pair().is_zero();
case STRING:
return as_string().empty();
+ case MASK:
+ return as_mask().empty();
case SEQUENCE:
return as_sequence().empty();
@@ -1505,6 +1546,7 @@ value_t value_t::strip_annotations(const bool keep_price,
case DATETIME:
case DATE:
case STRING:
+ case MASK:
case POINTER:
return *this;
@@ -1625,6 +1667,10 @@ void value_t::dump(std::ostream& out, const int first_width,
out << as_string();
break;
+ case MASK:
+ out << as_mask();
+ break;
+
case POINTER:
out << boost::unsafe_any_cast<const void *>(&as_any_pointer());
break;
@@ -1700,6 +1746,10 @@ void value_t::print(std::ostream& out, const bool relaxed) const
out << '"' << as_string() << '"';
break;
+ case MASK:
+ out << '/' << as_mask() << '/';
+ break;
+
case POINTER:
assert(false);
break;
@@ -1801,7 +1851,7 @@ void value_t::write(std::ostream& out) const
break;
}
- throw_(value_error, "Cannot read " << label() << " to a stream");
+ throw_(value_error, "Cannot write " << label() << " to a stream");
}
void value_t::write_xml(std::ostream& out, const int depth) const
@@ -1846,6 +1896,11 @@ void value_t::write_xml(std::ostream& out, const int depth) const
<< as_string()
<< "</string>\n";
break;
+ case MASK:
+ out << xml_str("<mask>", depth + 1)
+ << as_mask()
+ << "</mask>\n";
+ break;
case SEQUENCE:
out << xml_str("<sequence>\n", depth + 1);
foreach (const value_t& v, as_sequence())
diff --git a/src/value.h b/src/value.h
index ea135cdb..2940ac4b 100644
--- a/src/value.h
+++ b/src/value.h
@@ -50,6 +50,7 @@
#define _VALUE_H
#include "balpair.h" // pulls in balance.h and amount.h
+#include "mask.h"
namespace ledger {
@@ -108,6 +109,7 @@ public:
BALANCE, // a ledger::balance_t
BALANCE_PAIR, // a ledger::balance_pair_t
STRING, // a string object
+ MASK, // a regular expression mask
SEQUENCE, // a vector of value_t objects
POINTER // an opaque pointer of any type
};
@@ -299,6 +301,10 @@ public:
TRACE_CTOR(value_t, "const balance_pair_t&");
set_balance_pair(val);
}
+ value_t(const mask_t& val) {
+ TRACE_CTOR(value_t, "const mask_t&");
+ set_mask(val);
+ }
explicit value_t(const string& val, bool literal = false) {
TRACE_CTOR(value_t, "const string&, bool");
@@ -477,6 +483,7 @@ public:
* is_balance()
* is_balance_pair()
* is_string()
+ * is_mask()
* is_sequence()
* is_pointer()
*
@@ -649,6 +656,27 @@ public:
new(reinterpret_cast<string *>(storage->data)) string(val);
}
+ bool is_mask() const {
+ return is_type(MASK);
+ }
+ mask_t& as_mask_lval() {
+ assert(is_mask());
+ _dup();
+ return *reinterpret_cast<mask_t *>(storage->data);
+ }
+ const mask_t& as_mask() const {
+ assert(is_mask());
+ return *reinterpret_cast<mask_t *>(storage->data);
+ }
+ void set_mask(const string& val) {
+ set_type(MASK);
+ new(reinterpret_cast<mask_t *>(storage->data)) mask_t(val);
+ }
+ void set_mask(const mask_t& val) {
+ set_type(MASK);
+ new(reinterpret_cast<mask_t *>(storage->data)) mask_t(val);
+ }
+
bool is_sequence() const {
return is_type(SEQUENCE);
}
@@ -731,6 +759,7 @@ public:
balance_t to_balance() const;
balance_pair_t to_balance_pair() const;
string to_string() const;
+ mask_t to_mask() const;
sequence_t to_sequence() const;
/**
@@ -879,6 +908,8 @@ public:
return "a balance pair";
case STRING:
return "a string";
+ case MASK:
+ return "a regexp";
case SEQUENCE:
return "a sequence";
case POINTER:
@@ -928,6 +959,10 @@ inline value_t string_value(const string& str) {
return value_t(str, true);
}
+inline value_t mask_value(const string& str) {
+ return value_t(mask_t(str));
+}
+
inline std::ostream& operator<<(std::ostream& out, const value_t& val) {
val.print(out, 12);
return out;
diff --git a/src/xact.cc b/src/xact.cc
index 60294db9..6fb30c3f 100644
--- a/src/xact.cc
+++ b/src/xact.cc
@@ -64,6 +64,10 @@ item_t::state_t xact_t::state() const
}
namespace {
+ value_t get_entry(xact_t& xact) {
+ return value_t(static_cast<scope_t *>(xact.entry));
+ }
+
value_t get_code(xact_t& xact) {
if (xact.entry->code)
return string_value(*xact.entry->code);
@@ -172,6 +176,11 @@ expr_t::ptr_op_t xact_t::lookup(const string& name)
return WRAP_FUNCTOR(get_wrapper<&get_cost>);
break;
+ case 'e':
+ if (name == "entry")
+ return WRAP_FUNCTOR(get_wrapper<&get_entry>);
+ break;
+
case 'p':
if (name == "payee")
return WRAP_FUNCTOR(get_wrapper<&get_payee>);
@@ -189,14 +198,7 @@ expr_t::ptr_op_t xact_t::lookup(const string& name)
break;
}
-#if 0
- // jww (2008-09-19): I don't think we can lookup in entry, because
- // that means the functor returned would be expecting an entry to be
- // passed to it, rather than a transaction.
- return entry->lookup(name);
-#else
return item_t::lookup(name);
-#endif
}
bool xact_t::valid() const