diff options
Diffstat (limited to 'src/textual.cc')
-rw-r--r-- | src/textual.cc | 309 |
1 files changed, 178 insertions, 131 deletions
diff --git a/src/textual.cc b/src/textual.cc index d3a829d3..08a607fb 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -29,94 +29,107 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if defined(__GNUG__) && __GNUG__ < 3 -#define _XOPEN_SOURCE -#endif - -#include "textual.h" +#include "session.h" +#include "journal.h" #if defined(TIMELOG_SUPPORT) #include "timelog.h" #endif -#include "parser.h" -#include "session.h" namespace ledger { -#if defined(TEST_FOR_PARSER) - -bool textual_parser_t::test(std::istream& in) const -{ - char buf[12]; - char * p; - - in.read(buf, 11); - if (utf8::is_bom(buf)) - p = &buf[3]; - else - p = buf; - - if (std::strncmp(p, "<?xml", 5) == 0) - throw_(parse_error, - "Ledger file contains XML data, but format was not recognized"); - - in.clear(); - in.seekg(0, std::ios::beg); - assert(in.good()); - return true; -} - -#endif // TEST_FOR_PARSER - -std::size_t textual_parser_t::parse(std::istream& in, - session_t& session, - journal_t& journal, - account_t * master, - const path * original_file) -{ - TRACE_START(parsing_total, 1, "Total time spent parsing text:"); +namespace { + class instance_t : public noncopyable, public scope_t + { + static const std::size_t MAX_LINE = 1024; - std::list<account_t *> account_stack; + public: + std::list<account_t *>& account_stack; #if defined(TIMELOG_SUPPORT) - time_log_t timelog(journal); + time_log_t& timelog; #endif - instance_t parsing_instance(account_stack, + instance_t * parent; + std::istream& in; + session_t& session; + journal_t& journal; + account_t * master; + const path * original_file; + accounts_map account_aliases; + + path pathname; + char linebuf[MAX_LINE + 1]; + std::size_t linenum; + istream_pos_type line_beg_pos; + istream_pos_type curr_pos; + std::size_t count; + std::size_t errors; + + scoped_ptr<auto_entry_finalizer_t> auto_entry_finalizer; + + instance_t(std::list<account_t *>& _account_stack, #if defined(TIMELOG_SUPPORT) - timelog, + time_log_t& _timelog, #endif - in, session, journal, master, - original_file); - parsing_instance.parse(); - - session.clean_accounts(); // remove calculated totals - - TRACE_STOP(parsing_total, 1); - - // These tracers were started in textual.cc - TRACE_FINISH(entry_text, 1); - TRACE_FINISH(entry_details, 1); - TRACE_FINISH(entry_xacts, 1); - TRACE_FINISH(entries, 1); - TRACE_FINISH(instance_parse, 1); // report per-instance timers - TRACE_FINISH(parsing_total, 1); - - if (parsing_instance.errors > 0) - throw static_cast<int>(parsing_instance.errors); - - return parsing_instance.count; -} + std::istream& _in, + session_t& _session, + journal_t& _journal, + account_t * _master = NULL, + const path * _original_file = NULL, + instance_t * _parent = NULL); + + ~instance_t(); + + void parse(); + std::streamsize read_line(char *& line); + bool peek_whitespace_line() { + return (in.good() && ! in.eof() && + (in.peek() == ' ' || in.peek() == '\t')); + } + void read_next_directive(); -namespace { -#if defined(STORE_XACT_EXPRS) - optional<expr_t> -#else - void +#if defined(TIMELOG_SUPPORT) + void clock_in_directive(char * line, bool capitalized); + void clock_out_directive(char * line, bool capitalized); #endif - parse_amount_expr(session_t& session, - std::istream& in, - amount_t& amount, - xact_t * xact, - uint_least8_t flags = 0) + + void default_commodity_directive(char * line); + void default_account_directive(char * line); + void price_conversion_directive(char * line); + void price_entry_directive(char * line); + void nomarket_directive(char * line); + void year_directive(char * line); + void option_directive(char * line); + void automated_entry_directive(char * line); + void period_entry_directive(char * line); + void entry_directive(char * line, std::streamsize len); + void include_directive(char * line); + void account_directive(char * line); + void end_directive(char * line); + void alias_directive(char * line); + void define_directive(char * line); + void general_directive(char * line); + + xact_t * parse_xact(char * line, + std::streamsize len, + account_t * account, + entry_t * entry); + + bool parse_xacts(account_t * account, + entry_base_t& entry, + const string& kind); + + entry_t * parse_entry(char * line, + std::streamsize len, + account_t * account); + + virtual expr_t::ptr_op_t lookup(const string& name); + }; + + void parse_amount_expr(session_t& session, + std::istream& in, + amount_t& amount, + xact_t * xact, + uint_least8_t flags = 0) { expr_t expr(in, flags | static_cast<uint_least8_t>(expr_t::PARSE_PARTIAL)); @@ -140,47 +153,42 @@ namespace { amount = result.as_amount(); DEBUG("textual.parse", "The transaction amount is " << amount); -#if defined(STORE_XACT_EXPRS) - return expr; -#endif } -#if defined(STORE_XACT_EXPRS) - return none; -#endif } } -textual_parser_t::instance_t::instance_t - (std::list<account_t *>& _account_stack, +instance_t::instance_t(std::list<account_t *>& _account_stack, #if defined(TIMELOG_SUPPORT) - time_log_t& _timelog, + time_log_t& _timelog, #endif - std::istream& _in, - session_t& _session, - journal_t& _journal, - account_t * _master, - const path * _original_file, - instance_t * _parent) + std::istream& _in, + session_t& _session, + journal_t& _journal, + account_t * _master, + const path * _original_file, + instance_t * _parent) : account_stack(_account_stack), #if defined(TIMELOG_SUPPORT) timelog(_timelog), #endif - parent(_parent), in(_in), session(_session), - journal(_journal), master(_master), - original_file(_original_file) + parent(_parent), in(_in), session(_session), journal(_journal), + master(_master), original_file(_original_file) { - TRACE_CTOR(textual_parser_t::instance_t, "..."); + TRACE_CTOR(instance_t, "..."); if (! master) master = journal.master; account_stack.push_front(master); - pathname = journal.sources.back(); + if (_original_file) + pathname = *_original_file; + else + pathname = "/dev/stdin"; } -textual_parser_t::instance_t::~instance_t() +instance_t::~instance_t() { - TRACE_DTOR(textual_parser_t::instance_t); + TRACE_DTOR(instance_t); account_stack.pop_front(); @@ -188,7 +196,7 @@ textual_parser_t::instance_t::~instance_t() journal.remove_entry_finalizer(auto_entry_finalizer.get()); } -void textual_parser_t::instance_t::parse() +void instance_t::parse() { INFO("Parsing file '" << pathname.string() << "'"); @@ -241,13 +249,15 @@ void textual_parser_t::instance_t::parse() TRACE_STOP(instance_parse, 1); } -std::streamsize textual_parser_t::instance_t::read_line(char *& line) +std::streamsize instance_t::read_line(char *& line) { assert(in.good()); assert(! in.eof()); // no one should call us in that case line_beg_pos = curr_pos; - + + check_for_signal(); + in.getline(linebuf, MAX_LINE); std::streamsize len = in.gcount(); @@ -270,7 +280,7 @@ std::streamsize textual_parser_t::instance_t::read_line(char *& line) return 0; } -void textual_parser_t::instance_t::read_next_directive() +void instance_t::read_next_directive() { char * line; std::streamsize len = read_line(line); @@ -369,8 +379,8 @@ void textual_parser_t::instance_t::read_next_directive() #if defined(TIMELOG_SUPPORT) -void textual_parser_t::instance_t::clock_in_directive(char * line, - bool capitalized) +void instance_t::clock_in_directive(char * line, + bool capitalized) { string date(line, 2, 19); @@ -381,8 +391,8 @@ void textual_parser_t::instance_t::clock_in_directive(char * line, account_stack.front()->find_account(p), n ? n : ""); } -void textual_parser_t::instance_t::clock_out_directive(char * line, - bool capitalized) +void instance_t::clock_out_directive(char * line, + bool capitalized) { string date(line, 2, 19); @@ -395,19 +405,19 @@ void textual_parser_t::instance_t::clock_out_directive(char * line, } #endif // TIMELOG_SUPPORT -void textual_parser_t::instance_t::default_commodity_directive(char * line) +void instance_t::default_commodity_directive(char * line) { amount_t amt(skip_ws(line + 1)); assert(amt.valid()); amount_t::current_pool->default_commodity = &amt.commodity(); } -void textual_parser_t::instance_t::default_account_directive(char * line) +void instance_t::default_account_directive(char * line) { journal.basket = account_stack.front()->find_account(skip_ws(line + 1)); } -void textual_parser_t::instance_t::price_conversion_directive(char * line) +void instance_t::price_conversion_directive(char * line) { if (char * p = std::strchr(line + 1, '=')) { *p++ = '\0'; @@ -440,7 +450,7 @@ namespace { } } -void textual_parser_t::instance_t::price_entry_directive(char * line) +void instance_t::price_entry_directive(char * line) { char * date_field_ptr = skip_ws(line + 1); char * time_field_ptr = next_element(date_field_ptr); @@ -470,7 +480,7 @@ void textual_parser_t::instance_t::price_entry_directive(char * line) commodity->add_price(datetime, price); } -void textual_parser_t::instance_t::nomarket_directive(char * line) +void instance_t::nomarket_directive(char * line) { char * p = skip_ws(line + 1); string symbol; @@ -481,12 +491,12 @@ void textual_parser_t::instance_t::nomarket_directive(char * line) commodity->add_flags(COMMODITY_NOMARKET); } -void textual_parser_t::instance_t::year_directive(char * line) +void instance_t::year_directive(char * line) { session.current_year = std::atoi(skip_ws(line + 1)); } -void textual_parser_t::instance_t::option_directive(char * line) +void instance_t::option_directive(char * line) { char * p = next_element(line); if (! p) { @@ -497,7 +507,7 @@ void textual_parser_t::instance_t::option_directive(char * line) process_option(line + 2, session, p, line); } -void textual_parser_t::instance_t::automated_entry_directive(char * line) +void instance_t::automated_entry_directive(char * line) { if (! auto_entry_finalizer.get()) { auto_entry_finalizer.reset(new auto_entry_finalizer_t(&journal)); @@ -525,7 +535,7 @@ void textual_parser_t::instance_t::automated_entry_directive(char * line) } } -void textual_parser_t::instance_t::period_entry_directive(char * line) +void instance_t::period_entry_directive(char * line) { std::auto_ptr<period_entry_t> pe(new period_entry_t(skip_ws(line + 1))); if (! pe->period) @@ -553,7 +563,7 @@ void textual_parser_t::instance_t::period_entry_directive(char * line) } } -void textual_parser_t::instance_t::entry_directive(char * line, std::streamsize len) +void instance_t::entry_directive(char * line, std::streamsize len) { TRACE_START(entries, 1, "Time spent handling entries:"); @@ -574,7 +584,7 @@ void textual_parser_t::instance_t::entry_directive(char * line, std::streamsize TRACE_STOP(entries, 1); } -void textual_parser_t::instance_t::include_directive(char * line) +void instance_t::include_directive(char * line) { path filename(next_element(line)); @@ -606,7 +616,7 @@ void textual_parser_t::instance_t::include_directive(char * line) count += instance.count; } -void textual_parser_t::instance_t::account_directive(char * line) +void instance_t::account_directive(char * line) { if (account_t * acct = account_stack.front()->find_account(next_element(line))) @@ -615,12 +625,12 @@ void textual_parser_t::instance_t::account_directive(char * line) assert(! "Failed to create account"); } -void textual_parser_t::instance_t::end_directive(char * line) +void instance_t::end_directive(char * line) { account_stack.pop_front(); } -void textual_parser_t::instance_t::alias_directive(char * line) +void instance_t::alias_directive(char * line) { char * b = skip_ws(line + 1); if (char * e = std::strchr(b, '=')) { @@ -641,13 +651,13 @@ void textual_parser_t::instance_t::alias_directive(char * line) } } -void textual_parser_t::instance_t::define_directive(char * line) +void instance_t::define_directive(char * line) { expr_t def(skip_ws(line + 1)); def.compile(session); // causes definitions to be established } -void textual_parser_t::instance_t::general_directive(char * line) +void instance_t::general_directive(char * line) { char * p = next_element(line); string word(line + 1); @@ -698,10 +708,10 @@ void textual_parser_t::instance_t::general_directive(char * line) } } -xact_t * textual_parser_t::instance_t::parse_xact(char * line, - std::streamsize len, - account_t * account, - entry_t * entry) +xact_t * instance_t::parse_xact(char * line, + std::streamsize len, + account_t * account, + entry_t * entry) { TRACE_START(xact_details, 1, "Time spent parsing transactions:"); @@ -992,9 +1002,9 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line, } } -bool textual_parser_t::instance_t::parse_xacts(account_t * account, - entry_base_t& entry, - const string& kind) +bool instance_t::parse_xacts(account_t * account, + entry_base_t& entry, + const string& kind) { TRACE_START(entry_xacts, 1, "Time spent parsing transactions:"); @@ -1016,9 +1026,9 @@ bool textual_parser_t::instance_t::parse_xacts(account_t * account, return added; } -entry_t * textual_parser_t::instance_t::parse_entry(char * line, - std::streamsize len, - account_t * account) +entry_t * instance_t::parse_entry(char * line, + std::streamsize len, + account_t * account) { TRACE_START(entry_text, 1, "Time spent parsing entry text:"); @@ -1120,9 +1130,46 @@ entry_t * textual_parser_t::instance_t::parse_entry(char * line, return curr.release(); } -expr_t::ptr_op_t textual_parser_t::instance_t::lookup(const string& name) +expr_t::ptr_op_t instance_t::lookup(const string& name) { return session.lookup(name); } +std::size_t journal_t::parse(std::istream& in, + session_t& session, + account_t * master, + const path * original_file) +{ + TRACE_START(parsing_total, 1, "Total time spent parsing text:"); + + std::list<account_t *> account_stack; +#if defined(TIMELOG_SUPPORT) + time_log_t timelog(*this); +#endif + + instance_t parsing_instance(account_stack, +#if defined(TIMELOG_SUPPORT) + timelog, +#endif + in, session, *this, master, original_file); + parsing_instance.parse(); + + session.clean_accounts(); // remove calculated totals + + TRACE_STOP(parsing_total, 1); + + // These tracers were started in textual.cc + TRACE_FINISH(entry_text, 1); + TRACE_FINISH(entry_details, 1); + TRACE_FINISH(entry_xacts, 1); + TRACE_FINISH(entries, 1); + TRACE_FINISH(instance_parse, 1); // report per-instance timers + TRACE_FINISH(parsing_total, 1); + + if (parsing_instance.errors > 0) + throw static_cast<int>(parsing_instance.errors); + + return parsing_instance.count; +} + } // namespace ledger |