summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2009-11-25 14:20:07 -0500
committerJohn Wiegley <johnw@newartisans.com>2009-11-25 14:20:07 -0500
commite8a14d31b6aefe421796fca1e50f4ea1e3816536 (patch)
tree77ccfc5c88c9569b9ad02790c754d8d498b7240b
parent98ea23cd59cf959f3561632cc38043ab2d89f164 (diff)
parenta3338a2a67a793ddbbc1b0f93901d9a978dc1a8c (diff)
downloadfork-ledger-e8a14d31b6aefe421796fca1e50f4ea1e3816536.tar.gz
fork-ledger-e8a14d31b6aefe421796fca1e50f4ea1e3816536.tar.bz2
fork-ledger-e8a14d31b6aefe421796fca1e50f4ea1e3816536.zip
Merge branch 'next'
-rw-r--r--src/pool.cc16
-rw-r--r--src/pool.h3
-rw-r--r--src/query.cc2
-rw-r--r--src/quotes.cc8
-rw-r--r--src/report.cc28
-rw-r--r--src/report.h1
-rw-r--r--src/textual.cc75
-rw-r--r--test/baseline/opt-unrealized-gains.test20
-rw-r--r--test/baseline/opt-unrealized-losses.test20
-rw-r--r--test/baseline/opt-unrealized.test28
10 files changed, 178 insertions, 23 deletions
diff --git a/src/pool.cc b/src/pool.cc
index 00f4a3da..70f4eed6 100644
--- a/src/pool.cc
+++ b/src/pool.cc
@@ -286,7 +286,8 @@ commodity_pool_t::exchange(const amount_t& amount,
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 * 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;
datetime_t datetime;
+ string symbol;
if (std::isdigit(time_field_ptr[0])) {
symbol_and_price = next_element(time_field_ptr);
@@ -307,12 +309,13 @@ optional<price_point_t> commodity_pool_t::parse_price_directive(char * line)
datetime = datetime_t(parse_date(date_field));
}
else {
- symbol_and_price = date_field_ptr;
+ symbol = date_field_ptr;
+ symbol_and_price = time_field_ptr;
datetime = CURRENT_TIME();
}
- string symbol;
- commodity_t::parse_symbol(symbol_and_price, symbol);
+ if (symbol.empty())
+ commodity_t::parse_symbol(symbol_and_price, symbol);
price_point_t point;
point.when = datetime;
@@ -323,9 +326,10 @@ optional<price_point_t> commodity_pool_t::parse_price_directive(char * line)
if (commodity_t * commodity = find_or_create(symbol)) {
DEBUG("commodity.download", "Adding price for " << symbol << ": "
<< point.when << " " << point.price);
- commodity->add_price(point.when, point.price, true);
+ if (! do_not_add_price)
+ commodity->add_price(point.when, point.price, true);
commodity->add_flags(COMMODITY_KNOWN);
- return point;
+ return std::pair<commodity_t *, price_point_t>(commodity, point);
}
return none;
diff --git a/src/pool.h b/src/pool.h
index 85de73a9..995ab23c 100644
--- a/src/pool.h
+++ b/src/pool.h
@@ -123,7 +123,8 @@ public:
// 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 *
parse_price_expression(const std::string& str,
diff --git a/src/query.cc b/src/query.cc
index 2d6085fa..cfa321b0 100644
--- a/src/query.cc
+++ b/src/query.cc
@@ -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);
+ DEBUG("query.mask", "Mask from string: " << *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_right(mask);
diff --git a/src/quotes.cc b/src/quotes.cc
index ffe2142a..f892e93f 100644
--- a/src/quotes.cc
+++ b/src/quotes.cc
@@ -75,7 +75,7 @@ commodity_quote_from_script(commodity_t& commodity,
if (char * p = std::strchr(buf, '\n')) *p = '\0';
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)) {
if (commodity_pool_t::current_pool->price_db) {
#if defined(__GNUG__) && __GNUG__ < 3
@@ -86,12 +86,12 @@ commodity_quote_from_script(commodity_t& commodity,
std::ios_base::out | std::ios_base::app);
#endif
database << "P "
- << format_datetime(point->when, FMT_WRITTEN)
+ << format_datetime(point->second.when, FMT_WRITTEN)
<< " " << commodity.symbol()
- << " " << point->price
+ << " " << point->second.price
<< std::endl;
}
- return point;
+ return point->second;
}
} else {
DEBUG("commodity.download",
diff --git a/src/report.cc b/src/report.cc
index 894e0d89..2d9d7cc6 100644
--- a/src/report.cc
+++ b/src/report.cc
@@ -380,6 +380,32 @@ value_t report_t::fn_strip(call_scope_t& args)
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 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);
else if (is_eq(p, "t"))
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"))
return MAKE_FUNCTOR(report_t::fn_to_boolean);
else if (is_eq(p, "to_int"))
diff --git a/src/report.h b/src/report.h
index d942038b..94d39215 100644
--- a/src/report.h
+++ b/src/report.h
@@ -143,6 +143,7 @@ public:
value_t fn_get_at(call_scope_t& scope);
value_t fn_is_seq(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_quantity(call_scope_t& scope);
value_t fn_rounded(call_scope_t& scope);
diff --git a/src/textual.cc b/src/textual.cc
index 071e111d..cf670cae 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -54,7 +54,8 @@ namespace {
static const std::size_t MAX_LINE = 1024;
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;
@@ -99,6 +100,9 @@ namespace {
bool front_is_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() {
foreach (state_t& state, state_stack)
@@ -135,6 +139,7 @@ namespace {
void master_account_directive(char * line);
void end_directive(char * line);
void alias_directive(char * line);
+ void fixed_directive(char * line);
void tag_directive(char * line);
void define_directive(char * line);
bool general_directive(char * line);
@@ -345,8 +350,10 @@ void instance_t::read_next_directive()
break;
}
- case '#': // comment line
- case ';': // comment line
+ case ';': // comments
+ case '#':
+ case '*':
+ case '|':
break;
case '-': // option setting
@@ -510,7 +517,7 @@ void instance_t::price_conversion_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));
if (! point)
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())
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())
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)
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)
{
string tag(trim_ws(line));
@@ -797,6 +819,13 @@ bool instance_t::general_directive(char * line)
}
break;
+ case 'f':
+ if (std::strcmp(p, "fixed") == 0) {
+ fixed_directive(arg);
+ return true;
+ }
+ break;
+
case 'i':
if (std::strcmp(p, "include") == 0) {
include_directive(arg);
@@ -810,6 +839,13 @@ bool instance_t::general_directive(char * line)
return true;
}
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)) {
@@ -933,13 +969,28 @@ post_t * instance_t::parse_post(char * line,
post.get(), PARSE_NO_REDUCE | PARSE_SINGLE |
PARSE_NO_ASSIGN, defer_expr);
- if (! post->amount.is_null() && honor_strict && strict &&
- post->amount.has_commodity() &&
+ if (! post->amount.is_null() && post->amount.has_commodity()) {
+ if (honor_strict && strict &&
! post->amount.commodity().has_flags(COMMODITY_KNOWN)) {
- if (post->_state == item_t::UNCLEARED)
- warning_(_("\"%1\", line %2: Unknown commodity '%3'")
- << pathname << linenum << post->amount.commodity());
- post->amount.commodity().add_flags(COMMODITY_KNOWN);
+ if (post->_state == item_t::UNCLEARED)
+ warning_(_("\"%1\", line %2: Unknown commodity '%3'")
+ << pathname << linenum << post->amount.commodity());
+ 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 << ": "
diff --git a/test/baseline/opt-unrealized-gains.test b/test/baseline/opt-unrealized-gains.test
new file mode 100644
index 00000000..5b225a42
--- /dev/null
+++ b/test/baseline/opt-unrealized-gains.test
@@ -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
diff --git a/test/baseline/opt-unrealized-losses.test b/test/baseline/opt-unrealized-losses.test
new file mode 100644
index 00000000..2edd4e63
--- /dev/null
+++ b/test/baseline/opt-unrealized-losses.test
@@ -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
diff --git a/test/baseline/opt-unrealized.test b/test/baseline/opt-unrealized.test
index 7d5d20fb..c472d9ef 100644
--- a/test/baseline/opt-unrealized.test
+++ b/test/baseline/opt-unrealized.test
@@ -18,3 +18,31 @@ P 2008/10/20 12:00:00 AAPL $30.00
0
>>>2
=== 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