diff options
-rwxr-xr-x | acprep | 32 | ||||
-rw-r--r-- | amount.cc | 52 | ||||
-rw-r--r-- | balpair.h | 2 | ||||
-rw-r--r-- | binary.cc | 8 | ||||
-rw-r--r-- | derive.cc | 2 | ||||
-rw-r--r-- | format.cc | 36 | ||||
-rw-r--r-- | journal.cc | 18 | ||||
-rw-r--r-- | parsexp.cc | 164 | ||||
-rw-r--r-- | parsexp.h | 20 | ||||
-rw-r--r-- | reconcile.cc | 6 | ||||
-rw-r--r-- | textual.cc | 26 | ||||
-rw-r--r-- | valexpr.cc | 46 | ||||
-rw-r--r-- | valexpr.h | 58 | ||||
-rw-r--r-- | value.cc | 49 | ||||
-rw-r--r-- | value.h | 71 | ||||
-rw-r--r-- | walk.cc | 2 |
16 files changed, 381 insertions, 211 deletions
@@ -35,6 +35,10 @@ LIBDIRS="$LIBDIRS -L/usr/local/lib" PYTHON_HOME="/usr" +if [ -x /usr/bin/g++-4.2 ]; then + CXX=g++-4.2 +fi + SYSTEM=`uname -s` @@ -85,44 +89,50 @@ while [ -n "$1" ]; do SWITCHES="$SWITCHES --disable-shared" CPPFLAGS="$CPPFLAGS -DBOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING=1" CPPFLAGS="$CPPFLAGS -DBOOST_MULTI_INDEX_ENABLE_SAFE_MODE=1" - ;; + shift 1 ;; --debug) SWITCHES="$SWITCHES --enable-debug" #CPPFLAGS="$CPPFLAGS -D_GLIBCXX_DEBUG=1" - CXXFLAGS="$CXXFLAGS -g" ;; - + CXXFLAGS="$CXXFLAGS -g" + shift 1 ;; + --boost) shift 1 SWITCHES="$SWITCHES --with-boost-suffix=$1" - ;; + shift 1 ;; --gcov) - CXXFLAGS="$CXXFLAGS -fprofile-arcs -ftest-coverage" ;; + CXXFLAGS="$CXXFLAGS -fprofile-arcs -ftest-coverage" + shift 1 ;; --gprof) - CXXFLAGS="$CXXFLAGS -g -pg" ;; + CXXFLAGS="$CXXFLAGS -g -pg" + shift 1 ;; --python) if [ -d "$PYTHON_HOME" ]; then SWITCHES="$SWITCHES --enable-python" CPPFLAGS="$CPPFLAGS -I$PYTHON_HOME/include/python2.5" LDFLAGS="$LDFLAGS -L$PYTHON_HOME/lib/python2.5/config" - fi ;; + fi + shift 1;; --pic) - CXXFLAGS="$CXXFLAGS -fPIC" ;; + CXXFLAGS="$CXXFLAGS -fPIC" + shift 1 ;; --opt) - CXXFLAGS="$CXXFLAGS -fomit-frame-pointer -O3" ;; + CXXFLAGS="$CXXFLAGS -fomit-frame-pointer -O3" + shift 1 ;; --local) - LOCAL=true ;; + LOCAL=true + shift 1 ;; *) break ;; esac - shift 1 done @@ -97,6 +97,25 @@ struct amount_t::bigint_t : public supports_flags<> assert(ref == 0); mpz_clear(val); } + + bool valid() const { + if (prec > 32) { + DEBUG("ledger.validate", "amount_t::bigint_t: prec > 32"); + return false; + } + if (ref > 128) { + DEBUG("ledger.validate", "amount_t::bigint_t: ref > 128"); + return false; + } +#if 0 + // jww (2008-07-24): How does one check the validity of an mpz_t? + if (val[0]._mp_size < 0 || val[0]._mp_size > 100) { + DEBUG("ledger.validate", "amount_t::bigint_t: val._mp_size is bad"); + return false; + } +#endif + return true; + } }; uint_fast32_t amount_t::sizeof_bigint_t() @@ -139,6 +158,8 @@ void amount_t::shutdown() void amount_t::_copy(const amount_t& amt) { + assert(amt.valid()); + if (quantity != amt.quantity) { if (quantity) _release(); @@ -155,15 +176,21 @@ void amount_t::_copy(const amount_t& amt) } } commodity_ = amt.commodity_; + + assert(valid()); } void amount_t::_dup() { + assert(valid()); + if (quantity->ref > 1) { bigint_t * q = new bigint_t(*quantity); _release(); quantity = q; } + + assert(valid()); } void amount_t::_resize(precision_t prec) @@ -180,6 +207,8 @@ void amount_t::_resize(precision_t prec) mpz_mul(MPZ(quantity), MPZ(quantity), divisor); quantity->prec = prec; + + assert(valid()); } void amount_t::_clear() @@ -195,6 +224,8 @@ void amount_t::_clear() void amount_t::_release() { + assert(valid()); + DEBUG("amounts.refs", quantity << " ref--, now " << (quantity->ref - 1)); if (--quantity->ref == 0) { @@ -202,7 +233,11 @@ void amount_t::_release() quantity->~bigint_t(); else checked_delete(quantity); + quantity = NULL; + commodity_ = NULL; } + + assert(valid()); } @@ -328,6 +363,8 @@ amount_t& amount_t::operator=(const amount_t& amt) int amount_t::compare(const amount_t& amt) const { + assert(amt.valid()); + if (! quantity || ! amt.quantity) { if (quantity) throw_(amount_error, "Cannot compare an amount to an uninitialized amount"); @@ -361,6 +398,8 @@ int amount_t::compare(const amount_t& amt) const amount_t& amount_t::operator+=(const amount_t& amt) { + assert(amt.valid()); + if (! quantity || ! amt.quantity) { if (quantity) throw_(amount_error, "Cannot add an amount to an uninitialized amount"); @@ -397,6 +436,8 @@ amount_t& amount_t::operator+=(const amount_t& amt) amount_t& amount_t::operator-=(const amount_t& amt) { + assert(amt.valid()); + if (! quantity || ! amt.quantity) { if (quantity) throw_(amount_error, "Cannot subtract an amount from an uninitialized amount"); @@ -481,6 +522,8 @@ namespace { amount_t& amount_t::operator*=(const amount_t& amt) { + assert(amt.valid()); + if (! quantity || ! amt.quantity) { if (quantity) throw_(amount_error, "Cannot multiply an amount by an uninitialized amount"); @@ -521,6 +564,8 @@ amount_t& amount_t::operator*=(const amount_t& amt) amount_t& amount_t::operator/=(const amount_t& amt) { + assert(amt.valid()); + if (! quantity || ! amt.quantity) { if (quantity) throw_(amount_error, "Cannot divide an amount by an uninitialized amount"); @@ -1023,6 +1068,8 @@ void amount_t::parse(std::istream& in, flags_t flags) in_place_reduce(); safe_holder.release(); // `this->quantity' owns the pointer + + assert(valid()); } void amount_t::parse_conversion(const string& larger_str, @@ -1048,6 +1095,8 @@ void amount_t::parse_conversion(const string& larger_str, void amount_t::print(std::ostream& _out, bool omit_commodity, bool full_precision) const { + assert(valid()); + if (! quantity) throw_(amount_error, "Cannot write out an uninitialized amount"); @@ -1400,6 +1449,9 @@ void amount_t::write(std::ostream& out, bool optimized) const bool amount_t::valid() const { if (quantity) { + if (! quantity->valid()) + return false; + if (quantity->ref == 0) { DEBUG("ledger.validate", "amount_t: quantity->ref == 0"); return false; @@ -351,7 +351,7 @@ public: * valid() returns true if the balances within the balance pair are * valid. */ - virtual bool valid() { + bool valid() const { if (! balance_t::valid()) return false; @@ -827,16 +827,16 @@ void write_value(std::ostream& out, const value_t& val) switch (val.type()) { case value_t::BOOLEAN: - write_bool(out, const_cast<value_t&>(val).as_boolean_lval()); + write_bool(out, val.as_boolean()); break; case value_t::INTEGER: - write_long(out, const_cast<value_t&>(val).as_long_lval()); + write_long(out, val.as_long()); break; case value_t::DATETIME: - write_number(out, const_cast<value_t&>(val).as_datetime_lval()); + write_number(out,val.as_datetime()); break; case value_t::AMOUNT: - write_amount(out, const_cast<value_t&>(val).as_amount_lval()); + write_amount(out, val.as_amount()); break; //case value_t::BALANCE: @@ -67,7 +67,7 @@ entry_t * derive_new_entry(journal_t& journal, value_t total = account_xdata(*acct).total; if (total.is_type(value_t::AMOUNT)) - xact->amount.set_commodity(total.as_amount_lval().commodity()); + xact->amount.set_commodity(total.as_amount().commodity()); } } @@ -355,8 +355,8 @@ void format_t::format(std::ostream& out_str, const details_t& details) const if (! calc) break; - value_t value; - balance_t * bal = NULL; + value_t value; + const balance_t * bal = NULL; calc->compute(value, details); @@ -378,54 +378,54 @@ void format_t::format(std::ostream& out_str, const details_t& details) const switch (value.type()) { case value_t::BOOLEAN: - out << (value.as_boolean_lval() ? "true" : "false"); + out << (value.as_boolean() ? "true" : "false"); break; case value_t::INTEGER: if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { if (ansi_invert) { - if (value.as_long_lval() > 0) { + if (value.as_long() > 0) { mark_red(out, elem); highlighted = true; } } else { - if (value.as_long_lval() < 0) { + if (value.as_long() < 0) { mark_red(out, elem); highlighted = true; } } } - out << value.as_long_lval(); + out << value.as_long(); break; case value_t::DATETIME: - out << value.as_datetime_lval(); + out << value.as_datetime(); break; case value_t::AMOUNT: if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { if (ansi_invert) { - if (value.as_amount_lval().sign() > 0) { + if (value.as_amount().sign() > 0) { mark_red(out, elem); highlighted = true; } } else { - if (value.as_amount_lval().sign() < 0) { + if (value.as_amount().sign() < 0) { mark_red(out, elem); highlighted = true; } } } - out << value.as_amount_lval(); + out << value.as_amount(); break; case value_t::BALANCE: - bal = &(value.as_balance_lval()); + bal = &(value.as_balance()); // fall through... case value_t::BALANCE_PAIR: if (! bal) - bal = &(value.as_balance_pair_lval().quantity()); + bal = &(value.as_balance_pair().quantity()); if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { if (ansi_invert) { @@ -949,11 +949,11 @@ void format_equity::flush() summary.data = &xdata; if (total.type() >= value_t::BALANCE) { - balance_t * bal; + const balance_t * bal; if (total.is_type(value_t::BALANCE)) - bal = &(total.as_balance_lval()); + bal = &(total.as_balance()); else if (total.is_type(value_t::BALANCE_PAIR)) - bal = &(total.as_balance_pair_lval().quantity()); + bal = &(total.as_balance_pair().quantity()); else assert(false); @@ -977,11 +977,11 @@ void format_equity::operator()(account_t& account) value_t val = account_xdata_(account).value; if (val.type() >= value_t::BALANCE) { - balance_t * bal; + const balance_t * bal; if (val.is_type(value_t::BALANCE)) - bal = &(val.as_balance_lval()); + bal = &(val.as_balance()); else if (val.is_type(value_t::BALANCE_PAIR)) - bal = &(val.as_balance_pair_lval().quantity()); + bal = &(val.as_balance_pair().quantity()); else assert(false); @@ -128,7 +128,7 @@ bool entry_base_t::finalize() for (transactions_list::const_iterator x = transactions.begin(); x != transactions.end(); - x++) + x++) { if (! (*x)->has_flags(TRANSACTION_VIRTUAL) || (*x)->has_flags(TRANSACTION_BALANCE)) { amount_t& p((*x)->cost ? *(*x)->cost : (*x)->amount); @@ -154,6 +154,9 @@ bool entry_base_t::finalize() saw_null = true; } } + } + + assert(balance.valid()); // If it's a null entry, then let the user have their fun if (no_amounts) @@ -177,21 +180,24 @@ bool entry_base_t::finalize() // the balance. This is done for the last eligible commodity. if (! saw_null && balance && balance.is_balance()) { - balance_t& bal(balance.as_balance_lval()); + const balance_t& bal(balance.as_balance()); if (bal.amounts.size() == 2) { transactions_list::const_iterator x = transactions.begin(); - assert((*x)->amount); + assert(! (*x)->amount.is_null()); commodity_t& this_comm = (*x)->amount.commodity(); balance_t::amounts_map::const_iterator this_bal = bal.amounts.find(&this_comm); + assert(this_bal != bal.amounts.end()); + balance_t::amounts_map::const_iterator other_bal = bal.amounts.begin(); + if (this_bal == other_bal) other_bal++; amount_t per_unit_cost = - amount_t((*other_bal).second / (*this_bal).second.number()).unround(); + ((*other_bal).second / (*this_bal).second.number()).unround(); for (; x != transactions.end(); x++) { if ((*x)->cost || (*x)->has_flags(TRANSACTION_VIRTUAL) || @@ -241,12 +247,12 @@ bool entry_base_t::finalize() const balance_t * bal = NULL; switch (balance.type()) { case value_t::BALANCE_PAIR: - bal = &balance.as_balance_pair_lval().quantity(); + bal = &balance.as_balance_pair().quantity(); // fall through... case value_t::BALANCE: if (! bal) - bal = &balance.as_balance_lval(); + bal = &balance.as_balance(); if (bal->amounts.size() < 2) { balance.cast(value_t::AMOUNT); @@ -155,10 +155,17 @@ void parser_t::token_t::next(std::istream& in, const flags_t flags) in.get(c); kind = AT_SYM; break; +#if 0 case '$': in.get(c); kind = DOLLAR; break; +#endif + + case '&': + in.get(c); + kind = KW_AND; + break; case '(': in.get(c); @@ -171,7 +178,7 @@ void parser_t::token_t::next(std::istream& in, const flags_t flags) case '[': { in.get(c); - if (flags & EXPR_PARSE_ALLOW_DATE) { + if (! (flags & EXPR_PARSE_NO_DATES)) { char buf[256]; READ_INTO_(in, buf, 255, c, length, c != ']'); if (c != ']') @@ -193,6 +200,7 @@ void parser_t::token_t::next(std::istream& in, const flags_t flags) break; } + case '\'': case '"': { char delim; @@ -249,10 +257,68 @@ void parser_t::token_t::next(std::istream& in, const flags_t flags) kind = STAR; break; +#if 0 case '/': in.get(c); kind = SLASH; break; +#endif + + case 'c': + case 'C': + case 'p': + case 'w': + case 'W': + case 'e': + case '/': { + bool code_mask = c == 'c'; + bool commodity_mask = c == 'C'; + bool payee_mask = c == 'p'; + bool note_mask = c == 'e'; + bool short_account_mask = c == 'w'; + + in.get(c); + if (c == '/') { + c = peek_next_nonws(in); + if (c == '/') { + in.get(c); + c = in.peek(); + if (c == '/') { + in.get(c); + c = in.peek(); + short_account_mask = true; + } else { + payee_mask = true; + } + } + } else { + in.get(c); + } + + // Read in the regexp + char buf[256]; + READ_INTO_(in, buf, 255, c, length, c != '/'); + if (c != '/') + unexpected(c, '/'); + in.get(c); + length++; + + if (short_account_mask) + kind = SHORT_ACCOUNT_MASK; + else if (code_mask) + kind = CODE_MASK; + else if (commodity_mask) + kind = COMMODITY_MASK; + else if (payee_mask) + kind = PAYEE_MASK; + else if (note_mask) + kind = NOTE_MASK; + else + kind = ACCOUNT_MASK; + + value.set_string(buf); + break; + } case '=': in.get(c); @@ -336,7 +402,7 @@ void parser_t::token_t::next(std::istream& in, const flags_t flags) kind = VALUE; value = temp; } - catch (amount_error& err) { + catch (const amount_error& err) { // If the amount had no commodity, it must be an unambiguous // variable reference @@ -407,6 +473,31 @@ parser_t::parse_value_term(std::istream& in, scope_t& scope, const flags_t tflag node->set_value(tok.value); break; + case token_t::SHORT_ACCOUNT_MASK: + node = new op_t(op_t::F_SHORT_ACCOUNT_MASK); + node->set_mask(tok.value.as_string()); + break; + case token_t::CODE_MASK: + node = new op_t(op_t::F_CODE_MASK); + node->set_mask(tok.value.as_string()); + break; + case token_t::COMMODITY_MASK: + node = new op_t(op_t::F_COMMODITY_MASK); + node->set_mask(tok.value.as_string()); + break; + case token_t::PAYEE_MASK: + node = new op_t(op_t::F_PAYEE_MASK); + node->set_mask(tok.value.as_string()); + break; + case token_t::NOTE_MASK: + node = new op_t(op_t::F_NOTE_MASK); + node->set_mask(tok.value.as_string()); + break; + case token_t::ACCOUNT_MASK: + node = new op_t(op_t::F_ACCOUNT_MASK); + node->set_mask(tok.value.as_string()); + break; + case token_t::IDENT: { #if 0 #ifdef USE_BOOST_PYTHON @@ -473,12 +564,12 @@ parser_t::parse_value_term(std::istream& in, scope_t& scope, const flags_t tflag break; } +#if 0 case token_t::AT_SYM: { tok = next_token(in, tflags); if (tok.kind != token_t::IDENT) throw_(parse_error, "@ symbol must be followed by attribute name"); -#if 0 string ident = tok.value.as_string(); if (optional<node_t::nameid_t> id = document_t::lookup_builtin_id(ident)) { node = new op_t(op_t::ATTR_ID); @@ -488,11 +579,9 @@ parser_t::parse_value_term(std::istream& in, scope_t& scope, const flags_t tflag node = new op_t(op_t::ATTR_NAME); node->set_string(ident); } -#endif break; } -#if 0 case token_t::DOLLAR: tok = next_token(in, tflags); if (tok.kind != token_t::IDENT) @@ -546,7 +635,8 @@ parser_t::parse_value_term(std::istream& in, scope_t& scope, const flags_t tflag } ptr_op_t -parser_t::parse_unary_expr(std::istream& in, scope_t& scope, const flags_t tflags) const +parser_t::parse_unary_expr(std::istream& in, scope_t& scope, + const flags_t tflags) const { ptr_op_t node; @@ -561,7 +651,7 @@ parser_t::parse_unary_expr(std::istream& in, scope_t& scope, const flags_t tflag // A very quick optimization if (term->kind == op_t::VALUE) { - term->as_value().in_place_negate(); + term->as_value_lval().in_place_negate(); node = term; } else { node = new op_t(op_t::O_NOT); @@ -578,7 +668,7 @@ parser_t::parse_unary_expr(std::istream& in, scope_t& scope, const flags_t tflag // A very quick optimization if (term->kind == op_t::VALUE) { - term->as_value().in_place_negate(); + term->as_value_lval().in_place_negate(); node = term; } else { node = new op_t(op_t::O_NEG); @@ -1406,64 +1496,6 @@ ptr_op_t parse_value_term(std::istream& in, scope_t * scope, break; // Other -#if 0 - case 'c': - case 'C': - case 'p': - case 'w': - case 'W': - case 'e': - case '/': { - bool code_mask = c == 'c'; - bool commodity_mask = c == 'C'; - bool payee_mask = c == 'p'; - bool note_mask = c == 'e'; - bool short_account_mask = c == 'w'; - - if (c == '/') { - c = peek_next_nonws(in); - if (c == '/') { - in.get(c); - c = in.peek(); - if (c == '/') { - in.get(c); - c = in.peek(); - short_account_mask = true; - } else { - payee_mask = true; - } - } - } else { - in.get(c); - } - - // Read in the regexp - READ_INTO(in, buf, 255, c, c != '/'); - if (c != '/') - unexpected(c, '/'); - - op_t::kind_t kind; - - if (short_account_mask) - kind = op_t::F_SHORT_ACCOUNT_MASK; - else if (code_mask) - kind = op_t::F_CODE_MASK; - else if (commodity_mask) - kind = op_t::F_COMMODITY_MASK; - else if (payee_mask) - kind = op_t::F_PAYEE_MASK; - else if (note_mask) - kind = op_t::F_NOTE_MASK; - else - kind = op_t::F_ACCOUNT_MASK; - - in.get(c); - node.reset(new op_t(kind)); - node->mask = new mask_t(buf); - break; - } -#endif - case '{': { amount_t temp; temp.parse(in, AMOUNT_PARSE_NO_MIGRATE); @@ -46,7 +46,7 @@ class parser_t #define EXPR_PARSE_RELAXED 0x02 #define EXPR_PARSE_NO_MIGRATE 0x04 #define EXPR_PARSE_NO_REDUCE 0x08 -#define EXPR_PARSE_ALLOW_DATE 0x10 +#define EXPR_PARSE_NO_DATES 0x10 public: typedef uint_least8_t flags_t; @@ -57,6 +57,13 @@ private: enum kind_t { VALUE, // any kind of literal value + SHORT_ACCOUNT_MASK, + CODE_MASK, + COMMODITY_MASK, + PAYEE_MASK, + NOTE_MASK, + ACCOUNT_MASK, + IDENT, // [A-Za-z_][-A-Za-z0-9_:]* DOLLAR, // $ AT_SYM, // @ @@ -98,7 +105,7 @@ private: UNKNOWN } kind; - char symbol[3]; + char symbol[3]; value_t value; std::size_t length; @@ -225,9 +232,8 @@ public: return parse_expr(in, empty_string, *global_scope, flags); } - value_expr& parse(std::istream& in, - const flags_t flags = EXPR_PARSE_RELAXED, - scope_t& scope) + value_expr& parse(std::istream& in, scope_t& scope, + const flags_t flags = EXPR_PARSE_RELAXED) { return parse_expr(in, empty_string, scope, flags); } @@ -238,8 +244,8 @@ public: return parse_expr(stream, str, *global_scope, flags); } - value_expr& parse(string& str, const flags_t flags = EXPR_PARSE_RELAXED, - scope_t& scope) + value_expr& parse(string& str, scope_t& scope, + const flags_t flags = EXPR_PARSE_RELAXED) { std::istringstream stream(str); return parse_expr(stream, str, scope, flags); diff --git a/reconcile.cc b/reconcile.cc index b92ff9f1..8a1be816 100644 --- a/reconcile.cc +++ b/reconcile.cc @@ -64,8 +64,8 @@ void reconcile_transactions::flush() cleared_balance.cast(value_t::AMOUNT); balance.cast(value_t::AMOUNT); - commodity_t& cb_comm = cleared_balance.as_amount_lval().commodity(); - commodity_t& b_comm = balance.as_amount_lval().commodity(); + commodity_t& cb_comm = cleared_balance.as_amount().commodity(); + commodity_t& b_comm = balance.as_amount().commodity(); balance -= cleared_balance; if (balance.type() >= value_t::BALANCE) @@ -76,7 +76,7 @@ void reconcile_transactions::flush() // then assume an exact match and return the results right away. amount_t& to_reconcile(balance.as_amount_lval()); pending_balance.cast(value_t::AMOUNT); - if (to_reconcile == pending_balance.as_amount_lval() || + if (to_reconcile == pending_balance.as_amount() || search_for_balance(to_reconcile, &first, first)) { push_to_handler(first); } else { @@ -186,6 +186,8 @@ transaction_t * parse_transaction(char * line, account_t * account, "Reduced amount is " << xact->amount); } + // jww (2008-07-24): I don't think this is right, since amount_expr is + // always NULL right now if (xact->amount_expr) { unsigned long end = (long)in.tellg(); xact->amount_expr.expr_str = string(line, beg, end - beg); @@ -227,15 +229,18 @@ transaction_t * parse_transaction(char * line, account_t * account, EXPR_PARSE_NO_MIGRATE)) throw new parse_error ("A transaction's cost must evaluate to a constant value"); - - unsigned long end = (long)in.tellg(); - - if (per_unit) - xact->cost_expr = (string("@") + - string(line, beg, end - beg)); - else - xact->cost_expr = (string("@@") + - string(line, beg, end - beg)); + assert(xact->cost->valid()); + + // jww (2008-07-24): I don't think this is right... + if (xact->cost_expr) { + unsigned long end = (long)in.tellg(); + if (per_unit) + xact->cost_expr->expr_str = (string("@") + + string(line, beg, end - beg)); + else + xact->cost_expr->expr_str = (string("@@") + + string(line, beg, end - beg)); + } } catch (error * err) { err_desc = "While parsing transaction cost:"; @@ -554,6 +559,7 @@ static void clock_out_from_timelog(std::list<time_entry_t>& time_entries, std::sprintf(buf, "%lds", long((curr->_date - event.checkin).seconds())); amount_t amt; amt.parse(buf); + assert(amt.valid()); transaction_t * xact = new transaction_t(event.account, amt, TRANSACTION_VIRTUAL); @@ -666,6 +672,7 @@ unsigned int textual_parser_t::parse(std::istream& in, case 'D': { // a default commodity for "entry" amount_t amt(skip_ws(line + 1)); + assert(amt.valid()); amount_t::current_pool->default_commodity = &amt.commodity(); break; } @@ -706,6 +713,7 @@ unsigned int textual_parser_t::parse(std::istream& in, string symbol; parse_symbol(symbol_and_price, symbol); amount_t price(symbol_and_price); + assert(price.valid()); if (commodity_t * commodity = amount_t::current_pool->find_or_create(symbol)) @@ -59,8 +59,10 @@ bool compute_amount(ptr_op_t expr, amount_t& amt, // Most of the time when computing the amount of a transaction this cast // will do nothing at all. + assert(result.valid()); result.in_place_cast(value_t::AMOUNT); amt = result.as_amount(); + assert(amt.valid()); } catch (error * err) { if (err->context.empty() || @@ -134,7 +136,7 @@ namespace { } else { temp.reset(new op_t(op_t::VALUE)); temp->set_value(value_t()); - expr->compute(temp->as_value(), details, context); + expr->compute(temp->as_value_lval(), details, context); } } else { temp.reset(new op_t(op_t::O_COMMA)); @@ -417,7 +419,7 @@ void op_t::compute(value_t& result, const details_t& details, long arg_index = 0; ptr_op_t expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); - result = result.as_datetime_lval(); + result = result.as_datetime(); break; } @@ -425,7 +427,7 @@ void op_t::compute(value_t& result, const details_t& details, long arg_index = 0; ptr_op_t expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); - result = result.as_datetime_lval(); + result = result.as_datetime(); if (! result) break; @@ -455,7 +457,7 @@ void op_t::compute(value_t& result, const details_t& details, throw new compute_error("Invalid date passed to year|month|day(date)", new valexpr_context(expr)); - datetime_t& moment(result.as_datetime_lval()); + const datetime_t& moment(result.as_datetime()); switch (kind) { case F_YEAR: result = (long)moment.date().year(); @@ -519,7 +521,7 @@ void op_t::compute(value_t& result, const details_t& details, throw new compute_error("Argument to commodity() must be a commoditized amount", new valexpr_context(expr)); amount_t temp("1"); - temp.set_commodity(result.as_amount_lval().commodity()); + temp.set_commodity(result.as_amount().commodity()); result = temp; break; } @@ -538,7 +540,7 @@ void op_t::compute(value_t& result, const details_t& details, ("Second argument to set_commodity() must be a commoditized amount", new valexpr_context(expr)); amount_t one("1"); - one.set_commodity(result.as_amount_lval().commodity()); + one.set_commodity(result.as_amount().commodity()); result = one; result *= temp; @@ -550,15 +552,15 @@ void op_t::compute(value_t& result, const details_t& details, ptr_op_t expr = find_leaf(context, 0, arg_index); expr->compute(result, details, context); - balance_t * bal = NULL; + const balance_t * bal = NULL; switch (result.type()) { case value_t::BALANCE_PAIR: - bal = &(result.as_balance_pair_lval().quantity()); + bal = &(result.as_balance_pair().quantity()); // fall through... case value_t::BALANCE: if (! bal) - bal = &(result.as_balance_lval()); + bal = &result.as_balance(); if (bal->amounts.size() < 2) { result.cast(value_t::AMOUNT); @@ -586,55 +588,47 @@ void op_t::compute(value_t& result, const details_t& details, break; } -#if 0 case F_CODE_MASK: - assert(mask); - if (details.entry) - result = mask->match(details.entry->code); + if (details.entry && details.entry->code) + result = as_mask().match(*details.entry->code); else result = false; break; case F_PAYEE_MASK: - assert(mask); if (details.entry) - result = mask->match(details.entry->payee); + result = as_mask().match(details.entry->payee); else result = false; break; case F_NOTE_MASK: - assert(mask); - if (details.xact) - result = mask->match(details.xact->note); + if (details.xact && details.xact->note) + result = as_mask().match(*details.xact->note); else result = false; break; case F_ACCOUNT_MASK: - assert(mask); if (details.account) - result = mask->match(details.account->fullname()); + result = as_mask().match(details.account->fullname()); else result = false; break; case F_SHORT_ACCOUNT_MASK: - assert(mask); if (details.account) - result = mask->match(details.account->name); + result = as_mask().match(details.account->name); else result = false; break; case F_COMMODITY_MASK: - assert(mask); if (details.xact) - result = mask->match(details.xact->amount.commodity().base_symbol()); + result = as_mask().match(details.xact->amount.commodity().base_symbol()); else result = false; break; -#endif case O_ARG: { long arg_index = 0; @@ -686,7 +680,7 @@ void op_t::compute(value_t& result, const details_t& details, throw new compute_error("Invalid date passed to P(value,date)", new valexpr_context(expr)); - result = result.value(moment.as_datetime_lval()); + result = result.value(moment.as_datetime()); break; } @@ -340,6 +340,7 @@ struct op_t : public noncopyable enum kind_t { // Constants VALUE, + MASK, ARG_INDEX, CONSTANTS, @@ -385,12 +386,15 @@ struct op_t : public noncopyable F_YEAR, F_MONTH, F_DAY, + + BEGIN_MASKS, F_CODE_MASK, F_PAYEE_MASK, F_NOTE_MASK, F_ACCOUNT_MASK, F_SHORT_ACCOUNT_MASK, F_COMMODITY_MASK, + END_MASKS, TERMINALS, @@ -449,12 +453,12 @@ struct op_t : public noncopyable bool is_long() const { return data.type() == typeid(unsigned int); } - unsigned int& as_long() { + unsigned int& as_long_lval() { assert(kind == ARG_INDEX || kind == O_ARG); return boost::get<unsigned int>(data); } const unsigned int& as_long() const { - return const_cast<op_t *>(this)->as_long(); + return const_cast<op_t *>(this)->as_long_lval(); } void set_long(unsigned int val) { data = val; @@ -467,14 +471,17 @@ struct op_t : public noncopyable } return false; } - value_t& as_value() { + value_t& as_value_lval() { assert(is_value()); - return boost::get<value_t>(data); + value_t& val(boost::get<value_t>(data)); + assert(val.valid()); + return val; } const value_t& as_value() const { - return const_cast<op_t *>(this)->as_value(); + return const_cast<op_t *>(this)->as_value_lval(); } void set_value(const value_t& val) { + assert(val.valid()); data = val; } @@ -485,26 +492,47 @@ struct op_t : public noncopyable } return false; } - string& as_string() { + string& as_string_lval() { assert(is_string()); return boost::get<value_t>(data).as_string_lval(); } const string& as_string() const { - return const_cast<op_t *>(this)->as_string(); + return const_cast<op_t *>(this)->as_string_lval(); } void set_string(const string& val) { data = value_t(val); } + bool is_mask() const { + if (kind > BEGIN_MASKS && kind < END_MASKS) { + 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; } - function_t& as_function() { + function_t& as_function_lval() { assert(kind == FUNCTION); return boost::get<function_t>(data); } const function_t& as_function() const { - return const_cast<op_t *>(this)->as_function(); + return const_cast<op_t *>(this)->as_function_lval(); } void set_function(const function_t& val) { data = val; @@ -514,24 +542,24 @@ struct op_t : public noncopyable bool is_name() const { return data.type() == typeid(node_t::nameid_t); } - node_t::nameid_t& as_name() { + node_t::nameid_t& as_name_lval() { assert(kind == NODE_ID || kind == ATTR_ID); return boost::get<node_t::nameid_t>(data); } const node_t::nameid_t& as_name() const { - return const_cast<op_t *>(this)->as_name(); + return const_cast<op_t *>(this)->as_name_lval(); } void set_name(const node_t::nameid_t& val) { data = val; } #endif - ptr_op_t& as_op() { + ptr_op_t& as_op_lval() { assert(kind > TERMINALS); return boost::get<ptr_op_t>(data); } const ptr_op_t& as_op() const { - return const_cast<op_t *>(this)->as_op(); + return const_cast<op_t *>(this)->as_op_lval(); } void acquire() const { @@ -562,7 +590,7 @@ struct op_t : public noncopyable ptr_op_t& right() { assert(kind > TERMINALS); - return as_op(); + return as_op_lval(); } const ptr_op_t& right() const { assert(kind > TERMINALS); @@ -628,7 +656,6 @@ struct op_t : public noncopyable } }; -#if 0 class op_predicate { ptr_op_t op; @@ -638,7 +665,6 @@ public: return op->calc(scope).to_boolean(); } }; -#endif class valexpr_context : public error_context { public: @@ -1218,17 +1218,17 @@ value_t value_t::abs() const { switch (type()) { case INTEGER: { - long val = const_cast<value_t&>(*this).as_long_lval(); + long val = as_long(); if (val < 0) return - val; return val; } case AMOUNT: - return const_cast<value_t&>(*this).as_amount_lval().abs(); + return as_amount().abs(); case BALANCE: - return const_cast<value_t&>(*this).as_balance_lval().abs(); + return as_balance().abs(); case BALANCE_PAIR: - return const_cast<value_t&>(*this).as_balance_pair_lval().abs(); + return as_balance_pair().abs(); default: break; } @@ -1436,27 +1436,27 @@ void value_t::print(std::ostream& out, const int first_width, break; case BOOLEAN: - out << const_cast<value_t&>(*this).as_boolean_lval(); + out << as_boolean(); break; case DATETIME: - out << const_cast<value_t&>(*this).as_datetime_lval(); + out << as_datetime(); break; case INTEGER: - out << const_cast<value_t&>(*this).as_long_lval(); + out << as_long(); break; case AMOUNT: - out << const_cast<value_t&>(*this).as_amount_lval(); + out << as_amount(); break; case STRING: - out << const_cast<value_t&>(*this).as_string_lval(); + out << as_string(); break; case POINTER: - out << boost::unsafe_any_cast<void *>(&const_cast<value_t&>(*this).as_any_pointer_lval()); + out << boost::unsafe_any_cast<const void *>(&as_any_pointer()); break; case SEQUENCE: { @@ -1486,36 +1486,51 @@ void value_t::print(std::ostream& out, const int first_width, } } +bool value_t::valid() const +{ + switch (type()) { + case AMOUNT: + return as_amount().valid(); + case BALANCE: + return as_balance().valid(); + case BALANCE_PAIR: + return as_balance_pair().valid(); + default: + break; + } + return true; +} + void value_context::describe(std::ostream& out) const throw() { if (! desc.empty()) out << desc << std::endl; - balance_t * ptr = NULL; + const balance_t * ptr = NULL; out << std::right; out.width(20); switch (bal.type()) { case value_t::BOOLEAN: - out << (const_cast<value_t&>(bal).as_boolean_lval() ? "true" : "false"); + out << (bal.as_boolean() ? "true" : "false"); break; case value_t::INTEGER: - out << const_cast<value_t&>(bal).as_long_lval(); + out << bal.as_long(); break; case value_t::DATETIME: - out << const_cast<value_t&>(bal).as_datetime_lval(); + out << bal.as_datetime(); break; case value_t::AMOUNT: - out << const_cast<value_t&>(bal).as_amount_lval(); + out << bal.as_amount(); break; case value_t::BALANCE: - ptr = &(const_cast<value_t&>(bal).as_balance_lval()); + ptr = &bal.as_balance(); // fall through... case value_t::BALANCE_PAIR: if (! ptr) - ptr = &(const_cast<value_t&>(bal).as_balance_pair_lval().quantity()); + ptr = &bal.as_balance_pair().quantity(); ptr->print(out, 20); break; @@ -433,8 +433,8 @@ private: public: /** - * Data manipulation methods. A value object may be truth tested - * for the existence of every type it can contain: + * Data manipulation methods. A value object may be truth tested for the + * existence of every type it can contain: * * is_boolean() * is_long() @@ -446,20 +446,19 @@ public: * is_sequence() * is_pointer() * - * There are corresponding as_*() methods that represent a value as - * a reference to its underlying type. For example, as_integer() - * returns a reference to a "const long". + * There are corresponding as_*() methods that represent a value as a + * reference to its underlying type. For example, as_long() returns a + * reference to a "const long". * - * There are also as_*_lval() methods, which represent the - * underlying data as a reference to a non-const type. The - * difference here is that an _lval() call causes the underlying - * data to be fully copied before the resulting reference is - * returned. + * There are also as_*_lval() methods, which represent the underlying data + * as a reference to a non-const type. The difference here is that an + * _lval() call causes the underlying data to be fully copied before the + * resulting reference is returned. * * Lastly, there are corresponding set_*(data) methods for directly - * assigning data of a particular type, rather than using the - * regular assignment operator (whose implementation simply calls - * the various set_ methods). + * assigning data of a particular type, rather than using the regular + * assignment operator (whose implementation simply calls the various set_ + * methods). */ bool is_boolean() const { return is_type(BOOLEAN); @@ -518,13 +517,18 @@ public: amount_t& as_amount_lval() { assert(is_amount()); _dup(); - return *(amount_t *) storage->data; + amount_t& amt(*(amount_t *) storage->data); + assert(amt.valid()); + return amt; } const amount_t& as_amount() const { assert(is_amount()); - return *(amount_t *) storage->data; + amount_t& amt(*(amount_t *) storage->data); + assert(amt.valid()); + return amt; } void set_amount(const amount_t& val) { + assert(val.valid()); set_type(AMOUNT); new((amount_t *) storage->data) amount_t(val); } @@ -535,13 +539,18 @@ public: balance_t& as_balance_lval() { assert(is_balance()); _dup(); - return **(balance_t **) storage->data; + balance_t& bal(**(balance_t **) storage->data); + assert(bal.valid()); + return bal; } const balance_t& as_balance() const { assert(is_balance()); - return **(balance_t **) storage->data; + balance_t& bal(**(balance_t **) storage->data); + assert(bal.valid()); + return bal; } void set_balance(const balance_t& val) { + assert(val.valid()); set_type(BALANCE); *(balance_t **) storage->data = new balance_t(val); } @@ -552,13 +561,18 @@ public: balance_pair_t& as_balance_pair_lval() { assert(is_balance_pair()); _dup(); - return **(balance_pair_t **) storage->data; + balance_pair_t& bal_pair(**(balance_pair_t **) storage->data); + assert(bal_pair.valid()); + return bal_pair; } const balance_pair_t& as_balance_pair() const { assert(is_balance_pair()); - return **(balance_pair_t **) storage->data; + balance_pair_t& bal_pair(**(balance_pair_t **) storage->data); + assert(bal_pair.valid()); + return bal_pair; } void set_balance_pair(const balance_pair_t& val) { + assert(val.valid()); set_type(BALANCE_PAIR); *(balance_pair_t **) storage->data = new balance_pair_t(val); } @@ -579,6 +593,10 @@ public: set_type(STRING); new((string *) storage->data) string(val); } + void set_string(const char * val = "") { + set_type(STRING); + new((string *) storage->data) string(val); + } bool is_sequence() const { return is_type(SEQUENCE); @@ -617,7 +635,7 @@ public: _dup(); return *any_cast<T *>(*(boost::any *) storage->data); } - boost::any as_any_pointer() const { + const boost::any& as_any_pointer() const { assert(is_pointer()); return *(boost::any *) storage->data; } @@ -730,13 +748,13 @@ public: if (! is_sequence()) in_place_cast(SEQUENCE); - value_t::sequence_t& seq(as_sequence_lval()); if (! val.is_sequence()) { if (! val.is_null()) - seq.push_back(val); + as_sequence_lval().push_back(val); } else { const value_t::sequence_t& val_seq(val.as_sequence()); - std::copy(val_seq.begin(), val_seq.end(), back_inserter(seq)); + std::copy(val_seq.begin(), val_seq.end(), + back_inserter(as_sequence_lval())); } } } @@ -750,11 +768,12 @@ public: } else { as_sequence_lval().pop_back(); - std::size_t new_size = as_sequence().size(); + const value_t::sequence_t& seq(as_sequence()); + std::size_t new_size = seq.size(); if (new_size == 0) _reset(); else if (new_size == 1) - *this = as_sequence().front(); + *this = seq.front(); } } @@ -811,6 +830,8 @@ public: /** * Debugging methods. */ + + bool valid() const; }; #define NULL_VALUE (value_t()) @@ -230,7 +230,7 @@ void handle_value(const value_t& value, // fall through... case value_t::AMOUNT: - xact.amount = temp.as_amount_lval(); + xact.amount = temp.as_amount(); break; case value_t::BALANCE: |