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"
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

View file

@ -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;

View file

@ -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;

View file

@ -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:

View file

@ -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());
}
}

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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 {

View file

@ -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");
assert(xact->cost->valid());
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));
// 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))

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
// 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;
}

View file

@ -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:

View file

@ -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;

71
value.h
View file

@ -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())

View file

@ -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: