diff options
Diffstat (limited to 'src/textual.cc')
-rw-r--r-- | src/textual.cc | 379 |
1 files changed, 200 insertions, 179 deletions
diff --git a/src/textual.cc b/src/textual.cc index 13032236..8a1968fc 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -49,43 +49,45 @@ namespace ledger { namespace { - typedef std::pair<commodity_t *, amount_t> fixed_rate_t; - typedef variant<account_t *, string, fixed_rate_t> state_t; + typedef std::pair<commodity_t *, amount_t> fixed_rate_t; + + struct application_t + { + string label; + variant<account_t *, string, fixed_rate_t> value; + + application_t(string _label, account_t * acct) + : label(_label), value(acct) {} + application_t(string _label, string tag) + : label(_label), value(tag) {} + application_t(string _label, fixed_rate_t rate) + : label(_label), value(rate) {} + }; class parse_context_t : public noncopyable { public: - journal_t& journal; - scope_t& scope; - std::list<state_t> state_stack; + std::list<application_t> apply_stack; + + journal_t& journal; + scope_t& scope; #if defined(TIMELOG_SUPPORT) - time_log_t timelog; + time_log_t timelog; #endif - bool strict; - std::size_t count; - std::size_t errors; - std::size_t sequence; + std::size_t count; + std::size_t errors; + std::size_t sequence; parse_context_t(journal_t& _journal, scope_t& _scope) : journal(_journal), scope(_scope), timelog(journal, scope), - strict(false), count(0), errors(0), sequence(1) { + count(0), errors(0), sequence(1) { timelog.context_count = &count; } - bool front_is_account() { - return state_stack.front().type() == typeid(account_t *); - } - 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) - if (state.type() == typeid(account_t *)) - return boost::get<account_t *>(state); + foreach (application_t& state, apply_stack) + if (state.value.type() == typeid(account_t *)) + return boost::get<account_t *>(state.value); return NULL; } @@ -101,7 +103,6 @@ namespace { public: parse_context_t& context; instance_t * parent; - accounts_map account_aliases; const path * original_file; path pathname; std::istream& in; @@ -136,36 +137,52 @@ namespace { void clock_out_directive(char * line, bool capitalized); #endif - void default_commodity_directive(char * line); - void default_account_directive(char * line); - void price_conversion_directive(char * line); + bool general_directive(char * line); + + void account_directive(char * line); + void account_alias_directive(char * line); + void account_payee_directive(char * line); + + void payee_directive(char * line); + void payee_alias_directive(char * line); + + void commodity_directive(char * line); +#if 0 + void commodity_alias_directive(char * line); + void commodity_format_directive(char * line); + void commodity_nomarket_directive(char * line); +#endif + + void apply_directive(char * line); + void apply_account_directive(char * line); + void apply_tag_directive(char * line); + void apply_rate_directive(char * line); + void apply_year_directive(char * line); + void end_apply_directive(char * line); + + void xact_directive(char * line, std::streamsize len); + void period_xact_directive(char * line); + void automated_xact_directive(char * line); void price_xact_directive(char * line); + void price_conversion_directive(char * line); void nomarket_directive(char * line); - void year_directive(char * line); - void option_directive(char * line); - void automated_xact_directive(char * line); - void period_xact_directive(char * line); - void xact_directive(char * line, std::streamsize len); + + void default_account_directive(char * line); + void default_commodity_directive(char * line); + void include_directive(char * line); - void master_account_directive(char * line); - void end_directive(char * line); - void alias_directive(char * line); - void fixed_directive(char * line); - void payee_mapping_directive(char * line); - void account_mapping_directive(char * line); - void tag_directive(char * line); + void option_directive(char * line); void define_directive(char * line); + void expr_directive(char * line); void assert_directive(char * line); void check_directive(char * line); void comment_directive(char * line); - void expr_directive(char * line); - bool general_directive(char * line); post_t * parse_post(char * line, std::streamsize len, account_t * account, xact_t * xact, - bool defer_expr = false); + bool defer_expr = false); bool parse_posts(account_t * account, xact_base_t& xact, @@ -402,7 +419,7 @@ void instance_t::read_next_directive() price_xact_directive(line); break; case 'Y': // set the current year - year_directive(line); + apply_year_directive(line); break; } } @@ -514,16 +531,6 @@ void instance_t::nomarket_directive(char * line) commodity->add_flags(COMMODITY_NOMARKET | COMMODITY_KNOWN); } -void instance_t::year_directive(char * line) -{ - unsigned short year(lexical_cast<unsigned short>(skip_ws(line + 1))); - DEBUG("times.epoch", "Setting current year to " << year); - // This must be set to the last day of the year, otherwise partial - // dates like "11/01" will refer to last year's november, not the - // current year. - epoch = datetime_t(date_t(year, 12, 31)); -} - void instance_t::option_directive(char * line) { char * p = next_element(line); @@ -774,44 +781,97 @@ void instance_t::include_directive(char * line) if (! files_found) throw_(std::runtime_error, - _("File to include was not found: '%1'") << filename); + _("File to include was not found: %1") << filename); } -void instance_t::master_account_directive(char * line) +void instance_t::apply_directive(char * line) +{ + char * b = next_element(line); + string keyword(line); + if (keyword == "account") + apply_account_directive(b); + else if (keyword == "tag") + apply_tag_directive(b); + else if (keyword == "fixed" || keyword == "rate") + apply_rate_directive(b); + else if (keyword == "year") + apply_year_directive(b); +} + +void instance_t::apply_account_directive(char * line) { if (account_t * acct = context.top_account()->find_account(line)) - context.state_stack.push_front(acct); + context.apply_stack.push_front(application_t("account", acct)); #if !defined(NO_ASSERTS) else assert("Failed to create account" == NULL); #endif } -void instance_t::end_directive(char * kind) +void instance_t::apply_tag_directive(char * line) { - string name(kind ? kind : ""); + string tag(trim_ws(line)); - if ((name.empty() || name == "account") && ! context.front_is_account()) - throw_(std::runtime_error, - _("'end account' directive does not match open directive")); - else if (name == "tag" && ! context.front_is_string()) - throw_(std::runtime_error, - _("'end tag' directive does not match open directive")); - else if (name == "fixed" && ! context.front_is_fixed_rate()) + if (tag.find(':') == string::npos) + tag = string(":") + tag + ":"; + + context.apply_stack.push_front(application_t("tag", tag)); +} + +void instance_t::apply_rate_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)) { + context.apply_stack.push_front + (application_t("fixed", fixed_rate_t(price_point->first, + price_point->second.price))); + } else { + throw_(std::runtime_error, _("Error in fixed directive")); + } +} + +void instance_t::apply_year_directive(char * line) +{ + unsigned short year(lexical_cast<unsigned short>(skip_ws(line + 1))); + DEBUG("times.epoch", "Setting current year to " << year); + // This must be set to the last day of the year, otherwise partial + // dates like "11/01" will refer to last year's november, not the + // current year. + epoch = datetime_t(date_t(year, 12, 31)); +} + +void instance_t::end_apply_directive(char * kind) +{ + char * b = next_element(kind); + string name(b ? b : "account"); + + if (context.apply_stack.size() <= 1) throw_(std::runtime_error, - _("'end fixed' directive does not match open directive")); + _("'end %1' found, but no enclosing '%2' directive") + << name << name); - if (context.state_stack.size() <= 1) + if (name != context.apply_stack.front().label) throw_(std::runtime_error, - _("'end' found, but no enclosing tag or account directive")); - else - context.state_stack.pop_front(); + _("'end %1' directive does not match 'apply %2' directive") + << name << context.apply_stack.front().label); + + context.apply_stack.pop_front(); +} + +void instance_t::account_directive(char * line) +{ + char * p = skip_ws(line); + //account_t * account = + context.journal.register_account(p, NULL, + file_context(pathname, linenum), + context.top_account()); } -void instance_t::alias_directive(char * line) +void instance_t::account_alias_directive(char * line) { char * b = skip_ws(line); +#if 0 if (char * e = std::strchr(b, '=')) { char * z = e - 1; while (std::isspace(*z)) @@ -828,21 +888,19 @@ void instance_t::alias_directive(char * line) = account_aliases.insert(accounts_map::value_type(b, acct)); assert(result.second); } +#endif } -void instance_t::fixed_directive(char * line) +void instance_t::account_payee_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)) { - context.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::payee_mapping_directive(char * line) +void instance_t::payee_directive(char * line) +{ + context.journal.register_payee(line, NULL, file_context(pathname, linenum)); +} + +void instance_t::payee_alias_directive(char * line) { char * payee = skip_ws(line); char * regex = next_element(payee, true); @@ -868,6 +926,27 @@ void instance_t::payee_mapping_directive(char * line) } } +void instance_t::commodity_directive(char * line) +{ + char * p = skip_ws(line); + string symbol; + commodity_t::parse_symbol(p, symbol); + + if (commodity_t * commodity = + commodity_pool_t::current_pool->find_or_create(symbol)) + context.journal.register_commodity(*commodity, 0, + file_context(pathname, linenum)); +} + +#if 0 +void instance_t::commodity_alias_directive(char * line) +{ +} + +void instance_t::commodity_nomarket_directive(char * line) +{ +} + void instance_t::account_mapping_directive(char * line) { char * account_name = skip_ws(line); @@ -895,16 +974,7 @@ void instance_t::account_mapping_directive(char * line) context.top_account()->find_account(account_name))); } } - -void instance_t::tag_directive(char * line) -{ - string tag(trim_ws(line)); - - if (tag.find(':') == string::npos) - tag = string(":") + tag + ":"; - - context.state_stack.push_front(tag); -} +#endif void instance_t::define_directive(char * line) { @@ -958,11 +1028,11 @@ bool instance_t::general_directive(char * line) switch (*p) { case 'a': if (std::strcmp(p, "account") == 0) { - master_account_directive(arg); + account_directive(arg); return true; } - else if (std::strcmp(p, "alias") == 0) { - alias_directive(arg); + else if (std::strcmp(p, "apply") == 0) { + apply_directive(arg); return true; } else if (std::strcmp(p, "assert") == 0) { @@ -979,11 +1049,7 @@ bool instance_t::general_directive(char * line) break; case 'c': - if (std::strcmp(p, "capture") == 0) { - account_mapping_directive(arg); - return true; - } - else if (std::strcmp(p, "check") == 0) { + if (std::strcmp(p, "check") == 0) { check_directive(arg); return true; } @@ -991,6 +1057,10 @@ bool instance_t::general_directive(char * line) comment_directive(arg); return true; } + else if (std::strcmp(p, "commodity") == 0) { + commodity_directive(arg); + return true; + } break; case 'd': @@ -1002,7 +1072,7 @@ bool instance_t::general_directive(char * line) case 'e': if (std::strcmp(p, "end") == 0) { - end_directive(arg); + end_apply_directive(arg); return true; } else if (std::strcmp(p, "expr") == 0) { @@ -1011,13 +1081,6 @@ 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); @@ -1027,28 +1090,17 @@ bool instance_t::general_directive(char * line) case 'p': if (std::strcmp(p, "payee") == 0) { - payee_mapping_directive(arg); + payee_directive(arg); return true; } break; case 't': - if (std::strcmp(p, "tag") == 0) { - tag_directive(arg); - return true; - } - else if (std::strcmp(p, "test") == 0) { + if (std::strcmp(p, "test") == 0) { comment_directive(arg); 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)) { @@ -1140,30 +1192,10 @@ post_t * instance_t::parse_post(char * line, DEBUG("textual.parse", "line " << linenum << ": " << "Parsed account name " << name); - if (account_aliases.size() > 0) { - accounts_map::const_iterator i = account_aliases.find(name); - if (i != account_aliases.end()) - post->account = (*i).second; - } - if (! post->account) - post->account = account->find_account(name); - - if (context.strict && ! post->account->has_flags(ACCOUNT_KNOWN)) { - if (post->_state == item_t::UNCLEARED) - warning_(_("%1Unknown account '%2'") - << file_context(pathname, linenum) - << post->account->fullname()); - post->account->add_flags(ACCOUNT_KNOWN); - } - - if (post->account->name == _("Unknown")) { - foreach (account_mapping_t& value, context.journal.account_mappings) { - if (value.first.match(xact->payee)) { - post->account = value.second; - break; - } - } - } + post->account = + context.journal.register_account(name, post.get(), + file_context(pathname, linenum), + account); // Parse the optional amount @@ -1179,19 +1211,13 @@ post_t * instance_t::parse_post(char * line, defer_expr, &post->amount_expr); if (! post->amount.is_null() && post->amount.has_commodity()) { - if (context.strict && - ! post->amount.commodity().has_flags(COMMODITY_KNOWN)) { - if (post->_state == item_t::UNCLEARED) - warning_(_("%1Unknown commodity '%2'") - << file_context(pathname, linenum) - << post->amount.commodity()); - post->amount.commodity().add_flags(COMMODITY_KNOWN); - } + context.journal.register_commodity(post->amount.commodity(), post.get(), + file_context(pathname, linenum)); if (! post->amount.has_annotation()) { - foreach (state_t& state, context.state_stack) { - if (state.type() == typeid(fixed_rate_t)) { - fixed_rate_t& rate(boost::get<fixed_rate_t>(state)); + foreach (application_t& state, context.apply_stack) { + if (state.value.type() == typeid(fixed_rate_t)) { + fixed_rate_t& rate(boost::get<fixed_rate_t>(state.value)); if (*rate.first == post->amount.commodity()) { annotation_t details(rate.second); details.add_flags(ANNOTATION_PRICE_FIXATED); @@ -1388,10 +1414,11 @@ post_t * instance_t::parse_post(char * line, post->pos->end_pos = curr_pos; post->pos->end_line = linenum; - if (! context.state_stack.empty()) { - foreach (const state_t& state, context.state_stack) - if (state.type() == typeid(string)) - post->parse_tags(boost::get<string>(state).c_str(), context.scope, true); + if (! context.apply_stack.empty()) { + foreach (const application_t& state, context.apply_stack) + if (state.value.type() == typeid(string)) + post->parse_tags(boost::get<string>(state.value).c_str(), + context.scope, true); } TRACE_STOP(post_details, 1); @@ -1488,14 +1515,9 @@ xact_t * instance_t::parse_xact(char * line, if (next && *next) { char * p = next_element(next, true); - foreach (payee_mapping_t& value, context.journal.payee_mappings) { - if (value.first.match(next)) { - xact->payee = value.second; - break; - } - } - if (xact->payee.empty()) - xact->payee = next; + xact->payee = + context.journal.register_payee(next, xact.get(), + file_context(pathname, linenum)); next = p; } else { xact->payee = _("<Unspecified payee>"); @@ -1572,7 +1594,7 @@ xact_t * instance_t::parse_xact(char * line, #if 0 if (xact->_state == item_t::UNCLEARED) { - item_t::state_t result = item_t::CLEARED; + item_t::application_t result = item_t::CLEARED; foreach (post_t * post, xact->posts) { if (post->_state == item_t::UNCLEARED) { @@ -1589,11 +1611,11 @@ xact_t * instance_t::parse_xact(char * line, xact->pos->end_pos = curr_pos; xact->pos->end_line = linenum; - if (! context.state_stack.empty()) { - foreach (const state_t& state, context.state_stack) - if (state.type() == typeid(string)) - xact->parse_tags(boost::get<string>(state).c_str(), context.scope, - false); + if (! context.apply_stack.empty()) { + foreach (const application_t& state, context.apply_stack) + if (state.value.type() == typeid(string)) + xact->parse_tags(boost::get<string>(state.value).c_str(), + context.scope, false); } TRACE_STOP(xact_details, 1); @@ -1620,16 +1642,15 @@ expr_t::ptr_op_t instance_t::lookup(const symbol_t::kind_t kind, std::size_t journal_t::parse(std::istream& in, scope_t& scope, account_t * master_account, - const path * original_file, - bool strict) + const path * original_file) { TRACE_START(parsing_total, 1, "Total time spent parsing text:"); parse_context_t context(*this, scope); - context.strict = strict; if (master_account || this->master) - context.state_stack.push_front(master_account ? - master_account : this->master); + context.apply_stack.push_front(application_t("account", + master_account ? + master_account : this->master)); instance_t instance(context, in, original_file); instance.parse(); |