diff options
author | John Wiegley <johnw@newartisans.com> | 2004-08-03 04:34:50 +0000 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2008-04-13 02:40:47 -0400 |
commit | a32173ace60df5a1e9414f5e95b556c436f62718 (patch) | |
tree | fff5b881c657fddb8543b0f787bfa75cf009a0b7 /textual.cc | |
parent | cc98b59d1e99238270eb307b117da8b0b35e6f27 (diff) | |
download | fork-ledger-a32173ace60df5a1e9414f5e95b556c436f62718.tar.gz fork-ledger-a32173ace60df5a1e9414f5e95b556c436f62718.tar.bz2 fork-ledger-a32173ace60df5a1e9414f5e95b556c436f62718.zip |
changes
Diffstat (limited to 'textual.cc')
-rw-r--r-- | textual.cc | 550 |
1 files changed, 0 insertions, 550 deletions
diff --git a/textual.cc b/textual.cc deleted file mode 100644 index aea860e2..00000000 --- a/textual.cc +++ /dev/null @@ -1,550 +0,0 @@ -#include "journal.h" -#include "textual.h" -#include "datetime.h" -#include "autoxact.h" -#include "valexpr.h" -#include "error.h" -#include "option.h" -#include "config.h" -#include "timing.h" -#include "util.h" -#ifdef USE_BOOST_PYTHON -#include "python.h" -#endif - -#include <fstream> -#include <sstream> -#include <cstring> -#include <ctime> -#include <cctype> - -#define TIMELOG_SUPPORT 1 - -namespace ledger { - -#define MAX_LINE 1024 - -static std::string path; -static unsigned int linenum; - -#ifdef TIMELOG_SUPPORT -static std::time_t time_in; -static account_t * last_account; -static std::string last_desc; -#endif - -inline char * next_element(char * buf, bool variable = false) -{ - for (char * p = buf; *p; p++) { - if (! (*p == ' ' || *p == '\t')) - continue; - - if (! variable) { - *p = '\0'; - return skip_ws(p + 1); - } - else if (*p == '\t') { - *p = '\0'; - return skip_ws(p + 1); - } - else if (*(p + 1) == ' ') { - *p = '\0'; - return skip_ws(p + 2); - } - } - return NULL; -} - -transaction_t * parse_transaction_text(char * line, account_t * account, - entry_t * entry) -{ - // The account will be determined later... - - std::auto_ptr<transaction_t> xact(new transaction_t(NULL)); - - // The call to `next_element' will skip past the account name, - // and return a pointer to the beginning of the amount. Once - // we know where the amount is, we can strip off any - // transaction note, and parse it. - - char * p = skip_ws(line); - if (char * cost_str = next_element(p, true)) { - cost_str = skip_ws(cost_str); - bool has_amount = *cost_str; - - if (char * note_str = std::strchr(cost_str, ';')) { - if (cost_str == note_str) - has_amount = false; - *note_str++ = '\0'; - xact->note = skip_ws(note_str); - } - - if (has_amount) { - bool per_unit = true; - char * price_str = std::strchr(cost_str, '@'); - if (price_str) { - if (price_str == cost_str) - throw parse_error(path, linenum, "Cost specified without amount"); - - *price_str++ = '\0'; - if (*price_str == '@') { - per_unit = false; - price_str++; - } - xact->cost = new amount_t; - xact->cost->parse(price_str); - } - - xact->amount.parse(cost_str); - - if (price_str && per_unit) - *xact->cost *= xact->amount; - } - } - - if (*p == '[' || *p == '(') { - xact->flags |= TRANSACTION_VIRTUAL; - if (*p == '[') - xact->flags |= TRANSACTION_BALANCE; - p++; - - char * e = p + (std::strlen(p) - 1); - assert(*e == ')' || *e == ']'); - *e = '\0'; - } - - xact->account = account->find_account(p); - - return xact.release(); -} - -transaction_t * parse_transaction(std::istream& in, account_t * account, - entry_t * entry) -{ - static char line[MAX_LINE + 1]; - in.getline(line, MAX_LINE); - linenum++; - - // Skip a possible blank line - if (*skip_ws(line) == '\0') - return NULL; - - return parse_transaction_text(line, account, entry); -} - -void parse_automated_transactions(std::istream& in, - account_t * account, - automated_transactions_t& auto_xacts) -{ - static char line[MAX_LINE + 1]; - in.getline(line, MAX_LINE); - linenum++; - - transactions_deque xacts; - - while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) - if (transaction_t * xact = parse_transaction(in, account, NULL)) { - if (! xact->amount) - throw parse_error(path, linenum, - "All automated transactions must have values"); - else - xacts.push_back(xact); - } - - if (! xacts.empty()) - auto_xacts. - add_automated_transaction(new automated_transaction_t(line + 1, xacts)); -} - -namespace { - TIMER_DEF(entry_finish, "finalizing entry"); - TIMER_DEF(entry_xacts, "parsing transactions"); - TIMER_DEF(entry_details, "parsing entry details"); - TIMER_DEF(entry_date, "parsing entry date"); -} - -entry_t * parse_entry(std::istream& in, account_t * master, - textual_parser_t& parser) -{ - std::auto_ptr<entry_t> curr(new entry_t); - - static char line[MAX_LINE + 1]; - in.getline(line, MAX_LINE); - linenum++; - - // Parse the date - - TIMER_START(entry_date); - - char * next = next_element(line); - - if (! quick_parse_date(line, &curr->date)) - throw parse_error(path, linenum, "Failed to parse date"); - - TIMER_STOP(entry_date); - - // Parse the optional cleared flag: * - - TIMER_START(entry_details); - - if (*next == '*') { - curr->state = entry_t::CLEARED; - next = skip_ws(++next); - } - - // Parse the optional code: (TEXT) - - if (*next == '(') { - if (char * p = std::strchr(next++, ')')) { - *p++ = '\0'; - curr->code = next; - next = skip_ws(p); - } - } - - // Parse the description text - - curr->payee = next; - - TIMER_STOP(entry_details); - - // Parse all of the transactions associated with this entry - - TIMER_START(entry_xacts); - - while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) - if (transaction_t * xact = parse_transaction(in, master, curr.get())) - curr->add_transaction(xact); - - TIMER_STOP(entry_xacts); - - return curr.release(); -} - -template <typename T> -struct push_var { - T& var; - T prev; - push_var(T& _var) : var(_var), prev(var) {} - ~push_var() { var = prev; } -}; - -unsigned int textual_parser_t::parse(std::istream& in, - journal_t * journal, - account_t * master, - const std::string * original_file) -{ - static bool added_autoxact_hook = false; - static char line[MAX_LINE + 1]; - char c; - unsigned int count = 0; - unsigned int errors = 0; - commodity_t * time_commodity = NULL; - - std::deque<account_t *> account_stack; - autoxact_finalizer_t autoxact_finalizer; - - if (! master) - master = journal->master; - - account_stack.push_front(master); - - path = journal->sources.back(); - linenum = 1; - - while (! in.eof()) { - try { - switch (in.peek()) { - case -1: // end of file - goto done; - - case ' ': - case '\t': - if (peek_next_nonws(in) != '\n') { - in.getline(line, MAX_LINE); - linenum++; - throw parse_error(path, linenum - 1, "Line begins with whitespace"); - } - // fall through... - - case '\n': - linenum++; - case '\r': // skip blank lines - in.get(c); - break; - -#ifdef TIMELOG_SUPPORT - case 'i': - case 'I': { - std::string date, time; - - in >> c; - in >> date; - in >> time; - date += " "; - date += time; - - in.getline(line, MAX_LINE); - linenum++; - - char * p = skip_ws(line); - char * n = next_element(p, true); - last_desc = n ? n : ""; - - struct std::tm when; - if (strptime(date.c_str(), "%Y/%m/%d %H:%M:%S", &when)) { - time_in = std::mktime(&when); - last_account = account_stack.front()->find_account(p); - } else { - last_account = NULL; - throw parse_error(path, linenum, "Cannot parse timelog entry date"); - } - break; - } - - case 'o': - case 'O': - if (last_account) { - std::string date, time; - - in >> c; - in >> date; - in >> time; - date += " "; - date += time; - - in.getline(line, MAX_LINE); - linenum++; - - struct std::tm when; - if (strptime(date.c_str(), "%Y/%m/%d %H:%M:%S", &when)) { - std::auto_ptr<entry_t> curr(new entry_t); - curr->date = std::mktime(&when); - curr->state = entry_t::CLEARED; - curr->code = ""; - curr->payee = last_desc; - - double diff = std::difftime(curr->date, time_in) / 60.0 / 60.0; - char buf[32]; - std::sprintf(buf, "%fh", diff); - amount_t amt; - amt.parse(buf); - time_commodity = &amt.commodity(); - - transaction_t * xact - = new transaction_t(last_account, amt, TRANSACTION_VIRTUAL); - curr->add_transaction(xact); - - if (! journal->add_entry(curr.release())) - throw parse_error(path, linenum, - "Failed to record 'out' timelog entry"); - - count++; - } else { - throw parse_error(path, linenum, "Cannot parse timelog entry date"); - } - - last_account = NULL; - } else { - in.getline(line, MAX_LINE); - linenum++; - } - break; -#endif // TIMELOG_SUPPORT - - case 'P': { // a pricing entry - in >> c; - - std::time_t date; - std::string symbol; - - in >> line; // the date - if (! quick_parse_date(line, &date)) - throw parse_error(path, linenum, "Failed to parse date"); - - int hour, min, sec; - - in >> hour; // the time - in >> c; - in >> min; - in >> c; - in >> sec; - - date = std::time_t(((unsigned long) date) + - hour * 3600 + min * 60 + sec); - - parse_commodity(in, symbol); - - in.getline(line, MAX_LINE); - linenum++; - - amount_t price; - price.parse(skip_ws(line)); - - commodity_t * commodity = commodity_t::find_commodity(symbol, true); - commodity->add_price(date, price); - break; - } - - case 'N': { // don't download prices - std::string symbol; - - in >> c; - parse_commodity(in, symbol); - - commodity_t * commodity = commodity_t::find_commodity(symbol, true); - commodity->flags |= COMMODITY_STYLE_NOMARKET; - break; - } - - case 'C': { // a flat conversion - in >> c; - - std::string symbol; - amount_t price; - - parse_commodity(in, symbol); - - in.getline(line, MAX_LINE); - linenum++; - price.parse(skip_ws(line)); - - commodity_t * commodity = commodity_t::find_commodity(symbol, true); - commodity->conversion = price; - break; - } - - case 'Y': // set the current year - in >> c; - in >> now_year; - now_year -= 1900; - break; - -#ifdef TIMELOG_SUPPORT - case 'h': - case 'b': -#endif - case ';': // a comment line - in.getline(line, MAX_LINE); - linenum++; - break; - - case '-': { // option setting - std::string opt; - in >> c >> c; - in >> opt; - in.getline(line, MAX_LINE); - linenum++; - char * p = skip_ws(line); - process_option(config_options, opt, *p == '\n' ? NULL : p); - break; - } - - case '=': // automated transactions - if (! added_autoxact_hook) { - journal->add_entry_finalizer(&autoxact_finalizer); - added_autoxact_hook = true; - } - parse_automated_transactions(in, account_stack.front(), - autoxact_finalizer.auto_xacts); - break; - - case '!': { // directive - std::string word; - in.get(c); - in >> word; - if (word == "include") { - in.getline(line, MAX_LINE); - linenum++; - - push_var<unsigned int> save_linenum(linenum); - push_var<std::string> save_path(path); - - count += parse_journal_file(skip_ws(line), journal, - account_stack.front()); - } - else if (word == "account") { - in.getline(line, MAX_LINE); - linenum++; - - account_t * acct; - acct = account_stack.front()->find_account(skip_ws(line)); - account_stack.push_front(acct); - } - else if (word == "end") { - account_stack.pop_front(); - } -#ifdef USE_BOOST_PYTHON - else if (word == "python") { - in.getline(line, MAX_LINE); - python_eval(in); - } -#endif - break; - } - - default: { - unsigned int first_line = linenum; - if (entry_t * entry = parse_entry(in, account_stack.front(), *this)) { - if (journal->add_entry(entry)) - count++; - else - throw parse_error(path, first_line, "Entry does not balance"); - } else { - throw parse_error(path, first_line, "Failed to parse entry"); - } - break; - } - } - } - catch (const parse_error& err) { - std::cerr << "Error: " << err.what() << std::endl; - errors++; - } - catch (const amount_error& err) { - std::cerr << "Error: " << path << ", line " << (linenum - 1) << ": " - << err.what() << std::endl;; - errors++; - } - catch (const error& err) { - std::cerr << "Error: " << path << ", line " << (linenum - 1) << ": " - << err.what() << std::endl;; - errors++; - } - } - - done: - if (added_autoxact_hook) - journal->remove_entry_finalizer(&autoxact_finalizer); - - if (time_commodity) { - time_commodity->precision = 2; - time_commodity->flags |= COMMODITY_STYLE_NOMARKET; - } - - if (errors > 0) - throw error(std::string("Errors parsing file '") + path + "'"); - - return count; -} - -} // namespace ledger - -#ifdef USE_BOOST_PYTHON - -#include <boost/python.hpp> - -using namespace boost::python; -using namespace ledger; - -BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(textual_parse_overloads, - textual_parser_t::parse, 2, 4) - -void export_textual() { - class_< textual_parser_t, bases<parser_t> > ("TextualParser") - .def("test", &textual_parser_t::test) - .def("parse", &textual_parser_t::parse, textual_parse_overloads()) - ; -} - -#endif // USE_BOOST_PYTHON |