diff options
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | src/builder.h | 61 | ||||
-rw-r--r-- | src/compile.cc | 949 | ||||
-rw-r--r-- | src/compile.h | 342 | ||||
-rw-r--r-- | src/main.cc | 8 |
5 files changed, 59 insertions, 1306 deletions
diff --git a/Makefile.am b/Makefile.am index 21db24a1..0c6b833d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -44,8 +44,9 @@ libledger_la_SOURCES = \ src/value.cc \ src/document.cc \ src/node.cc \ + src/compile.cc \ + src/jbuilder.cc \ src/xpath.cc \ - src/builder.cc \ src/journal.cc \ src/textual.cc \ src/binary.cc \ @@ -100,7 +101,9 @@ pkginclude_HEADERS = \ src/balpair.h \ src/binary.h \ src/builder.h \ + src/jbuilder.h \ src/commodity.h \ + src/compile.h \ src/context.h \ src/document.h \ src/fdstream.hpp \ diff --git a/src/builder.h b/src/builder.h index b1fae018..a193879e 100644 --- a/src/builder.h +++ b/src/builder.h @@ -1,3 +1,34 @@ +/* + * Copyright (c) 2003-2007, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + #ifndef _BUILDER_H #define _BUILDER_H @@ -76,6 +107,7 @@ public: */ class document_builder_t : public builder_t { +protected: typedef std::list<std::pair<node_t::nameid_t, string> > attrs_list; document_t& document_; @@ -145,35 +177,6 @@ public: }; /** - * @class journal_builder_t - * - * @brief This custom builder creates an XML-mirrored Ledger journal. - * - * Rather than simply creating a node_t hierarchy, as xml_builder_t - * does, this code creates the associated journal elements referred to - * by those nodes, and then refers to those elements via minimalist - * "shadow nodes". - * - * Thus, after building a <transaction> element, the element itself - * will have no children, but instead will point to a transaction_t - * object. If later an XPath expression desires to traverse the - * <transaction> element, all of the appropriate child nodes will be - * constructed on the fly, as if they'd been created in the first - * place by a regular xml_builder_t. - */ -class journal_builder_t : public document_builder_t -{ -public: - virtual void set_start_position(std::istream& in) { - set_position(position_t(in.tellg(), 1)); - } - - virtual void set_position(const position_t& position) { - current_position = position; - } -}; - -/** * @class xml_writer_t * * @brief Create textual XML on the given output stream. diff --git a/src/compile.cc b/src/compile.cc index f7e8e0b2..fa17cf2a 100644 --- a/src/compile.cc +++ b/src/compile.cc @@ -29,953 +29,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "textual.h" -#include "session.h" +#include "compile.h" namespace ledger { +namespace xml { -#define MAX_LINE 1024 - -static path pathname; -static unsigned int linenum; -static unsigned int src_idx; -static accounts_map account_aliases; - -typedef std::list<std::pair<path, int> > include_stack_t; - -static include_stack_t include_stack; - -#define TIMELOG_SUPPORT 1 -#ifdef TIMELOG_SUPPORT - -struct time_entry_t { - moment_t checkin; - account_t * account; - string desc; -}; - -std::list<time_entry_t> time_entries; - -#endif // TIMELOG_SUPPORT - -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; -} - -static inline void -parse_amount_expr(std::istream& in, journal_t *, - transaction_t& xact, amount_t& amount, - unsigned short flags = 0) -{ - xml::xpath_t xpath(in, flags | XPATH_PARSE_RELAXED | XPATH_PARSE_PARTIAL); - - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed an amount expression"); - -#if 0 - IF_DEBUG("ledger.textual.parse") { - if (_debug_stream) { - xpath.dump(*_debug_stream); - *_debug_stream << std::endl; - } - } -#endif - - amount = xpath.calc(xact.data).as_amount(); - - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "The transaction amount is " << amount); -} - -transaction_t * parse_transaction(char * line, - journal_t * journal, - account_t * account, - entry_t * entry = NULL) -{ - // The account will be determined later... - std::auto_ptr<transaction_t> xact(new transaction_t(NULL)); - - // First cut up the input line into its various parts. - - char * state = NULL; - char * account_path = NULL; - char * amount = NULL; - char * note = NULL; - - char * p = line; - - if (*p == '*' || *p == '!') - state = p++; - - account_path = skip_ws(p); - - amount = next_element(account_path, true); - if (amount) { - char * p = amount; - while (*p && *p != ';') - p++; - - if (*p == ';') { - *p++ = '\0'; - note = skip_ws(p); - } - - p = amount + (std::strlen(amount) - 1); - while (p > amount && std::isspace(*p)) - p--; - - if (std::isspace(*(p + 1))) - *++p = '\0'; - } - - string err_desc; -#if 0 - try { -#endif - - xact->entry = entry; // this might be NULL - - // Parse the state flag - - if (state) - switch (*state) { - case '*': - xact->state = transaction_t::CLEARED; - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed the CLEARED flag"); - break; - case '!': - xact->state = transaction_t::PENDING; - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed the PENDING flag"); - break; - } - - // Parse the account name - - char * b = &account_path[0]; - char * e = &account_path[std::strlen(account_path) - 1]; - if ((*b == '[' && *e == ']') || - (*b == '(' && *e == ')')) { - xact->add_flags(TRANSACTION_VIRTUAL); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed a virtual account name"); - if (*b == '[') { - xact->add_flags(TRANSACTION_BALANCE); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed a balanced virtual account name"); - } - *account_path++ = '\0'; - *e = '\0'; - } - - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed account name " << account_path); - if (account_aliases.size() > 0) { - accounts_map::const_iterator i = account_aliases.find(account_path); - if (i != account_aliases.end()) - xact->account = (*i).second; - } - if (! xact->account) - xact->account = account->find_account(account_path); - - // Parse the optional amount - - if (amount && *amount) { - std::istringstream in(amount); - - PUSH_CONTEXT(); - - // jww (2006-09-15): Make sure it doesn't gobble up the upcoming @ symbol - - unsigned long beg = (long)in.tellg(); - - amount_t temp; - temp.parse(in, AMOUNT_PARSE_NO_REDUCE); - xact->amount = temp; - - char c; - if (! in.eof() && (c = peek_next_nonws(in)) != '@' && - c != ';' && ! in.eof()) { - in.seekg(beg, std::ios::beg); - - if (xact->entry) { - // Create a report item for this entry, so the transaction - // below may refer to it - - if (! xact->entry->data) - xact->entry->data = xml::wrap_node(journal->document, xact->entry, - journal->document->top); - - xact->data = xml::wrap_node(journal->document, xact.get(), - xact->entry->data); - } - - assert(xact->amount); - parse_amount_expr(in, journal, *xact, *xact->amount, - XPATH_PARSE_NO_REDUCE); - - if (xact->entry) { - checked_delete(xact->data); - xact->data = NULL; - } - - unsigned long end = (long)in.tellg(); - - xact->amount_expr = string(line, beg, end - beg); - } - - // jww (2007-04-30): This should be a string context, or perhaps a - // file context - POP_CONTEXT(context("While parsing transaction amount")); - - // Parse the optional cost (@ PER-UNIT-COST, @@ TOTAL-COST) - - if (in.good() && ! in.eof()) { - char c = peek_next_nonws(in); - if (c == '@') { - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Found a price indicator"); - bool per_unit = true; - in.get(c); - if (in.peek() == '@') { - in.get(c); - per_unit = false; - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "And it's for a total price"); - } - - if (in.good() && ! in.eof()) { - PUSH_CONTEXT(); - - unsigned long beg = (long)in.tellg(); - - amount_t temp; - temp.parse(in); - xact->cost = temp; - - unsigned long end = (long)in.tellg(); - - if (per_unit) - xact->cost_expr = (string("@") + - string(amount, beg, end - beg)); - else - xact->cost_expr = (string("@@") + - string(amount, beg, end - beg)); - - POP_CONTEXT(context("While parsing transaction cost")); - - if (xact->cost->sign() < 0) - throw_(parse_error, "A transaction's cost may not be negative"); - - assert(xact->amount); - - amount_t per_unit_cost(*xact->cost); - if (per_unit) - *xact->cost *= xact->amount->number(); - else - per_unit_cost /= xact->amount->number(); - - if (xact->amount->commodity() && - ! xact->amount->commodity().annotated) - xact->amount->annotate_commodity(annotation_t(per_unit_cost, - xact->entry->actual_date(), - xact->entry->code)); - - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Total cost is " << *xact->cost); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Per-unit cost is " << per_unit_cost); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Annotated amount is " << *xact->amount); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Bare amount is " << xact->amount->number()); - } - } - } - - if (xact->amount) { - xact->amount->in_place_reduce(); - - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Reduced amount is " << *xact->amount); - } - } - - // Parse the optional note - - if (note) { - xact->note = note; - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed a note '" << *xact->note << "'"); - - if (char * b = std::strchr(xact->note->c_str(), '[')) - if (char * e = std::strchr(xact->note->c_str(), ']')) { - char buf[256]; - std::strncpy(buf, b + 1, e - b - 1); - buf[e - b - 1] = '\0'; - - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed a transaction date " << buf); - - if (char * p = std::strchr(buf, '=')) { - *p++ = '\0'; - xact->_date_eff = parse_datetime(p); - } - if (buf[0]) - xact->_date = parse_datetime(buf); - } - } - - return xact.release(); - -#if 0 - } - catch (error * err) { - err->context.push_back - (new line_context(line, -1, ! err_desc.empty() ? - err_desc : "While parsing transaction:")); - throw err; - } -#endif -} - -bool parse_transactions(std::istream& in, - journal_t * journal, - account_t * account, - entry_base_t& entry, - const string& /* kind */, - unsigned long beg_pos) -{ - static char line[MAX_LINE + 1]; - bool added = false; - - while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) { - in.getline(line, MAX_LINE); - if (in.eof()) - break; - - beg_pos += std::strlen(line) + 1; - linenum++; - - char * p = skip_ws(line); - if (! *p || *p == '\r' || *p == '\n') - break; - - if (transaction_t * xact = parse_transaction(p, journal, account)) { - entry.add_transaction(xact); - added = true; - } - } - - return added; -} - -entry_t * parse_entry(std::istream& in, char * line, journal_t * journal, - account_t * master, textual_parser_t& /* parser */, - unsigned long beg_pos) -{ - TRACE_START(entry_text, 1, "Time spent preparing entry text:"); - - std::auto_ptr<entry_t> curr(new entry_t); - - // First cut up the input line into its various parts. - - char * date = NULL; - char * date_eff = NULL; - char * statep = NULL; - char * code = NULL; - char * payee = NULL; - - date = line; - - char * p = line; - - while (*p && (std::isdigit(*p) || *p == '/' || *p == '.' || *p == '-')) - p++; - assert(*p); - - if (*p == '=') { - *p++ = '\0'; - date_eff = p; - - while (*p && (std::isdigit(*p) || *p == '/' || *p == '.' || *p == '-')) - p++; - assert(*p); - } else { - *p++ = '\0'; - } - - p = skip_ws(p); - - if (*p == '*' || *p == '!') { - statep = p; - p++; *p++ = '\0'; - - p = skip_ws(p); - } - - if (*p == '(') { - code = ++p; - while (*p && *p != ')') - p++; - assert(*p); - *p++ = '\0'; - - p = skip_ws(p); - } - - payee = p; - - p = payee + (std::strlen(payee) - 1); - while (p > payee && std::isspace(*p)) - p--; - - if (std::isspace(*(p + 1))) - *++p = '\0'; - - TRACE_STOP(entry_text, 1); - - // Parse the date - - TRACE_START(entry_date, 1, "Time spent parsing entry dates:"); - - curr->_date = parse_datetime(date); - - if (date_eff) - curr->_date_eff = parse_datetime(date_eff); - - TRACE_STOP(entry_date, 1); - - // Parse the optional cleared flag: * - - TRACE_START(entry_details, 1, "Time spent parsing entry details:"); - - transaction_t::state_t state = transaction_t::UNCLEARED; - if (statep) { - switch (*statep) { - case '*': - state = transaction_t::CLEARED; - break; - case '!': - state = transaction_t::PENDING; - break; - } - } - - // Parse the optional code: (TEXT) - - if (code) - curr->code = code; - - // Parse the payee/description text - - assert(payee); - curr->payee = *payee != '\0' ? payee : "<Unspecified payee>"; - - TRACE_STOP(entry_details, 1); - - // Parse all of the transactions associated with this entry - - TRACE_START(entry_xacts, 1, "Time spent parsing transactions:"); - - unsigned long end_pos; - unsigned long beg_line = linenum; - - while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) { - line[0] = '\0'; - in.getline(line, MAX_LINE); - if (in.eof() || line[0] == '\0') - break; - end_pos = beg_pos + std::strlen(line) + 1; - linenum++; - - char * p = skip_ws(line); - if (! *p || *p == '\r' || *p == '\n') - break; - - if (transaction_t * xact = parse_transaction(p, journal, master, - curr.get())) { - if (state != transaction_t::UNCLEARED && - xact->state == transaction_t::UNCLEARED) - xact->state = state; - - xact->beg_pos = beg_pos; - xact->beg_line = beg_line; - xact->end_pos = end_pos; - xact->end_line = linenum; - beg_pos = end_pos; - - curr->add_transaction(xact); - } - - if (in.eof()) - break; - } - - if (curr->data) { - checked_delete(curr->data); - curr->data = NULL; - } - - TRACE_STOP(entry_xacts, 1); - - return curr.release(); -} - -static inline void parse_symbol(char *& p, string& symbol) -{ - if (*p == '"') { - char * q = std::strchr(p + 1, '"'); - if (! q) - throw_(parse_error, "Quoted commodity symbol lacks closing quote"); - symbol = string(p + 1, 0, q - p - 1); - p = q + 2; - } else { - char * q = next_element(p); - symbol = p; - if (q) - p = q; - else - p += symbol.length(); - } - if (symbol.empty()) - throw_(parse_error, "Failed to parse commodity"); -} - -bool textual_parser_t::test(std::istream& in) const -{ - char buf[5]; - - in.read(buf, 5); - if (std::strncmp(buf, "<?xml", 5) == 0) { -#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE) - throw_(parse_error, "Ledger file contains XML data, but format was not recognized"); -#else - throw_(parse_error, "Ledger file contains XML data, but no XML support present"); -#endif - } - - in.clear(); - in.seekg(0, std::ios::beg); - assert(in.good()); - return true; -} - -static void clock_out_from_timelog(const moment_t& when, - account_t * account, - const char * desc, - journal_t * journal) -{ - time_entry_t event; - - if (time_entries.size() == 1) { - event = time_entries.back(); - time_entries.clear(); - } - else if (time_entries.empty()) { - throw_(parse_error, "Timelog check-out event without a check-in"); - } - else if (! account) { - throw_(parse_error, - "When multiple check-ins are active, checking out requires an account"); - } - else { - bool found = false; - - for (std::list<time_entry_t>::iterator i = time_entries.begin(); - i != time_entries.end(); - i++) - if (account == (*i).account) { - event = *i; - found = true; - time_entries.erase(i); - break; - } - - if (! found) - throw_(parse_error, - "Timelog check-out event does not match any current check-ins"); - } - - if (desc && event.desc.empty()) { - event.desc = desc; - desc = NULL; - } - - std::auto_ptr<entry_t> curr(new entry_t); - curr->_date = when; - curr->code = desc ? desc : ""; - curr->payee = event.desc; - - if (curr->_date < event.checkin) - throw_(parse_error, - "Timelog check-out date less than corresponding check-in"); - - char buf[32]; - std::sprintf(buf, "%lds", (long)(curr->_date - event.checkin).total_seconds()); - amount_t amt; - amt.parse(buf); - - transaction_t * xact - = new transaction_t(event.account, amt, TRANSACTION_VIRTUAL); - xact->state = transaction_t::CLEARED; - curr->add_transaction(xact); - - if (! journal->add_entry(curr.get())) - throw_(parse_error, "Failed to record 'out' timelog entry"); - else - curr.release(); -} - -unsigned int textual_parser_t::parse(std::istream& in, - journal_t * journal, - account_t * master, - const optional<path>& original) -{ - static bool added_auto_entry_hook = false; - static char line[MAX_LINE + 1]; - unsigned int count = 0; - - TRACE_START(parsing_total, 1, "Total time spent parsing text:"); - - std::list<account_t *> account_stack; - - auto_entry_finalizer_t auto_entry_finalizer(journal); - - if (! master && journal) - master = journal->master; - - account_stack.push_front(master); - - pathname = (journal ? journal->sources.back() : - (assert(original), *original)); - src_idx = journal ? journal->sources.size() - 1 : 0; - linenum = 1; - - INFO("Parsing file '" << pathname.string() << "'"); - - unsigned long beg_pos = in.tellg(); - unsigned long end_pos; - unsigned long beg_line = linenum; - - while (in.good() && ! in.eof()) { - in.getline(line, MAX_LINE); - if (in.eof()) - break; - end_pos = beg_pos + std::strlen(line) + 1; - linenum++; - - PUSH_CONTEXT(); - - switch (line[0]) { - case '\0': - case '\r': - break; - - case ' ': - case '\t': { - char * p = skip_ws(line); - if (*p && *p != '\r') - throw_(parse_error, "Line begins with whitespace"); - break; - } - -#ifdef TIMELOG_SUPPORT - case 'i': - case 'I': { - string date(line, 2, 19); - - char * p = skip_ws(line + 22); - char * n = next_element(p, true); - - time_entry_t event; - event.desc = n ? n : ""; - event.checkin = parse_datetime(date); - event.account = account_stack.front()->find_account(p); - - if (! time_entries.empty()) - for (std::list<time_entry_t>::iterator i = time_entries.begin(); - i != time_entries.end(); - i++) - if (event.account == (*i).account) - throw_(parse_error, "Cannot double check-in to the same account"); - - time_entries.push_back(event); - break; - } - - case 'o': - case 'O': - if (time_entries.empty()) { - throw_(parse_error, "Timelog check-out event without a check-in"); - } else { - string date(line, 2, 19); - - char * p = skip_ws(line + 22); - char * n = next_element(p, true); - - clock_out_from_timelog - (parse_datetime(date), - p ? account_stack.front()->find_account(p) : NULL, n, journal); - count++; - } - break; -#endif // TIMELOG_SUPPORT - - case 'D': { // a default commodity for "entry" - amount_t amt(skip_ws(line + 1)); - amount_t::current_pool->default_commodity = &amt.commodity(); - break; - } - - case 'A': // a default account for unbalanced xacts - journal->basket = - account_stack.front()->find_account(skip_ws(line + 1)); - break; - - case 'C': // a set of conversions - if (char * p = std::strchr(line + 1, '=')) { - *p++ = '\0'; - amount_t::parse_conversion(line + 1, p); - } - break; - - case 'P': { // a pricing entry - char * date_field_ptr = skip_ws(line + 1); - char * time_field_ptr = next_element(date_field_ptr); - if (! time_field_ptr) break; - string date_field = date_field_ptr; - - char * symbol_and_price; - moment_t datetime; - - if (std::isdigit(time_field_ptr[0])) { - symbol_and_price = next_element(time_field_ptr); - if (! symbol_and_price) break; - datetime = parse_datetime(date_field + " " + time_field_ptr); - } else { - symbol_and_price = time_field_ptr; - datetime = parse_datetime(date_field); - } - - string symbol; - parse_symbol(symbol_and_price, symbol); - amount_t price(symbol_and_price); - - if (commodity_t * commodity = - amount_t::current_pool->find_or_create(symbol)) - commodity->add_price(datetime, price); - break; - } - - case 'N': { // don't download prices - char * p = skip_ws(line + 1); - string symbol; - parse_symbol(p, symbol); - - if (commodity_t * commodity = - amount_t::current_pool->find_or_create(symbol)) - commodity->add_flags(COMMODITY_STYLE_NOMARKET); - break; - } - - case 'Y': // set current year -#if 0 - // jww (2007-04-18): Need to set this up again - date_t::current_year = lexical_cast<int>(skip_ws(line + 1)); -#endif - break; - -#ifdef TIMELOG_SUPPORT - case 'h': - case 'b': -#endif - case ';': // comment - break; - - case '-': // option setting - throw_(parse_error, "Option settings are not allowed in journal files"); - - case '=': { // automated entry - if (! added_auto_entry_hook) { - journal->add_entry_finalizer(&auto_entry_finalizer); - added_auto_entry_hook = true; - } - - std::auto_ptr<auto_entry_t> ae(new auto_entry_t(skip_ws(line + 1))); - if (parse_transactions(in, journal, account_stack.front(), *ae, - "automated", end_pos)) { - ae->src_idx = src_idx; - ae->beg_pos = beg_pos; - ae->beg_line = beg_line; - ae->end_pos = end_pos; - ae->end_line = linenum; - journal->auto_entries.push_back(ae.release()); - } - break; - } - - case '~': { // period entry - std::auto_ptr<period_entry_t> pe(new period_entry_t(skip_ws(line + 1))); - if (! pe->period) - throw_(parse_error, string("Parsing time period '") + skip_ws(line + 1) + "'"); - - if (parse_transactions(in, journal, account_stack.front(), *pe, - "period", end_pos)) { - if (pe->finalize()) { - extend_entry_base(journal, *pe, true); - pe->src_idx = src_idx; - pe->beg_pos = beg_pos; - pe->beg_line = beg_line; - pe->end_pos = end_pos; - pe->end_line = linenum; - journal->period_entries.push_back(pe.release()); - } else { - throw_(parse_error, "Period entry failed to balance"); - } - } - break; - } - - case '@': - case '!': { // directive - char * p = next_element(line); - string word(line + 1); - if (word == "include") { - scoped_variable<path> save_path(pathname); - scoped_variable<unsigned int> save_src_idx(src_idx); - scoped_variable<unsigned long> save_beg_pos(beg_pos); - scoped_variable<unsigned long> save_end_pos(end_pos); - scoped_variable<unsigned int> save_linenum(linenum); - - if (*p != '~' && *p != '/') - pathname = (pathname.branch_path() / path(p)).normalize(); - else - pathname = resolve_path(p); - - DEBUG("ledger.textual.include", "Line " << linenum << ": " << - "Including path '" << pathname.string() << "'"); - - scoped_execute<void> - pop_include_stack(boost::bind(&include_stack_t::pop_back, - boost::ref(include_stack))); - include_stack.push_back - (std::pair<path, int>(journal->sources.back(), linenum - 1)); - - count += journal->session->read_journal(pathname, journal, - account_stack.front()); - } - else if (word == "account") { - if (account_t * acct = account_stack.front()->find_account(p)) - account_stack.push_front(acct); - else - ; // jww (2007-04-30): throw an error here - } - else if (word == "end") { - account_stack.pop_front(); - } - else if (word == "alias") { - char * b = p; - if (char * e = std::strchr(b, '=')) { - char * z = e - 1; - while (std::isspace(*z)) - *z-- = '\0'; - *e++ = '\0'; - e = skip_ws(e); - - // Once we have an alias name (b) and the target account - // name (e), add a reference to the account in the - // `account_aliases' map, which is used by the transaction - // parser to resolve alias references. - if (account_t * acct = account_stack.front()->find_account(e)) { - std::pair<accounts_map::iterator, bool> result - = account_aliases.insert(accounts_map::value_type(b, acct)); - assert(result.second); - } else { - ; // jww (2007-04-30): throw an error here - } - } - } - 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; - } - - default: { - TRACE_START(entries, 1, "Time spent handling entries:"); - - std::auto_ptr<entry_t> entry - (parse_entry(in, line, journal, account_stack.front(), - *this, end_pos)); - if (entry.get()) { - entry->src_idx = src_idx; - entry->beg_pos = beg_pos; - entry->beg_line = beg_line; - entry->end_pos = end_pos; - entry->end_line = linenum; - - if (journal->add_entry(entry.get())) { - entry.release(); - count++; - } else { - throw_(parse_error, "Entry does not balance"); - } - } else { - throw_(parse_error, "Failed to parse entry"); - } - - TRACE_STOP(entries, 1); - break; - } - } - - POP_CONTEXT(file_context(pathname, beg_line, linenum, - beg_pos, end_pos)); - - beg_pos = end_pos; - beg_line = linenum; - } - - if (! time_entries.empty()) { - for (std::list<time_entry_t>::iterator i = time_entries.begin(); - i != time_entries.end(); - i++) - clock_out_from_timelog(now, (*i).account, NULL, journal); - time_entries.clear(); - } - - if (added_auto_entry_hook) - journal->remove_entry_finalizer(&auto_entry_finalizer); - - TRACE_STOP(parsing_total, 1); - - return count; -} - +} // namespace xml } // namespace ledger diff --git a/src/compile.h b/src/compile.h index 25e12183..3c0876e3 100644 --- a/src/compile.h +++ b/src/compile.h @@ -29,323 +29,16 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _NODE_H -#define _NODE_H +#ifndef _COMPILE_H +#define _COMPILE_H +#include "node.h" #include "journal.h" -#include "value.h" -//#include "parser.h" namespace ledger { - -class transaction_t; -class entry_t; -class account_t; -class journal_t; - namespace xml { -#define XML_NODE_IS_PARENT 0x1 - -#define ACCOUNT_ATTR "account" -#define ACCOUNT_PATH_NODE "account-path" -#define AMOUNT_EXPR_NODE "amount-expr" -#define ARG_ATTR "arg" -#define AUTO_ENTRY_NODE "auto-entry" -#define BALANCE_ATTR "balance" -#define CHECKIN_NODE "checkin" -#define CLEARED_ATTR "cleared" -#define CODE_ATTR "code" -#define COMMODITY_CONVERSION_NODE "commodity-conversion" -#define COMMODITY_NOMARKET_NODE "commodity-nomarket" -#define COMMODITY_TEMPLATE_NODE "commodity-template" -#define CURRENT_YEAR_NODE "current-year" -#define DATE_ATTR "date" -#define DATE_EFF_ATTR "effective" -#define DEFAULT_ACCOUNT_NODE "default-account" -#define DIRECTIVE_NODE "directive" -#define ENTRY_NODE "entry" -#define FROM_ATTR "from" -#define JOURNAL_NODE "journal" -#define NAME_ATTR "name" -#define NOTE_NODE "note" -#define PAYEE_NODE "payee" -#define PENDING_ATTR "pending" -#define PERIOD_ENTRY_NODE "period-entry" -#define PERIOD_NODE "period" -#define PRICE_ATTR "price" -#define PRICE_HISTORY_NODE "price-history" -#define RULE_NODE "rule" -#define SYMBOL_ATTR "symbol" -#define TEMPLATE_ATTR "template" -#define TIME_ATTR "time" -#define TO_ATTR "to" -#define TRANSACTION_NODE "transaction" -#define VIRTUAL_ATTR "virtual" -#define YEAR_ATTR "year" - -DECLARE_EXCEPTION(conversion_error); - -class parent_node_t; -class document_t; - -class node_t : public supports_flags<> -{ -public: - typedef uint_fast16_t nameid_t; - - nameid_t name_id; -#ifdef THREADSAFE - document_t * document; -#else - static document_t * document; -#endif - parent_node_t * parent; - node_t * next; - node_t * prev; - - typedef std::map<string, string> attrs_map; - - attrs_map * attrs; - - node_t(document_t * _document, parent_node_t * _parent = NULL, - flags_t _flags = 0); - - virtual ~node_t() { - TRACE_DTOR(node_t); - if (parent) extract(); - if (attrs) checked_delete(attrs); - } - - parent_node_t * as_parent_node() { - if (! has_flags(XML_NODE_IS_PARENT)) - throw_(std::logic_error, "Request to cast leaf node to a parent node"); - return polymorphic_downcast<parent_node_t *>(this); - } - const parent_node_t * as_parent_node() const { - if (! has_flags(XML_NODE_IS_PARENT)) - throw_(std::logic_error, "Request to cast leaf node to a parent node"); - return polymorphic_downcast<const parent_node_t *>(this); - } - - void extract(); // extract this node from its parent's child list - - virtual const char * text() const { - assert(false); - return NULL; - } - - const char * name() const; - int set_name(const char * _name); - int set_name(int _name_id) { - name_id = _name_id; - return name_id; - } - - void set_attr(const char * n, const char * v) { - if (! attrs) - attrs = new attrs_map; - std::pair<attrs_map::iterator, bool> result = - attrs->insert(attrs_map::value_type(n, v)); - assert(result.second); - } - const char * get_attr(const char * n) { - if (attrs) { - attrs_map::iterator i = attrs->find(n); - if (i != attrs->end()) - return (*i).second.c_str(); - } - return NULL; - } - - node_t * lookup_child(const char * _name) const; - node_t * lookup_child(const string& _name) const; - virtual node_t * lookup_child(int /* _name_id */) const { - return NULL; - } - - virtual value_t to_value() const { - throw_(conversion_error, "Cannot convert node to a value"); - return value_t(); - } - - virtual void print(std::ostream& out, int depth = 0) const = 0; - -private: - node_t(const node_t&); - node_t& operator=(const node_t&); -}; - -class parent_node_t : public node_t -{ -public: - mutable node_t * _children; - mutable node_t * _last_child; - - parent_node_t(document_t * _document, parent_node_t * _parent = NULL) - : node_t(_document, _parent, XML_NODE_IS_PARENT), - _children(NULL), _last_child(NULL) - { - TRACE_CTOR(parent_node_t, "document_t *, parent_node_t *"); - } - virtual ~parent_node_t() { - TRACE_DTOR(parent_node_t); - if (_children) clear(); - } - - virtual void clear(); // clear out all child nodes - virtual node_t * children() const { - return _children; - } - virtual node_t * last_child() { - if (! _children) - children(); - return _last_child; - } - virtual void add_child(node_t * node); - - void print(std::ostream& out, int depth = 0) const; - -private: - parent_node_t(const parent_node_t&); - parent_node_t& operator=(const parent_node_t&); -}; - -class terminal_node_t : public node_t -{ - string data; - -public: - terminal_node_t(document_t * _document, parent_node_t * _parent = NULL) - : node_t(_document, _parent) - { - TRACE_CTOR(terminal_node_t, "document_t *, parent_node_t *"); - } - virtual ~terminal_node_t() { - TRACE_DTOR(terminal_node_t); - } - - virtual const char * text() const { - return data.c_str(); - } - virtual void set_text(const char * _data) { - data = _data; - } - virtual void set_text(const string& _data) { - data = _data; - } - - virtual value_t to_value() const { - return text(); - } - - void print(std::ostream& out, int depth = 0) const; - -private: - terminal_node_t(const node_t&); - terminal_node_t& operator=(const node_t&); -}; - -class document_t -{ - static const char * ledger_builtins[]; - static const std::size_t ledger_builtins_size; - -public: - enum ledger_builtins_t { - ACCOUNT = 10, - ACCOUNT_PATH, - AMOUNT, - CODE, - COMMODITY, - ENTRIES, - ENTRY, - JOURNAL, - NAME, - NOTE, - PAYEE, - TRANSACTION - }; - -private: - typedef std::vector<string> names_array; - - names_array names; - - typedef std::map<string, int> names_map; - - names_map names_index; - -public: - node_t * top; - -private: - terminal_node_t stub; - -public: - // Ids 0-9 are reserved. 10-999 are for "builtin" names. 1000+ are - // for dynamically registered names. - enum special_names_t { - CURRENT, PARENT, ROOT, ALL - }; - - document_t(node_t * _top = NULL) - : top(_top ? _top : &stub), stub(this) { - TRACE_CTOR(xml::document_t, "node_t *, const char **, const int"); - } - ~document_t(); - - void set_top(node_t * _top); - - int register_name(const string& name); - int lookup_name_id(const string& name) const; - static int lookup_builtin_id(const string& name); - const char * lookup_name(int id) const; - - void print(std::ostream& out) const; - -#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE) - class parser_t - { - public: - document_t * document; - XML_Parser parser; - string have_error; - const char * pending; - node_t::attrs_map * pending_attrs; - bool handled_data; - - std::list<parent_node_t *> node_stack; - - parser_t() : document(NULL), pending(NULL), pending_attrs(NULL), - handled_data(false) {} - virtual ~parser_t() {} - - virtual bool test(std::istream& in) const; - virtual document_t * parse(std::istream& in); - }; -#endif -}; - #if 0 -#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE) - -class xml_parser_t : public parser_t -{ - public: - virtual bool test(std::istream& in) const; - - virtual unsigned int parse(std::istream& in, - journal_t * journal, - account_t * master = NULL, - const optional<path>& original = none); -}; - -DECLARE_EXCEPTION(parse_error); - -#endif -#endif - class commodity_node_t : public parent_node_t { public: @@ -387,33 +80,29 @@ public: return *amount; } }; +#endif class transaction_node_t : public parent_node_t { - mutable terminal_node_t * payee_virtual_node; - public: - transaction_t * transaction; + shared_ptr<transaction_t> transaction; - transaction_node_t(document_t * _document, - transaction_t * _transaction, - parent_node_t * _parent = NULL) - : parent_node_t(_document, _parent), payee_virtual_node(NULL), + transaction_node_t(nameid_t _name_id, + document_t& _document, + const optional<parent_node_t&>& _parent = none, + transaction_t * _transaction = NULL) + : parent_node_t(_name_id, _document, _parent), transaction(_transaction) { - TRACE_CTOR(transaction_node_t, "document_t *, transaction_t *, parent_node_t *"); - set_name(document_t::TRANSACTION); + TRACE_CTOR(transaction_node_t, + "document_t&, parent_node_t, transaction_t *"); + assert(_name_id == TRANSACTION_NODE); } virtual ~transaction_node_t() { TRACE_DTOR(transaction_node_t); - if (payee_virtual_node) - checked_delete(payee_virtual_node); } - - virtual node_t * children() const; - virtual node_t * lookup_child(int _name_id) const; - virtual value_t to_value() const; }; +#if 0 class entry_node_t : public parent_node_t { entry_t * entry; @@ -503,8 +192,9 @@ inline journal_t::node_type * wrap_node(document_t * doc, journal_t * journal, void * parent_node) { return new journal_node_t(doc, journal, (parent_node_t *)parent_node); } +#endif } // namespace xml } // namespace ledger -#endif // _NODE_H +#endif // _COMPILE_H diff --git a/src/main.cc b/src/main.cc index 0e46a926..2b9b71f0 100644 --- a/src/main.cc +++ b/src/main.cc @@ -36,7 +36,7 @@ //#endif //#include "qif.h" //#include "ofx.h" -#include "builder.h" +#include "jbuilder.h" #include <ledger.h> @@ -192,9 +192,9 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[], INFO_START(journal, "Read journal file"); - xml::document_t xml_document(xml::LEDGER_NODE); - journal_t * journal = session.create_journal(); - xml::document_builder_t builder(xml_document); + xml::document_t xml_document(xml::LEDGER_NODE); + journal_t * journal = session.create_journal(); + xml::journal_builder_t builder(xml_document, journal); if (! session.read_data(builder, journal, report.account)) throw_(parse_error, "Failed to locate any journal entries; " |