summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rwxr-xr-xacprep2
-rw-r--r--amount.cc263
-rw-r--r--amount.h92
-rw-r--r--balance.cc116
-rw-r--r--balance.h34
-rw-r--r--binary.cc24
-rw-r--r--config.cc25
-rw-r--r--config.h3
-rw-r--r--derive.cc28
-rw-r--r--format.cc21
-rw-r--r--format.h4
-rw-r--r--gnucash.cc2
-rw-r--r--journal.cc8
-rw-r--r--journal.h11
-rw-r--r--main.cc20
-rw-r--r--ofx.cc6
-rw-r--r--textual.cc406
-rw-r--r--valexpr.cc96
-rw-r--r--valexpr.h77
-rw-r--r--value.cc777
-rw-r--r--value.h96
22 files changed, 1364 insertions, 749 deletions
diff --git a/Makefile.am b/Makefile.am
index 2af4a5eb..c3acc1a1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 \
diff --git a/acprep b/acprep
index 622f940b..631bf197 100755
--- a/acprep
+++ b/acprep
@@ -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"
diff --git a/amount.cc b/amount.cc
index 21f5bf6d..24707eff 100644
--- a/amount.cc
+++ b/amount.cc
@@ -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;
@@ -256,7 +259,7 @@ amount_t& amount_t::operator=(const std::string& value)
amount_t& amount_t::operator=(const char * value)
{
- std::string valstr(value);
+ std::string valstr(value);
std::istringstream str(valstr);
parse(str);
return *this;
@@ -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,79 +912,94 @@ 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;
+}
- bool added_name = false;
+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];
- c = peek_next_nonws(in);
+ bool added_name = false;
+ bool handled = false;
+ do {
+ char c = peek_next_nonws(in);
+ if (c == '{') {
+ if (! price.empty())
+ throw amount_error("Commodity specifies more than one price");
- if (c == '{') {
- in.get(c);
- READ_INTO(in, buf, 255, c, c != '}');
- if (c == '}')
in.get(c);
- else
- throw amount_error("Commodity price lacks closing brace");
- price = buf;
- if (! added_name) {
- added_name = true;
- if (commodity_t::needs_quotes(symbol)) {
- name = "\"";
- name += symbol;
- name += "\"";
+ READ_INTO(in, buf, 255, c, c != '}');
+ if (c == '}')
+ in.get(c);
+ else
+ throw amount_error("Commodity price lacks closing brace");
+ price = buf;
+ if (! added_name) {
+ added_name = true;
+ if (commodity_t::needs_quotes(symbol)) {
+ name = "\"";
+ name += symbol;
+ name += "\"";
+ }
}
+ name += " {";
+ name += price;
+ 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 == ']')
in.get(c);
- else
- throw amount_error("Commodity date lacks closing bracket");
- date = buf;
- if (! added_name) {
- added_name = true;
- if (commodity_t::needs_quotes(symbol)) {
- name = "\"";
- name += symbol;
- name += "\"";
+ READ_INTO(in, buf, 255, c, c != ']');
+ if (c == ']')
+ in.get(c);
+ else
+ throw amount_error("Commodity date lacks closing bracket");
+ date = buf;
+ if (! added_name) {
+ added_name = true;
+ if (commodity_t::needs_quotes(symbol)) {
+ name = "\"";
+ name += symbol;
+ name += "\"";
+ }
}
+ name += " [";
+ name += date;
+ 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 == ')')
in.get(c);
- else
- throw amount_error("Commodity tag lacks closing parenthesis");
- tag = buf;
- if (! added_name) {
- added_name = true;
- if (commodity_t::needs_quotes(symbol)) {
- name = "\"";
- name += symbol;
- name += "\"";
+ READ_INTO(in, buf, 255, c, c != ')');
+ if (c == ')')
+ in.get(c);
+ else
+ throw amount_error("Commodity tag lacks closing parenthesis");
+ tag = buf;
+ if (! added_name) {
+ added_name = true;
+ if (commodity_t::needs_quotes(symbol)) {
+ name = "\"";
+ name += symbol;
+ name += "\"";
+ }
}
+ name += " (";
+ name += tag;
+ 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;
- if (std::isspace(in.peek()))
- comm_flags |= COMMODITY_STYLE_SEPARATED;
+ parse_quantity(in, quant);
- 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")
diff --git a/amount.h b/amount.h
index be6f65b7..3fb9eed3 100644
--- a/amount.h
+++ b/amount.h
@@ -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,27 +554,23 @@ 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,
- 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,
- const amount_t& price,
- const std::time_t date,
- const std::string& tag);
- static
- annotated_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,
- const amount_t& price,
- const std::time_t date,
- const std::string& tag);
+ 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 commodity_t * create(const std::string& symbol,
+ const amount_t& price,
+ const std::time_t date,
+ const std::string& tag);
+ static commodity_t * create(const std::string& symbol,
+ const std::string& price,
+ const std::string& date,
+ const std::string& tag);
+ static commodity_t * find_or_create(const commodity_t& comm,
+ const amount_t& price,
+ const std::time_t date,
+ const std::string& tag);
explicit annotated_commodity_t() {
annotated = true;
@@ -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:
diff --git a/balance.cc b/balance.cc
index 13acef22..5cfd88ca 100644
--- a/balance.cc
+++ b/balance.cc
@@ -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,32 +120,51 @@ void balance_t::write(std::ostream& out,
if (lwidth == -1)
lwidth = first_width;
- typedef std::deque<const amount_t *> amounts_deque;
- amounts_deque sorted;
-
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- if ((*i).second)
- sorted.push_back(&(*i).second);
-
- std::stable_sort(sorted.begin(), sorted.end(), compare_amount_commodities());
-
- for (amounts_deque::const_iterator i = sorted.begin();
- i != sorted.end();
- i++) {
- int width;
- if (! first) {
- out << std::endl;
- width = lwidth;
- } else {
- first = false;
- width = 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;
+
+ for (amounts_map::const_iterator i = amounts.begin();
+ i != amounts.end();
+ i++)
+ if ((*i).second)
+ sorted.push_back(&(*i).second);
+
+ std::stable_sort(sorted.begin(), sorted.end(), compare_amount_commodities());
+
+ for (amounts_deque::const_iterator i = sorted.begin();
+ i != sorted.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;
}
-
- out.width(width);
- out.fill(' ');
- out << std::right << **i;
}
if (first) {
diff --git a/balance.h b/balance.h
index 563f620a..edc3c19c 100644
--- a/balance.h
+++ b/balance.h
@@ -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());
}
};
diff --git a/binary.cc b/binary.cc
index 6ef4bc65..f5d9c467 100644
--- a/binary.cc
+++ b/binary.cc
@@ -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);
- }
+ 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);
- }
+ 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;
}
diff --git a/config.cc b/config.cc
index bc67a7ef..b53c8110 100644
--- a/config.cc
+++ b/config.cc
@@ -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())
- format_t::date_format = date_format;
+ // 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);
//////////////////////////////////////////////////////////////////////
diff --git a/config.h b/config.h
index a19dd2b6..bad96c77 100644
--- a/config.h
+++ b/config.h
@@ -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();
diff --git a/derive.cc b/derive.cc
index 5e103dc9..20083796 100644
--- a/derive.cc
+++ b/derive.cc
@@ -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;
diff --git a/format.cc b/format.cc
index 732408e0..82662184 100644
--- a/format.cc
+++ b/format.cc
@@ -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;
diff --git a/format.h b/format.h
index a2c5c89b..60f94e65 100644
--- a/format.h
+++ b/format.h
@@ -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) {
diff --git a/gnucash.cc b/gnucash.cc
index cf858ba3..11e4ceec 100644
--- a/gnucash.cc
+++ b/gnucash.cc
@@ -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;
diff --git a/journal.cc b/journal.cc
index 4e03ae1a..7c1aeac5 100644
--- a/journal.cc
+++ b/journal.cc
@@ -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 : "");
diff --git a/journal.h b/journal.h
index b1eae918..d50a78ea 100644
--- a/journal.h
+++ b/journal.h
@@ -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");
diff --git a/main.cc b/main.cc
index 2ba7571b..dd34d7a7 100644
--- a/main.cc
+++ b/main.cc
@@ -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,22 +349,19 @@ 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);
}
- TIMER_STOP(cache_write);
-
#ifdef HAVE_UNIX_PIPES
if (! config.pager.empty()) {
delete out;
diff --git a/ofx.cc b/ofx.cc
index 31ea699e..8b53b284 100644
--- a/ofx.cc
+++ b/ofx.cc
@@ -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)
diff --git a/textual.cc b/textual.cc
index 59a2075d..78ff6bf3 100644
--- a/textual.cc
+++ b/textual.cc
@@ -64,265 +64,225 @@ 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;
-
- *note_str++ = '\0';
- note_str = skip_ws(note_str);
-
- if (char * b = std::strchr(note_str, '['))
- if (char * e = std::strchr(note_str, ']')) {
- char buf[256];
- std::strncpy(buf, b + 1, e - b);
- buf[e - b] = '\0';
-
- if (char * p = std::strchr(buf, '=')) {
- *p++ = '\0';
- if (! quick_parse_date(p, &xact->_date_eff))
- throw parse_error(path, linenum,
- "Failed to parse effective date");
- }
-
- 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");
+ // Parse the account name
- *price++ = '\0';
- if (*price == '@') {
- per_unit = false;
- price++;
- }
- price = skip_ws(price);
- }
- }
- }
+ 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++;
}
- char * q = p + std::strlen(p) - 1;
- while (q >= p && std::isspace(*q))
- *q-- = '\0';
+ if (account_beg == account_end)
+ throw parse_error(path, linenum, "No account was specified");
- if (*p == '[' || *p == '(') {
+ char * b = &line[account_beg];
+ char * e = &line[account_end];
+ if ((*b == '[' && *(e - 1) == ']') ||
+ (*b == '(' && *(e - 1) == ')')) {
xact->flags |= TRANSACTION_VIRTUAL;
- if (*p == '[')
+ DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
+ "Parsed a virtual account name");
+ if (*b == '[') {
xact->flags |= TRANSACTION_BALANCE;
- p++;
-
- char * e = p + (std::strlen(p) - 1);
- assert(*e == ')' || *e == ']');
- *e = '\0';
+ 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(p);
+ 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(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->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);
- *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());
+ } 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 : "");
+ }
- xact->amount.annotate_commodity(cost, xact->entry->actual_date(),
- 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();
}
+ 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 - 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';
+ if (! quick_parse_date(p, &xact->_date_eff))
+ throw parse_error(path, linenum,
+ "Failed to parse effective date");
+ }
+
+ if (buf[0] && ! quick_parse_date(buf, &xact->_date))
+ throw parse_error(path, linenum, "Failed to parse date");
+ }
+ }
+ }
+
+ 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());
diff --git a/valexpr.cc b/valexpr.cc
index c8495771..88136ee7 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -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));
@@ -1251,6 +1288,12 @@ void init_value_expr()
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;
node->set_right(new value_expr_t(value_expr_t::F_QUANTITY));
globals->define("S", node);
globals->define("quant", node);
@@ -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;
diff --git a/valexpr.h b/valexpr.h
index 8e890586..fc16a5d1 100644
--- a/valexpr.h
+++ b/valexpr.h
@@ -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,
- value_expr_t * context = NULL) = 0;
+ 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,
- value_expr_t * context = NULL) {
+ 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,
- value_expr_t * context = NULL) const;
+ 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,
- value_expr_t * context = NULL) {
+ 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));
}
};
diff --git a/value.cc b/value.cc
index 377a9631..6bd66b6d 100644
--- a/value.cc
+++ b/value.cc
@@ -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;
+ }
+
+ simplify();
+
+ return *this;
}
-DEF_VALUE_MULDIV_OP(*=)
-DEF_VALUE_MULDIV_OP(/=)
+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
{
diff --git a/value.h b/value.h
index bcc71d03..9c14ed10 100644
--- a/value.h
+++ b/value.h
@@ -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,40 +121,54 @@ class value_t
return *this = amount_t(value);
}
value_t& operator=(const amount_t& value) {
- if ((amount_t *) data != &value) {
- if (! value) {
- return *this = 0L;
- } else {
- destroy();
- new((amount_t *)data) amount_t(value);
- type = AMOUNT;
- }
+ 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) {
- return *this = (*value.amounts.begin()).second;
- } else {
- destroy();
- new((balance_t *)data) balance_t(value);
- type = BALANCE;
- }
+ 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 {
+ destroy();
+ new((balance_t *)data) balance_t(value);
+ type = BALANCE;
+ return *this;
}
- return *this;
}
value_t& operator=(const balance_pair_t& value) {
- if ((balance_pair_t *) data != &value) {
- if (! value.cost) {
- return *this = value.quantity;
- } else {
- destroy();
- new((balance_pair_t *)data) balance_pair_t(value);
- type = BALANCE_PAIR;
- }
+ 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 {
+ destroy();
+ new((balance_pair_t *)data) balance_pair_t(value);
+ type = BALANCE_PAIR;
+ return *this;
}
- return *this;
}
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;