*** empty log message ***

This commit is contained in:
John Wiegley 2006-03-04 17:13:45 +00:00
parent 11498f4807
commit 7acc1306d9
22 changed files with 1361 additions and 746 deletions

View file

@ -47,7 +47,7 @@ endif
if DEBUG
libledger_la_CXXFLAGS += -DDEBUG_LEVEL=4
endif
libledger_la_LDFLAGS = -version-info 2:6
libledger_la_LDFLAGS = -release 2.6
pkginclude_HEADERS = \
acconf.h \

2
acprep
View file

@ -17,7 +17,7 @@ fi
autoconf
INCDIRS="-I/sw/include -I/usr/local/include/boost-1_33 -I/usr/include/httpd/xml"
INCDIRS="$INCDIRS -I/sw/include/libofx"
#INCDIRS="$INCDIRS -I/sw/include/libofx"
INCDIRS="$INCDIRS -I/sw/include/python2.4"
INCDIRS="$INCDIRS -Wno-long-double"
LIBDIRS="-L/sw/lib -L/usr/local/lib -L/sw/lib/python2.4/config"

159
amount.cc
View file

@ -11,6 +11,7 @@
namespace ledger {
#define BIGINT_BULK_ALLOC 0x0001
#define BIGINT_KEEP_PREC 0x0002
class amount_t::bigint_t {
public:
@ -27,7 +28,8 @@ class amount_t::bigint_t {
mpz_init_set(val, _val);
}
bigint_t(const bigint_t& other)
: prec(other.prec), flags(0), ref(1), index(0) {
: prec(other.prec), flags(other.flags & BIGINT_KEEP_PREC),
ref(1), index(0) {
mpz_init_set(val, other.val);
}
~bigint_t() {
@ -51,6 +53,7 @@ base_commodities_map commodity_base_t::commodities;
commodity_base_t::updater_t * commodity_base_t::updater = NULL;
commodities_map commodity_t::commodities;
bool commodity_t::commodities_sorted = false;
commodity_t * commodity_t::null_commodity;
commodity_t * commodity_t::default_commodity = NULL;
@ -371,7 +374,8 @@ amount_t& amount_t::operator+=(const amount_t& amt)
else if (quantity->prec < amt.quantity->prec) {
_resize(amt.quantity->prec);
mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
} else {
}
else {
amount_t temp = amt;
temp._resize(quantity->prec);
mpz_add(MPZ(quantity), MPZ(quantity), MPZ(temp.quantity));
@ -403,7 +407,8 @@ amount_t& amount_t::operator-=(const amount_t& amt)
else if (quantity->prec < amt.quantity->prec) {
_resize(amt.quantity->prec);
mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
} else {
}
else {
amount_t temp = amt;
temp._resize(quantity->prec);
mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(temp.quantity));
@ -444,10 +449,10 @@ amount_t& amount_t::operator/=(const amount_t& amt)
// Increase the value's precision, to capture fractional parts after
// the divide.
mpz_ui_pow_ui(divisor, 10, amt.quantity->prec + 6);
mpz_ui_pow_ui(divisor, 10, amt.quantity->prec + 6U);
mpz_mul(MPZ(quantity), MPZ(quantity), divisor);
mpz_tdiv_q(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
quantity->prec += 6;
quantity->prec += 6U;
unsigned int comm_prec = commodity().precision();
if (quantity->prec > comm_prec + 6U) {
@ -562,13 +567,20 @@ amount_t::operator double() const
return std::atof(num.str().c_str());
}
bool amount_t::realzero() const
{
if (! quantity)
return true;
return mpz_sgn(MPZ(quantity)) == 0;
}
amount_t amount_t::value(const std::time_t moment) const
{
if (quantity) {
commodity_t& comm = commodity();
if (! (comm.flags() & COMMODITY_STYLE_NOMARKET))
if (amount_t amt = comm.value(moment))
return (amt * *this).round(amt.commodity().precision());
return (amt * *this).round();
}
return *this;
}
@ -587,6 +599,18 @@ amount_t amount_t::round(unsigned int prec) const
return temp;
}
amount_t amount_t::unround() const
{
if (! quantity || quantity->flags & BIGINT_KEEP_PREC)
return *this;
amount_t temp = *this;
temp._dup();
temp.quantity->flags |= BIGINT_KEEP_PREC;
return temp;
}
std::string amount_t::quantity_string() const
{
if (! quantity)
@ -610,7 +634,7 @@ std::string amount_t::quantity_string() const
commodity_t& comm(commodity());
unsigned short precision;
if (! comm || comm.flags() & COMMODITY_STYLE_VARIABLE) {
if (! comm || quantity->flags & BIGINT_KEEP_PREC) {
mpz_ui_pow_ui(divisor, 10, quantity->prec);
mpz_tdiv_qr(quotient, remainder, MPZ(quantity), divisor);
precision = quantity->prec;
@ -716,7 +740,7 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
commodity_t& comm(base.commodity());
unsigned short precision;
if (! comm || comm.flags() & COMMODITY_STYLE_VARIABLE) {
if (! comm || base.quantity->flags & BIGINT_KEEP_PREC) {
mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor);
precision = base.quantity->prec;
@ -814,12 +838,26 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
if (precision) {
out << ((comm.flags() & COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
out.width(precision);
out.fill('0');
std::ostringstream final;
final.width(precision);
final.fill('0');
char * p = mpz_get_str(NULL, 10, rquotient);
out << p;
final << p;
std::free(p);
const std::string& str(final.str());
int i, len = str.length();
const char * q = str.c_str();
for (i = len; i > 0; i--)
if (q[i - 1] != '0')
break;
if (i == len)
out << str;
else if (i < comm.precision())
out << std::string(str, 0, comm.precision());
else
out << std::string(str, 0, i);
}
if (comm.flags() & COMMODITY_STYLE_SUFFIXED) {
@ -859,9 +897,7 @@ void parse_quantity(std::istream& in, std::string& value)
value = buf;
}
void parse_commodity(std::istream& in, std::string& name,
std::string& symbol, std::string& price,
std::string& date, std::string& tag)
void parse_commodity(std::istream& in, std::string& symbol)
{
char buf[256];
char c = peek_next_nonws(in);
@ -876,13 +912,23 @@ void parse_commodity(std::istream& in, std::string& name,
READ_INTO(in, buf, 255, c, ! std::isspace(c) && ! std::isdigit(c) &&
c != '-' && c != '.');
}
name = symbol = buf;
symbol = buf;
}
void parse_annotations(std::istream& in, const std::string& symbol,
std::string& name, std::string& price,
std::string& date, std::string& tag)
{
char buf[256];
bool added_name = false;
c = peek_next_nonws(in);
bool handled = false;
do {
char c = peek_next_nonws(in);
if (c == '{') {
if (! price.empty())
throw amount_error("Commodity specifies more than one price");
in.get(c);
READ_INTO(in, buf, 255, c, c != '}');
if (c == '}')
@ -901,10 +947,11 @@ void parse_commodity(std::istream& in, std::string& name,
name += " {";
name += price;
name += "}";
c = peek_next_nonws(in);
}
else if (c == '[') {
if (! date.empty())
throw amount_error("Commodity specifies more than one date");
if (c == '[') {
in.get(c);
READ_INTO(in, buf, 255, c, c != ']');
if (c == ']')
@ -923,10 +970,11 @@ void parse_commodity(std::istream& in, std::string& name,
name += " [";
name += date;
name += "]";
c = peek_next_nonws(in);
}
else if (c == '(') {
if (! tag.empty())
throw amount_error("Commodity specifies more than one tag");
if (c == '(') {
in.get(c);
READ_INTO(in, buf, 255, c, c != ')');
if (c == ')')
@ -945,10 +993,13 @@ void parse_commodity(std::istream& in, std::string& name,
name += " (";
name += tag;
name += ")";
c = peek_next_nonws(in);
}
else {
break;
}
} while (true);
DEBUG_PRINT("amounts.commodities", "Parsed commodity: "
DEBUG_PRINT("amounts.commodities", "Parsed commodity annotations: "
<< "name " << name << " "
<< "symbol " << symbol << std::endl
<< " price " << price << " "
@ -979,26 +1030,36 @@ void amount_t::parse(std::istream& in, unsigned short flags)
c = peek_next_nonws(in);
}
char n;
if (std::isdigit(c) || c == '.') {
parse_quantity(in, quant);
char n;
if (! in.eof() && ((n = in.peek()) != '\n')) {
if (std::isspace(n))
comm_flags |= COMMODITY_STYLE_SEPARATED;
parse_commodity(in, name, symbol, price, date, tag);
parse_commodity(in, symbol);
name = symbol;
if (! symbol.empty())
comm_flags |= COMMODITY_STYLE_SUFFIXED;
if (! in.eof() && ((n = in.peek()) != '\n'))
parse_annotations(in, symbol, name, price, date, tag);
}
} else {
parse_commodity(in, name, symbol, price, date, tag);
parse_commodity(in, symbol);
name = symbol;
if (! in.eof() && ((n = in.peek()) != '\n')) {
if (std::isspace(in.peek()))
comm_flags |= COMMODITY_STYLE_SEPARATED;
parse_quantity(in, quant);
if (! in.eof() && ((n = in.peek()) != '\n'))
parse_annotations(in, symbol, name, price, date, tag);
}
}
if (quant.empty())
@ -1021,7 +1082,13 @@ void amount_t::parse(std::istream& in, unsigned short flags)
assert(name == symbol);
commodity_ = commodity_t::create(symbol);
}
assert(commodity_);
#ifdef DEBUG_ENABLED
if (! commodity_)
std::cerr << "Failed to find commodity for name "
<< name << " symbol " << symbol << std::endl;
#endif
if (! commodity_)
commodity_ = commodity_t::null_commodity;
}
// Determine the precision of the amount, based on the usage of
@ -1061,6 +1128,9 @@ void amount_t::parse(std::istream& in, unsigned short flags)
commodity().set_precision(quantity->prec);
}
if (flags & AMOUNT_PARSE_NO_MIGRATE)
quantity->flags |= BIGINT_KEEP_PREC;
// Now we have the final number. Remove commas and periods, if
// necessary.
@ -1267,12 +1337,11 @@ void amount_t::annotate_commodity(const amount_t& price,
<< " date " << date << " "
<< " tag " << tag);
annotated_commodity_t * ann_comm =
annotated_commodity_t::find_or_create(*this_base, price,
date == 0 && this_ann ?
this_ann->date : date,
tag.empty() && this_ann ?
this_ann->tag : tag);
commodity_t * ann_comm =
annotated_commodity_t::find_or_create
(*this_base, ! price && this_ann ? this_ann->price : price,
date == 0 && this_ann ? this_ann->date : date,
tag.empty() && this_ann ? this_ann->tag : tag);
if (ann_comm)
set_commodity(*ann_comm);
@ -1510,7 +1579,7 @@ annotated_commodity_t::make_qualified_name(const commodity_t& comm,
return name.str();
}
annotated_commodity_t *
commodity_t *
annotated_commodity_t::create(const commodity_t& comm,
const amount_t& price,
const std::time_t date,
@ -1554,7 +1623,7 @@ annotated_commodity_t::create(const commodity_t& comm,
return commodity.release();
}
annotated_commodity_t *
commodity_t *
annotated_commodity_t::create(const std::string& symbol,
const amount_t& price,
const std::time_t date,
@ -1562,10 +1631,13 @@ annotated_commodity_t::create(const std::string& symbol,
{
commodity_t * comm = commodity_t::find_or_create(symbol);
assert(comm);
if (! price && ! date && tag.empty())
return comm;
return create(*comm, price, date, tag);
}
annotated_commodity_t *
commodity_t *
annotated_commodity_t::create(const std::string& symbol,
const std::string& price,
const std::string& date,
@ -1576,7 +1648,7 @@ annotated_commodity_t::create(const std::string& symbol,
amount_t real_price;
if (! price.empty())
real_price.parse(price);
real_price.parse(price, AMOUNT_PARSE_NO_MIGRATE);
std::time_t real_date = 0;
if (! date.empty())
@ -1585,7 +1657,7 @@ annotated_commodity_t::create(const std::string& symbol,
return create(*comm, real_price, real_date, tag);
}
annotated_commodity_t *
commodity_t *
annotated_commodity_t::find_or_create(const commodity_t& comm,
const amount_t& price,
const std::time_t date,
@ -1594,10 +1666,8 @@ annotated_commodity_t::find_or_create(const commodity_t& comm,
std::string name = make_qualified_name(comm, price, date, tag);
commodity_t * base = commodity_t::find(name);
if (base) {
assert(base->annotated);
return static_cast<annotated_commodity_t *>(base);
}
if (base)
return base;
base = commodity_t::find_or_create(comm.base_symbol());
return create(*base, price, date, tag, name);
@ -1729,7 +1799,6 @@ void export_amount()
scope().attr("COMMODITY_STYLE_EUROPEAN") = COMMODITY_STYLE_EUROPEAN;
scope().attr("COMMODITY_STYLE_THOUSANDS") = COMMODITY_STYLE_THOUSANDS;
scope().attr("COMMODITY_STYLE_NOMARKET") = COMMODITY_STYLE_NOMARKET;
scope().attr("COMMODITY_STYLE_VARIABLE") = COMMODITY_STYLE_VARIABLE;
#if 0
class_< commodity_t > ("Commodity")

View file

@ -2,6 +2,7 @@
#define _AMOUNT_H
#include <map>
#include <stack>
#include <string>
#include <ctime>
#include <cctype>
@ -82,6 +83,7 @@ class amount_t
commodity_ = NULL;
}
amount_t price() const;
std::time_t date() const;
bool null() const {
return ! quantity && ! commodity_;
@ -100,6 +102,8 @@ class amount_t
// general methods
amount_t round(unsigned int prec) const;
amount_t round() const;
amount_t unround() const;
// in-place arithmetic
amount_t& operator+=(const amount_t& amt);
@ -187,6 +191,8 @@ class amount_t
operator long() const;
operator double() const;
bool realzero() const;
// comparisons between amounts
bool operator<(const amount_t& amt) const;
bool operator<=(const amount_t& amt) const;
@ -275,9 +281,38 @@ unsigned int sizeof_bigint_t();
void parse_quantity(std::istream& in, std::string& value);
void parse_commodity(std::istream& in, std::string& symbol);
void parse_annotations(std::istream& in, const std::string& symbol,
std::string& name, std::string& price,
std::string& date, std::string& tag);
void parse_conversion(const std::string& larger,
const std::string& smaller);
inline bool is_quote_or_paren(char * p) {
return *p == '"' || *p == '{' || *p == '[' || *p == '(';
}
inline char * scan_past_quotes_and_parens(char * expr)
{
std::stack<char> paren_stack;
for (const char * p = expr; *p; p++) {
if (*p == '"' ||
((*p == '(' || ((*p == '{' || *p == '[') &&
paren_stack.top() != '(')) &&
paren_stack.top() != '"')) {
paren_stack.push(*p);
}
else if ((*p == ')' && paren_stack.top() == '(') ||
(*p == '}' && paren_stack.top() == '{') ||
(*p == ']' && paren_stack.top() == '[') ||
(*p == '"' && paren_stack.top() == '"')) {
paren_stack.pop();
if (paren_stack.size() == 0)
break;
}
}
}
inline amount_t abs(const amount_t& amt) {
return amt < 0 ? amt.negated() : amt;
}
@ -296,8 +331,7 @@ inline std::istream& operator>>(std::istream& in, amount_t& amt) {
#define COMMODITY_STYLE_EUROPEAN 0x0004
#define COMMODITY_STYLE_THOUSANDS 0x0008
#define COMMODITY_STYLE_NOMARKET 0x0010
#define COMMODITY_STYLE_VARIABLE 0x0020
#define COMMODITY_STYLE_BUILTIN 0x0040
#define COMMODITY_STYLE_BUILTIN 0x0020
typedef std::map<const std::time_t, amount_t> history_map;
typedef std::pair<const std::time_t, amount_t> history_pair;
@ -385,6 +419,7 @@ class commodity_t
// This map remembers all commodities that have been defined.
static commodities_map commodities;
static bool commodities_sorted;
static commodity_t * null_commodity;
static commodity_t * default_commodity;
@ -519,24 +554,20 @@ class annotated_commodity_t : public commodity_t
const amount_t& price,
const std::time_t date,
const std::string& tag);
static
annotated_commodity_t * create(const commodity_t& comm,
static commodity_t * create(const commodity_t& comm,
const amount_t& price,
const std::time_t date,
const std::string& tag,
const std::string& entry_name = "");
static
annotated_commodity_t * create(const std::string& symbol,
static commodity_t * create(const std::string& symbol,
const amount_t& price,
const std::time_t date,
const std::string& tag);
static
annotated_commodity_t * create(const std::string& symbol,
static commodity_t * create(const std::string& symbol,
const std::string& price,
const std::string& date,
const std::string& tag);
static
annotated_commodity_t * find_or_create(const commodity_t& comm,
static commodity_t * find_or_create(const commodity_t& comm,
const amount_t& price,
const std::time_t date,
const std::string& tag);
@ -555,6 +586,10 @@ inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
return out;
}
inline amount_t amount_t::round() const {
return round(commodity().precision());
}
inline commodity_t& amount_t::commodity() const {
if (! commodity_)
return *commodity_t::null_commodity;
@ -574,6 +609,17 @@ inline amount_t amount_t::price() const {
}
}
inline std::time_t amount_t::date() const {
if (commodity_ && commodity_->annotated) {
DEBUG_PRINT("amounts.commodities",
"Returning date of " << *this << " = "
<< ((annotated_commodity_t *)commodity_)->date);
return ((annotated_commodity_t *)commodity_)->date;
} else {
return 0L;
}
}
class amount_error : public std::exception {
std::string reason;
public:

View file

@ -46,6 +46,23 @@ balance_t balance_t::price() const
return temp;
}
std::time_t balance_t::date() const
{
std::time_t temp = 0;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++) {
std::time_t date = (*i).second.date();
if (temp == 0 && date != 0)
temp = date;
else if (temp != date)
return 0;
}
return temp;
}
balance_t balance_t::reduce(const bool keep_price, const bool keep_date,
const bool keep_tag) const
{
@ -61,7 +78,35 @@ balance_t balance_t::reduce(const bool keep_price, const bool keep_date,
struct compare_amount_commodities {
bool operator()(const amount_t * left, const amount_t * right) const {
return left->commodity().symbol() < right->commodity().symbol();
commodity_t& leftcomm(left->commodity());
commodity_t& rightcomm(right->commodity());
int cmp = leftcomm.symbol().compare(rightcomm.symbol());
if (cmp != 0)
return cmp < 0;
if (! leftcomm.annotated) {
assert(rightcomm.annotated);
return true;
}
else if (! rightcomm.annotated) {
assert(leftcomm.annotated);
return false;
}
else {
annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm));
annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm));
amount_t val = aleftcomm.price - arightcomm.price;
if (val)
return val < 0;
int diff = aleftcomm.date - arightcomm.date;
if (diff)
return diff < 0;
return aleftcomm.tag < arightcomm.tag;
}
}
};
@ -75,6 +120,24 @@ void balance_t::write(std::ostream& out,
if (lwidth == -1)
lwidth = first_width;
if (commodity_t::commodities_sorted) {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++) {
int width;
if (! first) {
out << std::endl;
width = lwidth;
} else {
first = false;
width = first_width;
}
out.width(width);
out.fill(' ');
out << std::right << (*i).second;
}
} else {
typedef std::deque<const amount_t *> amounts_deque;
amounts_deque sorted;
@ -102,6 +165,7 @@ void balance_t::write(std::ostream& out,
out.fill(' ');
out << std::right << **i;
}
}
if (first) {
out.width(first_width);

View file

@ -97,10 +97,14 @@ class balance_t
}
balance_t& operator-=(const amount_t& amt) {
amounts_map::iterator i = amounts.find(&amt.commodity());
if (i != amounts.end())
if (i != amounts.end()) {
(*i).second -= amt;
else if (amt)
amounts.insert(amounts_pair(&amt.commodity(), amt));
if ((*i).second.realzero())
amounts.erase(&amt.commodity());
}
else if (! amt.realzero()) {
amounts.insert(amounts_pair(&amt.commodity(), - amt));
}
return *this;
}
template <typename T>
@ -147,7 +151,10 @@ class balance_t
balance_t& operator*=(const amount_t& amt) {
// Multiplying by the null commodity causes all amounts to be
// increased by the same factor.
if (! amt.commodity()) {
if (amt.realzero()) {
amounts.clear();
}
else if (! amt.commodity()) {
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
@ -411,6 +418,7 @@ class balance_t
amount_t amount(const commodity_t& commodity) const;
balance_t value(const std::time_t moment) const;
balance_t price() const;
std::time_t date() const;
balance_t reduce(const bool keep_price = false,
const bool keep_date = false,
const bool keep_tag = false) const;
@ -430,7 +438,18 @@ class balance_t
i != amounts.end();
i++)
if ((*i).second.commodity())
(*i).second = (*i).second.round((*i).second.commodity().precision());
(*i).second = (*i).second.round();
}
bool realzero() const {
if (amounts.size() == 0)
return true;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
if (! (*i).second.realzero())
return false;
return true;
}
};
@ -829,6 +848,11 @@ class balance_pair_t
void round() {
quantity.round();
if (cost) cost->round();
}
bool realzero() const {
return ((! cost || cost->realzero()) && quantity.realzero());
}
};

View file

@ -12,9 +12,9 @@ namespace ledger {
static unsigned long binary_magic_number = 0xFFEED765;
#ifdef DEBUG_ENABLED
static unsigned long format_version = 0x00020603;
static unsigned long format_version = 0x00020605;
#else
static unsigned long format_version = 0x00020602;
static unsigned long format_version = 0x00020604;
#endif
static account_t ** accounts;
@ -323,13 +323,7 @@ inline void read_binary_transaction(char *& data, transaction_t * xact)
if (*data++ == 1) {
xact->cost = new amount_t;
if (read_binary_number<char>(data) == 1) {
read_binary_value_expr(data, xact->cost_expr);
if (xact->cost_expr) xact->cost_expr->acquire();
} else {
read_binary_amount(data, *xact->cost);
}
} else {
xact->cost = NULL;
}
@ -348,8 +342,6 @@ inline void read_binary_transaction(char *& data, transaction_t * xact)
if (xact->amount_expr)
compute_amount(xact->amount_expr, xact->amount, xact);
if (xact->cost_expr)
compute_amount(xact->cost_expr, *xact->cost, xact);
}
inline void read_binary_entry_base(char *& data, entry_base_t * entry,
@ -884,13 +876,7 @@ void write_binary_transaction(std::ostream& out, transaction_t * xact,
if (xact->cost &&
(! (ignore_calculated && xact->flags & TRANSACTION_CALCULATED))) {
write_binary_number<char>(out, 1);
if (xact->cost_expr) {
write_binary_number<char>(out, 1);
write_binary_value_expr(out, xact->cost_expr);
} else {
write_binary_number<char>(out, 0);
write_binary_amount(out, *xact->cost);
}
} else {
write_binary_number<char>(out, 0);
}
@ -917,7 +903,7 @@ void write_binary_entry_base(std::ostream& out, entry_base_t * entry)
for (transactions_list::const_iterator i = entry->transactions.begin();
i != entry->transactions.end();
i++)
if ((*i)->amount_expr || (*i)->cost_expr) {
if ((*i)->amount_expr) {
ignore_calculated = true;
break;
}

View file

@ -91,6 +91,9 @@ void config_t::reset()
show_revalued_only = false;
download_quotes = false;
debug_mode = false;
keep_price = false;
keep_date = false;
keep_tag = false;
use_cache = false;
cache_dirty = false;
@ -296,8 +299,16 @@ void config_t::process_options(const std::string& command,
commodity_base_t::updater =
new quotes_by_script(price_db, pricing_leeway, cache_dirty);
if (! date_format.empty())
// Now setup the various formatting strings
if (! date_format.empty()) {
format_t::date_format = date_format;
annotated_commodity_t::date_format = date_format;
}
format_t::keep_price = keep_price;
format_t::keep_date = keep_date;
format_t::keep_tag = keep_tag;
}
item_handler<transaction_t> *
@ -780,19 +791,21 @@ OPT_BEGIN(actual, "L") {
} OPT_END(actual);
OPT_BEGIN(lots, "") {
keep_price = keep_date = keep_tag = true;
config->keep_price =
config->keep_date =
config->keep_tag = true;
} OPT_END(lots);
OPT_BEGIN(lot_prices, "") {
keep_price = true;
config->keep_price = true;
} OPT_END(lots_prices);
OPT_BEGIN(lot_dates, "") {
keep_date = true;
config->keep_date = true;
} OPT_END(lots_dates);
OPT_BEGIN(lot_tags, "") {
keep_tag = true;
config->keep_tag = true;
} OPT_END(lots_tags);
//////////////////////////////////////////////////////////////////////

View file

@ -66,6 +66,9 @@ class config_t
bool use_cache;
bool cache_dirty;
bool debug_mode;
bool keep_price;
bool keep_date;
bool keep_tag;
config_t() {
reset();

View file

@ -2,6 +2,7 @@
#include "datetime.h"
#include "error.h"
#include "mask.h"
#include "walk.h"
#include <memory>
@ -47,10 +48,31 @@ entry_t * derive_new_entry(journal_t& journal,
i++;
}
if (i == end)
if (i == end) {
added->add_transaction(new transaction_t(acct));
else
added->add_transaction(new transaction_t(acct, amount_t(*i++)));
} else {
transaction_t * xact = new transaction_t(acct, amount_t(*i++));
added->add_transaction(xact);
if (! xact->amount.commodity()) {
// If the amount has no commodity, we can determine it given
// the account by creating a final for the account and then
// checking if it contains only a single commodity. An
// account to which only dollars are applied would imply that
// dollars are wanted now too.
std::auto_ptr<item_handler<transaction_t> > formatter;
formatter.reset(new set_account_value);
walk_entries(journal.entries, *formatter.get());
formatter->flush();
sum_accounts(*journal.master);
value_t total = account_xdata(*acct).total;
if (total.type == value_t::AMOUNT)
xact->amount.set_commodity(((amount_t *) total.data)->commodity());
}
}
if (journal.basket)
acct = journal.basket;

View file

@ -7,6 +7,10 @@
namespace ledger {
bool format_t::keep_price = false;
bool format_t::keep_date = false;
bool format_t::keep_tag = false;
std::string truncated(const std::string& str, unsigned int width,
const int style)
{
@ -306,6 +310,9 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
calc->compute(value, details);
if (! keep_price || ! keep_date || ! keep_tag)
value = value.reduce(keep_price, keep_date, keep_tag);
switch (value.type) {
case value_t::BOOLEAN:
out << (*((bool *) value.data) ? "1" : "0");
@ -341,20 +348,12 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
bool use_disp = false;
if (details.xact->cost && details.xact->amount) {
amount_t unit_cost = *details.xact->cost / details.xact->amount;
commodity_t& comm(unit_cost.commodity());
bool has_flag = comm.flags() & COMMODITY_STYLE_VARIABLE;
if (! has_flag)
comm.add_flags(COMMODITY_STYLE_VARIABLE);
std::ostringstream stream;
stream << details.xact->amount << " @ " << unit_cost;
stream << details.xact->amount << " @ "
<< amount_t(*details.xact->cost /
details.xact->amount).unround();
disp = stream.str();
use_disp = true;
if (! has_flag)
comm.drop_flags(COMMODITY_STYLE_VARIABLE);
}
else if (details.entry) {
unsigned int xacts_count = 0;

View file

@ -72,6 +72,10 @@ struct format_t
std::string format_string;
element_t * elements;
static bool keep_price;
static bool keep_date;
static bool keep_tag;
static std::string date_format;
format_t() : elements(NULL) {

View file

@ -182,7 +182,7 @@ static void endElement(void *userData, const char *name)
if (default_commodity) {
curr_quant.set_commodity(*default_commodity);
value = curr_quant.round(default_commodity->precision());
value = curr_quant.round();
if (curr_value.commodity() == *default_commodity)
curr_value = value;

View file

@ -18,7 +18,6 @@ transaction_t::~transaction_t()
DEBUG_PRINT("ledger.memory.dtors", "dtor transaction_t");
if (cost) delete cost;
if (amount_expr) amount_expr->release();
if (cost_expr) cost_expr->release();
}
std::time_t transaction_t::actual_date() const
@ -130,8 +129,8 @@ bool entry_base_t::finalize()
nxact->flags |= TRANSACTION_CALCULATED;
}
// If one transaction of a two-line transaction is of a different
// commodity than the others, and it has no per-unit price,
// If the first transaction of a two-transaction entry is of a
// different commodity than the other, and it has no per-unit price,
// determine its price by dividing the unit count into the value of
// the balance. This is done for the last eligible commodity.
@ -158,6 +157,9 @@ bool entry_base_t::finalize()
balance -= (*x)->amount;
entry_t * entry = dynamic_cast<entry_t *>(this);
if ((*x)->amount.commodity().annotated)
throw error("Cannot self-balance an annotated commodity");
(*x)->amount.annotate_commodity(abs(per_unit_cost),
entry ? entry->actual_date() : 0,
entry ? entry->code : "");

View file

@ -40,7 +40,6 @@ class transaction_t
amount_t amount;
value_expr_t * amount_expr;
amount_t * cost;
value_expr_t * cost_expr;
state_t state;
unsigned short flags;
std::string note;
@ -54,9 +53,9 @@ class transaction_t
transaction_t(account_t * _account = NULL)
: entry(NULL), _date(0), _date_eff(0), account(_account),
amount_expr(NULL), cost(NULL), cost_expr(NULL),
state(UNCLEARED), flags(TRANSACTION_NORMAL),
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
amount_expr(NULL), cost(NULL), state(UNCLEARED),
flags(TRANSACTION_NORMAL), beg_pos(0), beg_line(0),
end_pos(0), end_line(0), data(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
}
transaction_t(account_t * _account,
@ -64,7 +63,7 @@ class transaction_t
unsigned int _flags = TRANSACTION_NORMAL,
const std::string& _note = "")
: entry(NULL), _date(0), _date_eff(0), account(_account),
amount(_amount), amount_expr(NULL), cost(NULL), cost_expr(NULL),
amount(_amount), amount_expr(NULL), cost(NULL),
state(UNCLEARED), flags(_flags), note(_note),
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
@ -72,7 +71,7 @@ class transaction_t
transaction_t(const transaction_t& xact)
: entry(xact.entry), _date(0), _date_eff(0), account(xact.account),
amount(xact.amount), amount_expr(NULL),
cost(xact.cost ? new amount_t(*xact.cost) : NULL), cost_expr(NULL),
cost(xact.cost ? new amount_t(*xact.cost) : NULL),
state(xact.state), flags(xact.flags), note(xact.note),
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");

20
main.cc
View file

@ -128,8 +128,11 @@ int parse_and_report(int argc, char * argv[], char * envp[])
ledger::dump_value_expr(std::cout, expr.get());
std::cout << std::endl;
}
value_t result;
expr->compute(result, details_t());
value_t result = expr->compute();
if (! config.keep_price || ! config.keep_date || ! config.keep_tag)
result = result.reduce(config.keep_price, config.keep_date,
config.keep_tag);
std::cout << result << std::endl;
return 0;
}
@ -233,8 +236,10 @@ int parse_and_report(int argc, char * argv[], char * envp[])
ledger::dump_value_expr(std::cout, expr.get());
std::cout << std::endl;
}
value_t result;
expr->compute(result, details_t());
value_t result = expr->compute();
if (! config.keep_price || ! config.keep_date || ! config.keep_tag)
result = result.reduce(config.keep_price, config.keep_date,
config.keep_tag);
std::cout << result << std::endl;
return 0;
}
@ -344,21 +349,18 @@ int parse_and_report(int argc, char * argv[], char * envp[])
i != formatter_ptrs.end();
i++)
delete *i;
formatter_ptrs.clear();
#endif
TIMER_STOP(cleanup);
// Write out the binary cache, if need be
TIMER_START(cache_write);
if (config.use_cache && config.cache_dirty && ! config.cache_file.empty()) {
TIMER_START(cache_write);
std::ofstream stream(config.cache_file.c_str());
write_binary_journal(stream, journal.get());
}
TIMER_STOP(cache_write);
}
#ifdef HAVE_UNIX_PIPES
if (! config.pager.empty()) {

6
ofx.cc
View file

@ -37,7 +37,7 @@ int ofx_proc_account_cb(struct OfxAccountData data, void * account_data)
ofx_accounts.insert(accounts_pair(data.account_id, account));
if (data.currency_valid) {
commodity_t * commodity = commodity_t::find_commodity(data.currency, true);
commodity_t * commodity = commodity_t::find_or_create(data.currency);
commodity->add_flags(COMMODITY_STYLE_SUFFIXED | COMMODITY_STYLE_SEPARATED);
commodities_map::iterator i = ofx_account_currencies.find(data.account_id);
@ -77,7 +77,7 @@ int ofx_proc_transaction_cb(struct OfxTransactionData data,
if (data.unique_id_valid) {
commodities_map::iterator s = ofx_securities.find(data.unique_id);
assert(s != ofx_securities.end());
xact->amount = stream.str() + " " + (*s).second->symbol;
xact->amount = stream.str() + " " + (*s).second->base_symbol();
} else {
xact->amount = stream.str() + " " + default_commodity->base_symbol();
}
@ -137,7 +137,7 @@ int ofx_proc_security_cb(struct OfxSecurityData data, void * security_data)
else
return -1;
commodity_t * commodity = commodity_t::find_commodity(symbol, true);
commodity_t * commodity = commodity_t::find_or_create(symbol);
commodity->add_flags(COMMODITY_STYLE_SUFFIXED | COMMODITY_STYLE_SEPARATED);
if (data.secname_valid)

View file

@ -64,159 +64,210 @@ inline char * next_element(char * buf, bool variable = false)
return NULL;
}
static inline bool is_mathchr(const char c) {
return (c == '(' || c == ')' ||
c == '+' || c == '-' ||
c == '*' || c == '/');
}
static inline void copy_wsbuf(char *& q, char *& wq, char * wsbuf) {
*wq = '\0';
std::strcpy(q, wsbuf);
q += std::strlen(wsbuf);
wq = wsbuf;
}
static char * parse_inline_math(const char * expr)
{
char * buf = new char[std::strlen(expr) * 2];
char * q = buf;
char wsbuf[64];
char * wq = wsbuf;
bool in_math = true;
bool could = true;
*q++ = '(';
for (const char * p = expr; *p; p++) {
if (std::isspace(*p)) {
*wq++ = *p;
} else {
bool saw_math = is_mathchr(*p);
if (in_math && ! saw_math) {
copy_wsbuf(q, wq, wsbuf);
*q++ = '{';
in_math = could = false;
}
else if (! in_math && saw_math && could) {
*q++ = '}';
copy_wsbuf(q, wq, wsbuf);
in_math = true;
}
else if (wq != wsbuf) {
copy_wsbuf(q, wq, wsbuf);
}
if (! in_math && std::isdigit(*p))
could = true;
*q++ = *p;
}
}
if (! in_math)
*q++ = '}';
*q++ = ')';
*q++ = '\0';
DEBUG_PRINT("ledger.textual.inlinemath",
"Parsed '" << expr << "' as '" << buf << "'");
return buf;
}
value_expr_t * parse_amount(const char * text, amount_t& amt,
unsigned short flags, transaction_t& xact)
{
char * altbuf = NULL;
if (*text && *text != '(' && *text != '-') {
bool in_quote = false;
bool seen_digit = false;
for (const char * p = text + 1; *p; p++)
if (*p == '"') {
in_quote = ! in_quote;
}
else if (! in_quote) {
if (is_mathchr(*p)) {
if (*p == '-' && ! seen_digit)
continue;
text = altbuf = parse_inline_math(text);
break;
}
else if (std::isdigit(*p)) {
seen_digit = true;
}
}
}
value_expr_t * expr = NULL;
if (*text != '(') {
amt.parse(text, flags);
if (altbuf)
delete[] altbuf;
} else {
expr = parse_value_expr(text);
if (altbuf)
delete[] altbuf;
if (! compute_amount(expr, amt, &xact))
throw parse_error(path, linenum, "Value expression yields a balance");
}
return expr;
}
transaction_t * parse_transaction(char * line, account_t * account,
entry_t * entry = NULL)
{
std::istringstream in(line);
// The account will be determined later...
std::auto_ptr<transaction_t> xact(new transaction_t(NULL));
if (entry)
xact->entry = entry;
// The call to `next_element' will skip past the account name, and
// return a pointer to the beginning of the amount. Once we know
// where the amount is, we can strip off any transaction note, and
// parse it.
// Parse the state flag
char * amount = NULL;
char * price = NULL;
bool per_unit = true;
char * p = skip_ws(line);
switch (*p) {
char p = peek_next_nonws(in);
switch (p) {
case '*':
xact->state = transaction_t::CLEARED;
p = skip_ws(++p);
in.get(p);
p = peek_next_nonws(in);
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Parsed the CLEARED flag");
break;
case '!':
xact->state = transaction_t::PENDING;
p = skip_ws(++p);
in.get(p);
p = peek_next_nonws(in);
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Parsed the PENDING flag");
break;
}
if (amount = next_element(p, true)) {
amount = skip_ws(amount);
if (! *amount)
amount = NULL;
else {
if (char * note_str = std::strchr(amount, ';')) {
if (amount == note_str)
amount = NULL;
// Parse the account name
*note_str++ = '\0';
note_str = skip_ws(note_str);
unsigned long account_beg = in.tellg();
unsigned long account_end = account_beg;
while (! in.eof()) {
in.get(p);
if (in.eof() || (std::isspace(p) &&
(p == '\t' || std::isspace(in.peek()))))
break;
account_end++;
}
if (char * b = std::strchr(note_str, '['))
if (char * e = std::strchr(note_str, ']')) {
if (account_beg == account_end)
throw parse_error(path, linenum, "No account was specified");
char * b = &line[account_beg];
char * e = &line[account_end];
if ((*b == '[' && *(e - 1) == ']') ||
(*b == '(' && *(e - 1) == ')')) {
xact->flags |= TRANSACTION_VIRTUAL;
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Parsed a virtual account name");
if (*b == '[') {
xact->flags |= TRANSACTION_BALANCE;
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Parsed a balanced virtual account name");
}
b++; e--;
}
std::string name(b, e - b);
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Parsed account name " << name);
if (account_aliases.size() > 0) {
accounts_map::const_iterator i = account_aliases.find(name);
if (i != account_aliases.end())
xact->account = (*i).second;
}
if (! xact->account)
xact->account = account->find_account(name);
// Parse the optional amount
if (in.good() && ! in.eof()) {
p = peek_next_nonws(in);
if (in.eof())
goto finished;
if (p == ';')
goto parse_note;
if (p == '(') {
xact->amount_expr = parse_value_expr(in)->acquire();
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Parsed an amount expression");
#ifdef DEBUG_ENABLED
DEBUG_IF("ledger.textual.parse") {
if (_debug_stream) {
ledger::dump_value_expr(*_debug_stream, xact->amount_expr);
*_debug_stream << std::endl;
}
}
#endif
if (! compute_amount(xact->amount_expr, xact->amount, xact.get()))
throw parse_error(path, linenum,
"Value expression for amount failed to compute");
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"The computed amount is " << xact->amount);
} else {
xact->amount.parse(in, AMOUNT_PARSE_NO_REDUCE);
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Parsed amount " << xact->amount);
// Parse any inline math
p = peek_next_nonws(in);
while (in.good() && ! in.eof() && (p == '-' || p == '+')) {
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Parsed inline math operator " << p);
in.get(p);
amount_t temp;
temp.parse(in, AMOUNT_PARSE_NO_REDUCE);
switch (p) {
case '-':
xact->amount -= temp;
break;
case '+':
xact->amount += temp;
break;
}
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Calculated amount is " << xact->amount);
p = peek_next_nonws(in);
}
}
}
// Parse the optional cost (@ PER-UNIT-COST, @@ TOTAL-COST)
if (in.good() && ! in.eof()) {
p = peek_next_nonws(in);
if (p == '@') {
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Found a price indicator");
bool per_unit = true;
in.get(p);
if (in.peek() == '@') {
in.get(p);
per_unit = false;
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"And it's for a total price");
}
if (in.good() && ! in.eof()) {
xact->cost = new amount_t;
p = peek_next_nonws(in);
if (p == '(')
throw parse_error(path, linenum,
"A transaction's cost may not be a value expression");
xact->cost->parse(in, AMOUNT_PARSE_NO_MIGRATE);
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Parsed cost " << *xact->cost);
amount_t per_unit_cost(*xact->cost);
if (per_unit)
*xact->cost *= xact->amount;
else
per_unit_cost /= xact->amount;
if (! xact->amount.commodity().annotated) {
xact->amount.annotate_commodity(per_unit_cost,
xact->entry->actual_date(),
xact->entry->code);
} else {
annotated_commodity_t& ann(static_cast<annotated_commodity_t&>
(xact->amount.commodity()));
xact->amount.annotate_commodity(! ann.price ? per_unit_cost : amount_t(),
! ann.date ? xact->entry->actual_date() : 0,
ann.tag.empty() ? xact->entry->code : "");
}
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Total cost is " << *xact->cost);
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Per-unit cost is " << per_unit_cost);
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Annotated amount is " << xact->amount);
}
}
}
xact->amount.reduce();
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Reduced amount is " << xact->amount);
// Parse the optional note
parse_note:
if (in.good() && ! in.eof()) {
p = peek_next_nonws(in);
if (p == ';') {
in.get(p);
p = peek_next_nonws(in);
xact->note = &line[in.tellg()];
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Parsed a note '" << xact->note << "'");
if (char * b = std::strchr(xact->note.c_str(), '['))
if (char * e = std::strchr(xact->note.c_str(), ']')) {
char buf[256];
std::strncpy(buf, b + 1, e - b);
buf[e - b] = '\0';
std::strncpy(buf, b + 1, e - b - 1);
buf[e - b - 1] = '\0';
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
"Parsed a transaction date " << buf);
if (char * p = std::strchr(buf, '=')) {
*p++ = '\0';
@ -228,101 +279,10 @@ transaction_t * parse_transaction(char * line, account_t * account,
if (buf[0] && ! quick_parse_date(buf, &xact->_date))
throw parse_error(path, linenum, "Failed to parse date");
}
xact->note = skip_ws(note_str);
}
if (amount) {
bool in_quote = false;
int paren_depth = 0;
for (char * q = amount; *q; q++) {
if (*q == '"') {
in_quote = ! in_quote;
}
else if (! in_quote) {
if (*q == '(')
paren_depth++;
else if (*q == ')')
paren_depth--;
else if (paren_depth == 0 && *q == '@') {
price = q;
break;
}
}
}
if (price) {
if (price == amount)
throw parse_error(path, linenum, "Cost specified without amount");
*price++ = '\0';
if (*price == '@') {
per_unit = false;
price++;
}
price = skip_ws(price);
}
}
}
}
char * q = p + std::strlen(p) - 1;
while (q >= p && std::isspace(*q))
*q-- = '\0';
if (*p == '[' || *p == '(') {
xact->flags |= TRANSACTION_VIRTUAL;
if (*p == '[')
xact->flags |= TRANSACTION_BALANCE;
p++;
char * e = p + (std::strlen(p) - 1);
assert(*e == ')' || *e == ']');
*e = '\0';
}
if (account_aliases.size() > 0) {
accounts_map::const_iterator i = account_aliases.find(p);
if (i != account_aliases.end())
xact->account = (*i).second;
}
if (! xact->account)
xact->account = account->find_account(p);
// If an amount (and optional price) were seen, parse them now
if (amount) {
xact->amount_expr = parse_amount(amount, xact->amount,
AMOUNT_PARSE_NO_REDUCE, *xact);
if (xact->amount_expr)
xact->amount_expr->acquire();
if (price) {
xact->cost = new amount_t;
xact->cost_expr = parse_amount(price, *xact->cost,
AMOUNT_PARSE_NO_MIGRATE, *xact);
if (xact->cost_expr)
xact->cost_expr->acquire();
if (per_unit) {
if (! xact->amount.commodity().annotated)
xact->amount.annotate_commodity(*xact->cost,
xact->entry->actual_date(),
xact->entry->code);
*xact->cost *= xact->amount;
*xact->cost = xact->cost->round(xact->cost->commodity().precision());
}
else if (! xact->amount.commodity().annotated) {
amount_t cost(*xact->cost);
cost /= xact->amount;
cost = cost.round(cost.commodity().precision());
xact->amount.annotate_commodity(cost, xact->entry->actual_date(),
xact->entry->code);
}
}
xact->amount.reduce();
}
finished:
return xact.release();
}
@ -771,7 +731,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
path = std::string(save_path.prev, 0, pos + 1) + path;
}
DEBUG_PRINT("ledger.textual.include",
DEBUG_PRINT("ledger.textual.include", "line " << linenum << ": " <<
"Including path '" << path << "'");
count += parse_journal_file(path, config, journal,
account_stack.front());

View file

@ -13,10 +13,6 @@ std::auto_ptr<value_calc> total_expr;
std::auto_ptr<scope_t> global_scope;
std::time_t terminus;
bool keep_price = false;
bool keep_date = false;
bool keep_tag = false;
details_t::details_t(const transaction_t& _xact)
: entry(_xact.entry), xact(&_xact), account(xact_account(_xact))
{
@ -351,6 +347,40 @@ void value_expr_t::compute(value_t& result, const details_t& details,
result = 0L;
break;
case F_PRICE: {
int index = 0;
value_expr_t * expr = find_leaf(context, 0, index);
expr->compute(result, details, context);
result = result.price();
break;
}
case F_DATE: {
int index = 0;
value_expr_t * expr = find_leaf(context, 0, index);
expr->compute(result, details, context);
result = result.date();
break;
}
case F_DATECMP: {
int index = 0;
value_expr_t * expr = find_leaf(context, 0, index);
expr->compute(result, details, context);
result = result.date();
if (! result)
break;
index = 0;
expr = find_leaf(context, 1, index);
amount_t moment;
if (compute_amount(expr, moment, NULL, context))
result -= moment;
else
throw compute_error("Invalid date passed to datecmp(value,date)");
break;
}
case F_ARITH_MEAN: {
int index = 0;
value_expr_t * expr = find_leaf(context, 0, index);
@ -382,6 +412,14 @@ void value_expr_t::compute(value_t& result, const details_t& details,
break;
}
case F_ROUND: {
int index = 0;
value_expr_t * expr = find_leaf(context, 0, index);
expr->compute(result, details, context);
result.round();
break;
}
case F_COMMODITY: {
int index = 0;
value_expr_t * expr = find_leaf(context, 0, index);
@ -647,9 +685,6 @@ void value_expr_t::compute(value_t& result, const details_t& details,
assert(0);
break;
}
if (! keep_price || ! keep_date || ! keep_tag)
result = result.reduce(keep_price, keep_date, keep_tag);
}
static inline void unexpected(char c, char wanted = '\0') {
@ -685,7 +720,8 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
if (std::strchr(buf, '.')) {
node.reset(new value_expr_t(value_expr_t::CONSTANT_A));
node->constant_a = new amount_t(buf);
node->constant_a = new amount_t;
node->constant_a->parse(buf, AMOUNT_PARSE_NO_MIGRATE);
} else {
node.reset(new value_expr_t(value_expr_t::CONSTANT_I));
node->constant_i = std::atol(buf);
@ -911,7 +947,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
unexpected(c, '}');
node.reset(new value_expr_t(value_expr_t::CONSTANT_A));
node->constant_a = new amount_t();
node->constant_a = new amount_t;
node->constant_a->parse(buf, AMOUNT_PARSE_NO_MIGRATE);
break;
}
@ -1171,6 +1207,7 @@ void init_value_expr()
node = new value_expr_t(value_expr_t::F_NOW);
globals->define("m", node);
globals->define("now", node);
globals->define("today", node);
node = new value_expr_t(value_expr_t::AMOUNT);
globals->define("a", node);
@ -1230,11 +1267,11 @@ void init_value_expr()
node = new value_expr_t(value_expr_t::PRICE_TOTAL);
globals->define("I", node);
globals->define("price_total", node);
globals->define("total_price", node);
node = new value_expr_t(value_expr_t::COST_TOTAL);
globals->define("B", node);
globals->define("cost_total", node);
globals->define("total_cost", node);
// Relating to format_t
globals->define("t", new value_expr_t(value_expr_t::VALUE_EXPR));
@ -1248,6 +1285,12 @@ void init_value_expr()
globals->define("U", node);
globals->define("abs", node);
node = new value_expr_t(value_expr_t::O_DEF);
node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
node->left->constant_i = 1;
node->set_right(new value_expr_t(value_expr_t::F_ROUND));
globals->define("round", node);
node = new value_expr_t(value_expr_t::O_DEF);
node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
node->left->constant_i = 1;
@ -1284,9 +1327,28 @@ void init_value_expr()
node->left->constant_i = 2;
node->set_right(new value_expr_t(value_expr_t::F_VALUE));
globals->define("P", node);
globals->define("val", node);
globals->define("value", node);
value_auto_ptr cval(parse_boolean_expr("current_value(x)=P(x,m)", globals));
value_auto_ptr val(parse_boolean_expr("value=P(t,m)", globals));
value_auto_ptr tval(parse_boolean_expr("total_value=P(T,m)", globals));
value_auto_ptr valof(parse_boolean_expr("valueof(x)=P(x,m)", globals));
value_auto_ptr dvalof(parse_boolean_expr("datedvalueof(x,y)=P(x,y)", globals));
node = new value_expr_t(value_expr_t::O_DEF);
node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
node->left->constant_i = 1;
node->set_right(new value_expr_t(value_expr_t::F_PRICE));
globals->define("priceof", node);
node = new value_expr_t(value_expr_t::O_DEF);
node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
node->left->constant_i = 1;
node->set_right(new value_expr_t(value_expr_t::F_DATE));
globals->define("dateof", node);
node = new value_expr_t(value_expr_t::O_DEF);
node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
node->left->constant_i = 2;
node->set_right(new value_expr_t(value_expr_t::F_DATECMP));
globals->define("datecmp", node);
// Macros
node = parse_value_expr("P(a,d)");
@ -1295,7 +1357,7 @@ void init_value_expr()
node = parse_value_expr("P(O,d)");
globals->define("V", node);
globals->define("market_total", node);
globals->define("total_market", node);
node = parse_value_expr("v-b");
globals->define("g", node);
@ -1303,7 +1365,7 @@ void init_value_expr()
node = parse_value_expr("V-B");
globals->define("G", node);
globals->define("gain_total", node);
globals->define("total_gain", node);
value_auto_ptr minx(parse_boolean_expr("min(x,y)=x<y?x:y", globals));
value_auto_ptr maxx(parse_boolean_expr("max(x,y)=x>y?x:y", globals));
@ -1417,6 +1479,8 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node,
case value_expr_t::F_COMMODITY_MASK:
out << "F_COMMODITY_MASK"; break;
case value_expr_t::F_VALUE: out << "F_VALUE"; break;
case value_expr_t::F_PRICE: out << "F_PRICE"; break;
case value_expr_t::F_DATE: out << "F_DATE"; break;
case value_expr_t::O_NOT: out << "O_NOT"; break;
case value_expr_t::O_ARG: out << "O_ARG"; break;

View file

@ -33,27 +33,37 @@ struct details_t
#endif
};
typedef void (*value_func_t)(value_t& result, const details_t& details,
value_expr_t * context);
class value_calc
{
public:
virtual ~value_calc() {}
virtual void compute(value_t& result, const details_t& details,
virtual void compute(value_t& result,
const details_t& details = details_t(),
value_expr_t * context = NULL) = 0;
virtual value_t compute(const details_t& details = details_t(),
value_expr_t * context = NULL) = 0;
};
typedef void (*value_func_t)(value_t& result, const details_t& details,
value_expr_t * context);
class value_func : public value_calc
{
value_func_t func;
public:
value_func(value_func_t _func) : func(_func) {}
virtual void compute(value_t& result, const details_t& details,
virtual void compute(value_t& result,
const details_t& details = details_t(),
value_expr_t * context = NULL) {
func(result, details, context);
}
virtual value_t compute(const details_t& details = details_t(),
value_expr_t * context = NULL) {
value_t temp;
func(temp, details, context);
return temp;
}
};
struct value_expr_t
@ -99,6 +109,10 @@ struct value_expr_t
F_SET_COMMODITY,
F_VALUE,
F_ABS,
F_ROUND,
F_PRICE,
F_DATE,
F_DATECMP,
F_CODE_MASK,
F_PAYEE_MASK,
F_NOTE_MASK,
@ -193,8 +207,15 @@ struct value_expr_t
right = expr ? expr->acquire() : NULL;
}
void compute(value_t& result, const details_t& details,
void compute(value_t& result,
const details_t& details = details_t(),
value_expr_t * context = NULL) const;
value_t compute(const details_t& details = details_t(),
value_expr_t * context = NULL) const {
value_t temp;
compute(temp, details, context);
return temp;
}
};
struct scope_t
@ -249,10 +270,6 @@ extern std::auto_ptr<scope_t> global_scope;
extern std::time_t terminus;
extern bool initialized;
extern bool keep_price;
extern bool keep_date;
extern bool keep_tag;
void init_value_expr();
bool compute_amount(value_expr_t * expr, amount_t& amt,
@ -357,25 +374,44 @@ public:
parsed->release();
}
virtual void compute(value_t& result, const details_t& details,
virtual void compute(value_t& result,
const details_t& details = details_t(),
value_expr_t * context = NULL) {
parsed->compute(result, details, context);
}
virtual value_t compute(const details_t& details = details_t(),
value_expr_t * context = NULL) {
value_t temp;
parsed->compute(temp, details, context);
return temp;
}
};
extern std::auto_ptr<value_calc> amount_expr;
extern std::auto_ptr<value_calc> total_expr;
inline void compute_amount(value_t& result, const details_t& details) {
inline void compute_amount(value_t& result,
const details_t& details = details_t()) {
if (amount_expr.get())
amount_expr->compute(result, details);
}
inline void compute_total(value_t& result, const details_t& details) {
inline value_t compute_amount(const details_t& details = details_t()) {
if (amount_expr.get())
return amount_expr->compute(details);
}
inline void compute_total(value_t& result,
const details_t& details = details_t()) {
if (total_expr.get())
total_expr->compute(result, details);
}
inline value_t compute_total(const details_t& details = details_t()) {
if (total_expr.get())
return total_expr->compute(details);
}
//////////////////////////////////////////////////////////////////////
template <typename T>
@ -408,12 +444,7 @@ class item_predicate
}
bool operator()(const T& item) const {
if (predicate) {
value_t result;
predicate->compute(result, details_t(item));
return result;
}
return true;
return ! predicate || predicate->compute(details_t(item));
}
};

777
value.cc
View file

@ -1,4 +1,5 @@
#include "value.h"
#include "debug.h"
namespace ledger {
@ -19,6 +20,34 @@ void value_t::destroy()
}
}
void value_t::simplify()
{
if (realzero()) {
DEBUG_PRINT("amounts.values.simplify", "Zeroing type " << type);
*this = 0L;
return;
}
if (type == BALANCE_PAIR &&
(! ((balance_pair_t *) data)->cost ||
((balance_pair_t *) data)->cost->realzero())) {
DEBUG_PRINT("amounts.values.simplify", "Reducing balance pair to balance");
cast(BALANCE);
}
if (type == BALANCE &&
((balance_t *) data)->amounts.size() == 1) {
DEBUG_PRINT("amounts.values.simplify", "Reducing balance to amount");
cast(AMOUNT);
}
if (type == AMOUNT &&
! ((amount_t *) data)->commodity()) {
DEBUG_PRINT("amounts.values.simplify", "Reducing amount to integer");
cast(INTEGER);
}
}
value_t& value_t::operator=(const value_t& value)
{
if (this == &value)
@ -57,253 +86,493 @@ value_t& value_t::operator=(const value_t& value)
return *this;
}
#define DEF_VALUE_ADDSUB_OP(OP) \
value_t& value_t::operator OP(const value_t& value) \
{ \
switch (type) { \
case BOOLEAN: \
case INTEGER: \
cast(INTEGER); \
switch (value.type) { \
case BOOLEAN: \
*((long *) data) OP (*((bool *) value.data) ? 1L : 0L); \
break; \
case INTEGER: \
*((long *) data) OP *((long *) value.data); \
break; \
case AMOUNT: \
cast(AMOUNT); \
*((amount_t *) data) OP *((amount_t *) value.data); \
break; \
case BALANCE: \
cast(BALANCE); \
*((balance_t *) data) OP *((balance_t *) value.data); \
break; \
case BALANCE_PAIR: \
cast(BALANCE_PAIR); \
*((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \
break; \
default: \
assert(0); \
break; \
} \
break; \
\
case AMOUNT: \
switch (value.type) { \
case BOOLEAN: \
if (*((bool *) value.data) && \
((amount_t *) data)->commodity()) { \
cast(BALANCE); \
return *this OP value; \
} \
*((amount_t *) data) OP (*((bool *) value.data) ? 1L : 0L); \
break; \
\
case INTEGER: \
if (*((long *) value.data) && \
((amount_t *) data)->commodity()) { \
cast(BALANCE); \
return *this OP value; \
} \
*((amount_t *) data) OP *((long *) value.data); \
break; \
\
case AMOUNT: \
if (((amount_t *) data)->commodity() != \
((amount_t *) value.data)->commodity()) { \
cast(BALANCE); \
return *this OP value; \
} \
*((amount_t *) data) OP *((amount_t *) value.data); \
break; \
\
case BALANCE: \
cast(BALANCE); \
*((balance_t *) data) OP *((balance_t *) value.data); \
break; \
\
case BALANCE_PAIR: \
cast(BALANCE_PAIR); \
*((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \
break; \
\
default: \
assert(0); \
break; \
} \
break; \
\
case BALANCE: \
switch (value.type) { \
case BOOLEAN: \
*((balance_t *) data) OP (*((bool *) value.data) ? 1L : 0L); \
break; \
case INTEGER: \
*((balance_t *) data) OP *((long *) value.data); \
break; \
case AMOUNT: \
*((balance_t *) data) OP *((amount_t *) value.data); \
break; \
case BALANCE: \
*((balance_t *) data) OP *((balance_t *) value.data); \
break; \
case BALANCE_PAIR: \
cast(BALANCE_PAIR); \
*((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \
break; \
default: \
assert(0); \
break; \
} \
break; \
\
case BALANCE_PAIR: \
switch (value.type) { \
case BOOLEAN: \
*((balance_pair_t *) data) OP (*((bool *) value.data) ? 1L : 0L); \
break; \
case INTEGER: \
*((balance_pair_t *) data) OP *((long *) value.data); \
break; \
case AMOUNT: \
*((balance_pair_t *) data) OP *((amount_t *) value.data); \
break; \
case BALANCE: \
*((balance_pair_t *) data) OP *((balance_t *) value.data); \
break; \
case BALANCE_PAIR: \
*((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \
break; \
default: \
assert(0); \
break; \
} \
break; \
\
default: \
assert(0); \
break; \
} \
return *this; \
value_t& value_t::operator+=(const value_t& value)
{
switch (type) {
case BOOLEAN:
case INTEGER:
cast(INTEGER);
switch (value.type) {
case BOOLEAN:
*((long *) data) += (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((long *) data) += *((long *) value.data);
break;
case AMOUNT:
cast(AMOUNT);
*((amount_t *) data) += *((amount_t *) value.data);
break;
case BALANCE:
cast(BALANCE);
*((balance_t *) data) += *((balance_t *) value.data);
break;
case BALANCE_PAIR:
cast(BALANCE_PAIR);
*((balance_pair_t *) data) += *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
case AMOUNT:
switch (value.type) {
case BOOLEAN:
if (*((bool *) value.data) &&
((amount_t *) data)->commodity()) {
cast(BALANCE);
return *this += value;
}
*((amount_t *) data) += (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
if (*((long *) value.data) &&
((amount_t *) data)->commodity()) {
cast(BALANCE);
return *this += value;
}
*((amount_t *) data) += *((long *) value.data);
break;
case AMOUNT:
if (((amount_t *) data)->commodity() !=
((amount_t *) value.data)->commodity()) {
cast(BALANCE);
return *this += value;
}
*((amount_t *) data) += *((amount_t *) value.data);
break;
case BALANCE:
cast(BALANCE);
*((balance_t *) data) += *((balance_t *) value.data);
break;
case BALANCE_PAIR:
cast(BALANCE_PAIR);
*((balance_pair_t *) data) += *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
case BALANCE:
switch (value.type) {
case BOOLEAN:
*((balance_t *) data) += (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((balance_t *) data) += *((long *) value.data);
break;
case AMOUNT:
*((balance_t *) data) += *((amount_t *) value.data);
break;
case BALANCE:
*((balance_t *) data) += *((balance_t *) value.data);
break;
case BALANCE_PAIR:
cast(BALANCE_PAIR);
*((balance_pair_t *) data) += *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
case BALANCE_PAIR:
switch (value.type) {
case BOOLEAN:
*((balance_pair_t *) data) += (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((balance_pair_t *) data) += *((long *) value.data);
break;
case AMOUNT:
*((balance_pair_t *) data) += *((amount_t *) value.data);
break;
case BALANCE:
*((balance_pair_t *) data) += *((balance_t *) value.data);
break;
case BALANCE_PAIR:
*((balance_pair_t *) data) += *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
default:
assert(0);
break;
}
return *this;
}
DEF_VALUE_ADDSUB_OP(+=)
DEF_VALUE_ADDSUB_OP(-=)
value_t& value_t::operator-=(const value_t& value)
{
switch (type) {
case BOOLEAN:
case INTEGER:
cast(INTEGER);
switch (value.type) {
case BOOLEAN:
*((long *) data) -= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((long *) data) -= *((long *) value.data);
break;
case AMOUNT:
cast(AMOUNT);
*((amount_t *) data) -= *((amount_t *) value.data);
break;
case BALANCE:
cast(BALANCE);
*((balance_t *) data) -= *((balance_t *) value.data);
break;
case BALANCE_PAIR:
cast(BALANCE_PAIR);
*((balance_pair_t *) data) -= *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
#define DEF_VALUE_MULDIV_OP(OP) \
value_t& value_t::operator OP(const value_t& value) \
{ \
switch (type) { \
case BOOLEAN: \
case INTEGER: \
cast(INTEGER); \
switch (value.type) { \
case BOOLEAN: \
*((long *) data) OP (*((bool *) value.data) ? 1L : 0L); \
break; \
case INTEGER: \
*((long *) data) OP *((long *) value.data); \
break; \
case AMOUNT: \
cast(AMOUNT); \
*((amount_t *) data) OP *((amount_t *) value.data); \
break; \
case BALANCE: \
cast(BALANCE); \
*((balance_t *) data) OP *((balance_t *) value.data); \
break; \
case BALANCE_PAIR: \
cast(BALANCE_PAIR); \
*((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \
break; \
default: \
assert(0); \
break; \
} \
break; \
\
case AMOUNT: \
switch (value.type) { \
case BOOLEAN: \
*((amount_t *) data) OP (*((bool *) value.data) ? 1L : 0L); \
break; \
case INTEGER: \
*((amount_t *) data) OP *((long *) value.data); \
break; \
case AMOUNT: \
*((amount_t *) data) OP *((amount_t *) value.data); \
break; \
case BALANCE: \
cast(BALANCE); \
*((balance_t *) data) OP *((balance_t *) value.data); \
break; \
case BALANCE_PAIR: \
cast(BALANCE_PAIR); \
*((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \
break; \
default: \
assert(0); \
break; \
} \
break; \
\
case BALANCE: \
switch (value.type) { \
case BOOLEAN: \
*((balance_t *) data) OP (*((bool *) value.data) ? 1L : 0L); \
break; \
case INTEGER: \
*((balance_t *) data) OP *((long *) value.data); \
break; \
case AMOUNT: \
*((balance_t *) data) OP *((amount_t *) value.data); \
break; \
case BALANCE: \
*((balance_t *) data) OP *((balance_t *) value.data); \
break; \
case BALANCE_PAIR: \
cast(BALANCE_PAIR); \
*((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \
break; \
default: \
assert(0); \
break; \
} \
break; \
\
case BALANCE_PAIR: \
switch (value.type) { \
case BOOLEAN: \
*((balance_pair_t *) data) OP (*((bool *) value.data) ? 1L : 0L); \
break; \
case INTEGER: \
*((balance_pair_t *) data) OP *((long *) value.data); \
break; \
case AMOUNT: \
*((balance_pair_t *) data) OP *((amount_t *) value.data); \
break; \
case BALANCE: \
*((balance_pair_t *) data) OP *((balance_t *) value.data); \
break; \
case BALANCE_PAIR: \
*((balance_pair_t *) data) OP *((balance_pair_t *) value.data); \
break; \
default: \
assert(0); \
break; \
} \
break; \
\
default: \
assert(0); \
break; \
} \
return *this; \
case AMOUNT:
switch (value.type) {
case BOOLEAN:
if (*((bool *) value.data) &&
((amount_t *) data)->commodity()) {
cast(BALANCE);
return *this -= value;
}
*((amount_t *) data) -= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
if (*((long *) value.data) &&
((amount_t *) data)->commodity()) {
cast(BALANCE);
return *this -= value;
}
*((amount_t *) data) -= *((long *) value.data);
break;
case AMOUNT:
if (((amount_t *) data)->commodity() !=
((amount_t *) value.data)->commodity()) {
cast(BALANCE);
return *this -= value;
}
*((amount_t *) data) -= *((amount_t *) value.data);
break;
case BALANCE:
cast(BALANCE);
*((balance_t *) data) -= *((balance_t *) value.data);
break;
case BALANCE_PAIR:
cast(BALANCE_PAIR);
*((balance_pair_t *) data) -= *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
case BALANCE:
switch (value.type) {
case BOOLEAN:
*((balance_t *) data) -= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((balance_t *) data) -= *((long *) value.data);
break;
case AMOUNT:
*((balance_t *) data) -= *((amount_t *) value.data);
break;
case BALANCE:
*((balance_t *) data) -= *((balance_t *) value.data);
break;
case BALANCE_PAIR:
cast(BALANCE_PAIR);
*((balance_pair_t *) data) -= *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
case BALANCE_PAIR:
switch (value.type) {
case BOOLEAN:
*((balance_pair_t *) data) -= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((balance_pair_t *) data) -= *((long *) value.data);
break;
case AMOUNT:
*((balance_pair_t *) data) -= *((amount_t *) value.data);
break;
case BALANCE:
*((balance_pair_t *) data) -= *((balance_t *) value.data);
break;
case BALANCE_PAIR:
*((balance_pair_t *) data) -= *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
default:
assert(0);
break;
}
DEF_VALUE_MULDIV_OP(*=)
DEF_VALUE_MULDIV_OP(/=)
simplify();
return *this;
}
value_t& value_t::operator*=(const value_t& value)
{
if (value.realzero()) {
*this = 0L;
return *this;
}
switch (type) {
case BOOLEAN:
case INTEGER:
cast(INTEGER);
switch (value.type) {
case BOOLEAN:
*((long *) data) *= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((long *) data) *= *((long *) value.data);
break;
case AMOUNT:
cast(AMOUNT);
*((amount_t *) data) *= *((amount_t *) value.data);
break;
case BALANCE:
cast(BALANCE);
*((balance_t *) data) *= *((balance_t *) value.data);
break;
case BALANCE_PAIR:
cast(BALANCE_PAIR);
*((balance_pair_t *) data) *= *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
case AMOUNT:
switch (value.type) {
case BOOLEAN:
*((amount_t *) data) *= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((amount_t *) data) *= *((long *) value.data);
break;
case AMOUNT:
*((amount_t *) data) *= *((amount_t *) value.data);
break;
case BALANCE:
cast(BALANCE);
*((balance_t *) data) *= *((balance_t *) value.data);
break;
case BALANCE_PAIR:
cast(BALANCE_PAIR);
*((balance_pair_t *) data) *= *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
case BALANCE:
switch (value.type) {
case BOOLEAN:
*((balance_t *) data) *= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((balance_t *) data) *= *((long *) value.data);
break;
case AMOUNT:
*((balance_t *) data) *= *((amount_t *) value.data);
break;
case BALANCE:
*((balance_t *) data) *= *((balance_t *) value.data);
break;
case BALANCE_PAIR:
cast(BALANCE_PAIR);
*((balance_pair_t *) data) *= *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
case BALANCE_PAIR:
switch (value.type) {
case BOOLEAN:
*((balance_pair_t *) data) *= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((balance_pair_t *) data) *= *((long *) value.data);
break;
case AMOUNT:
*((balance_pair_t *) data) *= *((amount_t *) value.data);
break;
case BALANCE:
*((balance_pair_t *) data) *= *((balance_t *) value.data);
break;
case BALANCE_PAIR:
*((balance_pair_t *) data) *= *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
default:
assert(0);
break;
}
return *this;
}
value_t& value_t::operator/=(const value_t& value)
{
switch (type) {
case BOOLEAN:
case INTEGER:
cast(INTEGER);
switch (value.type) {
case BOOLEAN:
*((long *) data) /= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((long *) data) /= *((long *) value.data);
break;
case AMOUNT:
cast(AMOUNT);
*((amount_t *) data) /= *((amount_t *) value.data);
break;
case BALANCE:
cast(BALANCE);
*((balance_t *) data) /= *((balance_t *) value.data);
break;
case BALANCE_PAIR:
cast(BALANCE_PAIR);
*((balance_pair_t *) data) /= *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
case AMOUNT:
switch (value.type) {
case BOOLEAN:
*((amount_t *) data) /= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((amount_t *) data) /= *((long *) value.data);
break;
case AMOUNT:
*((amount_t *) data) /= *((amount_t *) value.data);
break;
case BALANCE:
cast(BALANCE);
*((balance_t *) data) /= *((balance_t *) value.data);
break;
case BALANCE_PAIR:
cast(BALANCE_PAIR);
*((balance_pair_t *) data) /= *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
case BALANCE:
switch (value.type) {
case BOOLEAN:
*((balance_t *) data) /= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((balance_t *) data) /= *((long *) value.data);
break;
case AMOUNT:
*((balance_t *) data) /= *((amount_t *) value.data);
break;
case BALANCE:
*((balance_t *) data) /= *((balance_t *) value.data);
break;
case BALANCE_PAIR:
cast(BALANCE_PAIR);
*((balance_pair_t *) data) /= *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
case BALANCE_PAIR:
switch (value.type) {
case BOOLEAN:
*((balance_pair_t *) data) /= (*((bool *) value.data) ? 1L : 0L);
break;
case INTEGER:
*((balance_pair_t *) data) /= *((long *) value.data);
break;
case AMOUNT:
*((balance_pair_t *) data) /= *((amount_t *) value.data);
break;
case BALANCE:
*((balance_pair_t *) data) /= *((balance_t *) value.data);
break;
case BALANCE_PAIR:
*((balance_pair_t *) data) /= *((balance_pair_t *) value.data);
break;
default:
assert(0);
break;
}
break;
default:
assert(0);
break;
}
return *this;
}
#define DEF_VALUE_CMP_OP(OP) \
bool value_t::operator OP(const value_t& value) \
@ -747,6 +1016,30 @@ value_t value_t::price() const
return value_t();
}
value_t value_t::date() const
{
switch (type) {
case BOOLEAN:
case INTEGER:
return *this;
case AMOUNT:
return ((amount_t *) data)->date();
case BALANCE:
return (long)((balance_t *) data)->date();
case BALANCE_PAIR:
return (long)((balance_pair_t *) data)->quantity.date();
default:
assert(0);
break;
}
assert(0);
return value_t();
}
value_t value_t::reduce(const bool keep_price, const bool keep_date,
const bool keep_tag) const
{

70
value.h
View file

@ -89,6 +89,7 @@ class value_t
}
void destroy();
void simplify();
value_t& operator=(const value_t& value);
value_t& operator=(const bool value) {
@ -120,41 +121,55 @@ class value_t
return *this = amount_t(value);
}
value_t& operator=(const amount_t& value) {
if ((amount_t *) data != &value) {
if (! value) {
if (type == AMOUNT &&
(amount_t *) data == &value)
return *this;
if (value.realzero()) {
return *this = 0L;
} else {
destroy();
new((amount_t *)data) amount_t(value);
type = AMOUNT;
}
}
return *this;
}
value_t& operator=(const balance_t& value) {
if ((balance_t *) data != &value) {
if (value.amounts.size() == 1) {
if (type == BALANCE &&
(balance_t *) data == &value)
return *this;
if (value.realzero()) {
return *this = 0L;
}
else if (value.amounts.size() == 1) {
return *this = (*value.amounts.begin()).second;
} else {
}
else {
destroy();
new((balance_t *)data) balance_t(value);
type = BALANCE;
}
}
return *this;
}
}
value_t& operator=(const balance_pair_t& value) {
if ((balance_pair_t *) data != &value) {
if (! value.cost) {
if (type == BALANCE_PAIR &&
(balance_pair_t *) data == &value)
return *this;
if (value.realzero()) {
return *this = 0L;
}
else if (! value.cost) {
return *this = value.quantity;
} else {
}
else {
destroy();
new((balance_pair_t *)data) balance_pair_t(value);
type = BALANCE_PAIR;
}
}
return *this;
}
}
value_t& operator+=(const value_t& value);
value_t& operator-=(const value_t& value);
@ -263,10 +278,32 @@ class value_t
return negated();
}
bool realzero() const {
switch (type) {
case BOOLEAN:
return ! *((bool *) data);
case INTEGER:
return *((long *) data) == 0;
case AMOUNT:
return ((amount_t *) data)->realzero();
case BALANCE:
return ((balance_t *) data)->realzero();
case BALANCE_PAIR:
return ((balance_pair_t *) data)->realzero();
default:
assert(0);
break;
}
assert(0);
return 0;
}
void abs();
void cast(type_t cast_type);
value_t cost() const;
value_t price() const;
value_t date() const;
value_t reduce(const bool keep_price = false,
const bool keep_date = false,
const bool keep_tag = false) const;
@ -291,12 +328,9 @@ class value_t
case BOOLEAN:
case INTEGER:
break;
case AMOUNT: {
amount_t& amount = *((amount_t *) data);
if (amount.commodity())
amount = amount.round(amount.commodity().precision());
case AMOUNT:
*((amount_t *) data) = ((amount_t *) data)->round();
break;
}
case BALANCE:
((balance_t *) data)->round();
break;