diff options
-rw-r--r-- | src/item.cc | 150 | ||||
-rw-r--r-- | src/item.h | 18 | ||||
-rw-r--r-- | src/mask.h | 8 | ||||
-rw-r--r-- | src/op.cc | 97 | ||||
-rw-r--r-- | src/op.h | 25 | ||||
-rw-r--r-- | src/parser.cc | 36 | ||||
-rw-r--r-- | src/parser.h | 2 | ||||
-rw-r--r-- | src/predicate.cc | 47 | ||||
-rw-r--r-- | src/report.cc | 2 | ||||
-rw-r--r-- | src/session.cc | 2 | ||||
-rw-r--r-- | src/textual.cc | 26 | ||||
-rw-r--r-- | src/token.cc | 12 | ||||
-rw-r--r-- | src/token.h | 1 | ||||
-rw-r--r-- | src/value.cc | 61 | ||||
-rw-r--r-- | src/value.h | 35 | ||||
-rw-r--r-- | src/xact.cc | 16 |
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>); @@ -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 @@ -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; }; @@ -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; @@ -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 |