diff options
Diffstat (limited to 'value.cc')
-rw-r--r-- | value.cc | 2241 |
1 files changed, 1072 insertions, 1169 deletions
@@ -1,1696 +1,1599 @@ +/* + * Copyright (c) 2003-2008, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + #include "value.h" -#include "debug.h" -#include "error.h" namespace ledger { -void value_t::destroy() +intrusive_ptr<value_t::storage_t> value_t::true_value; +intrusive_ptr<value_t::storage_t> value_t::false_value; + +value_t::storage_t& value_t::storage_t::operator=(const value_t::storage_t& rhs) { + type = rhs.type; + switch (type) { + case DATETIME: + new((datetime_t *) data) datetime_t(*(datetime_t *) rhs.data); + break; + case AMOUNT: - ((amount_t *)data)->~amount_t(); + new((amount_t *) data) amount_t(*(amount_t *) rhs.data); break; + case BALANCE: - ((balance_t *)data)->~balance_t(); + *(balance_t **) data = new balance_t(**(balance_t **) rhs.data); break; + case BALANCE_PAIR: - ((balance_pair_t *)data)->~balance_pair_t(); + *(balance_pair_t **) data = + new balance_pair_t(**(balance_pair_t **) rhs.data); + break; + + case STRING: + new((string *) data) string(*(string *) rhs.data); break; + + case SEQUENCE: + *(sequence_t **) data = new sequence_t(**(sequence_t **) rhs.data); + break; + default: + // The rest are fundamental types, which can copy using std::memcpy + std::memcpy(data, rhs.data, sizeof(data)); break; } + + return *this; } -void value_t::simplify() +void value_t::storage_t::destroy() { - if (realzero()) { - DEBUG_PRINT("amounts.values.simplify", "Zeroing type " << type); - *this = 0L; - return; + switch (type) { + case AMOUNT: + reinterpret_cast<amount_t *>(data)->~amount_t(); + break; + case BALANCE: + checked_delete(*reinterpret_cast<balance_t **>(data)); + break; + case BALANCE_PAIR: + checked_delete(*reinterpret_cast<balance_pair_t **>(data)); + break; + case STRING: + reinterpret_cast<string *>(data)->~string(); + break; + case SEQUENCE: + checked_delete(*reinterpret_cast<sequence_t **>(data)); + break; + case POINTER: + reinterpret_cast<boost::any *>(data)->~any(); + break; + + default: + break; } + type = VOID; +} + +void value_t::initialize() +{ +#if 0 + LOGGER("value.initialize"); +#endif + + true_value = new storage_t; + true_value->type = BOOLEAN; + *reinterpret_cast<bool *>(true_value->data) = true; + + false_value = new storage_t; + false_value->type = BOOLEAN; + *reinterpret_cast<bool *>(false_value->data) = false; + + BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(bool)); + BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(datetime_t)); + BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(long)); + BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(amount_t)); + BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_t *)); + BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_pair_t *)); + BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(string)); + BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(sequence_t *)); + BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(boost::any)); + +#if 0 + DEBUG_(std::setw(3) << std::right << sizeof(bool) + << " sizeof(bool)"); + DEBUG_(std::setw(3) << std::right << sizeof(datetime_t) + << " sizeof(datetime_t)"); + DEBUG_(std::setw(3) << std::right << sizeof(long) + << " sizeof(long)"); + DEBUG_(std::setw(3) << std::right << sizeof(amount_t) + << " sizeof(amount_t)"); + DEBUG_(std::setw(3) << std::right << sizeof(balance_t *) + << " sizeof(balance_t *)"); + DEBUG_(std::setw(3) << std::right << sizeof(balance_pair_t *) + << " sizeof(balance_pair_t *)"); + DEBUG_(std::setw(3) << std::right << sizeof(string) + << " sizeof(string)"); + DEBUG_(std::setw(3) << std::right << sizeof(sequence_t *) + << " sizeof(sequence_t *)"); + DEBUG_(std::setw(3) << std::right << sizeof(boost::any) + << " sizeof(boost::any)"); +#endif +} + +void value_t::shutdown() +{ + true_value = intrusive_ptr<storage_t>(); + false_value = intrusive_ptr<storage_t>(); +} + +void value_t::_dup() +{ + assert(storage); + if (storage->refc > 1) + storage = new storage_t(*storage.get()); +} - 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); +value_t::operator bool() const +{ + switch (type()) { + case BOOLEAN: + return as_boolean(); + case INTEGER: + return as_long(); + case DATETIME: + return is_valid(as_datetime()); + case AMOUNT: + return as_amount(); + case BALANCE: + return as_balance(); + case BALANCE_PAIR: + return as_balance_pair(); + case STRING: + return ! as_string().empty(); + case SEQUENCE: + return ! as_sequence().empty(); + case POINTER: + return ! as_any_pointer().empty(); + default: + assert(false); + break; } + assert(false); + return 0; +} - if (type == BALANCE && - ((balance_t *) data)->amounts.size() == 1) { - DEBUG_PRINT("amounts.values.simplify", "Reducing balance to amount"); - cast(AMOUNT); +bool value_t::to_boolean() const +{ + if (is_boolean()) { + return as_boolean(); + } else { + value_t temp(*this); + temp.in_place_cast(BOOLEAN); + return temp.as_boolean(); } +} - if (type == AMOUNT && - ! ((amount_t *) data)->commodity()) { - DEBUG_PRINT("amounts.values.simplify", "Reducing amount to integer"); - cast(INTEGER); +long value_t::to_long() const +{ + if (is_long()) { + return as_long(); + } else { + value_t temp(*this); + temp.in_place_cast(INTEGER); + return temp.as_long(); } } -value_t& value_t::operator=(const value_t& value) +datetime_t value_t::to_datetime() const { - if (this == &value) - return *this; + if (is_datetime()) { + return as_datetime(); + } else { + value_t temp(*this); + temp.in_place_cast(DATETIME); + return temp.as_datetime(); + } +} - destroy(); +amount_t value_t::to_amount() const +{ + if (is_amount()) { + return as_amount(); + } else { + value_t temp(*this); + temp.in_place_cast(AMOUNT); + return temp.as_amount(); + } +} - switch (value.type) { - case BOOLEAN: - *((bool *) data) = *((bool *) value.data); - break; +balance_t value_t::to_balance() const +{ + if (is_balance()) { + return as_balance(); + } else { + value_t temp(*this); + temp.in_place_cast(BALANCE); + return temp.as_balance(); + } +} - case INTEGER: - *((long *) data) = *((long *) value.data); - break; +balance_pair_t value_t::to_balance_pair() const +{ + if (is_balance_pair()) { + return as_balance_pair(); + } else { + value_t temp(*this); + temp.in_place_cast(BALANCE_PAIR); + return temp.as_balance_pair(); + } +} - case DATETIME: - *((datetime_t *) data) = *((datetime_t *) value.data); - break; +string value_t::to_string() const +{ + if (is_string()) { + return as_string(); + } else { + value_t temp(*this); + temp.in_place_cast(STRING); + return temp.as_string(); + } +} - case AMOUNT: - new((amount_t *)data) amount_t(*((amount_t *) value.data)); - break; +value_t::sequence_t value_t::to_sequence() const +{ + if (is_sequence()) { + return as_sequence(); + } else { + value_t temp(*this); + temp.in_place_cast(SEQUENCE); + return temp.as_sequence(); + } +} - case BALANCE: - new((balance_t *)data) balance_t(*((balance_t *) value.data)); - break; - case BALANCE_PAIR: - new((balance_pair_t *)data) balance_pair_t(*((balance_pair_t *) value.data)); - break; +void value_t::in_place_simplify() +{ + LOGGER("amounts.values.simplify"); - default: - assert(0); - break; + if (is_realzero()) { + DEBUG_("Zeroing type " << type()); + set_long(0L); + return; } - type = value.type; + if (is_balance_pair() && + (! as_balance_pair().cost || as_balance_pair().cost->is_realzero())) { + DEBUG_("Reducing balance pair to balance"); + in_place_cast(BALANCE); + } - return *this; + if (is_balance() && as_balance().amounts.size() == 1) { + DEBUG_("Reducing balance to amount"); + DEBUG("ledger.value.reduce", "as a balance it looks like: " << *this); + in_place_cast(AMOUNT); + DEBUG("ledger.value.reduce", "as an amount it looks like: " << *this); + } + +#if 0 + if (is_amount() && ! as_amount().has_commodity() && + as_amount().fits_in_long()) { + DEBUG_("Reducing amount to integer"); + in_place_cast(INTEGER); + } +#endif } -value_t& value_t::operator+=(const value_t& value) +value_t& value_t::operator+=(const value_t& val) { - if (value.type == BOOLEAN) - throw new value_error("Cannot add a boolean to a value"); - else if (value.type == DATETIME) - throw new value_error("Cannot add a date/time to a value"); - - switch (type) { - case BOOLEAN: - throw new value_error("Cannot add a value to a boolean"); + if (is_string()) { + if (val.is_string()) + as_string_lval() += val.as_string(); + else + as_string_lval() += val.to_string(); + return *this; + } + else if (is_sequence()) { + if (val.is_sequence()) { + sequence_t& seq(as_sequence_lval()); + seq.insert(seq.end(), val.as_sequence().begin(), + val.as_sequence().end()); + } else { + as_sequence_lval().push_back(val); + } + return *this; + } - case INTEGER: - switch (value.type) { + switch (type()) { + case DATETIME: + switch (val.type()) { case INTEGER: - *((long *) data) += *((long *) value.data); - break; + as_datetime_lval() += date_duration(val.as_long()); + return *this; 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; + as_datetime_lval() += date_duration(val.as_amount().to_long()); + return *this; default: - assert(0); break; } break; - case DATETIME: - switch (value.type) { + case INTEGER: + switch (val.type()) { case INTEGER: - *((datetime_t *) data) += *((long *) value.data); - break; + as_long_lval() += val.as_long(); + return *this; case AMOUNT: - *((datetime_t *) data) += long(*((amount_t *) value.data)); - break; + in_place_cast(AMOUNT); + as_amount_lval() += val.as_amount(); + return *this; case BALANCE: - *((datetime_t *) data) += long(*((balance_t *) value.data)); - break; + in_place_cast(BALANCE); + as_balance_lval() += val.as_balance(); + return *this; case BALANCE_PAIR: - *((datetime_t *) data) += long(*((balance_pair_t *) value.data)); - break; + in_place_cast(BALANCE_PAIR); + as_balance_pair_lval() += val.as_balance_pair(); + return *this; default: - assert(0); break; } break; case AMOUNT: - switch (value.type) { + switch (val.type()) { case INTEGER: - if (*((long *) value.data) && - ((amount_t *) data)->commodity()) { - cast(BALANCE); - return *this += value; + if (as_amount().has_commodity()) { + in_place_cast(BALANCE); + return *this += val; + } else { + as_amount_lval() += val.as_long(); + return *this; } - *((amount_t *) data) += *((long *) value.data); break; case AMOUNT: - if (((amount_t *) data)->commodity() != - ((amount_t *) value.data)->commodity()) { - cast(BALANCE); - return *this += value; + if (as_amount().commodity() != val.as_amount().commodity()) { + in_place_cast(BALANCE); + return *this += val; + } else { + as_amount_lval() += val.as_amount(); + return *this; } - *((amount_t *) data) += *((amount_t *) value.data); break; case BALANCE: - cast(BALANCE); - *((balance_t *) data) += *((balance_t *) value.data); - break; + in_place_cast(BALANCE); + as_balance_lval() += val.as_balance(); + return *this; case BALANCE_PAIR: - cast(BALANCE_PAIR); - *((balance_pair_t *) data) += *((balance_pair_t *) value.data); - break; - + in_place_cast(BALANCE_PAIR); + as_balance_pair_lval() += val.as_balance_pair(); + return *this; default: - assert(0); break; } break; case BALANCE: - switch (value.type) { + switch (val.type()) { case INTEGER: - *((balance_t *) data) += *((long *) value.data); - break; + as_balance_lval() += val.to_amount(); + return *this; case AMOUNT: - *((balance_t *) data) += *((amount_t *) value.data); - break; + as_balance_lval() += val.as_amount(); + return *this; case BALANCE: - *((balance_t *) data) += *((balance_t *) value.data); - break; + as_balance_lval() += val.as_balance(); + return *this; case BALANCE_PAIR: - cast(BALANCE_PAIR); - *((balance_pair_t *) data) += *((balance_pair_t *) value.data); - break; + in_place_cast(BALANCE_PAIR); + as_balance_pair_lval() += val.as_balance_pair(); + return *this; default: - assert(0); break; } break; case BALANCE_PAIR: - switch (value.type) { + switch (val.type()) { case INTEGER: - *((balance_pair_t *) data) += *((long *) value.data); - break; + as_balance_pair_lval() += val.to_amount(); + return *this; case AMOUNT: - *((balance_pair_t *) data) += *((amount_t *) value.data); - break; + as_balance_pair_lval() += val.as_amount(); + return *this; case BALANCE: - *((balance_pair_t *) data) += *((balance_t *) value.data); - break; + as_balance_pair_lval() += val.as_balance(); + return *this; case BALANCE_PAIR: - *((balance_pair_t *) data) += *((balance_pair_t *) value.data); - break; + as_balance_pair_lval() += val.as_balance_pair(); + return *this; default: - assert(0); break; } break; default: - assert(0); break; } + + throw_(value_error, "Cannot add " << val.label() << " to " << label()); + return *this; } -value_t& value_t::operator-=(const value_t& value) +value_t& value_t::operator-=(const value_t& val) { - if (value.type == BOOLEAN) - throw new value_error("Cannot subtract a boolean from a value"); - else if (value.type == DATETIME && type != DATETIME) - throw new value_error("Cannot subtract a date/time from a value"); - - switch (type) { - case BOOLEAN: - throw new value_error("Cannot subtract a value from a boolean"); + if (is_sequence()) { + sequence_t& seq(as_sequence_lval()); + + if (val.is_sequence()) { + for (sequence_t::const_iterator i = val.as_sequence().begin(); + i != val.as_sequence().end(); + i++) { + sequence_t::iterator j = std::find(seq.begin(), seq.end(), *i); + if (j != seq.end()) + seq.erase(j); + } + } else { + sequence_t::iterator i = std::find(seq.begin(), seq.end(), val); + if (i != seq.end()) + seq.erase(i); + } + return *this; + } - case INTEGER: - switch (value.type) { + switch (type()) { + case DATETIME: + switch (val.type()) { case INTEGER: - *((long *) data) -= *((long *) value.data); - break; + as_datetime_lval() -= date_duration(val.as_long()); + return *this; 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; + as_datetime_lval() -= date_duration(val.as_amount().to_long()); + return *this; default: - assert(0); break; } break; - case DATETIME: - switch (value.type) { + case INTEGER: + switch (val.type()) { case INTEGER: - *((datetime_t *) data) -= *((long *) value.data); - break; - case DATETIME: { - long val = *((datetime_t *) data) - *((datetime_t *) value.data); - cast(INTEGER); - *((long *) data) = val; - break; - } + as_long_lval() -= val.as_long(); + return *this; case AMOUNT: - *((datetime_t *) data) -= long(*((amount_t *) value.data)); - break; + in_place_cast(AMOUNT); + as_amount_lval() -= val.as_amount(); + in_place_simplify(); + return *this; case BALANCE: - *((datetime_t *) data) -= long(*((balance_t *) value.data)); - break; + in_place_cast(BALANCE); + as_balance_lval() -= val.as_balance(); + in_place_simplify(); + return *this; case BALANCE_PAIR: - *((datetime_t *) data) -= long(*((balance_pair_t *) value.data)); - break; + in_place_cast(BALANCE_PAIR); + as_balance_pair_lval() -= val.as_balance_pair(); + in_place_simplify(); + return *this; default: - assert(0); break; } break; case AMOUNT: - switch (value.type) { + switch (val.type()) { case INTEGER: - if (*((long *) value.data) && - ((amount_t *) data)->commodity()) { - cast(BALANCE); - return *this -= value; + if (as_amount().has_commodity()) { + in_place_cast(BALANCE); + *this -= val; + in_place_simplify(); + return *this; + } else { + as_amount_lval() -= val.as_long(); + in_place_simplify(); + return *this; } - *((amount_t *) data) -= *((long *) value.data); break; case AMOUNT: - if (((amount_t *) data)->commodity() != - ((amount_t *) value.data)->commodity()) { - cast(BALANCE); - return *this -= value; + if (as_amount().commodity() != val.as_amount().commodity()) { + in_place_cast(BALANCE); + *this -= val; + in_place_simplify(); + return *this; + } else { + as_amount_lval() -= val.as_amount(); + in_place_simplify(); + return *this; } - *((amount_t *) data) -= *((amount_t *) value.data); break; case BALANCE: - cast(BALANCE); - *((balance_t *) data) -= *((balance_t *) value.data); - break; + in_place_cast(BALANCE); + as_balance_lval() -= val.as_balance(); + in_place_simplify(); + return *this; case BALANCE_PAIR: - cast(BALANCE_PAIR); - *((balance_pair_t *) data) -= *((balance_pair_t *) value.data); - break; - + in_place_cast(BALANCE_PAIR); + as_balance_pair_lval() -= val.as_balance_pair(); + in_place_simplify(); + return *this; default: - assert(0); break; } break; case BALANCE: - switch (value.type) { + switch (val.type()) { case INTEGER: - *((balance_t *) data) -= *((long *) value.data); - break; + as_balance_lval() -= val.to_amount(); + in_place_simplify(); + return *this; case AMOUNT: - *((balance_t *) data) -= *((amount_t *) value.data); - break; + as_balance_lval() -= val.as_amount(); + in_place_simplify(); + return *this; case BALANCE: - *((balance_t *) data) -= *((balance_t *) value.data); - break; + as_balance_lval() -= val.as_balance(); + in_place_simplify(); + return *this; case BALANCE_PAIR: - cast(BALANCE_PAIR); - *((balance_pair_t *) data) -= *((balance_pair_t *) value.data); - break; + in_place_cast(BALANCE_PAIR); + as_balance_pair_lval() -= val.as_balance_pair(); + in_place_simplify(); + return *this; default: - assert(0); break; } break; case BALANCE_PAIR: - switch (value.type) { + switch (val.type()) { case INTEGER: - *((balance_pair_t *) data) -= *((long *) value.data); - break; + as_balance_pair_lval() -= val.to_amount(); + in_place_simplify(); + return *this; case AMOUNT: - *((balance_pair_t *) data) -= *((amount_t *) value.data); - break; + as_balance_pair_lval() -= val.as_amount(); + in_place_simplify(); + return *this; case BALANCE: - *((balance_pair_t *) data) -= *((balance_t *) value.data); - break; + as_balance_pair_lval() -= val.as_balance(); + in_place_simplify(); + return *this; case BALANCE_PAIR: - *((balance_pair_t *) data) -= *((balance_pair_t *) value.data); - break; + as_balance_pair_lval() -= val.as_balance_pair(); + in_place_simplify(); + return *this; default: - assert(0); break; } break; default: - assert(0); break; } - simplify(); + throw_(value_error, "Cannot subtract " << val.label() << " from " << label()); return *this; } -value_t& value_t::operator*=(const value_t& value) +value_t& value_t::operator*=(const value_t& val) { - if (value.type == BOOLEAN) - throw new value_error("Cannot multiply a boolean by a value"); - else if (value.type == DATETIME) - throw new value_error("Cannot multiply a date/time by a value"); - - if (value.realzero()) { - *this = 0L; + if (is_string()) { + string temp; + long count = val.to_long(); + for (long i = 0; i < count; i++) + temp += as_string(); + set_string(temp); return *this; } + else if (is_sequence()) { + value_t temp; + long count = val.to_long(); + for (long i = 0; i < count; i++) + temp += as_sequence(); + return *this = temp; + } - switch (type) { - case BOOLEAN: - throw new value_error("Cannot multiply a value by a boolean"); - + switch (type()) { case INTEGER: - switch (value.type) { + switch (val.type()) { case INTEGER: - *((long *) data) *= *((long *) value.data); - break; + as_long_lval() *= val.as_long(); + return *this; 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; + set_amount(val.as_amount() * as_long()); + return *this; default: - assert(0); break; } break; case AMOUNT: - switch (value.type) { + switch (val.type()) { case INTEGER: - *((amount_t *) data) *= *((long *) value.data); + as_amount_lval() *= val.as_long(); + return *this; + case AMOUNT: + if (as_amount().commodity() == val.as_amount().commodity() || + ! val.as_amount().has_commodity()) { + as_amount_lval() *= val.as_amount(); + return *this; + } break; + default: + break; + } + break; + + case BALANCE: + switch (val.type()) { + case INTEGER: + as_balance_lval() *= val.as_long(); + return *this; case AMOUNT: - *((amount_t *) data) *= *((amount_t *) value.data); + if (! val.as_amount().has_commodity()) { + as_balance_lval() *= val.as_amount(); + return *this; + } break; - case BALANCE: - cast(BALANCE); - *((balance_t *) data) *= *((balance_t *) value.data); + default: break; - case BALANCE_PAIR: - cast(BALANCE_PAIR); - *((balance_pair_t *) data) *= *((balance_pair_t *) value.data); + } + break; + + case BALANCE_PAIR: + switch (val.type()) { + case INTEGER: + as_balance_pair_lval() *= val.as_long(); + return *this; + case AMOUNT: + if (! val.as_amount().has_commodity()) { + as_balance_pair_lval() *= val.as_amount(); + return *this; + } break; default: - assert(0); break; } break; - case BALANCE: - switch (value.type) { + default: + break; + } + + throw_(value_error, "Cannot multiply " << label() << " with " << val.label()); + + return *this; +} + +value_t& value_t::operator/=(const value_t& val) +{ + switch (type()) { + case INTEGER: + switch (val.type()) { case INTEGER: - *((balance_t *) data) *= *((long *) value.data); + as_long_lval() /= val.as_long(); + return *this; + case AMOUNT: + set_amount(val.as_amount() / as_long()); + return *this; + default: break; + } + break; + + case AMOUNT: + switch (val.type()) { + case INTEGER: + as_amount_lval() /= val.as_long(); + return *this; + case AMOUNT: - *((balance_t *) data) *= *((amount_t *) value.data); + if (as_amount().commodity() == val.as_amount().commodity() || + ! val.as_amount().has_commodity()) { + as_amount_lval() /= val.as_amount(); + return *this; + } break; - case BALANCE: - *((balance_t *) data) *= *((balance_t *) value.data); + default: break; - case BALANCE_PAIR: - cast(BALANCE_PAIR); - *((balance_pair_t *) data) *= *((balance_pair_t *) value.data); + } + break; + + case BALANCE: + switch (val.type()) { + case INTEGER: + as_balance_lval() /= val.as_long(); + return *this; + case AMOUNT: + if (! val.as_amount().has_commodity()) { + as_balance_lval() /= val.as_amount(); + return *this; + } break; default: - assert(0); break; } break; case BALANCE_PAIR: - switch (value.type) { + switch (val.type()) { case INTEGER: - *((balance_pair_t *) data) *= *((long *) value.data); - break; + as_balance_pair_lval() /= val.as_long(); + return *this; 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); + if (! val.as_amount().has_commodity()) { + as_balance_pair_lval() /= val.as_amount(); + return *this; + } break; default: - assert(0); break; } break; default: - assert(0); break; } + + throw_(value_error, "Cannot divide " << label() << " by " << val.label()); + return *this; } -value_t& value_t::operator/=(const value_t& value) -{ - if (value.type == BOOLEAN) - throw new value_error("Cannot divide a boolean by a value"); - else if (value.type == DATETIME) - throw new value_error("Cannot divide a date/time by a value"); - switch (type) { +bool value_t::operator==(const value_t& val) const +{ + switch (type()) { case BOOLEAN: - throw new value_error("Cannot divide a value by a boolean"); + if (val.is_boolean()) + return as_boolean() == val.as_boolean(); + break; + + case DATETIME: + if (val.is_datetime()) + return as_datetime() == val.as_datetime(); + break; case INTEGER: - switch (value.type) { + switch (val.type()) { case INTEGER: - *((long *) data) /= *((long *) value.data); - break; + return as_long() == val.as_long(); case AMOUNT: - cast(AMOUNT); - *((amount_t *) data) /= *((amount_t *) value.data); - break; + return val.as_amount() == to_amount(); case BALANCE: - cast(BALANCE); - *((balance_t *) data) /= *((balance_t *) value.data); - break; + return val.as_balance() == to_amount(); case BALANCE_PAIR: - cast(BALANCE_PAIR); - *((balance_pair_t *) data) /= *((balance_pair_t *) value.data); - break; + return val.as_balance_pair() == to_amount(); default: - assert(0); break; } break; case AMOUNT: - switch (value.type) { + switch (val.type()) { case INTEGER: - *((amount_t *) data) /= *((long *) value.data); - break; + return as_amount() == val.as_long(); case AMOUNT: - *((amount_t *) data) /= *((amount_t *) value.data); - break; + return as_amount() == val.as_amount(); case BALANCE: - cast(BALANCE); - *((balance_t *) data) /= *((balance_t *) value.data); - break; + return val.as_balance() == as_amount(); case BALANCE_PAIR: - cast(BALANCE_PAIR); - *((balance_pair_t *) data) /= *((balance_pair_t *) value.data); - break; + return val.as_balance_pair() == as_amount(); default: - assert(0); break; } break; case BALANCE: - switch (value.type) { + switch (val.type()) { case INTEGER: - *((balance_t *) data) /= *((long *) value.data); - break; + return as_balance() == val.to_amount(); case AMOUNT: - *((balance_t *) data) /= *((amount_t *) value.data); - break; + return as_balance() == val.as_amount(); case BALANCE: - *((balance_t *) data) /= *((balance_t *) value.data); - break; + return as_balance() == val.as_balance(); case BALANCE_PAIR: - cast(BALANCE_PAIR); - *((balance_pair_t *) data) /= *((balance_pair_t *) value.data); - break; + return val.as_balance_pair() == as_balance(); default: - assert(0); break; } break; case BALANCE_PAIR: - switch (value.type) { + switch (val.type()) { case INTEGER: - *((balance_pair_t *) data) /= *((long *) value.data); - break; + return as_balance_pair() == val.to_amount(); case AMOUNT: - *((balance_pair_t *) data) /= *((amount_t *) value.data); - break; + return as_balance_pair() == val.as_amount(); case BALANCE: - *((balance_pair_t *) data) /= *((balance_t *) value.data); - break; + return as_balance_pair() == val.as_balance(); case BALANCE_PAIR: - *((balance_pair_t *) data) /= *((balance_pair_t *) value.data); - break; + return as_balance_pair() == val.as_balance_pair(); default: - assert(0); break; } break; + case STRING: + if (val.is_string()) + return as_string() == val.as_string(); + break; + + case SEQUENCE: + if (val.is_sequence()) + return as_sequence() == val.as_sequence(); + break; + default: - assert(0); break; } - return *this; -} -#define DEF_VALUE_CMP_OP(OP) \ -bool value_t::operator OP(const value_t& value) \ -{ \ - switch (type) { \ - case BOOLEAN: \ - switch (value.type) { \ - case BOOLEAN: \ - return *((bool *) data) OP *((bool *) value.data); \ - \ - case INTEGER: \ - return *((bool *) data) OP bool(*((long *) value.data)); \ - \ - case DATETIME: \ - return *((bool *) data) OP bool(*((datetime_t *) value.data)); \ - \ - case AMOUNT: \ - return *((bool *) data) OP bool(*((amount_t *) value.data)); \ - \ - case BALANCE: \ - return *((bool *) data) OP bool(*((balance_t *) value.data)); \ - \ - case BALANCE_PAIR: \ - return *((bool *) data) OP bool(*((balance_pair_t *) value.data)); \ - \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - case INTEGER: \ - switch (value.type) { \ - case BOOLEAN: \ - return (*((long *) data) OP \ - ((long) *((bool *) value.data))); \ - \ - case INTEGER: \ - return (*((long *) data) OP *((long *) value.data)); \ - \ - case DATETIME: \ - return (*((long *) data) OP \ - ((long) ((datetime_t *) value.data)->when)); \ - \ - case AMOUNT: \ - return (amount_t(*((long *) data)) OP \ - *((amount_t *) value.data)); \ - \ - case BALANCE: \ - return (balance_t(*((long *) data)) OP \ - *((balance_t *) value.data)); \ - \ - case BALANCE_PAIR: \ - return (balance_pair_t(*((long *) data)) OP \ - *((balance_pair_t *) value.data)); \ - \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - case DATETIME: \ - switch (value.type) { \ - case BOOLEAN: \ - throw new value_error("Cannot compare a date/time to a boolean"); \ - \ - case INTEGER: \ - return (*((datetime_t *) data) OP \ - datetime_t(std::time_t(*((long *) value.data)))); \ - \ - case DATETIME: \ - return (*((datetime_t *) data) OP \ - *((datetime_t *) value.data)); \ - \ - case AMOUNT: \ - throw new value_error("Cannot compare a date/time to an amount"); \ - \ - case BALANCE: \ - throw new value_error("Cannot compare a date/time to a balance"); \ - \ - case BALANCE_PAIR: \ - throw new value_error("Cannot compare a date/time to a balance pair"); \ - \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - case AMOUNT: \ - switch (value.type) { \ - case BOOLEAN: \ - throw new value_error("Cannot compare an amount to a boolean"); \ - \ - case INTEGER: \ - return (*((amount_t *) data) OP \ - amount_t(*((long *) value.data))); \ - \ - case DATETIME: \ - throw new value_error("Cannot compare an amount to a date/time"); \ - \ - case AMOUNT: \ - return *((amount_t *) data) OP *((amount_t *) value.data); \ - \ - case BALANCE: \ - return (balance_t(*((amount_t *) data)) OP \ - *((balance_t *) value.data)); \ - \ - case BALANCE_PAIR: \ - return (balance_t(*((amount_t *) data)) OP \ - *((balance_pair_t *) value.data)); \ - \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - case BALANCE: \ - switch (value.type) { \ - case BOOLEAN: \ - throw new value_error("Cannot compare a balance to a boolean"); \ - \ - case INTEGER: \ - return *((balance_t *) data) OP *((long *) value.data); \ - \ - case DATETIME: \ - throw new value_error("Cannot compare a balance to a date/time"); \ - \ - case AMOUNT: \ - return *((balance_t *) data) OP *((amount_t *) value.data); \ - \ - case BALANCE: \ - return *((balance_t *) data) OP *((balance_t *) value.data); \ - \ - case BALANCE_PAIR: \ - return (*((balance_t *) data) OP \ - ((balance_pair_t *) value.data)->quantity); \ - \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - case BALANCE_PAIR: \ - switch (value.type) { \ - case BOOLEAN: \ - throw new value_error("Cannot compare a balance pair to a boolean"); \ - \ - case INTEGER: \ - return (((balance_pair_t *) data)->quantity OP \ - *((long *) value.data)); \ - \ - case DATETIME: \ - throw new value_error("Cannot compare a balance pair to a date/time"); \ - \ - case AMOUNT: \ - return (((balance_pair_t *) data)->quantity OP \ - *((amount_t *) value.data)); \ - \ - case BALANCE: \ - return (((balance_pair_t *) data)->quantity OP \ - *((balance_t *) value.data)); \ - \ - case BALANCE_PAIR: \ - return (*((balance_pair_t *) data) OP \ - *((balance_pair_t *) value.data)); \ - \ - default: \ - assert(0); \ - break; \ - } \ - break; \ - \ - default: \ - assert(0); \ - break; \ - } \ - return *this; \ -} + throw_(value_error, "Cannot compare " << label() << " to " << val.label()); -DEF_VALUE_CMP_OP(==) -DEF_VALUE_CMP_OP(<) -DEF_VALUE_CMP_OP(<=) -DEF_VALUE_CMP_OP(>) -DEF_VALUE_CMP_OP(>=) + return *this; +} -template <> -value_t::operator long() const +bool value_t::operator<(const value_t& val) const { - switch (type) { - case BOOLEAN: - throw new value_error("Cannot convert a boolean to an integer"); - case INTEGER: - return *((long *) data); + switch (type()) { case DATETIME: - return ((datetime_t *) data)->when; - case AMOUNT: - return *((amount_t *) data); - case BALANCE: - throw new value_error("Cannot convert a balance to an integer"); - case BALANCE_PAIR: - throw new value_error("Cannot convert a balance pair to an integer"); - - default: - assert(0); + if (val.is_datetime()) + return as_datetime() < val.as_datetime(); break; - } - assert(0); - return 0; -} -template <> -value_t::operator datetime_t() const -{ - switch (type) { - case BOOLEAN: - throw new value_error("Cannot convert a boolean to a date/time"); case INTEGER: - return *((long *) data); - case DATETIME: - return *((datetime_t *) data); + switch (val.type()) { + case INTEGER: + return as_long() < val.as_long(); + case AMOUNT: + return val.as_amount() < as_long(); + default: + break; + } + break; + case AMOUNT: - throw new value_error("Cannot convert an amount to a date/time"); - case BALANCE: - throw new value_error("Cannot convert a balance to a date/time"); - case BALANCE_PAIR: - throw new value_error("Cannot convert a balance pair to a date/time"); + switch (val.type()) { + case INTEGER: + return as_amount() < val.as_long(); + case AMOUNT: + return as_amount() < val.as_amount(); + default: + break; + } + break; + + case STRING: + if (val.is_string()) + return as_string() < val.as_string(); + break; default: - assert(0); break; } - assert(0); - return 0; + + throw_(value_error, "Cannot compare " << label() << " to " << val.label()); + + return *this; } -template <> -value_t::operator double() const +#if 0 +bool value_t::operator>(const value_t& val) const { - switch (type) { - case BOOLEAN: - throw new value_error("Cannot convert a boolean to a double"); - case INTEGER: - return *((long *) data); + switch (type()) { case DATETIME: - throw new value_error("Cannot convert a date/time to a double"); + if (val.is_datetime()) + return as_datetime() > val.as_datetime(); + break; + + case INTEGER: + switch (val.type()) { + case INTEGER: + return as_long() > val.as_long(); + case AMOUNT: + return val.as_amount() > as_long(); + default: + break; + } + break; + case AMOUNT: - return *((amount_t *) data); - case BALANCE: - throw new value_error("Cannot convert a balance to a double"); - case BALANCE_PAIR: - throw new value_error("Cannot convert a balance pair to a double"); + switch (val.type()) { + case INTEGER: + return as_amount() > val.as_long(); + case AMOUNT: + return as_amount() > val.as_amount(); + default: + break; + } + break; + + case STRING: + if (val.is_string()) + return as_string() > val.as_string(); + break; default: - assert(0); break; } - assert(0); - return 0; + + throw_(value_error, "Cannot compare " << label() << " to " << val.label()); + + return *this; } +#endif -void value_t::cast(type_t cast_type) +void value_t::in_place_cast(type_t cast_type) { - switch (type) { + if (type() == cast_type) + return; + + if (cast_type == BOOLEAN) { + set_boolean(bool(*this)); + return; + } + else if (cast_type == SEQUENCE) { + sequence_t temp; + if (! is_null()) + temp.push_back(*this); + set_sequence(temp); + return; + } + + switch (type()) { case BOOLEAN: switch (cast_type) { - case BOOLEAN: - break; - case INTEGER: - throw new value_error("Cannot convert a boolean to an integer"); - case DATETIME: - throw new value_error("Cannot convert a boolean to a date/time"); - case AMOUNT: - throw new value_error("Cannot convert a boolean to an amount"); - case BALANCE: - throw new value_error("Cannot convert a boolean to a balance"); - case BALANCE_PAIR: - throw new value_error("Cannot convert a boolean to a balance pair"); - + case STRING: + set_string(as_boolean() ? "true" : "false"); + return; default: - assert(0); break; } break; case INTEGER: switch (cast_type) { - case BOOLEAN: - *((bool *) data) = *((long *) data); - break; - case INTEGER: - break; - case DATETIME: - *((datetime_t *) data) = datetime_t(std::time_t(*((long *) data))); - break; case AMOUNT: - new((amount_t *)data) amount_t(*((long *) data)); - break; + set_amount(as_long()); + return; case BALANCE: - new((balance_t *)data) balance_t(amount_t(*((long *) data))); - break; + set_balance(to_amount()); + return; case BALANCE_PAIR: - new((balance_pair_t *)data) balance_pair_t(amount_t(*((long *) data))); - break; - + set_balance_pair(to_amount()); + return; + case STRING: + set_string(lexical_cast<string>(as_long())); + return; default: - assert(0); break; } break; - case DATETIME: + case AMOUNT: { + const amount_t& amt(as_amount()); switch (cast_type) { - case BOOLEAN: - *((bool *) data) = *((datetime_t *) data); - break; case INTEGER: - *((long *) data) = ((datetime_t *) data)->when; - break; - case DATETIME: - break; - case AMOUNT: - throw new value_error("Cannot convert a date/time to an amount"); + if (amt.is_null()) + set_long(0L); + else + set_long(as_amount().to_long()); + return; case BALANCE: - throw new value_error("Cannot convert a date/time to a balance"); + if (amt.is_null()) + set_balance(balance_t()); + else + set_balance(as_amount()); // creates temporary + return; case BALANCE_PAIR: - throw new value_error("Cannot convert a date/time to a balance pair"); - - default: - assert(0); - break; - } - break; - - case AMOUNT: - switch (cast_type) { - case BOOLEAN: { - bool temp = *((amount_t *) data); - destroy(); - *((bool *)data) = temp; - break; - } - case INTEGER: { - long temp = *((amount_t *) data); - destroy(); - *((long *)data) = temp; - break; - } - case DATETIME: - throw new value_error("Cannot convert an amount to a date/time"); - case AMOUNT: - break; - case BALANCE: { - amount_t temp = *((amount_t *) data); - destroy(); - new((balance_t *)data) balance_t(temp); - break; - } - case BALANCE_PAIR: { - amount_t temp = *((amount_t *) data); - destroy(); - new((balance_pair_t *)data) balance_pair_t(temp); - break; - } - + if (amt.is_null()) + set_balance_pair(balance_pair_t()); + else + set_balance_pair(as_amount()); // creates temporary + return; + case STRING: + if (amt.is_null()) + set_string(""); + else + set_string(as_amount().to_string()); + return; default: - assert(0); break; } break; + } case BALANCE: switch (cast_type) { - case BOOLEAN: { - bool temp = *((balance_t *) data); - destroy(); - *((bool *)data) = temp; - break; - } - case INTEGER: - throw new value_error("Cannot convert a balance to an integer"); - case DATETIME: - throw new value_error("Cannot convert a balance to a date/time"); - case AMOUNT: { - balance_t * temp = (balance_t *) data; - if (temp->amounts.size() == 1) { - amount_t amt = (*temp->amounts.begin()).second; - destroy(); - new((amount_t *)data) amount_t(amt); + const balance_t& temp(as_balance()); + if (temp.amounts.size() == 1) { + // Because we are changing the current balance value to an amount + // value, and because set_amount takes a reference (and that memory is + // about to be repurposed), we must pass in a copy. + set_amount(amount_t((*temp.amounts.begin()).second)); + return; } - else if (temp->amounts.size() == 0) { - new((amount_t *)data) amount_t(); + else if (temp.amounts.size() == 0) { + set_amount(0L); + return; } else { - throw new value_error("Cannot convert a balance with " - "multiple commodities to an amount"); + throw_(value_error, "Cannot convert " << label() << + " with multiple commodities to " << label(cast_type)); } break; } - case BALANCE: - break; - case BALANCE_PAIR: { - balance_t temp = *((balance_t *) data); - destroy(); - new((balance_pair_t *)data) balance_pair_t(temp); - break; - } - + case BALANCE_PAIR: + set_balance_pair(as_balance()); + return; default: - assert(0); break; } break; case BALANCE_PAIR: switch (cast_type) { - case BOOLEAN: { - bool temp = *((balance_pair_t *) data); - destroy(); - *((bool *)data) = temp; - break; - } - case INTEGER: - throw new value_error("Cannot convert a balance pair to an integer"); - case DATETIME: - throw new value_error("Cannot convert a balance pair to a date/time"); - case AMOUNT: { - balance_t * temp = &((balance_pair_t *) data)->quantity; - if (temp->amounts.size() == 1) { - amount_t amt = (*temp->amounts.begin()).second; - destroy(); - new((amount_t *)data) amount_t(amt); + const balance_t& temp(as_balance_pair().quantity()); + if (temp.amounts.size() == 1) { + set_amount(amount_t((*temp.amounts.begin()).second)); + return; } - else if (temp->amounts.size() == 0) { - new((amount_t *)data) amount_t(); + else if (temp.amounts.size() == 0) { + set_amount(0L); + return; } else { - throw new value_error("Cannot convert a balance pair with " - "multiple commodities to an amount"); + throw_(value_error, "Cannot convert " << label() << + " with multiple commodities to " << label(cast_type)); } break; } - case BALANCE: { - balance_t temp = ((balance_pair_t *) data)->quantity; - destroy(); - new((balance_t *)data) balance_t(temp); + case BALANCE: + // A temporary is required, becaues set_balance is going to wipe us out + // before assigned the value passed in. + set_balance(balance_t(as_balance_pair().quantity())); + return; + default: break; } - case BALANCE_PAIR: - break; + break; + case STRING: + switch (cast_type) { + case INTEGER: { + if (all(as_string(), is_digit())) { + set_long(lexical_cast<long>(as_string())); + return; + } else { + throw_(value_error, + "Cannot convert string '" << *this << "' to an integer"); + } + break; + } + case AMOUNT: + set_amount(amount_t(as_string())); + return; default: - assert(0); break; } break; default: - assert(0); break; } - type = cast_type; + + throw_(value_error, + "Cannot convert " << label() << " to " << label(cast_type)); } -void value_t::negate() +void value_t::in_place_negate() { - switch (type) { + switch (type()) { case BOOLEAN: - *((bool *) data) = ! *((bool *) data); - break; + set_boolean(! as_boolean()); + return; case INTEGER: - *((long *) data) = - *((long *) data); - break; case DATETIME: - cast(INTEGER); - negate(); - break; + set_long(- as_long()); + return; case AMOUNT: - ((amount_t *) data)->negate(); - break; + as_amount_lval().in_place_negate(); + return; case BALANCE: - ((balance_t *) data)->negate(); - break; + as_balance_lval().in_place_negate(); + return; case BALANCE_PAIR: - ((balance_pair_t *) data)->negate(); - break; - + as_balance_pair_lval().in_place_negate(); + return; default: - assert(0); break; } + + throw_(value_error, "Cannot negate " << label()); } -void value_t::abs() +bool value_t::is_realzero() const { - switch (type) { + switch (type()) { case BOOLEAN: - break; + return ! as_boolean(); case INTEGER: - if (*((long *) data) < 0) - *((long *) data) = - *((long *) data); - break; + return as_long() == 0; case DATETIME: - break; + return ! is_valid(as_datetime()); case AMOUNT: - ((amount_t *) data)->abs(); - break; + return as_amount().is_realzero(); case BALANCE: - ((balance_t *) data)->abs(); - break; + return as_balance().is_realzero(); case BALANCE_PAIR: - ((balance_pair_t *) data)->abs(); - break; + return as_balance_pair().is_realzero(); + case STRING: + return as_string().empty(); + case SEQUENCE: + return as_sequence().empty(); + + case POINTER: + return as_any_pointer().empty(); default: - assert(0); + assert(false); break; } + assert(false); + return true; } -value_t value_t::value(const datetime_t& moment) const +bool value_t::is_zero() const { - switch (type) { + switch (type()) { case BOOLEAN: - throw new value_error("Cannot find the value of a boolean"); - case DATETIME: - throw new value_error("Cannot find the value of a date/time"); + return ! as_boolean(); case INTEGER: - return *this; + return as_long() == 0; + case DATETIME: + return ! is_valid(as_datetime()); case AMOUNT: - return ((amount_t *) data)->value(moment); + return as_amount().is_zero(); case BALANCE: - return ((balance_t *) data)->value(moment); + return as_balance().is_zero(); case BALANCE_PAIR: - return ((balance_pair_t *) data)->quantity.value(moment); + return as_balance_pair().is_zero(); + case STRING: + return as_string().empty(); + case SEQUENCE: + return as_sequence().empty(); + + case POINTER: + return as_any_pointer().empty(); + + default: + assert(false); + break; } + assert(false); + return true; } -void value_t::reduce() +value_t value_t::value(const optional<datetime_t>& moment) const { - switch (type) { - case BOOLEAN: - case DATETIME: + switch (type()) { case INTEGER: + return *this; + + case AMOUNT: { + if (optional<amount_t> val = as_amount().value(moment)) + return *val; + return false; + } + case BALANCE: { + if (optional<balance_t> bal = as_balance().value(moment)) + return *bal; + return false; + } + case BALANCE_PAIR: { + if (optional<balance_t> bal_pair = + as_balance_pair().quantity().value(moment)) + return *bal_pair; + return false; + } + + default: break; + } + + throw_(value_error, "Cannot find the value of " << label()); + return NULL_VALUE; +} + +void value_t::in_place_reduce() +{ + switch (type()) { + case INTEGER: + return; case AMOUNT: - ((amount_t *) data)->reduce(); - break; + as_amount_lval().in_place_reduce(); + return; case BALANCE: - ((balance_t *) data)->reduce(); - break; + as_balance_lval().in_place_reduce(); + return; case BALANCE_PAIR: - ((balance_pair_t *) data)->reduce(); + as_balance_pair_lval().in_place_reduce(); + return; + default: break; } + + throw_(value_error, "Cannot reduce " << label()); } -void value_t::round() +value_t value_t::abs() const { - switch (type) { - case BOOLEAN: - throw new value_error("Cannot round a boolean"); - case DATETIME: - throw new value_error("Cannot round a date/time"); - case INTEGER: - break; + switch (type()) { + case INTEGER: { + long val = as_long(); + if (val < 0) + return - val; + return val; + } case AMOUNT: - *((amount_t *) data) = ((amount_t *) data)->round(); - break; + return as_amount().abs(); case BALANCE: - ((balance_t *) data)->round(); - break; + return as_balance().abs(); case BALANCE_PAIR: - ((balance_pair_t *) data)->round(); + return as_balance_pair().abs(); + default: break; } + + throw_(value_error, "Cannot abs " << label()); + return NULL_VALUE; } -value_t value_t::unround() const +value_t value_t::round() const { - value_t temp; - switch (type) { - case BOOLEAN: - throw new value_error("Cannot un-round a boolean"); - case DATETIME: - throw new value_error("Cannot un-round a date/time"); + switch (type()) { case INTEGER: - break; + return *this; case AMOUNT: - temp = ((amount_t *) data)->unround(); - break; + return as_amount().round(); case BALANCE: - temp = ((balance_t *) data)->unround(); - break; + return as_balance().round(); case BALANCE_PAIR: - temp = ((balance_pair_t *) data)->unround(); + return as_balance_pair().round(); + default: break; } - return temp; + + throw_(value_error, "Cannot round " << label()); + return NULL_VALUE; } -value_t value_t::price() const +value_t value_t::unround() const { - switch (type) { - case BOOLEAN: - throw new value_error("Cannot find the price of a boolean"); + switch (type()) { case INTEGER: return *this; - case DATETIME: - throw new value_error("Cannot find the price of a date/time"); - case AMOUNT: - return ((amount_t *) data)->price(); + return as_amount().unround(); + default: + break; + } - case BALANCE: - return ((balance_t *) data)->price(); + throw_(value_error, "Cannot unround " << label()); + return NULL_VALUE; +} - case BALANCE_PAIR: - return ((balance_pair_t *) data)->quantity.price(); +value_t value_t::annotated_price() const +{ + switch (type()) { + case AMOUNT: { + optional<amount_t> temp = as_amount().annotation_details().price; + if (! temp) + return false; + return *temp; + } default: - assert(0); break; } - assert(0); - return value_t(); + + throw_(value_error, "Cannot find the annotated price of " << label()); + return NULL_VALUE; } -value_t value_t::date() const +value_t value_t::annotated_date() const { - switch (type) { - case BOOLEAN: - throw new value_error("Cannot find the date of a boolean"); - case INTEGER: - return datetime_t(); + switch (type()) { case DATETIME: return *this; - case AMOUNT: - return datetime_t(((amount_t *) data)->date()); + case AMOUNT: { + optional<datetime_t> temp = as_amount().annotation_details().date; + if (! temp) + return false; + return *temp; + } - case BALANCE: - return datetime_t(((balance_t *) data)->date()); + default: + break; + } - case BALANCE_PAIR: - return datetime_t(((balance_pair_t *) data)->quantity.date()); + throw_(value_error, "Cannot find the annotated date of " << label()); + return NULL_VALUE; +} + +value_t value_t::annotated_tag() const +{ + switch (type()) { + case DATETIME: + return *this; + + case AMOUNT: { + optional<string> temp = as_amount().annotation_details().tag; + if (! temp) + return false; + return value_t(*temp, true); + } default: - assert(0); break; } - assert(0); - return value_t(); + + throw_(value_error, "Cannot find the annotated tag of " << label()); + return NULL_VALUE; } value_t value_t::strip_annotations(const bool keep_price, const bool keep_date, const bool keep_tag) const { - switch (type) { + switch (type()) { + case VOID: case BOOLEAN: case INTEGER: case DATETIME: + case STRING: + case POINTER: return *this; + case SEQUENCE: { + sequence_t temp; + foreach (const value_t& value, as_sequence()) + temp.push_back(value.strip_annotations(keep_price, keep_date, keep_tag)); + return temp; + } + case AMOUNT: - return ((amount_t *) data)->strip_annotations - (keep_price, keep_date, keep_tag); + return as_amount().strip_annotations(keep_price, keep_date, keep_tag); case BALANCE: - return ((balance_t *) data)->strip_annotations - (keep_price, keep_date, keep_tag); + return as_balance().strip_annotations(keep_price, keep_date, keep_tag); case BALANCE_PAIR: - return ((balance_pair_t *) data)->quantity.strip_annotations - (keep_price, keep_date, keep_tag); + return as_balance_pair().quantity().strip_annotations(keep_price, keep_date, + keep_tag); default: - assert(0); + assert(false); break; } - assert(0); - return value_t(); + assert(false); + return NULL_VALUE; } value_t value_t::cost() const { - switch (type) { - case BOOLEAN: - throw new value_error("Cannot find the cost of a boolean"); + switch (type()) { case INTEGER: case AMOUNT: case BALANCE: return *this; - case DATETIME: - throw new value_error("Cannot find the cost of a date/time"); case BALANCE_PAIR: - assert(((balance_pair_t *) data)->cost); - if (((balance_pair_t *) data)->cost) - return *(((balance_pair_t *) data)->cost); + assert(as_balance_pair().cost); + if (as_balance_pair().cost) + return *(as_balance_pair().cost); else - return ((balance_pair_t *) data)->quantity; + return as_balance_pair().quantity(); default: - assert(0); break; } - assert(0); - return value_t(); + + throw_(value_error, "Cannot find the cost of " << label()); + return NULL_VALUE; } -value_t& value_t::add(const amount_t& amount, const amount_t * cost) +value_t& value_t::add(const amount_t& amount, const optional<amount_t>& tcost) { - switch (type) { - case BOOLEAN: - throw new value_error("Cannot add an amount to a boolean"); - case DATETIME: - throw new value_error("Cannot add an amount to a date/time"); + switch (type()) { case INTEGER: case AMOUNT: - if (cost) { - cast(BALANCE_PAIR); - return add(amount, cost); + if (tcost) { + in_place_cast(BALANCE_PAIR); + return add(amount, tcost); } - else if ((type == AMOUNT && - ((amount_t *) data)->commodity() != amount.commodity()) || - (type != AMOUNT && amount.commodity())) { - cast(BALANCE); - return add(amount, cost); + else if ((is_amount() && + as_amount().commodity() != amount.commodity()) || + (! is_amount() && amount.commodity())) { + in_place_cast(BALANCE); + return add(amount, tcost); } - else if (type != AMOUNT) { - cast(AMOUNT); + else if (! is_amount()) { + in_place_cast(AMOUNT); } - *((amount_t *) data) += amount; + *this += amount; break; case BALANCE: - if (cost) { - cast(BALANCE_PAIR); - return add(amount, cost); + if (tcost) { + in_place_cast(BALANCE_PAIR); + return add(amount, tcost); } - *((balance_t *) data) += amount; + *this += amount; break; case BALANCE_PAIR: - ((balance_pair_t *) data)->add(amount, cost); + as_balance_pair_lval().add(amount, tcost); break; default: - assert(0); break; } + throw_(value_error, "Cannot add an amount to " << label()); return *this; } -value_context::value_context(const value_t& _bal, - const std::string& desc) throw() - : bal(new value_t(_bal)), error_context(desc) {} - -value_context::~value_context() throw() -{ - delete bal; -} - -void value_context::describe(std::ostream& out) const throw() +void value_t::print(std::ostream& out, const int first_width, + const int latter_width) const { - if (! desc.empty()) - out << desc << std::endl; + switch (type()) { + case VOID: + out << "VOID"; + break; - ledger::balance_t * ptr = NULL; + case BOOLEAN: + out << as_boolean(); + break; - out << std::right; - out.width(20); + case DATETIME: + out << as_datetime(); + break; - switch (bal->type) { - case ledger::value_t::BOOLEAN: - out << (*((bool *) bal->data) ? "true" : "false"); + case INTEGER: + out << as_long(); break; - case ledger::value_t::INTEGER: - out << *((long *) bal->data); + + case AMOUNT: + out << as_amount(); break; - case ledger::value_t::DATETIME: - out << *((datetime_t *) bal->data); + + case STRING: + out << as_string(); break; - case ledger::value_t::AMOUNT: - out << *((ledger::amount_t *) bal->data); + + case POINTER: + out << boost::unsafe_any_cast<const void *>(&as_any_pointer()); break; - case ledger::value_t::BALANCE: - ptr = (ledger::balance_t *) bal->data; - // fall through... - case ledger::value_t::BALANCE_PAIR: - if (! ptr) - ptr = &((ledger::balance_pair_t *) bal->data)->quantity; + case SEQUENCE: { + out << '('; + bool first = true; + foreach (const value_t& value, as_sequence()) { + if (first) + first = false; + else + out << ", "; + + value.print(out, first_width, latter_width); + } + out << ')'; + break; + } - ptr->write(out, 20); + case BALANCE: + as_balance().print(out, first_width, latter_width); + break; + case BALANCE_PAIR: + as_balance_pair().print(out, first_width, latter_width); break; default: - assert(0); + assert(false); break; } - out << std::endl; } -} // namespace ledger - -#ifdef USE_BOOST_PYTHON - -#include <boost/python.hpp> - -using namespace boost::python; -using namespace ledger; - -long balance_len(balance_t& bal); -amount_t balance_getitem(balance_t& bal, int i); -long balance_pair_len(balance_pair_t& bal_pair); -amount_t balance_pair_getitem(balance_pair_t& bal_pair, int i); - -long value_len(value_t& value) +bool value_t::valid() const { - switch (value.type) { - case value_t::BOOLEAN: - case value_t::INTEGER: - case value_t::DATETIME: - case value_t::AMOUNT: - return 1; - - case value_t::BALANCE: - return balance_len(*((balance_t *) value.data)); - - case value_t::BALANCE_PAIR: - return balance_pair_len(*((balance_pair_t *) value.data)); - + switch (type()) { + case AMOUNT: + return as_amount().valid(); + case BALANCE: + return as_balance().valid(); + case BALANCE_PAIR: + return as_balance_pair().valid(); default: - assert(0); break; } - assert(0); - return 0; + return true; } -amount_t value_getitem(value_t& value, int i) +void value_context::describe(std::ostream& out) const throw() { - std::size_t len = value_len(value); + if (! desc.empty()) + out << desc << std::endl; - if (abs(i) >= len) { - PyErr_SetString(PyExc_IndexError, "Index out of range"); - throw_error_already_set(); - } + const balance_t * ptr = NULL; - switch (value.type) { - case value_t::BOOLEAN: - throw new value_error("Cannot cast a boolean to an amount"); + out << std::right; + out.width(20); + switch (bal.type()) { + case value_t::BOOLEAN: + out << (bal.as_boolean() ? "true" : "false"); + break; case value_t::INTEGER: - return long(value); - + out << bal.as_long(); + break; case value_t::DATETIME: - throw new value_error("Cannot cast a date/time to an amount"); - + out << bal.as_datetime(); + break; case value_t::AMOUNT: - return *((amount_t *) value.data); - + out << bal.as_amount(); + break; case value_t::BALANCE: - return balance_getitem(*((balance_t *) value.data), i); + ptr = &bal.as_balance(); + // fall through... case value_t::BALANCE_PAIR: - return balance_pair_getitem(*((balance_pair_t *) value.data), i); + if (! ptr) + ptr = &bal.as_balance_pair().quantity(); + ptr->print(out, 20); + break; default: assert(0); break; } - assert(0); - return 0L; -} - -double py_to_float(value_t& value) -{ - return double(value); -} - -void export_value() -{ - scope in_value = class_< value_t > ("Value") - .def(init<value_t>()) - .def(init<balance_pair_t>()) - .def(init<balance_t>()) - .def(init<amount_t>()) - .def(init<std::string>()) - .def(init<double>()) - .def(init<long>()) - .def(init<datetime_t>()) - - .def(self + self) - .def(self + other<balance_pair_t>()) - .def(self + other<balance_t>()) - .def(self + other<amount_t>()) - .def(self + long()) - .def(self + double()) - - .def(other<balance_pair_t>() + self) - .def(other<balance_t>() + self) - .def(other<amount_t>() + self) - .def(long() + self) - .def(double() + self) - - .def(self - self) - .def(self - other<balance_pair_t>()) - .def(self - other<balance_t>()) - .def(self - other<amount_t>()) - .def(self - long()) - .def(self - double()) - - .def(other<balance_pair_t>() - self) - .def(other<balance_t>() - self) - .def(other<amount_t>() - self) - .def(long() - self) - .def(double() - self) - - .def(self * self) - .def(self * other<balance_pair_t>()) - .def(self * other<balance_t>()) - .def(self * other<amount_t>()) - .def(self * long()) - .def(self * double()) - - .def(other<balance_pair_t>() * self) - .def(other<balance_t>() * self) - .def(other<amount_t>() * self) - .def(long() * self) - .def(double() * self) - - .def(self / self) - .def(self / other<balance_pair_t>()) - .def(self / other<balance_t>()) - .def(self / other<amount_t>()) - .def(self / long()) - .def(self / double()) - - .def(other<balance_pair_t>() / self) - .def(other<balance_t>() / self) - .def(other<amount_t>() / self) - .def(long() / self) - .def(double() / self) - - .def(- self) - - .def(self += self) - .def(self += other<balance_pair_t>()) - .def(self += other<balance_t>()) - .def(self += other<amount_t>()) - .def(self += long()) - .def(self += double()) - - .def(self -= self) - .def(self -= other<balance_pair_t>()) - .def(self -= other<balance_t>()) - .def(self -= other<amount_t>()) - .def(self -= long()) - .def(self -= double()) - - .def(self *= self) - .def(self *= other<balance_pair_t>()) - .def(self *= other<balance_t>()) - .def(self *= other<amount_t>()) - .def(self *= long()) - .def(self *= double()) - - .def(self /= self) - .def(self /= other<balance_pair_t>()) - .def(self /= other<balance_t>()) - .def(self /= other<amount_t>()) - .def(self /= long()) - .def(self /= double()) - - .def(self < self) - .def(self < other<balance_pair_t>()) - .def(self < other<balance_t>()) - .def(self < other<amount_t>()) - .def(self < long()) - .def(self < other<datetime_t>()) - .def(self < double()) - - .def(other<balance_pair_t>() < self) - .def(other<balance_t>() < self) - .def(other<amount_t>() < self) - .def(long() < self) - .def(other<datetime_t>() < self) - .def(double() < self) - - .def(self <= self) - .def(self <= other<balance_pair_t>()) - .def(self <= other<balance_t>()) - .def(self <= other<amount_t>()) - .def(self <= long()) - .def(self <= other<datetime_t>()) - .def(self <= double()) - - .def(other<balance_pair_t>() <= self) - .def(other<balance_t>() <= self) - .def(other<amount_t>() <= self) - .def(long() <= self) - .def(other<datetime_t>() <= self) - .def(double() <= self) - - .def(self > self) - .def(self > other<balance_pair_t>()) - .def(self > other<balance_t>()) - .def(self > other<amount_t>()) - .def(self > long()) - .def(self > other<datetime_t>()) - .def(self > double()) - - .def(other<balance_pair_t>() > self) - .def(other<balance_t>() > self) - .def(other<amount_t>() > self) - .def(long() > self) - .def(other<datetime_t>() > self) - .def(double() > self) - - .def(self >= self) - .def(self >= other<balance_pair_t>()) - .def(self >= other<balance_t>()) - .def(self >= other<amount_t>()) - .def(self >= long()) - .def(self >= other<datetime_t>()) - .def(self >= double()) - - .def(other<balance_pair_t>() >= self) - .def(other<balance_t>() >= self) - .def(other<amount_t>() >= self) - .def(long() >= self) - .def(other<datetime_t>() >= self) - .def(double() >= self) - - .def(self == self) - .def(self == other<balance_pair_t>()) - .def(self == other<balance_t>()) - .def(self == other<amount_t>()) - .def(self == long()) - .def(self == other<datetime_t>()) - .def(self == double()) - - .def(other<balance_pair_t>() == self) - .def(other<balance_t>() == self) - .def(other<amount_t>() == self) - .def(long() == self) - .def(other<datetime_t>() == self) - .def(double() == self) - - .def(self != self) - .def(self != other<balance_pair_t>()) - .def(self != other<balance_t>()) - .def(self != other<amount_t>()) - .def(self != long()) - .def(self != other<datetime_t>()) - .def(self != double()) - - .def(other<balance_pair_t>() != self) - .def(other<balance_t>() != self) - .def(other<amount_t>() != self) - .def(long() != self) - .def(other<datetime_t>() != self) - .def(double() != self) - - .def(! self) - - .def(self_ns::int_(self)) - .def(self_ns::float_(self)) - .def(self_ns::str(self)) - .def(abs(self)) - - .def_readonly("type", &value_t::type) - - .def("__len__", value_len) - .def("__getitem__", value_getitem) - - .def("cast", &value_t::cast) - .def("cost", &value_t::cost) - .def("price", &value_t::price) - .def("date", &value_t::date) - .def("strip_annotations", &value_t::strip_annotations) - .def("add", &value_t::add, return_internal_reference<>()) - .def("value", &value_t::value) - .def("round", &value_t::round) - .def("negate", &value_t::negate) - .def("negated", &value_t::negated) - ; - - enum_< value_t::type_t > ("ValueType") - .value("BOOLEAN", value_t::BOOLEAN) - .value("INTEGER", value_t::INTEGER) - .value("DATETIME", value_t::DATETIME) - .value("AMOUNT", value_t::AMOUNT) - .value("BALANCE", value_t::BALANCE) - .value("BALANCE_PAIR", value_t::BALANCE_PAIR) - ; + out << std::endl; } -#endif // USE_BOOST_PYTHON +} // namespace ledger |