More work toward getting the textual parser working again. Since this means

that value expressions must work, there are a lot of details involved.
This commit is contained in:
John Wiegley 2008-07-24 09:02:47 -04:00
parent 7409b050be
commit 643f2d33cf
16 changed files with 380 additions and 210 deletions

32
acprep
View file

@ -35,6 +35,10 @@ LIBDIRS="$LIBDIRS -L/usr/local/lib"
PYTHON_HOME="/usr" PYTHON_HOME="/usr"
if [ -x /usr/bin/g++-4.2 ]; then
CXX=g++-4.2
fi
SYSTEM=`uname -s` SYSTEM=`uname -s`
@ -85,44 +89,50 @@ while [ -n "$1" ]; do
SWITCHES="$SWITCHES --disable-shared" SWITCHES="$SWITCHES --disable-shared"
CPPFLAGS="$CPPFLAGS -DBOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING=1" CPPFLAGS="$CPPFLAGS -DBOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING=1"
CPPFLAGS="$CPPFLAGS -DBOOST_MULTI_INDEX_ENABLE_SAFE_MODE=1" CPPFLAGS="$CPPFLAGS -DBOOST_MULTI_INDEX_ENABLE_SAFE_MODE=1"
;; shift 1 ;;
--debug) --debug)
SWITCHES="$SWITCHES --enable-debug" SWITCHES="$SWITCHES --enable-debug"
#CPPFLAGS="$CPPFLAGS -D_GLIBCXX_DEBUG=1" #CPPFLAGS="$CPPFLAGS -D_GLIBCXX_DEBUG=1"
CXXFLAGS="$CXXFLAGS -g" ;; CXXFLAGS="$CXXFLAGS -g"
shift 1 ;;
--boost) --boost)
shift 1 shift 1
SWITCHES="$SWITCHES --with-boost-suffix=$1" SWITCHES="$SWITCHES --with-boost-suffix=$1"
;; shift 1 ;;
--gcov) --gcov)
CXXFLAGS="$CXXFLAGS -fprofile-arcs -ftest-coverage" ;; CXXFLAGS="$CXXFLAGS -fprofile-arcs -ftest-coverage"
shift 1 ;;
--gprof) --gprof)
CXXFLAGS="$CXXFLAGS -g -pg" ;; CXXFLAGS="$CXXFLAGS -g -pg"
shift 1 ;;
--python) --python)
if [ -d "$PYTHON_HOME" ]; then if [ -d "$PYTHON_HOME" ]; then
SWITCHES="$SWITCHES --enable-python" SWITCHES="$SWITCHES --enable-python"
CPPFLAGS="$CPPFLAGS -I$PYTHON_HOME/include/python2.5" CPPFLAGS="$CPPFLAGS -I$PYTHON_HOME/include/python2.5"
LDFLAGS="$LDFLAGS -L$PYTHON_HOME/lib/python2.5/config" LDFLAGS="$LDFLAGS -L$PYTHON_HOME/lib/python2.5/config"
fi ;; fi
shift 1;;
--pic) --pic)
CXXFLAGS="$CXXFLAGS -fPIC" ;; CXXFLAGS="$CXXFLAGS -fPIC"
shift 1 ;;
--opt) --opt)
CXXFLAGS="$CXXFLAGS -fomit-frame-pointer -O3" ;; CXXFLAGS="$CXXFLAGS -fomit-frame-pointer -O3"
shift 1 ;;
--local) --local)
LOCAL=true ;; LOCAL=true
shift 1 ;;
*) *)
break ;; break ;;
esac esac
shift 1
done done

View file

@ -97,6 +97,25 @@ struct amount_t::bigint_t : public supports_flags<>
assert(ref == 0); assert(ref == 0);
mpz_clear(val); 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() uint_fast32_t amount_t::sizeof_bigint_t()
@ -139,6 +158,8 @@ void amount_t::shutdown()
void amount_t::_copy(const amount_t& amt) void amount_t::_copy(const amount_t& amt)
{ {
assert(amt.valid());
if (quantity != amt.quantity) { if (quantity != amt.quantity) {
if (quantity) if (quantity)
_release(); _release();
@ -155,15 +176,21 @@ void amount_t::_copy(const amount_t& amt)
} }
} }
commodity_ = amt.commodity_; commodity_ = amt.commodity_;
assert(valid());
} }
void amount_t::_dup() void amount_t::_dup()
{ {
assert(valid());
if (quantity->ref > 1) { if (quantity->ref > 1) {
bigint_t * q = new bigint_t(*quantity); bigint_t * q = new bigint_t(*quantity);
_release(); _release();
quantity = q; quantity = q;
} }
assert(valid());
} }
void amount_t::_resize(precision_t prec) 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); mpz_mul(MPZ(quantity), MPZ(quantity), divisor);
quantity->prec = prec; quantity->prec = prec;
assert(valid());
} }
void amount_t::_clear() void amount_t::_clear()
@ -195,6 +224,8 @@ void amount_t::_clear()
void amount_t::_release() void amount_t::_release()
{ {
assert(valid());
DEBUG("amounts.refs", quantity << " ref--, now " << (quantity->ref - 1)); DEBUG("amounts.refs", quantity << " ref--, now " << (quantity->ref - 1));
if (--quantity->ref == 0) { if (--quantity->ref == 0) {
@ -202,7 +233,11 @@ void amount_t::_release()
quantity->~bigint_t(); quantity->~bigint_t();
else else
checked_delete(quantity); 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 int amount_t::compare(const amount_t& amt) const
{ {
assert(amt.valid());
if (! quantity || ! amt.quantity) { if (! quantity || ! amt.quantity) {
if (quantity) if (quantity)
throw_(amount_error, "Cannot compare an amount to an uninitialized amount"); 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) amount_t& amount_t::operator+=(const amount_t& amt)
{ {
assert(amt.valid());
if (! quantity || ! amt.quantity) { if (! quantity || ! amt.quantity) {
if (quantity) if (quantity)
throw_(amount_error, "Cannot add an amount to an uninitialized amount"); 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) amount_t& amount_t::operator-=(const amount_t& amt)
{ {
assert(amt.valid());
if (! quantity || ! amt.quantity) { if (! quantity || ! amt.quantity) {
if (quantity) if (quantity)
throw_(amount_error, "Cannot subtract an amount from an uninitialized amount"); 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) amount_t& amount_t::operator*=(const amount_t& amt)
{ {
assert(amt.valid());
if (! quantity || ! amt.quantity) { if (! quantity || ! amt.quantity) {
if (quantity) if (quantity)
throw_(amount_error, "Cannot multiply an amount by an uninitialized amount"); 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) amount_t& amount_t::operator/=(const amount_t& amt)
{ {
assert(amt.valid());
if (! quantity || ! amt.quantity) { if (! quantity || ! amt.quantity) {
if (quantity) if (quantity)
throw_(amount_error, "Cannot divide an amount by an uninitialized amount"); 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(); in_place_reduce();
safe_holder.release(); // `this->quantity' owns the pointer safe_holder.release(); // `this->quantity' owns the pointer
assert(valid());
} }
void amount_t::parse_conversion(const string& larger_str, 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, void amount_t::print(std::ostream& _out, bool omit_commodity,
bool full_precision) const bool full_precision) const
{ {
assert(valid());
if (! quantity) if (! quantity)
throw_(amount_error, "Cannot write out an uninitialized amount"); 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 bool amount_t::valid() const
{ {
if (quantity) { if (quantity) {
if (! quantity->valid())
return false;
if (quantity->ref == 0) { if (quantity->ref == 0) {
DEBUG("ledger.validate", "amount_t: quantity->ref == 0"); DEBUG("ledger.validate", "amount_t: quantity->ref == 0");
return false; return false;

View file

@ -351,7 +351,7 @@ public:
* valid() returns true if the balances within the balance pair are * valid() returns true if the balances within the balance pair are
* valid. * valid.
*/ */
virtual bool valid() { bool valid() const {
if (! balance_t::valid()) if (! balance_t::valid())
return false; return false;

View file

@ -827,16 +827,16 @@ void write_value(std::ostream& out, const value_t& val)
switch (val.type()) { switch (val.type()) {
case value_t::BOOLEAN: case value_t::BOOLEAN:
write_bool(out, const_cast<value_t&>(val).as_boolean_lval()); write_bool(out, val.as_boolean());
break; break;
case value_t::INTEGER: case value_t::INTEGER:
write_long(out, const_cast<value_t&>(val).as_long_lval()); write_long(out, val.as_long());
break; break;
case value_t::DATETIME: case value_t::DATETIME:
write_number(out, const_cast<value_t&>(val).as_datetime_lval()); write_number(out,val.as_datetime());
break; break;
case value_t::AMOUNT: case value_t::AMOUNT:
write_amount(out, const_cast<value_t&>(val).as_amount_lval()); write_amount(out, val.as_amount());
break; break;
//case value_t::BALANCE: //case value_t::BALANCE:

View file

@ -67,7 +67,7 @@ entry_t * derive_new_entry(journal_t& journal,
value_t total = account_xdata(*acct).total; value_t total = account_xdata(*acct).total;
if (total.is_type(value_t::AMOUNT)) if (total.is_type(value_t::AMOUNT))
xact->amount.set_commodity(total.as_amount_lval().commodity()); xact->amount.set_commodity(total.as_amount().commodity());
} }
} }

View file

@ -355,8 +355,8 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
if (! calc) if (! calc)
break; break;
value_t value; value_t value;
balance_t * bal = NULL; const balance_t * bal = NULL;
calc->compute(value, details); calc->compute(value, details);
@ -378,54 +378,54 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
switch (value.type()) { switch (value.type()) {
case value_t::BOOLEAN: case value_t::BOOLEAN:
out << (value.as_boolean_lval() ? "true" : "false"); out << (value.as_boolean() ? "true" : "false");
break; break;
case value_t::INTEGER: case value_t::INTEGER:
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) {
if (ansi_invert) { if (ansi_invert) {
if (value.as_long_lval() > 0) { if (value.as_long() > 0) {
mark_red(out, elem); mark_red(out, elem);
highlighted = true; highlighted = true;
} }
} else { } else {
if (value.as_long_lval() < 0) { if (value.as_long() < 0) {
mark_red(out, elem); mark_red(out, elem);
highlighted = true; highlighted = true;
} }
} }
} }
out << value.as_long_lval(); out << value.as_long();
break; break;
case value_t::DATETIME: case value_t::DATETIME:
out << value.as_datetime_lval(); out << value.as_datetime();
break; break;
case value_t::AMOUNT: case value_t::AMOUNT:
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) {
if (ansi_invert) { if (ansi_invert) {
if (value.as_amount_lval().sign() > 0) { if (value.as_amount().sign() > 0) {
mark_red(out, elem); mark_red(out, elem);
highlighted = true; highlighted = true;
} }
} else { } else {
if (value.as_amount_lval().sign() < 0) { if (value.as_amount().sign() < 0) {
mark_red(out, elem); mark_red(out, elem);
highlighted = true; highlighted = true;
} }
} }
} }
out << value.as_amount_lval(); out << value.as_amount();
break; break;
case value_t::BALANCE: case value_t::BALANCE:
bal = &(value.as_balance_lval()); bal = &(value.as_balance());
// fall through... // fall through...
case value_t::BALANCE_PAIR: case value_t::BALANCE_PAIR:
if (! bal) if (! bal)
bal = &(value.as_balance_pair_lval().quantity()); bal = &(value.as_balance_pair().quantity());
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) {
if (ansi_invert) { if (ansi_invert) {
@ -949,11 +949,11 @@ void format_equity::flush()
summary.data = &xdata; summary.data = &xdata;
if (total.type() >= value_t::BALANCE) { if (total.type() >= value_t::BALANCE) {
balance_t * bal; const balance_t * bal;
if (total.is_type(value_t::BALANCE)) 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)) else if (total.is_type(value_t::BALANCE_PAIR))
bal = &(total.as_balance_pair_lval().quantity()); bal = &(total.as_balance_pair().quantity());
else else
assert(false); assert(false);
@ -977,11 +977,11 @@ void format_equity::operator()(account_t& account)
value_t val = account_xdata_(account).value; value_t val = account_xdata_(account).value;
if (val.type() >= value_t::BALANCE) { if (val.type() >= value_t::BALANCE) {
balance_t * bal; const balance_t * bal;
if (val.is_type(value_t::BALANCE)) 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)) else if (val.is_type(value_t::BALANCE_PAIR))
bal = &(val.as_balance_pair_lval().quantity()); bal = &(val.as_balance_pair().quantity());
else else
assert(false); assert(false);

View file

@ -128,7 +128,7 @@ bool entry_base_t::finalize()
for (transactions_list::const_iterator x = transactions.begin(); for (transactions_list::const_iterator x = transactions.begin();
x != transactions.end(); x != transactions.end();
x++) x++) {
if (! (*x)->has_flags(TRANSACTION_VIRTUAL) || if (! (*x)->has_flags(TRANSACTION_VIRTUAL) ||
(*x)->has_flags(TRANSACTION_BALANCE)) { (*x)->has_flags(TRANSACTION_BALANCE)) {
amount_t& p((*x)->cost ? *(*x)->cost : (*x)->amount); amount_t& p((*x)->cost ? *(*x)->cost : (*x)->amount);
@ -154,6 +154,9 @@ bool entry_base_t::finalize()
saw_null = true; saw_null = true;
} }
} }
}
assert(balance.valid());
// If it's a null entry, then let the user have their fun // If it's a null entry, then let the user have their fun
if (no_amounts) if (no_amounts)
@ -177,21 +180,24 @@ bool entry_base_t::finalize()
// the balance. This is done for the last eligible commodity. // the balance. This is done for the last eligible commodity.
if (! saw_null && balance && balance.is_balance()) { 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) { if (bal.amounts.size() == 2) {
transactions_list::const_iterator x = transactions.begin(); transactions_list::const_iterator x = transactions.begin();
assert((*x)->amount); assert(! (*x)->amount.is_null());
commodity_t& this_comm = (*x)->amount.commodity(); commodity_t& this_comm = (*x)->amount.commodity();
balance_t::amounts_map::const_iterator this_bal = balance_t::amounts_map::const_iterator this_bal =
bal.amounts.find(&this_comm); bal.amounts.find(&this_comm);
assert(this_bal != bal.amounts.end());
balance_t::amounts_map::const_iterator other_bal = balance_t::amounts_map::const_iterator other_bal =
bal.amounts.begin(); bal.amounts.begin();
if (this_bal == other_bal) if (this_bal == other_bal)
other_bal++; other_bal++;
amount_t per_unit_cost = 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++) { for (; x != transactions.end(); x++) {
if ((*x)->cost || (*x)->has_flags(TRANSACTION_VIRTUAL) || if ((*x)->cost || (*x)->has_flags(TRANSACTION_VIRTUAL) ||
@ -241,12 +247,12 @@ bool entry_base_t::finalize()
const balance_t * bal = NULL; const balance_t * bal = NULL;
switch (balance.type()) { switch (balance.type()) {
case value_t::BALANCE_PAIR: case value_t::BALANCE_PAIR:
bal = &balance.as_balance_pair_lval().quantity(); bal = &balance.as_balance_pair().quantity();
// fall through... // fall through...
case value_t::BALANCE: case value_t::BALANCE:
if (! bal) if (! bal)
bal = &balance.as_balance_lval(); bal = &balance.as_balance();
if (bal->amounts.size() < 2) { if (bal->amounts.size() < 2) {
balance.cast(value_t::AMOUNT); balance.cast(value_t::AMOUNT);

View file

@ -155,10 +155,17 @@ void parser_t::token_t::next(std::istream& in, const flags_t flags)
in.get(c); in.get(c);
kind = AT_SYM; kind = AT_SYM;
break; break;
#if 0
case '$': case '$':
in.get(c); in.get(c);
kind = DOLLAR; kind = DOLLAR;
break; break;
#endif
case '&':
in.get(c);
kind = KW_AND;
break;
case '(': case '(':
in.get(c); in.get(c);
@ -171,7 +178,7 @@ void parser_t::token_t::next(std::istream& in, const flags_t flags)
case '[': { case '[': {
in.get(c); in.get(c);
if (flags & EXPR_PARSE_ALLOW_DATE) { if (! (flags & EXPR_PARSE_NO_DATES)) {
char buf[256]; char buf[256];
READ_INTO_(in, buf, 255, c, length, c != ']'); READ_INTO_(in, buf, 255, c, length, c != ']');
if (c != ']') if (c != ']')
@ -193,6 +200,7 @@ void parser_t::token_t::next(std::istream& in, const flags_t flags)
break; break;
} }
case '\'': case '\'':
case '"': { case '"': {
char delim; char delim;
@ -249,10 +257,68 @@ void parser_t::token_t::next(std::istream& in, const flags_t flags)
kind = STAR; kind = STAR;
break; break;
#if 0
case '/': case '/':
in.get(c); in.get(c);
kind = SLASH; kind = SLASH;
break; 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 '=': case '=':
in.get(c); in.get(c);
@ -336,7 +402,7 @@ void parser_t::token_t::next(std::istream& in, const flags_t flags)
kind = VALUE; kind = VALUE;
value = temp; value = temp;
} }
catch (amount_error& err) { catch (const amount_error& err) {
// If the amount had no commodity, it must be an unambiguous // If the amount had no commodity, it must be an unambiguous
// variable reference // 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); node->set_value(tok.value);
break; 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: { case token_t::IDENT: {
#if 0 #if 0
#ifdef USE_BOOST_PYTHON #ifdef USE_BOOST_PYTHON
@ -473,12 +564,12 @@ parser_t::parse_value_term(std::istream& in, scope_t& scope, const flags_t tflag
break; break;
} }
#if 0
case token_t::AT_SYM: { case token_t::AT_SYM: {
tok = next_token(in, tflags); tok = next_token(in, tflags);
if (tok.kind != token_t::IDENT) if (tok.kind != token_t::IDENT)
throw_(parse_error, "@ symbol must be followed by attribute name"); throw_(parse_error, "@ symbol must be followed by attribute name");
#if 0
string ident = tok.value.as_string(); string ident = tok.value.as_string();
if (optional<node_t::nameid_t> id = document_t::lookup_builtin_id(ident)) { if (optional<node_t::nameid_t> id = document_t::lookup_builtin_id(ident)) {
node = new op_t(op_t::ATTR_ID); 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 = new op_t(op_t::ATTR_NAME);
node->set_string(ident); node->set_string(ident);
} }
#endif
break; break;
} }
#if 0
case token_t::DOLLAR: case token_t::DOLLAR:
tok = next_token(in, tflags); tok = next_token(in, tflags);
if (tok.kind != token_t::IDENT) 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 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; 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 // A very quick optimization
if (term->kind == op_t::VALUE) { if (term->kind == op_t::VALUE) {
term->as_value().in_place_negate(); term->as_value_lval().in_place_negate();
node = term; node = term;
} else { } else {
node = new op_t(op_t::O_NOT); 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 // A very quick optimization
if (term->kind == op_t::VALUE) { if (term->kind == op_t::VALUE) {
term->as_value().in_place_negate(); term->as_value_lval().in_place_negate();
node = term; node = term;
} else { } else {
node = new op_t(op_t::O_NEG); 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; break;
// Other // 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 '{': { case '{': {
amount_t temp; amount_t temp;
temp.parse(in, AMOUNT_PARSE_NO_MIGRATE); temp.parse(in, AMOUNT_PARSE_NO_MIGRATE);

View file

@ -46,7 +46,7 @@ class parser_t
#define EXPR_PARSE_RELAXED 0x02 #define EXPR_PARSE_RELAXED 0x02
#define EXPR_PARSE_NO_MIGRATE 0x04 #define EXPR_PARSE_NO_MIGRATE 0x04
#define EXPR_PARSE_NO_REDUCE 0x08 #define EXPR_PARSE_NO_REDUCE 0x08
#define EXPR_PARSE_ALLOW_DATE 0x10 #define EXPR_PARSE_NO_DATES 0x10
public: public:
typedef uint_least8_t flags_t; typedef uint_least8_t flags_t;
@ -57,6 +57,13 @@ private:
enum kind_t { enum kind_t {
VALUE, // any kind of literal value 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_:]* IDENT, // [A-Za-z_][-A-Za-z0-9_:]*
DOLLAR, // $ DOLLAR, // $
AT_SYM, // @ AT_SYM, // @
@ -98,7 +105,7 @@ private:
UNKNOWN UNKNOWN
} kind; } kind;
char symbol[3]; char symbol[3];
value_t value; value_t value;
std::size_t length; std::size_t length;
@ -225,9 +232,8 @@ public:
return parse_expr(in, empty_string, *global_scope, flags); return parse_expr(in, empty_string, *global_scope, flags);
} }
value_expr& parse(std::istream& in, value_expr& parse(std::istream& in, scope_t& scope,
const flags_t flags = EXPR_PARSE_RELAXED, const flags_t flags = EXPR_PARSE_RELAXED)
scope_t& scope)
{ {
return parse_expr(in, empty_string, scope, flags); return parse_expr(in, empty_string, scope, flags);
} }
@ -238,8 +244,8 @@ public:
return parse_expr(stream, str, *global_scope, flags); return parse_expr(stream, str, *global_scope, flags);
} }
value_expr& parse(string& str, const flags_t flags = EXPR_PARSE_RELAXED, value_expr& parse(string& str, scope_t& scope,
scope_t& scope) const flags_t flags = EXPR_PARSE_RELAXED)
{ {
std::istringstream stream(str); std::istringstream stream(str);
return parse_expr(stream, str, scope, flags); return parse_expr(stream, str, scope, flags);

View file

@ -64,8 +64,8 @@ void reconcile_transactions::flush()
cleared_balance.cast(value_t::AMOUNT); cleared_balance.cast(value_t::AMOUNT);
balance.cast(value_t::AMOUNT); balance.cast(value_t::AMOUNT);
commodity_t& cb_comm = cleared_balance.as_amount_lval().commodity(); commodity_t& cb_comm = cleared_balance.as_amount().commodity();
commodity_t& b_comm = balance.as_amount_lval().commodity(); commodity_t& b_comm = balance.as_amount().commodity();
balance -= cleared_balance; balance -= cleared_balance;
if (balance.type() >= value_t::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. // then assume an exact match and return the results right away.
amount_t& to_reconcile(balance.as_amount_lval()); amount_t& to_reconcile(balance.as_amount_lval());
pending_balance.cast(value_t::AMOUNT); 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)) { search_for_balance(to_reconcile, &first, first)) {
push_to_handler(first); push_to_handler(first);
} else { } else {

View file

@ -186,6 +186,8 @@ transaction_t * parse_transaction(char * line, account_t * account,
"Reduced amount is " << xact->amount); "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) { if (xact->amount_expr) {
unsigned long end = (long)in.tellg(); unsigned long end = (long)in.tellg();
xact->amount_expr.expr_str = string(line, beg, end - beg); 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)) EXPR_PARSE_NO_MIGRATE))
throw new parse_error throw new parse_error
("A transaction's cost must evaluate to a constant value"); ("A transaction's cost must evaluate to a constant value");
assert(xact->cost->valid());
unsigned long end = (long)in.tellg(); // jww (2008-07-24): I don't think this is right...
if (xact->cost_expr) {
if (per_unit) unsigned long end = (long)in.tellg();
xact->cost_expr = (string("@") + if (per_unit)
string(line, beg, end - beg)); xact->cost_expr->expr_str = (string("@") +
else string(line, beg, end - beg));
xact->cost_expr = (string("@@") + else
string(line, beg, end - beg)); xact->cost_expr->expr_str = (string("@@") +
string(line, beg, end - beg));
}
} }
catch (error * err) { catch (error * err) {
err_desc = "While parsing transaction cost:"; 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())); std::sprintf(buf, "%lds", long((curr->_date - event.checkin).seconds()));
amount_t amt; amount_t amt;
amt.parse(buf); amt.parse(buf);
assert(amt.valid());
transaction_t * xact transaction_t * xact
= new transaction_t(event.account, amt, TRANSACTION_VIRTUAL); = 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" case 'D': { // a default commodity for "entry"
amount_t amt(skip_ws(line + 1)); amount_t amt(skip_ws(line + 1));
assert(amt.valid());
amount_t::current_pool->default_commodity = &amt.commodity(); amount_t::current_pool->default_commodity = &amt.commodity();
break; break;
} }
@ -706,6 +713,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
string symbol; string symbol;
parse_symbol(symbol_and_price, symbol); parse_symbol(symbol_and_price, symbol);
amount_t price(symbol_and_price); amount_t price(symbol_and_price);
assert(price.valid());
if (commodity_t * commodity = if (commodity_t * commodity =
amount_t::current_pool->find_or_create(symbol)) amount_t::current_pool->find_or_create(symbol))

View file

@ -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 // Most of the time when computing the amount of a transaction this cast
// will do nothing at all. // will do nothing at all.
assert(result.valid());
result.in_place_cast(value_t::AMOUNT); result.in_place_cast(value_t::AMOUNT);
amt = result.as_amount(); amt = result.as_amount();
assert(amt.valid());
} }
catch (error * err) { catch (error * err) {
if (err->context.empty() || if (err->context.empty() ||
@ -134,7 +136,7 @@ namespace {
} else { } else {
temp.reset(new op_t(op_t::VALUE)); temp.reset(new op_t(op_t::VALUE));
temp->set_value(value_t()); temp->set_value(value_t());
expr->compute(temp->as_value(), details, context); expr->compute(temp->as_value_lval(), details, context);
} }
} else { } else {
temp.reset(new op_t(op_t::O_COMMA)); 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; long arg_index = 0;
ptr_op_t expr = find_leaf(context, 0, arg_index); ptr_op_t expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context); expr->compute(result, details, context);
result = result.as_datetime_lval(); result = result.as_datetime();
break; break;
} }
@ -425,7 +427,7 @@ void op_t::compute(value_t& result, const details_t& details,
long arg_index = 0; long arg_index = 0;
ptr_op_t expr = find_leaf(context, 0, arg_index); ptr_op_t expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context); expr->compute(result, details, context);
result = result.as_datetime_lval(); result = result.as_datetime();
if (! result) if (! result)
break; 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)", throw new compute_error("Invalid date passed to year|month|day(date)",
new valexpr_context(expr)); new valexpr_context(expr));
datetime_t& moment(result.as_datetime_lval()); const datetime_t& moment(result.as_datetime());
switch (kind) { switch (kind) {
case F_YEAR: case F_YEAR:
result = (long)moment.date().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", throw new compute_error("Argument to commodity() must be a commoditized amount",
new valexpr_context(expr)); new valexpr_context(expr));
amount_t temp("1"); amount_t temp("1");
temp.set_commodity(result.as_amount_lval().commodity()); temp.set_commodity(result.as_amount().commodity());
result = temp; result = temp;
break; 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", ("Second argument to set_commodity() must be a commoditized amount",
new valexpr_context(expr)); new valexpr_context(expr));
amount_t one("1"); amount_t one("1");
one.set_commodity(result.as_amount_lval().commodity()); one.set_commodity(result.as_amount().commodity());
result = one; result = one;
result *= temp; 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); ptr_op_t expr = find_leaf(context, 0, arg_index);
expr->compute(result, details, context); expr->compute(result, details, context);
balance_t * bal = NULL; const balance_t * bal = NULL;
switch (result.type()) { switch (result.type()) {
case value_t::BALANCE_PAIR: case value_t::BALANCE_PAIR:
bal = &(result.as_balance_pair_lval().quantity()); bal = &(result.as_balance_pair().quantity());
// fall through... // fall through...
case value_t::BALANCE: case value_t::BALANCE:
if (! bal) if (! bal)
bal = &(result.as_balance_lval()); bal = &result.as_balance();
if (bal->amounts.size() < 2) { if (bal->amounts.size() < 2) {
result.cast(value_t::AMOUNT); result.cast(value_t::AMOUNT);
@ -586,55 +588,47 @@ void op_t::compute(value_t& result, const details_t& details,
break; break;
} }
#if 0
case F_CODE_MASK: case F_CODE_MASK:
assert(mask); if (details.entry && details.entry->code)
if (details.entry) result = as_mask().match(*details.entry->code);
result = mask->match(details.entry->code);
else else
result = false; result = false;
break; break;
case F_PAYEE_MASK: case F_PAYEE_MASK:
assert(mask);
if (details.entry) if (details.entry)
result = mask->match(details.entry->payee); result = as_mask().match(details.entry->payee);
else else
result = false; result = false;
break; break;
case F_NOTE_MASK: case F_NOTE_MASK:
assert(mask); if (details.xact && details.xact->note)
if (details.xact) result = as_mask().match(*details.xact->note);
result = mask->match(details.xact->note);
else else
result = false; result = false;
break; break;
case F_ACCOUNT_MASK: case F_ACCOUNT_MASK:
assert(mask);
if (details.account) if (details.account)
result = mask->match(details.account->fullname()); result = as_mask().match(details.account->fullname());
else else
result = false; result = false;
break; break;
case F_SHORT_ACCOUNT_MASK: case F_SHORT_ACCOUNT_MASK:
assert(mask);
if (details.account) if (details.account)
result = mask->match(details.account->name); result = as_mask().match(details.account->name);
else else
result = false; result = false;
break; break;
case F_COMMODITY_MASK: case F_COMMODITY_MASK:
assert(mask);
if (details.xact) if (details.xact)
result = mask->match(details.xact->amount.commodity().base_symbol()); result = as_mask().match(details.xact->amount.commodity().base_symbol());
else else
result = false; result = false;
break; break;
#endif
case O_ARG: { case O_ARG: {
long arg_index = 0; 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)", throw new compute_error("Invalid date passed to P(value,date)",
new valexpr_context(expr)); new valexpr_context(expr));
result = result.value(moment.as_datetime_lval()); result = result.value(moment.as_datetime());
break; break;
} }

View file

@ -340,6 +340,7 @@ struct op_t : public noncopyable
enum kind_t { enum kind_t {
// Constants // Constants
VALUE, VALUE,
MASK,
ARG_INDEX, ARG_INDEX,
CONSTANTS, CONSTANTS,
@ -385,12 +386,15 @@ struct op_t : public noncopyable
F_YEAR, F_YEAR,
F_MONTH, F_MONTH,
F_DAY, F_DAY,
BEGIN_MASKS,
F_CODE_MASK, F_CODE_MASK,
F_PAYEE_MASK, F_PAYEE_MASK,
F_NOTE_MASK, F_NOTE_MASK,
F_ACCOUNT_MASK, F_ACCOUNT_MASK,
F_SHORT_ACCOUNT_MASK, F_SHORT_ACCOUNT_MASK,
F_COMMODITY_MASK, F_COMMODITY_MASK,
END_MASKS,
TERMINALS, TERMINALS,
@ -449,12 +453,12 @@ struct op_t : public noncopyable
bool is_long() const { bool is_long() const {
return data.type() == typeid(unsigned int); return data.type() == typeid(unsigned int);
} }
unsigned int& as_long() { unsigned int& as_long_lval() {
assert(kind == ARG_INDEX || kind == O_ARG); assert(kind == ARG_INDEX || kind == O_ARG);
return boost::get<unsigned int>(data); return boost::get<unsigned int>(data);
} }
const unsigned int& as_long() const { 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) { void set_long(unsigned int val) {
data = val; data = val;
@ -467,14 +471,17 @@ struct op_t : public noncopyable
} }
return false; return false;
} }
value_t& as_value() { value_t& as_value_lval() {
assert(is_value()); 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 { 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) { void set_value(const value_t& val) {
assert(val.valid());
data = val; data = val;
} }
@ -485,26 +492,47 @@ struct op_t : public noncopyable
} }
return false; return false;
} }
string& as_string() { string& as_string_lval() {
assert(is_string()); assert(is_string());
return boost::get<value_t>(data).as_string_lval(); return boost::get<value_t>(data).as_string_lval();
} }
const string& as_string() const { 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) { void set_string(const string& val) {
data = value_t(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 { bool is_function() const {
return kind == FUNCTION; return kind == FUNCTION;
} }
function_t& as_function() { function_t& as_function_lval() {
assert(kind == FUNCTION); assert(kind == FUNCTION);
return boost::get<function_t>(data); return boost::get<function_t>(data);
} }
const function_t& as_function() const { 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) { void set_function(const function_t& val) {
data = val; data = val;
@ -514,24 +542,24 @@ struct op_t : public noncopyable
bool is_name() const { bool is_name() const {
return data.type() == typeid(node_t::nameid_t); 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); assert(kind == NODE_ID || kind == ATTR_ID);
return boost::get<node_t::nameid_t>(data); return boost::get<node_t::nameid_t>(data);
} }
const node_t::nameid_t& as_name() const { 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) { void set_name(const node_t::nameid_t& val) {
data = val; data = val;
} }
#endif #endif
ptr_op_t& as_op() { ptr_op_t& as_op_lval() {
assert(kind > TERMINALS); assert(kind > TERMINALS);
return boost::get<ptr_op_t>(data); return boost::get<ptr_op_t>(data);
} }
const ptr_op_t& as_op() const { 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 { void acquire() const {
@ -562,7 +590,7 @@ struct op_t : public noncopyable
ptr_op_t& right() { ptr_op_t& right() {
assert(kind > TERMINALS); assert(kind > TERMINALS);
return as_op(); return as_op_lval();
} }
const ptr_op_t& right() const { const ptr_op_t& right() const {
assert(kind > TERMINALS); assert(kind > TERMINALS);
@ -628,7 +656,6 @@ struct op_t : public noncopyable
} }
}; };
#if 0
class op_predicate { class op_predicate {
ptr_op_t op; ptr_op_t op;
@ -638,7 +665,6 @@ public:
return op->calc(scope).to_boolean(); return op->calc(scope).to_boolean();
} }
}; };
#endif
class valexpr_context : public error_context { class valexpr_context : public error_context {
public: public:

View file

@ -1218,17 +1218,17 @@ value_t value_t::abs() const
{ {
switch (type()) { switch (type()) {
case INTEGER: { case INTEGER: {
long val = const_cast<value_t&>(*this).as_long_lval(); long val = as_long();
if (val < 0) if (val < 0)
return - val; return - val;
return val; return val;
} }
case AMOUNT: case AMOUNT:
return const_cast<value_t&>(*this).as_amount_lval().abs(); return as_amount().abs();
case BALANCE: case BALANCE:
return const_cast<value_t&>(*this).as_balance_lval().abs(); return as_balance().abs();
case BALANCE_PAIR: case BALANCE_PAIR:
return const_cast<value_t&>(*this).as_balance_pair_lval().abs(); return as_balance_pair().abs();
default: default:
break; break;
} }
@ -1436,27 +1436,27 @@ void value_t::print(std::ostream& out, const int first_width,
break; break;
case BOOLEAN: case BOOLEAN:
out << const_cast<value_t&>(*this).as_boolean_lval(); out << as_boolean();
break; break;
case DATETIME: case DATETIME:
out << const_cast<value_t&>(*this).as_datetime_lval(); out << as_datetime();
break; break;
case INTEGER: case INTEGER:
out << const_cast<value_t&>(*this).as_long_lval(); out << as_long();
break; break;
case AMOUNT: case AMOUNT:
out << const_cast<value_t&>(*this).as_amount_lval(); out << as_amount();
break; break;
case STRING: case STRING:
out << const_cast<value_t&>(*this).as_string_lval(); out << as_string();
break; break;
case POINTER: 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; break;
case SEQUENCE: { 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() void value_context::describe(std::ostream& out) const throw()
{ {
if (! desc.empty()) if (! desc.empty())
out << desc << std::endl; out << desc << std::endl;
balance_t * ptr = NULL; const balance_t * ptr = NULL;
out << std::right; out << std::right;
out.width(20); out.width(20);
switch (bal.type()) { switch (bal.type()) {
case value_t::BOOLEAN: case value_t::BOOLEAN:
out << (const_cast<value_t&>(bal).as_boolean_lval() ? "true" : "false"); out << (bal.as_boolean() ? "true" : "false");
break; break;
case value_t::INTEGER: case value_t::INTEGER:
out << const_cast<value_t&>(bal).as_long_lval(); out << bal.as_long();
break; break;
case value_t::DATETIME: case value_t::DATETIME:
out << const_cast<value_t&>(bal).as_datetime_lval(); out << bal.as_datetime();
break; break;
case value_t::AMOUNT: case value_t::AMOUNT:
out << const_cast<value_t&>(bal).as_amount_lval(); out << bal.as_amount();
break; break;
case value_t::BALANCE: case value_t::BALANCE:
ptr = &(const_cast<value_t&>(bal).as_balance_lval()); ptr = &bal.as_balance();
// fall through... // fall through...
case value_t::BALANCE_PAIR: case value_t::BALANCE_PAIR:
if (! ptr) if (! ptr)
ptr = &(const_cast<value_t&>(bal).as_balance_pair_lval().quantity()); ptr = &bal.as_balance_pair().quantity();
ptr->print(out, 20); ptr->print(out, 20);
break; break;

71
value.h
View file

@ -433,8 +433,8 @@ private:
public: public:
/** /**
* Data manipulation methods. A value object may be truth tested * Data manipulation methods. A value object may be truth tested for the
* for the existence of every type it can contain: * existence of every type it can contain:
* *
* is_boolean() * is_boolean()
* is_long() * is_long()
@ -446,20 +446,19 @@ public:
* is_sequence() * is_sequence()
* is_pointer() * is_pointer()
* *
* There are corresponding as_*() methods that represent a value as * There are corresponding as_*() methods that represent a value as a
* a reference to its underlying type. For example, as_integer() * reference to its underlying type. For example, as_long() returns a
* returns a reference to a "const long". * reference to a "const long".
* *
* There are also as_*_lval() methods, which represent the * There are also as_*_lval() methods, which represent the underlying data
* underlying data as a reference to a non-const type. The * as a reference to a non-const type. The difference here is that an
* difference here is that an _lval() call causes the underlying * _lval() call causes the underlying data to be fully copied before the
* data to be fully copied before the resulting reference is * resulting reference is returned.
* returned.
* *
* Lastly, there are corresponding set_*(data) methods for directly * Lastly, there are corresponding set_*(data) methods for directly
* assigning data of a particular type, rather than using the * assigning data of a particular type, rather than using the regular
* regular assignment operator (whose implementation simply calls * assignment operator (whose implementation simply calls the various set_
* the various set_ methods). * methods).
*/ */
bool is_boolean() const { bool is_boolean() const {
return is_type(BOOLEAN); return is_type(BOOLEAN);
@ -518,13 +517,18 @@ public:
amount_t& as_amount_lval() { amount_t& as_amount_lval() {
assert(is_amount()); assert(is_amount());
_dup(); _dup();
return *(amount_t *) storage->data; amount_t& amt(*(amount_t *) storage->data);
assert(amt.valid());
return amt;
} }
const amount_t& as_amount() const { const amount_t& as_amount() const {
assert(is_amount()); 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) { void set_amount(const amount_t& val) {
assert(val.valid());
set_type(AMOUNT); set_type(AMOUNT);
new((amount_t *) storage->data) amount_t(val); new((amount_t *) storage->data) amount_t(val);
} }
@ -535,13 +539,18 @@ public:
balance_t& as_balance_lval() { balance_t& as_balance_lval() {
assert(is_balance()); assert(is_balance());
_dup(); _dup();
return **(balance_t **) storage->data; balance_t& bal(**(balance_t **) storage->data);
assert(bal.valid());
return bal;
} }
const balance_t& as_balance() const { const balance_t& as_balance() const {
assert(is_balance()); 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) { void set_balance(const balance_t& val) {
assert(val.valid());
set_type(BALANCE); set_type(BALANCE);
*(balance_t **) storage->data = new balance_t(val); *(balance_t **) storage->data = new balance_t(val);
} }
@ -552,13 +561,18 @@ public:
balance_pair_t& as_balance_pair_lval() { balance_pair_t& as_balance_pair_lval() {
assert(is_balance_pair()); assert(is_balance_pair());
_dup(); _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 { const balance_pair_t& as_balance_pair() const {
assert(is_balance_pair()); 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) { void set_balance_pair(const balance_pair_t& val) {
assert(val.valid());
set_type(BALANCE_PAIR); set_type(BALANCE_PAIR);
*(balance_pair_t **) storage->data = new balance_pair_t(val); *(balance_pair_t **) storage->data = new balance_pair_t(val);
} }
@ -579,6 +593,10 @@ public:
set_type(STRING); set_type(STRING);
new((string *) storage->data) string(val); 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 { bool is_sequence() const {
return is_type(SEQUENCE); return is_type(SEQUENCE);
@ -617,7 +635,7 @@ public:
_dup(); _dup();
return *any_cast<T *>(*(boost::any *) storage->data); return *any_cast<T *>(*(boost::any *) storage->data);
} }
boost::any as_any_pointer() const { const boost::any& as_any_pointer() const {
assert(is_pointer()); assert(is_pointer());
return *(boost::any *) storage->data; return *(boost::any *) storage->data;
} }
@ -730,13 +748,13 @@ public:
if (! is_sequence()) if (! is_sequence())
in_place_cast(SEQUENCE); in_place_cast(SEQUENCE);
value_t::sequence_t& seq(as_sequence_lval());
if (! val.is_sequence()) { if (! val.is_sequence()) {
if (! val.is_null()) if (! val.is_null())
seq.push_back(val); as_sequence_lval().push_back(val);
} else { } else {
const value_t::sequence_t& val_seq(val.as_sequence()); 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 { } else {
as_sequence_lval().pop_back(); 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) if (new_size == 0)
_reset(); _reset();
else if (new_size == 1) else if (new_size == 1)
*this = as_sequence().front(); *this = seq.front();
} }
} }
@ -811,6 +830,8 @@ public:
/** /**
* Debugging methods. * Debugging methods.
*/ */
bool valid() const;
}; };
#define NULL_VALUE (value_t()) #define NULL_VALUE (value_t())

View file

@ -230,7 +230,7 @@ void handle_value(const value_t& value,
// fall through... // fall through...
case value_t::AMOUNT: case value_t::AMOUNT:
xact.amount = temp.as_amount_lval(); xact.amount = temp.as_amount();
break; break;
case value_t::BALANCE: case value_t::BALANCE: