diff options
Diffstat (limited to 'textual.cc')
-rw-r--r-- | textual.cc | 297 |
1 files changed, 102 insertions, 195 deletions
@@ -1,27 +1,21 @@ -#if defined(__GNUG__) && __GNUG__ < 3 -#define _XOPEN_SOURCE -#endif - -#include "journal.h" +#ifdef USE_PCH +#include "pch.h" +#else #include "textual.h" -#include "datetime.h" -#include "valexpr.h" -#include "error.h" -#include "option.h" -#include "config.h" -#include "timing.h" +#include "session.h" #include "util.h" #include "acconf.h" +#if defined(__GNUG__) && __GNUG__ < 3 +#define _XOPEN_SOURCE +#endif + #include <fstream> #include <sstream> #include <cstring> #include <cctype> #include <cstdio> #include <cstdlib> - -#ifdef HAVE_REALPATH -extern "C" char *realpath(const char *, char resolved_path[]); #endif #define TIMELOG_SUPPORT 1 @@ -68,12 +62,12 @@ inline char * next_element(char * buf, bool variable = false) return NULL; } -static value_expr parse_amount_expr(std::istream& in, amount_t& amount, - transaction_t * xact, - unsigned short flags = 0) +static inline void +parse_amount_expr(std::istream& in, journal_t * journal, + transaction_t& xact, amount_t& amount, + unsigned short flags = 0) { - value_expr expr(parse_value_expr(in, NULL, flags | PARSE_VALEXPR_RELAXED | - PARSE_VALEXPR_PARTIAL)->acquire()); + xml::xpath_t xpath(in, flags | XPATH_PARSE_RELAXED | XPATH_PARSE_PARTIAL); DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << "Parsed an amount expression"); @@ -81,35 +75,33 @@ static value_expr parse_amount_expr(std::istream& in, amount_t& amount, #ifdef DEBUG_ENABLED DEBUG_IF("ledger.textual.parse") { if (_debug_stream) { - ledger::dump_value_expr(*_debug_stream, expr); + xpath.dump(*_debug_stream); *_debug_stream << std::endl; } } #endif - if (! compute_amount(expr, amount, xact)) - throw new parse_error("Amount expression failed to compute"); - - if (expr->kind == value_expr_t::CONSTANT) - expr = NULL; + amount = xpath.calc(static_cast<xml::transaction_node_t *>(xact.data)).to_amount(); DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " << - "The transaction amount is " << xact->amount); - return expr; + "The transaction amount is " << amount); } -transaction_t * parse_transaction(char * line, account_t * account, - entry_t * entry = NULL) +transaction_t * parse_transaction(char * line, + journal_t * journal, + account_t * account, + entry_t * entry = NULL) { - std::istringstream in(line); + // The account will be determined later... + std::auto_ptr<transaction_t> xact(new transaction_t(NULL)); + std::istringstream in(line); std::string err_desc; try { - // The account will be determined later... - std::auto_ptr<transaction_t> xact(new transaction_t(NULL)); - if (entry) - xact->entry = entry; + xact->entry = entry; // this might be NULL + if (xact->entry) + xact->data = xml::wrap_node(journal->document, xact.get(), xact->entry->data); // Parse the state flag @@ -182,14 +174,13 @@ transaction_t * parse_transaction(char * line, account_t * account, goto parse_note; try { - unsigned long beg = (long)in.tellg(); - - xact->amount_expr = - parse_amount_expr(in, xact->amount, xact.get(), - PARSE_VALEXPR_NO_REDUCE); + // jww (2006-09-15): Make sure it doesn't gobble up the upcoming @ symbol + unsigned long beg = (long)in.tellg(); + parse_amount_expr(in, journal, *xact, xact->amount, + XPATH_PARSE_NO_REDUCE); unsigned long end = (long)in.tellg(); - xact->amount_expr.expr = std::string(line, beg, end - beg); + xact->amount_expr = std::string(line, beg, end - beg); } catch (error * err) { err_desc = "While parsing transaction amount:"; @@ -219,10 +210,8 @@ transaction_t * parse_transaction(char * line, account_t * account, try { unsigned long beg = (long)in.tellg(); - if (parse_amount_expr(in, *xact->cost, xact.get(), - PARSE_VALEXPR_NO_MIGRATE)) - throw new parse_error - ("A transaction's cost must evalute to a constant value"); + parse_amount_expr(in, journal, *xact, *xact->cost, + XPATH_PARSE_NO_MIGRATE); unsigned long end = (long)in.tellg(); @@ -312,6 +301,7 @@ transaction_t * parse_transaction(char * line, account_t * account, } bool parse_transactions(std::istream& in, + journal_t * journal, account_t * account, entry_base_t& entry, const std::string& kind, @@ -332,7 +322,7 @@ bool parse_transactions(std::istream& in, if (! *p || *p == '\r') break; } - if (transaction_t * xact = parse_transaction(line, account)) { + if (transaction_t * xact = parse_transaction(line, journal, account)) { entry.add_transaction(xact); added = true; } @@ -348,22 +338,25 @@ namespace { TIMER_DEF(entry_date, "parsing entry date"); } -entry_t * parse_entry(std::istream& in, char * line, account_t * master, - textual_parser_t& parser, unsigned long beg_pos) +entry_t * parse_entry(std::istream& in, char * line, journal_t * journal, + account_t * master, textual_parser_t& parser, + unsigned long beg_pos) { std::auto_ptr<entry_t> curr(new entry_t); + std::istringstream line_in(line); + char c; + // Parse the date TIMER_START(entry_date); - char * next = next_element(line); + curr->_date.parse(line_in); - if (char * p = std::strchr(line, '=')) { - *p++ = '\0'; - curr->_date_eff = p; + if (peek_next_nonws(line_in) == '=') { + line_in.get(c); + curr->_date_eff.parse(line_in); } - curr->_date = line; TIMER_STOP(entry_date); @@ -372,35 +365,43 @@ entry_t * parse_entry(std::istream& in, char * line, account_t * master, TIMER_START(entry_details); transaction_t::state_t state = transaction_t::UNCLEARED; - if (next) { - switch (*next) { - case '*': - state = transaction_t::CLEARED; - next = skip_ws(++next); - break; - case '!': - state = transaction_t::PENDING; - next = skip_ws(++next); - break; - } + switch (peek_next_nonws(line_in)) { + case '*': + state = transaction_t::CLEARED; + line_in.get(c); + break; + case '!': + state = transaction_t::PENDING; + line_in.get(c); + break; } // Parse the optional code: (TEXT) - if (next && *next == '(') { - if (char * p = std::strchr(next++, ')')) { - *p++ = '\0'; - curr->code = next; - next = skip_ws(p); - } + char buf[256]; + + if (peek_next_nonws(line_in) == '(') { + line_in.get(c); + READ_INTO(line_in, buf, 255, c, c != ')'); + curr->code = buf; + if (c == ')') + line_in.get(c); + peek_next_nonws(line_in); } - // Parse the description text + // Parse the payee/description text - curr->payee = next ? next : "<Unspecified payee>"; + std::memset(buf, 0, 255); + line_in.read(buf, 255); + curr->payee = buf[0] != '\0' ? buf : "<Unspecified payee>"; TIMER_STOP(entry_details); + // Create a report item for this entry, so the transaction below may + // refer to it + + curr->data = xml::wrap_node(journal->document, curr.get(), journal->data); + // Parse all of the transactions associated with this entry TIMER_START(entry_xacts); @@ -422,7 +423,8 @@ entry_t * parse_entry(std::istream& in, char * line, account_t * master, break; } - if (transaction_t * xact = parse_transaction(line, master, curr.get())) { + if (transaction_t * xact = + parse_transaction(line, journal, master, curr.get())) { if (state != transaction_t::UNCLEARED && xact->state == transaction_t::UNCLEARED) xact->state = state; @@ -559,29 +561,29 @@ static void clock_out_from_timelog(const datetime_t& when, } unsigned int textual_parser_t::parse(std::istream& in, - config_t& config, journal_t * journal, account_t * master, const std::string * original_file) { - static bool added_auto_entry_hook = false; - static char line[MAX_LINE + 1]; - char c; - unsigned int count = 0; - unsigned int errors = 0; + static bool added_auto_entry_hook = false; + static char line[MAX_LINE + 1]; + char c; + unsigned int count = 0; + unsigned int errors = 0; TIMER_START(parsing_total); std::list<account_t *> account_stack; + auto_entry_finalizer_t auto_entry_finalizer(journal); - if (! master) + if (! master && journal) master = journal->master; account_stack.push_front(master); - path = journal->sources.back(); - src_idx = journal->sources.size() - 1; + path = journal ? journal->sources.back() : *original_file; + src_idx = journal ? journal->sources.size() - 1 : 0; linenum = 1; unsigned long beg_pos = in.tellg(); @@ -707,7 +709,7 @@ unsigned int textual_parser_t::parse(std::istream& in, break; } - case 'Y': // set the current year + case 'Y': // set current year date_t::current_year = std::atoi(skip_ws(line + 1)) - 1900; break; @@ -715,19 +717,11 @@ unsigned int textual_parser_t::parse(std::istream& in, case 'h': case 'b': #endif - case ';': // a comment line + case ';': // comment break; - case '-': { // option setting - char * p = next_element(line); - if (! p) { - p = std::strchr(line, '='); - if (p) - *p++ = '\0'; - } - process_option(config_options, line + 2, p); - break; - } + case '-': // option setting + throw new parse_error("Option settings are not allowed in journal files"); case '=': { // automated entry if (! added_auto_entry_hook) { @@ -736,7 +730,7 @@ unsigned int textual_parser_t::parse(std::istream& in, } auto_entry_t * ae = new auto_entry_t(skip_ws(line + 1)); - if (parse_transactions(in, account_stack.front(), *ae, + if (parse_transactions(in, journal, account_stack.front(), *ae, "automated", end_pos)) { journal->auto_entries.push_back(ae); ae->src_idx = src_idx; @@ -753,7 +747,7 @@ unsigned int textual_parser_t::parse(std::istream& in, if (! pe->period) throw new parse_error(std::string("Parsing time period '") + line + "'"); - if (parse_transactions(in, account_stack.front(), *pe, + if (parse_transactions(in, journal, account_stack.front(), *pe, "period", end_pos)) { if (pe->finalize()) { extend_entry_base(journal, *pe, true); @@ -796,8 +790,8 @@ unsigned int textual_parser_t::parse(std::istream& in, include_stack.push_back(std::pair<std::string, int> (journal->sources.back(), linenum - 1)); - count += parse_journal_file(path, config, journal, - account_stack.front()); + count += journal->session->read_journal(path, journal, + account_stack.front()); include_stack.pop_back(); } else if (word == "account") { @@ -827,10 +821,14 @@ unsigned int textual_parser_t::parse(std::istream& in, assert(result.second); } } - else if (word == "def") { - if (! global_scope.get()) - init_value_expr(); - parse_value_definition(p); + else if (word == "def" || word == "eval") { + // jww (2006-09-13): Read the string after and evaluate it. + // But also keep a list of these value expressions, and a + // way to know where they fall in the transaction sequence. + // This will be necessary so that binary file reading can + // re-evaluate them at the appopriate time. + + // compile(&journal->defs); } break; } @@ -838,8 +836,9 @@ unsigned int textual_parser_t::parse(std::istream& in, default: { unsigned int first_line = linenum; unsigned long pos = end_pos; - if (entry_t * entry = - parse_entry(in, line, account_stack.front(), *this, pos)) { + if (entry_t * entry = parse_entry(in, line, journal, + account_stack.front(), + *this, pos)) { if (journal->add_entry(entry)) { entry->src_idx = src_idx; entry->beg_pos = beg_pos; @@ -899,96 +898,4 @@ unsigned int textual_parser_t::parse(std::istream& in, return count; } -void write_textual_journal(journal_t& journal, std::string path, - item_handler<transaction_t>& formatter, - const std::string& write_hdr_format, - std::ostream& out) -{ - unsigned long index = 0; - std::string found; - - if (path.empty()) { - if (! journal.sources.empty()) - found = *journal.sources.begin(); - } else { -#ifdef HAVE_REALPATH - char buf1[PATH_MAX]; - char buf2[PATH_MAX]; - - ::realpath(path.c_str(), buf1); - - for (strings_list::iterator i = journal.sources.begin(); - i != journal.sources.end(); - i++) { - ::realpath((*i).c_str(), buf2); - if (std::strcmp(buf1, buf2) == 0) { - found = *i; - break; - } - index++; - } -#else - for (strings_list::iterator i = journal.sources.begin(); - i != journal.sources.end(); - i++) { - if (path == *i) { - found = *i; - break; - } - index++; - } -#endif - } - - if (found.empty()) - throw new error(std::string("Journal does not refer to file '") + - path + "'"); - - entries_list::iterator el = journal.entries.begin(); - auto_entries_list::iterator al = journal.auto_entries.begin(); - period_entries_list::iterator pl = journal.period_entries.begin(); - - unsigned long pos = 0; - - format_t hdr_fmt(write_hdr_format); - std::ifstream in(found.c_str()); - - while (! in.eof()) { - entry_base_t * base = NULL; - if (el != journal.entries.end() && pos == (*el)->beg_pos) { - hdr_fmt.format(out, details_t(**el)); - base = *el++; - } - else if (al != journal.auto_entries.end() && pos == (*al)->beg_pos) { - out << "= " << (*al)->predicate_string << '\n'; - base = *al++; - } - else if (pl != journal.period_entries.end() && pos == (*pl)->beg_pos) { - out << "~ " << (*pl)->period_string << '\n'; - base = *pl++; - } - - char c; - if (base) { - for (transactions_list::iterator x = base->transactions.begin(); - x != base->transactions.end(); - x++) - if (! ((*x)->flags & TRANSACTION_AUTO)) { - transaction_xdata(**x).dflags |= TRANSACTION_TO_DISPLAY; - formatter(**x); - } - formatter.flush(); - - while (pos < base->end_pos) { - in.get(c); - pos = in.tellg(); // pos++; - } - } else { - in.get(c); - pos = in.tellg(); // pos++; - out.put(c); - } - } -} - } // namespace ledger |