Merge branch 'next'
This commit is contained in:
commit
e8a14d31b6
10 changed files with 178 additions and 23 deletions
12
src/pool.cc
12
src/pool.cc
|
|
@ -286,7 +286,8 @@ commodity_pool_t::exchange(const amount_t& amount,
|
||||||
return breakdown;
|
return breakdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<price_point_t> commodity_pool_t::parse_price_directive(char * line)
|
optional<std::pair<commodity_t *, price_point_t> >
|
||||||
|
commodity_pool_t::parse_price_directive(char * line, bool do_not_add_price)
|
||||||
{
|
{
|
||||||
char * date_field_ptr = line;
|
char * date_field_ptr = line;
|
||||||
char * time_field_ptr = next_element(date_field_ptr);
|
char * time_field_ptr = next_element(date_field_ptr);
|
||||||
|
|
@ -295,6 +296,7 @@ optional<price_point_t> commodity_pool_t::parse_price_directive(char * line)
|
||||||
|
|
||||||
char * symbol_and_price;
|
char * symbol_and_price;
|
||||||
datetime_t datetime;
|
datetime_t datetime;
|
||||||
|
string symbol;
|
||||||
|
|
||||||
if (std::isdigit(time_field_ptr[0])) {
|
if (std::isdigit(time_field_ptr[0])) {
|
||||||
symbol_and_price = next_element(time_field_ptr);
|
symbol_and_price = next_element(time_field_ptr);
|
||||||
|
|
@ -307,11 +309,12 @@ optional<price_point_t> commodity_pool_t::parse_price_directive(char * line)
|
||||||
datetime = datetime_t(parse_date(date_field));
|
datetime = datetime_t(parse_date(date_field));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
symbol_and_price = date_field_ptr;
|
symbol = date_field_ptr;
|
||||||
|
symbol_and_price = time_field_ptr;
|
||||||
datetime = CURRENT_TIME();
|
datetime = CURRENT_TIME();
|
||||||
}
|
}
|
||||||
|
|
||||||
string symbol;
|
if (symbol.empty())
|
||||||
commodity_t::parse_symbol(symbol_and_price, symbol);
|
commodity_t::parse_symbol(symbol_and_price, symbol);
|
||||||
|
|
||||||
price_point_t point;
|
price_point_t point;
|
||||||
|
|
@ -323,9 +326,10 @@ optional<price_point_t> commodity_pool_t::parse_price_directive(char * line)
|
||||||
if (commodity_t * commodity = find_or_create(symbol)) {
|
if (commodity_t * commodity = find_or_create(symbol)) {
|
||||||
DEBUG("commodity.download", "Adding price for " << symbol << ": "
|
DEBUG("commodity.download", "Adding price for " << symbol << ": "
|
||||||
<< point.when << " " << point.price);
|
<< point.when << " " << point.price);
|
||||||
|
if (! do_not_add_price)
|
||||||
commodity->add_price(point.when, point.price, true);
|
commodity->add_price(point.when, point.price, true);
|
||||||
commodity->add_flags(COMMODITY_KNOWN);
|
commodity->add_flags(COMMODITY_KNOWN);
|
||||||
return point;
|
return std::pair<commodity_t *, price_point_t>(commodity, point);
|
||||||
}
|
}
|
||||||
|
|
||||||
return none;
|
return none;
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,8 @@ public:
|
||||||
|
|
||||||
// Parse commodity prices from a textual representation
|
// Parse commodity prices from a textual representation
|
||||||
|
|
||||||
optional<price_point_t> parse_price_directive(char * line);
|
optional<std::pair<commodity_t *, price_point_t> >
|
||||||
|
parse_price_directive(char * line, bool do_not_add_price = false);
|
||||||
|
|
||||||
commodity_t *
|
commodity_t *
|
||||||
parse_price_expression(const std::string& str,
|
parse_price_expression(const std::string& str,
|
||||||
|
|
|
||||||
|
|
@ -337,7 +337,9 @@ query_t::parser_t::parse_query_term(query_t::lexer_t::token_t::kind_t tok_contex
|
||||||
}
|
}
|
||||||
|
|
||||||
expr_t::ptr_op_t mask = new expr_t::op_t(expr_t::op_t::VALUE);
|
expr_t::ptr_op_t mask = new expr_t::op_t(expr_t::op_t::VALUE);
|
||||||
|
DEBUG("query.mask", "Mask from string: " << *tok.value);
|
||||||
mask->set_value(mask_t(*tok.value));
|
mask->set_value(mask_t(*tok.value));
|
||||||
|
DEBUG("query.mask", "Mask is: " << mask->as_value().as_mask().str());
|
||||||
|
|
||||||
node->set_left(ident);
|
node->set_left(ident);
|
||||||
node->set_right(mask);
|
node->set_right(mask);
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ commodity_quote_from_script(commodity_t& commodity,
|
||||||
if (char * p = std::strchr(buf, '\n')) *p = '\0';
|
if (char * p = std::strchr(buf, '\n')) *p = '\0';
|
||||||
DEBUG("commodity.download", "downloaded quote: " << buf);
|
DEBUG("commodity.download", "downloaded quote: " << buf);
|
||||||
|
|
||||||
if (optional<price_point_t> point =
|
if (optional<std::pair<commodity_t *, price_point_t> > point =
|
||||||
commodity_pool_t::current_pool->parse_price_directive(buf)) {
|
commodity_pool_t::current_pool->parse_price_directive(buf)) {
|
||||||
if (commodity_pool_t::current_pool->price_db) {
|
if (commodity_pool_t::current_pool->price_db) {
|
||||||
#if defined(__GNUG__) && __GNUG__ < 3
|
#if defined(__GNUG__) && __GNUG__ < 3
|
||||||
|
|
@ -86,12 +86,12 @@ commodity_quote_from_script(commodity_t& commodity,
|
||||||
std::ios_base::out | std::ios_base::app);
|
std::ios_base::out | std::ios_base::app);
|
||||||
#endif
|
#endif
|
||||||
database << "P "
|
database << "P "
|
||||||
<< format_datetime(point->when, FMT_WRITTEN)
|
<< format_datetime(point->second.when, FMT_WRITTEN)
|
||||||
<< " " << commodity.symbol()
|
<< " " << commodity.symbol()
|
||||||
<< " " << point->price
|
<< " " << point->second.price
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
return point;
|
return point->second;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DEBUG("commodity.download",
|
DEBUG("commodity.download",
|
||||||
|
|
|
||||||
|
|
@ -380,6 +380,32 @@ value_t report_t::fn_strip(call_scope_t& args)
|
||||||
return args.value().strip_annotations(what_to_keep());
|
return args.value().strip_annotations(what_to_keep());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value_t report_t::fn_trim(call_scope_t& args)
|
||||||
|
{
|
||||||
|
string temp(args.value().to_string());
|
||||||
|
scoped_array<char> buf(new char[temp.length() + 1]);
|
||||||
|
std::strcpy(buf.get(), temp.c_str());
|
||||||
|
|
||||||
|
const char * p = buf.get();
|
||||||
|
while (*p && std::isspace(*p))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
const char * e = buf.get() + temp.length();
|
||||||
|
while (e > p && std::isspace(*e))
|
||||||
|
e--;
|
||||||
|
|
||||||
|
if (e == p) {
|
||||||
|
return string_value(empty_string);
|
||||||
|
}
|
||||||
|
else if (e < p) {
|
||||||
|
assert(false);
|
||||||
|
return string_value(empty_string);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return string_value(string(p, e - p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
value_t report_t::fn_scrub(call_scope_t& args)
|
value_t report_t::fn_scrub(call_scope_t& args)
|
||||||
{
|
{
|
||||||
value_t temp(args.value().strip_annotations(what_to_keep()));
|
value_t temp(args.value().strip_annotations(what_to_keep()));
|
||||||
|
|
@ -1078,6 +1104,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
|
||||||
return MAKE_FUNCTOR(report_t::fn_today);
|
return MAKE_FUNCTOR(report_t::fn_today);
|
||||||
else if (is_eq(p, "t"))
|
else if (is_eq(p, "t"))
|
||||||
return MAKE_FUNCTOR(report_t::fn_display_amount);
|
return MAKE_FUNCTOR(report_t::fn_display_amount);
|
||||||
|
else if (is_eq(p, "trim"))
|
||||||
|
return MAKE_FUNCTOR(report_t::fn_trim);
|
||||||
else if (is_eq(p, "to_boolean"))
|
else if (is_eq(p, "to_boolean"))
|
||||||
return MAKE_FUNCTOR(report_t::fn_to_boolean);
|
return MAKE_FUNCTOR(report_t::fn_to_boolean);
|
||||||
else if (is_eq(p, "to_int"))
|
else if (is_eq(p, "to_int"))
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,7 @@ public:
|
||||||
value_t fn_get_at(call_scope_t& scope);
|
value_t fn_get_at(call_scope_t& scope);
|
||||||
value_t fn_is_seq(call_scope_t& scope);
|
value_t fn_is_seq(call_scope_t& scope);
|
||||||
value_t fn_strip(call_scope_t& scope);
|
value_t fn_strip(call_scope_t& scope);
|
||||||
|
value_t fn_trim(call_scope_t& scope);
|
||||||
value_t fn_scrub(call_scope_t& scope);
|
value_t fn_scrub(call_scope_t& scope);
|
||||||
value_t fn_quantity(call_scope_t& scope);
|
value_t fn_quantity(call_scope_t& scope);
|
||||||
value_t fn_rounded(call_scope_t& scope);
|
value_t fn_rounded(call_scope_t& scope);
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,8 @@ namespace {
|
||||||
static const std::size_t MAX_LINE = 1024;
|
static const std::size_t MAX_LINE = 1024;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef variant<account_t *, string> state_t;
|
typedef std::pair<commodity_t *, amount_t> fixed_rate_t;
|
||||||
|
typedef variant<account_t *, string, fixed_rate_t> state_t;
|
||||||
|
|
||||||
std::list<state_t>& state_stack;
|
std::list<state_t>& state_stack;
|
||||||
|
|
||||||
|
|
@ -99,6 +100,9 @@ namespace {
|
||||||
bool front_is_string() {
|
bool front_is_string() {
|
||||||
return state_stack.front().type() == typeid(string);
|
return state_stack.front().type() == typeid(string);
|
||||||
}
|
}
|
||||||
|
bool front_is_fixed_rate() {
|
||||||
|
return state_stack.front().type() == typeid(fixed_rate_t);
|
||||||
|
}
|
||||||
|
|
||||||
account_t * top_account() {
|
account_t * top_account() {
|
||||||
foreach (state_t& state, state_stack)
|
foreach (state_t& state, state_stack)
|
||||||
|
|
@ -135,6 +139,7 @@ namespace {
|
||||||
void master_account_directive(char * line);
|
void master_account_directive(char * line);
|
||||||
void end_directive(char * line);
|
void end_directive(char * line);
|
||||||
void alias_directive(char * line);
|
void alias_directive(char * line);
|
||||||
|
void fixed_directive(char * line);
|
||||||
void tag_directive(char * line);
|
void tag_directive(char * line);
|
||||||
void define_directive(char * line);
|
void define_directive(char * line);
|
||||||
bool general_directive(char * line);
|
bool general_directive(char * line);
|
||||||
|
|
@ -345,8 +350,10 @@ void instance_t::read_next_directive()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case '#': // comment line
|
case ';': // comments
|
||||||
case ';': // comment line
|
case '#':
|
||||||
|
case '*':
|
||||||
|
case '|':
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '-': // option setting
|
case '-': // option setting
|
||||||
|
|
@ -510,7 +517,7 @@ void instance_t::price_conversion_directive(char * line)
|
||||||
|
|
||||||
void instance_t::price_xact_directive(char * line)
|
void instance_t::price_xact_directive(char * line)
|
||||||
{
|
{
|
||||||
optional<price_point_t> point =
|
optional<std::pair<commodity_t *, price_point_t> > point =
|
||||||
commodity_pool_t::current_pool->parse_price_directive(skip_ws(line + 1));
|
commodity_pool_t::current_pool->parse_price_directive(skip_ws(line + 1));
|
||||||
if (! point)
|
if (! point)
|
||||||
throw parse_error(_("Pricing entry failed to parse"));
|
throw parse_error(_("Pricing entry failed to parse"));
|
||||||
|
|
@ -703,10 +710,13 @@ void instance_t::end_directive(char * kind)
|
||||||
|
|
||||||
if ((name.empty() || name == "account") && ! front_is_account())
|
if ((name.empty() || name == "account") && ! front_is_account())
|
||||||
throw_(std::runtime_error,
|
throw_(std::runtime_error,
|
||||||
_("'end account' directive does not match open tag directive"));
|
_("'end account' directive does not match open directive"));
|
||||||
else if (name == "tag" && ! front_is_string())
|
else if (name == "tag" && ! front_is_string())
|
||||||
throw_(std::runtime_error,
|
throw_(std::runtime_error,
|
||||||
_("'end tag' directive does not match open account directive"));
|
_("'end tag' directive does not match open directive"));
|
||||||
|
else if (name == "fixed" && ! front_is_fixed_rate())
|
||||||
|
throw_(std::runtime_error,
|
||||||
|
_("'end fixed' directive does not match open directive"));
|
||||||
|
|
||||||
if (state_stack.size() <= 1)
|
if (state_stack.size() <= 1)
|
||||||
throw_(std::runtime_error,
|
throw_(std::runtime_error,
|
||||||
|
|
@ -736,6 +746,18 @@ void instance_t::alias_directive(char * line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void instance_t::fixed_directive(char * line)
|
||||||
|
{
|
||||||
|
if (optional<std::pair<commodity_t *, price_point_t> > price_point =
|
||||||
|
commodity_pool_t::current_pool->parse_price_directive(trim_ws(line),
|
||||||
|
true)) {
|
||||||
|
state_stack.push_front(fixed_rate_t(price_point->first,
|
||||||
|
price_point->second.price));
|
||||||
|
} else {
|
||||||
|
throw_(std::runtime_error, _("Error in fixed directive"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void instance_t::tag_directive(char * line)
|
void instance_t::tag_directive(char * line)
|
||||||
{
|
{
|
||||||
string tag(trim_ws(line));
|
string tag(trim_ws(line));
|
||||||
|
|
@ -797,6 +819,13 @@ bool instance_t::general_directive(char * line)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
if (std::strcmp(p, "fixed") == 0) {
|
||||||
|
fixed_directive(arg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'i':
|
case 'i':
|
||||||
if (std::strcmp(p, "include") == 0) {
|
if (std::strcmp(p, "include") == 0) {
|
||||||
include_directive(arg);
|
include_directive(arg);
|
||||||
|
|
@ -810,6 +839,13 @@ bool instance_t::general_directive(char * line)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'y':
|
||||||
|
if (std::strcmp(p, "year") == 0) {
|
||||||
|
year_directive(arg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expr_t::ptr_op_t op = lookup(symbol_t::DIRECTIVE, p)) {
|
if (expr_t::ptr_op_t op = lookup(symbol_t::DIRECTIVE, p)) {
|
||||||
|
|
@ -933,8 +969,8 @@ post_t * instance_t::parse_post(char * line,
|
||||||
post.get(), PARSE_NO_REDUCE | PARSE_SINGLE |
|
post.get(), PARSE_NO_REDUCE | PARSE_SINGLE |
|
||||||
PARSE_NO_ASSIGN, defer_expr);
|
PARSE_NO_ASSIGN, defer_expr);
|
||||||
|
|
||||||
if (! post->amount.is_null() && honor_strict && strict &&
|
if (! post->amount.is_null() && post->amount.has_commodity()) {
|
||||||
post->amount.has_commodity() &&
|
if (honor_strict && strict &&
|
||||||
! post->amount.commodity().has_flags(COMMODITY_KNOWN)) {
|
! post->amount.commodity().has_flags(COMMODITY_KNOWN)) {
|
||||||
if (post->_state == item_t::UNCLEARED)
|
if (post->_state == item_t::UNCLEARED)
|
||||||
warning_(_("\"%1\", line %2: Unknown commodity '%3'")
|
warning_(_("\"%1\", line %2: Unknown commodity '%3'")
|
||||||
|
|
@ -942,6 +978,21 @@ post_t * instance_t::parse_post(char * line,
|
||||||
post->amount.commodity().add_flags(COMMODITY_KNOWN);
|
post->amount.commodity().add_flags(COMMODITY_KNOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! post->amount.has_annotation()) {
|
||||||
|
foreach (state_t& state, state_stack) {
|
||||||
|
if (state.type() == typeid(fixed_rate_t)) {
|
||||||
|
fixed_rate_t& rate(boost::get<fixed_rate_t>(state));
|
||||||
|
if (*rate.first == post->amount.commodity()) {
|
||||||
|
annotation_t details(rate.second);
|
||||||
|
details.add_flags(ANNOTATION_PRICE_FIXATED);
|
||||||
|
post->amount.annotate(details);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DEBUG("textual.parse", "line " << linenum << ": "
|
DEBUG("textual.parse", "line " << linenum << ": "
|
||||||
<< "post amount = " << post->amount);
|
<< "post amount = " << post->amount);
|
||||||
|
|
||||||
|
|
|
||||||
20
test/baseline/opt-unrealized-gains.test
Normal file
20
test/baseline/opt-unrealized-gains.test
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
bal -V --unrealized --unrealized-gains G
|
||||||
|
<<<
|
||||||
|
2008/10/01 Sample
|
||||||
|
Assets:Brokerage 10 AAPL
|
||||||
|
Assets:Checking $-200.00
|
||||||
|
|
||||||
|
P 2008/10/20 12:00:00 AAPL $30.00
|
||||||
|
|
||||||
|
; 2008/10/20 <Generated Transaction>
|
||||||
|
; Assets:Brokerage $100
|
||||||
|
; Equity:Unrealized Gains
|
||||||
|
>>>1
|
||||||
|
$100.00 Assets
|
||||||
|
$300.00 Brokerage
|
||||||
|
$-200.00 Checking
|
||||||
|
$-100.00 G
|
||||||
|
--------------------
|
||||||
|
0
|
||||||
|
>>>2
|
||||||
|
=== 0
|
||||||
20
test/baseline/opt-unrealized-losses.test
Normal file
20
test/baseline/opt-unrealized-losses.test
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
bal -V --unrealized --unrealized-losses L
|
||||||
|
<<<
|
||||||
|
2008/10/01 Sample
|
||||||
|
Assets:Brokerage 10 AAPL
|
||||||
|
Assets:Checking $-200.00
|
||||||
|
|
||||||
|
P 2008/10/20 12:00:00 AAPL $10.00
|
||||||
|
|
||||||
|
; 2008/10/20 <Generated Transaction>
|
||||||
|
; Assets:Brokerage $100
|
||||||
|
; Equity:Unrealized Gains
|
||||||
|
>>>1
|
||||||
|
$-100.00 Assets
|
||||||
|
$100.00 Brokerage
|
||||||
|
$-200.00 Checking
|
||||||
|
$100.00 L
|
||||||
|
--------------------
|
||||||
|
0
|
||||||
|
>>>2
|
||||||
|
=== 0
|
||||||
|
|
@ -18,3 +18,31 @@ P 2008/10/20 12:00:00 AAPL $30.00
|
||||||
0
|
0
|
||||||
>>>2
|
>>>2
|
||||||
=== 0
|
=== 0
|
||||||
|
bal -V --unrealized --now=2009/11/25
|
||||||
|
<<<
|
||||||
|
2008/10/01 Sample
|
||||||
|
Assets:Brokerage 10 AAPL
|
||||||
|
Assets:Checking $-200.00
|
||||||
|
|
||||||
|
2008/10/01 Sample
|
||||||
|
Assets:Brokerage -10 QQQQ
|
||||||
|
Assets:Checking $1000
|
||||||
|
|
||||||
|
P 2008/10/20 12:00:00 AAPL $30.00
|
||||||
|
P 2008/10/20 12:00:00 QQQQ $110
|
||||||
|
|
||||||
|
; 2008/10/20 <Generated Transaction>
|
||||||
|
; Assets:Brokerage $100
|
||||||
|
; Equity:Unrealized Gains
|
||||||
|
|
||||||
|
; 2008/10/20 <Generated Transaction>
|
||||||
|
; Assets:Brokerage $-100
|
||||||
|
; Equity:Unrealized Losses
|
||||||
|
>>>1
|
||||||
|
0 Assets
|
||||||
|
$-800.00 Brokerage
|
||||||
|
$800.00 Checking
|
||||||
|
--------------------
|
||||||
|
0
|
||||||
|
>>>2
|
||||||
|
=== 0
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue