Add support for valuation expressions on commodities

This commit is contained in:
John Wiegley 2012-03-05 23:01:41 -06:00
parent 7b12b3041d
commit 8ae2fb8721
6 changed files with 97 additions and 95 deletions

View file

@ -33,6 +33,7 @@
#include "amount.h"
#include "commodity.h"
#include "expr.h"
#include "annotate.h"
#include "pool.h"
@ -47,6 +48,9 @@ bool annotation_t::operator<(const annotation_t& rhs) const
if (! tag && rhs.tag) return true;
if (tag && ! rhs.tag) return false;
if (! value_expr && rhs.value_expr) return true;
if (value_expr && ! rhs.value_expr) return false;
if (price) {
if (price->commodity().symbol() < rhs.price->commodity().symbol())
return true;
@ -63,6 +67,10 @@ bool annotation_t::operator<(const annotation_t& rhs) const
if (*tag < *rhs.tag) return true;
if (*tag > *rhs.tag) return false;
}
if (value_expr) {
if (value_expr->text() < rhs.value_expr->text()) return true;
if (value_expr->text() > rhs.value_expr->text()) return false;
}
return false;
}
@ -112,17 +120,41 @@ void annotation_t::parse(std::istream& in)
date = parse_date(buf);
}
else if (c == '(') {
if (tag)
throw_(amount_error, _("Commodity specifies more than one tag"));
in.get(c);
READ_INTO(in, buf, 255, c, c != ')');
if (c == ')')
in.get(c);
else
throw_(amount_error, _("Commodity tag lacks closing parenthesis"));
c = static_cast<char>(in.peek());
if (c == '(') {
if (value_expr)
throw_(amount_error,
_("Commodity specifies more than one valuation expresion"));
tag = buf;
in.get(c);
READ_INTO(in, buf, 255, c, c != ')');
if (c == ')') {
in.get(c);
c = static_cast<char>(in.peek());
if (c == ')')
in.get(c);
else
throw_(amount_error,
_("Commodity valuation expression lacks closing parentheses"));
} else {
throw_(amount_error,
_("Commodity valuation expression lacks closing parentheses"));
}
value_expr = expr_t(buf);
} else {
if (tag)
throw_(amount_error, _("Commodity specifies more than one tag"));
READ_INTO(in, buf, 255, c, c != ')');
if (c == ')')
in.get(c);
else
throw_(amount_error, _("Commodity tag lacks closing parenthesis"));
tag = buf;
}
}
else {
in.clear();
@ -156,6 +188,10 @@ void annotation_t::print(std::ostream& out, bool keep_base,
if (tag &&
(! no_computed_annotations || ! has_flags(ANNOTATION_TAG_CALCULATED)))
out << " (" << *tag << ')';
if (value_expr && (! no_computed_annotations ||
! has_flags(ANNOTATION_VALUE_EXPR_CALCULATED)))
out << " ((" << *value_expr << "))";
}
bool keep_details_t::keep_all(const commodity_t& comm) const
@ -220,12 +256,14 @@ annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep)
if ((keep_price && details.price) ||
(keep_date && details.date) ||
(keep_tag && details.tag))
(keep_tag && details.tag) ||
details.value_expr)
{
new_comm = pool().find_or_create
(referent(), annotation_t(keep_price ? details.price : none,
keep_date ? details.date : none,
keep_tag ? details.tag : none));
keep_tag ? details.tag : none,
details.value_expr));
// Transfer over any relevant annotation flags, as they still apply.
if (new_comm->annotated) {
@ -238,6 +276,8 @@ annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep)
new_details.add_flags(details.flags() & ANNOTATION_DATE_CALCULATED);
if (keep_tag)
new_details.add_flags(details.flags() & ANNOTATION_TAG_CALCULATED);
if (details.value_expr)
new_details.add_flags(details.flags() & ANNOTATION_VALUE_EXPR_CALCULATED);
}
return *new_comm;

View file

@ -46,29 +46,38 @@
#ifndef _ANNOTATE_H
#define _ANNOTATE_H
#include "expr.h"
namespace ledger {
struct annotation_t : public supports_flags<>,
public equality_comparable<annotation_t>
{
#define ANNOTATION_PRICE_CALCULATED 0x01
#define ANNOTATION_PRICE_FIXATED 0x02
#define ANNOTATION_DATE_CALCULATED 0x04
#define ANNOTATION_TAG_CALCULATED 0x08
#define ANNOTATION_PRICE_CALCULATED 0x01
#define ANNOTATION_PRICE_FIXATED 0x02
#define ANNOTATION_DATE_CALCULATED 0x04
#define ANNOTATION_TAG_CALCULATED 0x08
#define ANNOTATION_VALUE_EXPR_CALCULATED 0x10
optional<amount_t> price;
optional<date_t> date;
optional<string> tag;
optional<expr_t> value_expr;
explicit annotation_t(const optional<amount_t>& _price = none,
const optional<date_t>& _date = none,
const optional<string>& _tag = none)
: supports_flags<>(), price(_price), date(_date), tag(_tag) {
TRACE_CTOR(annotation_t, "const optional<amount_t>& + date_t + string");
explicit annotation_t(const optional<amount_t>& _price = none,
const optional<date_t>& _date = none,
const optional<string>& _tag = none,
const optional<expr_t>& _value_expr = none)
: supports_flags<>(), price(_price), date(_date), tag(_tag),
value_expr(_value_expr) {
TRACE_CTOR(annotation_t,
"const optional<amount_t>& + date_t + string + expr_t");
}
annotation_t(const annotation_t& other)
: supports_flags<>(other.flags()),
price(other.price), date(other.date), tag(other.tag) {
price(other.price), date(other.date), tag(other.tag),
value_expr(other.value_expr)
{
TRACE_CTOR(annotation_t, "copy");
}
~annotation_t() {
@ -76,14 +85,15 @@ struct annotation_t : public supports_flags<>,
}
operator bool() const {
return price || date || tag;
return price || date || tag || value_expr;
}
bool operator<(const annotation_t& rhs) const;
bool operator==(const annotation_t& rhs) const {
return (price == rhs.price &&
date == rhs.date &&
tag == rhs.tag);
return (price == rhs.price &&
date == rhs.date &&
tag == rhs.tag &&
value_expr == rhs.value_expr);
}
void parse(std::istream& in);
@ -133,6 +143,12 @@ inline void to_xml(std::ostream& out, const annotation_t& details)
push_xml y(out, "tag");
out << y.guard(*details.tag);
}
if (details.value_expr)
{
push_xml y(out, "value-expr");
out << y.guard(details.value_expr->text());
}
}
struct keep_details_t

View file

@ -58,6 +58,9 @@ public:
typedef intrusive_ptr<op_t> ptr_op_t;
typedef intrusive_ptr<const op_t> const_ptr_op_t;
friend void intrusive_ptr_add_ref(const op_t * op);
friend void intrusive_ptr_release(const op_t * op);
enum check_expr_kind_t {
EXPR_GENERAL,
EXPR_ASSERTION,

View file

@ -1,40 +0,0 @@
/*
* Copyright (c) 2003-2012, 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 <system.hh>
#include "predicate.h"
#include "query.h"
#include "op.h"
namespace ledger {
} // namespace ledger

View file

@ -1,5 +1,5 @@
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE expr
//#define BOOST_TEST_MODULE expr
#include <boost/test/unit_test.hpp>
#include <system.hh>

View file

@ -8,7 +8,6 @@ DISTCLEANFILES = .timestamp
lib_LTLIBRARIES = \
libledger_report.la \
libledger_data.la \
libledger_expr.la \
libledger_math.la \
libledger_util.la
@ -28,6 +27,14 @@ libledger_util_la_CPPFLAGS = $(lib_cppflags)
libledger_util_la_LDFLAGS = -release $(LIBVERSION)
libledger_math_la_SOURCES = \
src/format.cc \
src/query.cc \
src/scope.cc \
src/expr.cc \
src/op.cc \
src/parser.cc \
src/token.cc \
src/value.cc \
src/balance.cc \
src/quotes.cc \
src/history.cc \
@ -39,22 +46,8 @@ libledger_math_la_SOURCES = \
libledger_math_la_CPPFLAGS = $(lib_cppflags)
libledger_math_la_LDFLAGS = -release $(LIBVERSION)
libledger_expr_la_SOURCES = \
src/option.cc \
src/format.cc \
src/query.cc \
src/predicate.cc \
src/scope.cc \
src/expr.cc \
src/op.cc \
src/parser.cc \
src/token.cc \
src/value.cc
libledger_expr_la_CPPFLAGS = $(lib_cppflags)
libledger_expr_la_LDFLAGS = -release $(LIBVERSION)
libledger_data_la_SOURCES = \
src/option.cc \
src/lookup.cc \
src/compare.cc \
src/iterators.cc \
@ -204,7 +197,6 @@ DISTCLEANFILES += ledger.elc timeclock.elc
all_sources = $(libledger_util_la_SOURCES) \
$(libledger_math_la_SOURCES) \
$(libledger_expr_la_SOURCES) \
$(libledger_data_la_SOURCES) \
$(libledger_report_la_SOURCES) \
$(libledger_python_la_SOURCES) \
@ -259,8 +251,7 @@ TESTS = RegressTests BaselineTests ManualTests ConfirmTests \
if HAVE_BOOST_TEST
TESTS += \
UtilTests \
MathTests \
ExprTests
MathTests
# DataTests \
# ReportTests
endif
@ -285,6 +276,7 @@ UtilTests_CPPFLAGS = -I$(srcdir)/test $(lib_cppflags)
UtilTests_LDADD = libledger_util.la $(TESTLIBS)
MathTests_SOURCES = \
test/unit/t_expr.cc \
test/unit/t_commodity.cc \
test/unit/t_amount.cc \
test/unit/t_balance.cc
@ -292,16 +284,10 @@ MathTests_SOURCES = \
MathTests_CPPFLAGS = -I$(srcdir)/test $(lib_cppflags)
MathTests_LDADD = libledger_math.la $(UtilTests_LDADD)
ExprTests_SOURCES = \
test/unit/t_expr.cc
ExprTests_CPPFLAGS = -I$(srcdir)/test $(lib_cppflags)
ExprTests_LDADD = libledger_expr.la $(MathTests_LDADD)
DataTests_SOURCES =
DataTests_CPPFLAGS = -I$(srcdir)/test $(lib_cppflags)
DataTests_LDADD = libledger_data.la $(ExprTests_LDADD)
DataTests_LDADD = libledger_data.la $(MathTests_LDADD)
ReportTests_SOURCES =
@ -311,7 +297,6 @@ ReportTests_LDADD = libledger_report.la $(DataTests_LDADD)
all_tests_sources = \
$(UtilTests_SOURCES) \
$(MathTests_SOURCES) \
$(ExprTests_SOURCES) \
$(DataTests_SOURCES) \
$(ReportTests_SOURCES)
@ -421,8 +406,6 @@ unittests: check
2>&1 | grep -v '^GuardMalloc:'
@sh $(FULLCHECK) $(top_builddir)/MathTests$(EXEEXT) --verify \
2>&1 | grep -v '^GuardMalloc:'
@sh $(FULLCHECK) $(top_builddir)/ExprTests$(EXEEXT) --verify \
2>&1 | grep -v '^GuardMalloc:'
# @sh $(FULLCHECK) $(top_builddir)/DataTests$(EXEEXT) --verify \
# 2>&1 | grep -v '^GuardMalloc:'
# @sh $(FULLCHECK) $(top_builddir)/ReportTests$(EXEEXT) --verify \