summaryrefslogtreecommitdiff
path: root/textual.cc
diff options
context:
space:
mode:
Diffstat (limited to 'textual.cc')
-rw-r--r--textual.cc297
1 files changed, 102 insertions, 195 deletions
diff --git a/textual.cc b/textual.cc
index 37cf911e..ecd87f33 100644
--- a/textual.cc
+++ b/textual.cc
@@ -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