diff options
author | John Wiegley <johnw@newartisans.com> | 2009-11-25 14:20:07 -0500 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2009-11-25 14:20:07 -0500 |
commit | e8a14d31b6aefe421796fca1e50f4ea1e3816536 (patch) | |
tree | 77ccfc5c88c9569b9ad02790c754d8d498b7240b | |
parent | 98ea23cd59cf959f3561632cc38043ab2d89f164 (diff) | |
parent | a3338a2a67a793ddbbc1b0f93901d9a978dc1a8c (diff) | |
download | fork-ledger-e8a14d31b6aefe421796fca1e50f4ea1e3816536.tar.gz fork-ledger-e8a14d31b6aefe421796fca1e50f4ea1e3816536.tar.bz2 fork-ledger-e8a14d31b6aefe421796fca1e50f4ea1e3816536.zip |
Merge branch 'next'
-rw-r--r-- | src/pool.cc | 16 | ||||
-rw-r--r-- | src/pool.h | 3 | ||||
-rw-r--r-- | src/query.cc | 2 | ||||
-rw-r--r-- | src/quotes.cc | 8 | ||||
-rw-r--r-- | src/report.cc | 28 | ||||
-rw-r--r-- | src/report.h | 1 | ||||
-rw-r--r-- | src/textual.cc | 75 | ||||
-rw-r--r-- | test/baseline/opt-unrealized-gains.test | 20 | ||||
-rw-r--r-- | test/baseline/opt-unrealized-losses.test | 20 | ||||
-rw-r--r-- | test/baseline/opt-unrealized.test | 28 |
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; @@ -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 |