*** empty log message ***
This commit is contained in:
parent
11498f4807
commit
7acc1306d9
22 changed files with 1361 additions and 746 deletions
|
|
@ -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
2
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"
|
||||
|
|
|
|||
159
amount.cc
159
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;
|
||||
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
66
amount.h
66
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,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:
|
||||
|
|
|
|||
66
balance.cc
66
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,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);
|
||||
|
|
|
|||
34
balance.h
34
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());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
20
binary.cc
20
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);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
|
|
|||
23
config.cc
23
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())
|
||||
// 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);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
3
config.h
3
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();
|
||||
|
|
|
|||
28
derive.cc
28
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;
|
||||
|
|
|
|||
21
format.cc
21
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;
|
||||
|
|
|
|||
4
format.h
4
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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 : "");
|
||||
|
|
|
|||
11
journal.h
11
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");
|
||||
|
|
|
|||
20
main.cc
20
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,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
6
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)
|
||||
|
|
|
|||
412
textual.cc
412
textual.cc
|
|
@ -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());
|
||||
|
|
|
|||
96
valexpr.cc
96
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));
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
69
valexpr.h
69
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,
|
||||
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
777
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;
|
||||
}
|
||||
|
||||
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
70
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,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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue